From 6e33f039b2c0e7c6f070c4199a83d833aae6ed29 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 26 Oct 2016 19:43:13 +0300 Subject: [PATCH 001/100] Dropdown replaced by Ui::DropdownMenu. ScrolledWidget removed. Ui::DropdownMenu is like Ui::PopupMenu, both based on Ui::Menu. --- Telegram/Resources/art/sprite.png | Bin 27678 -> 22300 bytes Telegram/Resources/art/sprite_200x.png | Bin 60320 -> 48811 bytes Telegram/Resources/basic.style | 211 +----- Telegram/Resources/basic_types.style | 53 -- Telegram/Resources/icons/media_type_file.png | Bin 0 -> 417 bytes .../Resources/icons/media_type_file@2x.png | Bin 0 -> 829 bytes Telegram/Resources/icons/media_type_link.png | Bin 0 -> 450 bytes .../Resources/icons/media_type_link@2x.png | Bin 0 -> 884 bytes Telegram/Resources/icons/media_type_photo.png | Bin 0 -> 451 bytes .../Resources/icons/media_type_photo@2x.png | Bin 0 -> 918 bytes Telegram/Resources/icons/media_type_song.png | Bin 0 -> 577 bytes .../Resources/icons/media_type_song@2x.png | Bin 0 -> 1050 bytes Telegram/Resources/icons/media_type_video.png | Bin 0 -> 302 bytes .../Resources/icons/media_type_video@2x.png | Bin 0 -> 605 bytes Telegram/Resources/icons/media_type_voice.png | Bin 0 -> 421 bytes .../Resources/icons/media_type_voice@2x.png | Bin 0 -> 779 bytes .../icons/send_control_bot_command.png | Bin 0 -> 400 bytes .../icons/send_control_bot_command@2x.png | Bin 0 -> 904 bytes .../icons/send_control_bot_keyboard.png | Bin 0 -> 308 bytes .../icons/send_control_bot_keyboard@2x.png | Bin 0 -> 643 bytes .../icons/send_control_bot_keyboard_hide.png | Bin 0 -> 160 bytes .../send_control_bot_keyboard_hide@2x.png | Bin 0 -> 263 bytes .../Resources/icons/send_control_emoji.png | Bin 0 -> 227 bytes .../Resources/icons/send_control_emoji@2x.png | Bin 0 -> 360 bytes .../Resources/icons/send_control_record.png | Bin 0 -> 408 bytes .../icons/send_control_record@2x.png | Bin 0 -> 680 bytes Telegram/SourceFiles/application.cpp | 4 +- Telegram/SourceFiles/boxes/abstractbox.cpp | 2 +- Telegram/SourceFiles/boxes/abstractbox.h | 2 +- Telegram/SourceFiles/boxes/backgroundbox.cpp | 2 +- Telegram/SourceFiles/boxes/backgroundbox.h | 2 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 8 +- Telegram/SourceFiles/boxes/contactsbox.h | 2 +- Telegram/SourceFiles/boxes/members_box.cpp | 2 +- Telegram/SourceFiles/boxes/members_box.h | 2 +- Telegram/SourceFiles/boxes/sessionsbox.cpp | 2 +- Telegram/SourceFiles/boxes/sessionsbox.h | 2 +- Telegram/SourceFiles/boxes/sharebox.cpp | 2 +- Telegram/SourceFiles/boxes/sharebox.h | 2 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 4 +- Telegram/SourceFiles/boxes/stickers_box.h | 2 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 2 +- Telegram/SourceFiles/boxes/stickersetbox.h | 2 +- .../SourceFiles/core/click_handler_types.cpp | 4 +- Telegram/SourceFiles/dialogswidget.cpp | 4 +- Telegram/SourceFiles/dialogswidget.h | 4 +- Telegram/SourceFiles/dropdown.cpp | 456 ------------ .../history/field_autocomplete.cpp | 39 +- .../SourceFiles/history/field_autocomplete.h | 12 +- Telegram/SourceFiles/history/history.style | 86 ++- .../SourceFiles/history/history_drag_area.cpp | 175 +++++ .../history_drag_area.h} | 76 +- Telegram/SourceFiles/historywidget.cpp | 369 +++++----- Telegram/SourceFiles/historywidget.h | 29 +- Telegram/SourceFiles/mainwidget.cpp | 31 +- Telegram/SourceFiles/mainwidget.h | 4 +- Telegram/SourceFiles/mainwindow.cpp | 4 +- .../media/player/media_player_list.h | 2 +- .../media/player/media_player_panel.cpp | 15 +- .../media/player/media_player_panel.h | 2 +- .../player/media_player_volume_controller.h | 6 +- .../SourceFiles/media/view/mediaview.style | 47 ++ Telegram/SourceFiles/mediaview.cpp | 92 ++- Telegram/SourceFiles/mediaview.h | 27 +- Telegram/SourceFiles/overviewwidget.cpp | 14 +- Telegram/SourceFiles/overviewwidget.h | 7 +- .../platform/win/main_window_win.cpp | 2 +- .../platform/win/main_window_win.h | 4 +- .../profile/profile_block_widget.cpp | 2 +- .../profile/profile_block_widget.h | 2 +- .../settings/settings_block_widget.cpp | 2 +- .../settings/settings_block_widget.h | 2 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 148 ++-- Telegram/SourceFiles/stickers/emoji_pan.h | 30 +- .../ui/buttons/history_down_button.cpp | 68 +- .../ui/buttons/history_down_button.h | 27 + Telegram/SourceFiles/ui/countryinput.cpp | 6 +- Telegram/SourceFiles/ui/countryinput.h | 8 +- Telegram/SourceFiles/ui/flatbutton.cpp | 79 +- Telegram/SourceFiles/ui/flatbutton.h | 39 - Telegram/SourceFiles/ui/flatinput.cpp | 15 +- Telegram/SourceFiles/ui/flatlabel.cpp | 4 +- Telegram/SourceFiles/ui/flatlabel.h | 4 +- Telegram/SourceFiles/ui/flattextarea.cpp | 4 +- Telegram/SourceFiles/ui/popupmenu.cpp | 678 ------------------ Telegram/SourceFiles/ui/popupmenu.h | 158 ---- Telegram/SourceFiles/ui/scrollarea.h | 16 - Telegram/SourceFiles/ui/twidget.h | 8 + .../SourceFiles/ui/widgets/dropdown_menu.cpp | 270 +++++++ .../SourceFiles/ui/widgets/dropdown_menu.h | 110 +++ .../ui/{ => widgets}/inner_dropdown.cpp | 135 ++-- .../ui/{ => widgets}/inner_dropdown.h | 52 +- Telegram/SourceFiles/ui/widgets/menu.cpp | 343 +++++++++ Telegram/SourceFiles/ui/widgets/menu.h | 134 ++++ .../SourceFiles/ui/widgets/multi_select.cpp | 2 +- .../SourceFiles/ui/widgets/multi_select.h | 2 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 340 +++++++++ Telegram/SourceFiles/ui/widgets/popup_menu.h | 112 +++ Telegram/SourceFiles/ui/widgets/tooltip.cpp | 185 +++++ Telegram/SourceFiles/ui/widgets/tooltip.h | 69 ++ Telegram/SourceFiles/ui/widgets/widgets.style | 103 +++ Telegram/SourceFiles/window/top_bar_widget.h | 1 - Telegram/gyp/Telegram.gyp | 18 +- Telegram/gyp/codegen_rules.gypi | 1 + 104 files changed, 2688 insertions(+), 2308 deletions(-) create mode 100644 Telegram/Resources/icons/media_type_file.png create mode 100644 Telegram/Resources/icons/media_type_file@2x.png create mode 100644 Telegram/Resources/icons/media_type_link.png create mode 100644 Telegram/Resources/icons/media_type_link@2x.png create mode 100644 Telegram/Resources/icons/media_type_photo.png create mode 100644 Telegram/Resources/icons/media_type_photo@2x.png create mode 100644 Telegram/Resources/icons/media_type_song.png create mode 100644 Telegram/Resources/icons/media_type_song@2x.png create mode 100644 Telegram/Resources/icons/media_type_video.png create mode 100644 Telegram/Resources/icons/media_type_video@2x.png create mode 100644 Telegram/Resources/icons/media_type_voice.png create mode 100644 Telegram/Resources/icons/media_type_voice@2x.png create mode 100644 Telegram/Resources/icons/send_control_bot_command.png create mode 100644 Telegram/Resources/icons/send_control_bot_command@2x.png create mode 100644 Telegram/Resources/icons/send_control_bot_keyboard.png create mode 100644 Telegram/Resources/icons/send_control_bot_keyboard@2x.png create mode 100644 Telegram/Resources/icons/send_control_bot_keyboard_hide.png create mode 100644 Telegram/Resources/icons/send_control_bot_keyboard_hide@2x.png create mode 100644 Telegram/Resources/icons/send_control_emoji.png create mode 100644 Telegram/Resources/icons/send_control_emoji@2x.png create mode 100644 Telegram/Resources/icons/send_control_record.png create mode 100644 Telegram/Resources/icons/send_control_record@2x.png delete mode 100644 Telegram/SourceFiles/dropdown.cpp create mode 100644 Telegram/SourceFiles/history/history_drag_area.cpp rename Telegram/SourceFiles/{dropdown.h => history/history_drag_area.h} (59%) delete mode 100644 Telegram/SourceFiles/ui/popupmenu.cpp delete mode 100644 Telegram/SourceFiles/ui/popupmenu.h create mode 100644 Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp create mode 100644 Telegram/SourceFiles/ui/widgets/dropdown_menu.h rename Telegram/SourceFiles/ui/{ => widgets}/inner_dropdown.cpp (60%) rename Telegram/SourceFiles/ui/{ => widgets}/inner_dropdown.h (73%) create mode 100644 Telegram/SourceFiles/ui/widgets/menu.cpp create mode 100644 Telegram/SourceFiles/ui/widgets/menu.h create mode 100644 Telegram/SourceFiles/ui/widgets/popup_menu.cpp create mode 100644 Telegram/SourceFiles/ui/widgets/popup_menu.h create mode 100644 Telegram/SourceFiles/ui/widgets/tooltip.cpp create mode 100644 Telegram/SourceFiles/ui/widgets/tooltip.h diff --git a/Telegram/Resources/art/sprite.png b/Telegram/Resources/art/sprite.png index 04c9fa5a8917828653cf0b7c9bb64bc0fb44611a..e8c0edafc83ff70bc7fb89b08fae1f6d319de58f 100644 GIT binary patch literal 22300 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 L9;=inSv~(>0Afz$ diff --git a/Telegram/Resources/art/sprite_200x.png b/Telegram/Resources/art/sprite_200x.png index 91644529fe17cbe5bd1e37f0bc808f0848f61d3d..be3a0b00c91906868a53733b5ea9d56947e77d71 100644 GIT binary patch literal 48811 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 VO!Xy8_;F%J5sK;xdGZ$i{|kilPSgMZ literal 60320 zcmd43cRbbo|3Ch+y0TIk2NmJi96OPfBFD}Q$0&Qt-ZW4~I96mk_KG<6i0qJqRL4kG z2wB;DpQr2nzWqMG?{EG4JGZO4UdQXXALIVGKORpnwKeZiQ!rCN5JZhsxvK*~B#hvX z|M_!-Z*Ff$K+tsvdH1%SZ~CvPfb=W7K^td#b~R1YyUrqxJ&tqx6l~|NJf)?*d|vZt z?vZ7*4<#JM{44QBN&z_%j|tj&{OH!w#J1z8Ik9-Yl!m6|ttU6D5=IoZyO8xu;j9~Jt9t`k1R!~FoY;vq<0*-OrR zW#020-!0;E7lpe>qm%dzUp=UG81K_BH85gHo@Z`zjl9$Qf<7g)4OCm8Hw~q;b`D%f<5Hqj0)}Zw8AVwJ0rX?F)?lA z5uF?@W$&xYtmXIL)2t8HaXz1s?BcM1PT4KS%50EX31f)9orYvZBeXR9(=zja=6`l zh`4*L(DsU?%W*RNPy_Q);_f{;u zd#i!g$WtQH=!cB{zp8pFs3SVh%x_+8?ceaPt`cxeur@Mp+w8t#9=CsYn6d^eG?y%I zWnAsBF&%NY)~XicI*{sDVn^6@mXPDkRrOI6D@7Rb2#q*%%pdRxc3Piocyj>eOMLFT z5V5lUEpyxny5P|Cg_c>ujs)^vr0hjgOlYVGl_gTUBMctaIeQ4!>w0MV4+I&hC)c2A zuOQ${j^m z3lXxWB47$E!c>-}&iE#HpKvsdN}5gD;&~b^+Sd#)NI|1P5l4bfJ4RrhQYGxfVUFM@ zt#Kq5g+rO?W0XT48h!S$9nL&KH7-fIO!}Jg&>dbG6+g?KtR;f9&!t4V#Um5a_ZFkj zW_^2Iq*U46ck@O_lC7t11pN7BZj0XtzvAMnh@)MdsLZlWTjZE^^HoVzC55ub3RDhZ zmk`zV=xvrMe)|A9AuFnL+IIv7z6FDg;vWr*B8a?MgZo0L%l&t@!)CNE@5(X6DAS}A zszQ*e0|pb4{NW$l2I}~2vzY!RC3Rg&(vJyRaz>v*zC94|uCA-wv-DW}=qFg^y!Nfu z@(MF^D=kG>#f30MOU}OPGE?-8Yx2Rzt93KM0p>-%;J{C>U%gNXCh@}BD4?=wy>XME z6r*Frr;pOUwU;J{y*SWmqa=TA2tO&tym*sg{~mI9Zfz8jbro^LWpcl`ySv-4bM|-P zaV8l!AJfE9uu`b7pL z>z}3C%FlXq3M!W z5CkQ*XCeEgEZHe+`t8}6Lqmzi!nMj#4eob;+;(-{XaUxee6FIvqujeP=keBqz&^=ctBBR?wfXH22u!+iC<#N2|+PlC7O%mGk6d@3Ln#ogS{r z-v~K9Ze4Z;`nqrH<>j?~T+9MN6KMK!~1AO-*?b{>*j9-1-FuO5<3P-LU+E zUDFkWC^qL4v>1-FZZJe;{90RUu?9ZEk9udi{_$LS_(K_BQC2CXD*uO)WIk-J!(Q6p z)(-AF`{&*4Sz+ZmdPl!4s`p+{&`D>!%7;WuW21B_P)L_H$No_gG>fK8HW^6~TaLD! z@6D^hfnXNqCN*~Vhu;4FbccreD;Vj_#L%#=x&BV5-pCG3zEUbpkEd{Wkkj z$p`Iou+X)7?2a3TJOr(0HkOwUl(ueC_Az~&8XNO0#RviOIdZB;0Vzz&6^TE4#cv8e zWlc@^4be!E+d~0ojdy{#CEKUzs*fIPnwn1e5R5jWQ+PTNj>i^`7!yI~ixf2TUD7rN zvvdp%VILT%`usM`Q+~3ODGUkd7T6C7Yj~WSC5A3qWbHJC67N!f6VVVWBJn+(4j?9A zo|5-u=7(pXpsUTSlew1Cw>qNgP zV??%g_`{u;rlQi)?RSZfKu_1xfOag}i64p#RNqj_8;Q&qMKH&Bga4x@81&zQb(7Uo z8~&jvs?MUXS0>*%;;J~mC)bpS$EvTmHgA$4 z-yK(wKylZI5b{dT-%+-db=uxO%V~HKN|f!ie}}TUr&O!qAcRZ>%>_OKTNK~J=v4qT0IdigW-V3joQVX(x#GJGWUMbP4D{eD^qa^=-ZjK&1WJZInF9TkY$7 zKiMUogFX<)TPbZ+YQ_kAjevloVb0>byu2byY+W6HaC#Gb>biXnoWJTC^gV6ahsdn> z>FsG7!bINdA}MtnjC=)H5W#%Qn#fpBPtU;q((Rtku2{~KDoj>U{MF4DeFg&G!_W$ApWhcwjK>+=S2 zWc;KcpPzDZsI*!;6nga=9?u%@8QV4~6EY|DMBuQMA0hyGfNPgOni;o0#aa!JkRu;65`3=2!{PWnmG&pGHz&o zVnYAPUD)nZ_9GMFV4s79l(wC3j+Ocy{@3rFXN2Mki50tT6Xj5!1Sz2As^QoKZ%dM)F(wXE^xMiAx`bL0+!hy
z$6tEn{@`I|z7KsLb&eJ5YPB6Kz8U2Vs{iziVfh-^Bszp)Z!aSPehC4!-?|kfvlo#S zu-=fGwf}+pu=|P3>Hgdx0fiXjN?k5k7?Ei_-QB94FgwC8Wd>A8Rn3CklB)Gu{XC#C zdTht`&rUC4TM+;)jLr!*s*ep*Dcb5iJ#cUHW6WdbS zTP4#*&F-Dlk}^j-qgs1mQQ#+;feT57HUL4S9;8>2VlN3p*}kcY%8hQDQKFy2_-q)% z-{}8*V*D6|W6N@U-3kD*rpU!FeO_2W{5P%{Md0i>gRNI#jKX8Tv4!@f^fMeMjfnj1r`f zRQf!W4XMi-@Tr_vZ*gAPZtg4v}|A2Vf-_Pb-QRK|->;c-!0S6%|UVpeaW zS+8=6@edep@_?Y~+M<{B%r)$2#-mbPD%M^?k9*x6_DaSw3I zOY$3(FtDB;7Q({Nh~37Y>^m)IV8T2@Q|?diG!jiMp9`iHg|a<^7kr3#<=#4sm#oTf zrw;GbT7gbEmZm(0Wwm;z#v1YKtw-zJ!GGRm?Z!0J?|wFk^#X=U2RC+k%p|&XFB42O zY=8j~ob&OW+9lATc;@e>P;msRHSoa8XUfaaymx;yHY9M`R{rSgZe&FCya*$5uWl-6 zZ%T_W&`C#EiGJxRtG-!%9Og`Y5|~~EB5AO7559IPDR#$=jEvmwg&@}!gniwN66W!2 zCyjidrMp@;E??~)Tru1}t#}UMJq>0HdNKtA29B5LPpBO#q&f`suplcrSs1YyE?3Dz zud8H`WStF%_0=XTtQ;}Zzy=a$D5iY+$l-m@+69q&HFb6JXv6nBsezx9ejlB^NpZB; zKdFUSnoMv(!La!EL&IPXR#OnB((HQ`8x5-!O|RsSe|JtG8{gjbqF*IK(r~kUdP)TO z@d%?-YOF|!Vc!(=A8;43$Y$C5px$k^iuLq+z6K1rTp_eS}15)ttS{vehSln z0R}>-4b>?X;1ecY;(TV=*>rpBTVGgqaMV14^k-+r&U~CWnfJawqnS>>D%$!xOz7&- z^j@n^0*3rFX(5tx`Jd&?^C=A%AK-Mq*LScJ-7KY#M9 zOakj4`yn>|wdu+wWR~DcHWd221;qDLi#}{p&1dibVQqHan(w<-V81eac07#dtUWYM zq|9(HD7+SG@9+eoRLxe=N4EJ)Hb-EcOY`zLsuZTvKx~|Zp1<9$eSFiW&%R;bDIg#q zKjfQ%*j7es{p9Nc1~=>abjsO>AhxyER7(?Dwrb=CY(yxTgas*e8@D4Zh@hCA(WgTI z_kWxjAa}7BIY$Ce!nji{W-u6bU+KmG2v~B-NX`oz3->>pNvM6vK$Tq*9bcn}vj7oY zy%>5uZpQ_i0sJ?BC$#~FIQc`ksdyl|sR z(m!DESp3(n;}2(N7Qc^{NJ+-jKoG8WjDTC1C3se1%p`Bo3Wq{-#CbCNvdcwdS#_Q> zS%D6Me|D21v+y|gBe9OX9QGJ=jlijp`z^Tr?PGI8fG?r*HJ&hktn*Uy+kqM_3E(h| zuAz6Vk*2}0%aE!2H2=h`B8b1Bk_YT};ZN_bSV9txO_;Vg&(9l*d|!;DHb(vgngd~g zNYn6dzuU+g3x%dt>*IphtuP)2Ljmt<<6vT7hrX+Fngs^zN|s%2!#>*FYpG#;c3&UR z2)4*OCkTvUdh)1XNwymsphN%^R)@KSKdf)vAFwrt^c_nv@=2cUV6=83sSTy~vx;DM zq3=4JrY~$MVYl!1x3z^o{N3mG60kB!#_%9RoL(E{W}hSaR0lWvhz1~Us9uK`wh{cC zvhvIzSsJ=1tQs47zjlQ)=nhU0oU=`AzfnGZTpHE@IJfL>0rS?ta5%y4z1j9Nt)MKi z9!MUH!;n!j%zI4+?vK0QZdc$VRvv%D!EHzNrz!68Ee`Gn^~O1P!ERu)sEvTZi#|L& zoEe{(I73_#Z+`3awYqhV>uLBzNsb9!XuGdEE(0CB;lswc6Ch=tSNQLG9F*BnaM#tf zwUf8_6JMeLlb({J8Fct;ylY4(M`$GV_HsPVNrp` zLYQyI&r|->cT9>{%<3-1&_BB(NG+AaCm7j{e-fp{4jD+;_5{*W;0ra-v%lwV}qiMj&d2!E?g3R8oL~?mKPc9(t~VN#aW}};P#0o4xAs4X)r@fQ?OGc3p)_bnmSt z)5VKr{QAqrUTJfVoq3g1^`{vi8pw#``MkRAn{_E6gSCpTwmW}UmUvFSPN_7Z28C0V zniKAS*owh(3dV`OO$WZv&5D9)*_R%H`PQ6$-GY>rC{KL9K~S|Nohk2BWhm^lQ138K z#2hy~w{)7Hp7cK8nP`X*l>K47VJUO)B}0@`GZl!~wBQj@ALgSAur|Qe)I3Aw^~I z20P&_Nr;m8t#}4JDhliJ!D`2Z>t@*#)Ti>}jHEiqAgj32rbKQ>TY@!F#%M~IIMu7D zP&1hcw*Le1ymVsY(7#Q&4Mu?+K5i`x*s5rL?_Kh0kn_```aL9x8v8br<6dhq_U*{n z#WP-!zW1}KDqrBZYgiNy1$0rNlRL=n9lShvaiQ64;$SBk)#Q|XXzm2Qu&xdIQGHB7 z3Xw9KVjw6pHDqw7EEeo9o8f9$;!Nbs2_dDBd0ib+xL!{VBPR=^0tO4}=bGAoW_)z( z17q|`J0le-9UvhgKvqA06ZU7zX!8~aP$3A?o|=3frMY0n-KpPu9kc=tZ@{@;g-6=z zZ=ND_CD9c;@%{Hn*A6`0{O^+v*Z>}$|31y?*Q@m2`tQ?sFYv@~_4X4Bld^iQMP8MuEr{wkV zDaHc>1YGJ{!ML4)MCbhTFme!vL|_5{NFsc_tJV}fTBCV-CS3V_QBO6<{egtwI1A<;Z==HN5@ z`~(3(q1i@_p7z@>E^#n`80aAUA&P|}ESx*@oq%H$rVtQ$)mSVxB~XAC3cL*@SwX2} z458yVQDN%D*d5fD!%q7zU2=4Mp7}UPND#*EW4k)g{7Q^6 zZ0L=os}XsB|BL@jg~I}5Gjuq~qUre!)ET8cf2tLiGXR1vvg=W)Wz~b+uw1FD7luWt zEN`#0E?i(Dj6i%YjtLlc?RY*;3yfpKTLDKkac0xF;#J*aiM`Z<1|GivEiEk*6BGQ3 z&V<0@coPSZWe@{Wkj<{I*bX8^u%<1)?L|Si?Zcs=p@G$vl}yzdn-7nw5+Z0=GvB(+ zcF=P2HPD32s6~YmSt+*z#t7a*s08ztrFx--f`U;Ot$LU;69DvI&tKjx*W@aj18FE3z&<|+gu8mJL8gn zrMiOjPe1`bhEtyrKP z?xHBl=P7#zIKoH?$Ik*{8r#VqEC;HwSEbyoZs+vea-Z-05zlcCaXw73^G?pE-ynPq zX~kHxBvOn~=ni=~wxm-o9b%kcu6ZHP(l(E%3IDBmZJu2Dadsi{%p|3zT+Y6&F|lVh zaqfA-5J*(zR%qkeY;b~!3pOC96{s8Dqv|1_QGJ7)7RZN6was9f^{m@|=7%}o`qxP^ zE{_N+0iTqJ&RHYC@fQPLw>i-n)dj*0tumu`HeNh*s#krI&oe5>q2*(ScZ!1=wN@Hidv}3KL1V zwNVr89yqPZxh*2!QVm=wIv4>Im{zY5r1tK>O|QyNs3I#XOzJA8x>otBH3`zX*wxvE z`!Gl7_VP=r^RPZ5__a~-Hr&^*sbVsib94}WXW~vQh$l2Qt6uSRdq{5HF@WDNiO z$D2vgO$ml|579OshLcuSS3j3GNvL><_FjRs6MmFBRccN%M0Kru&?3-#@fbj}CfcW& z_j${WiZ5}mGnrb>*Sq zDX!(GiO$vyc3KkP9G8A9Y2Glvtx>3C5DcWoV+8?lu;;FdHx+7erz0$yZho)6|F5?zKCUU%Q zmi_X}Knaa{#)}HyzI{9Q{3*fM+{w|N8LQbWc4k$X>JcAQLLQY3VR={~^Fa8*CZBo9 zPgm>yIU4@!UZNo8(56kU+BPzPpnq!t5T@TIakwo#9y;c%FCe#Yz~$J{S{L}no6C1^@mB#|Nc$e@(s-q#FDy?sGL`CFTqwTA|LImdy_H6yu70AsdXbA30mrxvV zgzi)jV8AEX)-!$@jC3Qqj?BY^@#6oe8k=-8uj?Nwyw$>GC4Toh`2^sWw%BK220`9+ zCd>C~RF|eQd;Gz2hl0aJB<$)IIX~pkT?o&}s$Ppg=USJ|iY$n%Cj3*m8bL4wzuR`O;fucNtn7nNLQ*%);R}wt!!_8|4oqoOoH^ou(h@2L$<66?347MN~KMW zr<+(xOog+w;WH&b)KKWR1dof}K+YM>)3tOK-Qt_U)ELk^2Z_#waM@pX1kvIqxF&Z4 z8m-orD6PsdVVepUdZpQH#_Qefu9lXaq=)Qy0^*I$S%sz!UvIG zu}GZbf>6Hb-;J*?ww#d9bSThrCH#;&5MMA zpC|Zx6M*tOR)Uv1fWEys4})-aaXZ|+_~|6Q(RTwP8iFA1m<-$=i~`8+mi(ow9kah> zIqtnmuk~L0HV!nW7PIJfQ)WOonyk+f0m=@5(#0RN(qG6%Pa$kiqO9cO=yr3Gt&WoX zKHkOZcTonJ%KuddWkbV>n- zu|!{s*W*BB`GGRoGIp2UEmZ>YNgLIbSuL>oWS%J>n937$IG1d28^$oOg!7?l|J(`M zpDy%YBR}@}^PfaaVIYD;!P?gdr-Y1kO*n(C*X7HcW~A>*@U?0J7z{z#pYw+fcQlZ! zyLlIp#&s@J_c#Srzcw2a6(=wNnYsQIyXHd_BhVc3wFW2!Jvwe{omSY!x%A^y0~vogNt=6_ zWRtlS008{bP(~9UkJn{kNKcSBybj1iiT-hiDY{i~xiUzAGb4nM?wrW~KGw%b1%dc_ zvkraQwZV>-ffp9&(VKEx0{ylxe^A9<`!c&aO&DqqNCu>tSm&g?-FLdOg=$mW%uvRy zJs4AFIVN$gw>9lQHE71!i3Ui8dm6o{U(3eQ(j&koH)j9;!Kb|5QG%f*6NQIXk2+a zwUHd{34-?mjW-3KKO6iT6BhFcTFq0yE(`vhzEWvo+$`7MYzR2I9|zY&C&T~7-GCZ= zYR<-y<;F$`Y(S=!$EUYf5~TuaXrLe`{GTRL2y$0NeM#}Bz$=1UB&OrrWZLCaoxP4{ z4ktnNkGd$#+uq*VXclX8eXcfsctRWsHwqb{XND$u7QYb05rT+|^rrG7sS&`G0B@h& ze1XZVCi36N6x!yq@*Q7@6agp5U@07#Anl!}XUR*^wMVHy-Mb1i6(M>*uzB_01?y*j zN~Ws(C+Z)Uv~Pj(@#-ok82-oUmBG8EF`ytqEJeTwm*jlrAih=@AS1u{`BqOrP;XoE zj&tm*U4ZK6N2hjm9&#ZD+(Cb*!6-ae?o2LTeY>?(aGM-j58kF`lq&1 z!p!@t^)+-j;gvr>x}uugukrs>y~L+#m(xrMLj!1fLCtt^Rs0JlX69M+(dsA-kh&AO!TswsJt@t7#ULtLQ0im%9e2WIHXK1)5*@q`* zqMxHh-%w@N%boL4Yc#`Es(A+LohMd)4AcR15Hey-Ah|9fFc8fa*7??|plBC3c zH}uTHD0$n7I)$U7>$;*~wp6et@3piRt3GrEc=Q3Gfa49eyj)P7b{qFwG`-YFlfUn| zXgVcOKgkBg@RV6a3b*!NxXFMLfGOBZqN*ikICB+$Gn~DYMqq;rp0lMgS^}&3ZJ`?L zfez+gmP1lomt6wFYL}`Q4ydV;>d<(tdX*Ou(I(_fnauplWXLlC`vQ!EFU=MBV4P~SwvO_PAQhi*BU7SzrEc?_%OW?tlt!PW;lbMR~0diQ%fvL*N7J+3Bv_BxX zlj$1~nq0S!f*v?fZJmQ=lICHik49Q_o>La98VkFJQP$0duIps(r8yS!W7F4Nl3xyzTO)*r=#9rnodrc2a1wg0kYK&R=()^0u_lrx%Lb!=&f^rg83TC+^3@0<(V{B=c?Rl@vrO?1^UX|L(Bwo zw?(31aY)E~)CLUFD^S2%{FPY@G9h!fdVyUZn;eZy;F47XWEunsYkw#1&Q*#ESAQ)$ zTL*lS|CYB9$j=qTfojJBEy9=2sj~fl+uwws>GP=e*TN4w3fyi^R&E4Vjb--d5{(fI1hV)}+LC}d-UP$$lOy;qAfR+Q5KPX{|K<>1Q~K<*V3EONbFwNP1~=;-yi7VTh4!jaM3qY3Z=8uNO0 z)j5D>5DOcYUb}CfW%$Q!qHvh;=H0+0B?#-PwZl+-l(Aa#x^&s?gqIh#CbAtY7dcznti$oZR0lq6=@3FubS6d;w z_3OAb)9a@PpZeA^B2WWUg8c(~i^0|xU~S1qfFjVHt4u{NHatVKd~A{i`(vd~FUUyl z(qpA^B7ab_;1<74i}abwJ$$8WMX6kwUdyjjnCpz*30Qz3&@JrCooIg@Y@Ld~p^oX3 zo+P{Xzd`G9!HK<;zbaE=_gxS3)m`{`NWAO(~| zshBz2(TUJ8L>@Z#W3SBDIQh;dxEH+D`14q5PMkss#>+aQRMJSGlb`ZVaW~Qn#!^OB z01q*!Nqq^FNy>r(yf(E-t>D|`VNo^5_6;gu;%)HiG_mr!BqYj>Oa)*>?d+0O_9OvQ zA6|klnQGiMF{yJoNwzk+g2vJ!7-Xg9J05dn?eYTa)F+NN9(Cd$dyujOytcv<;5AfE z?f`z~3fAc>F8J(Z&nINBX=ZI2IJ+kB?Rgj)w71%-PoC+g(%PtN2?SLpz}#PVvlOOS z^T;^#;(E0`UKw2b_{-aeHd}PqijWG+<7ts&Z%L$XCravIN0 zkD9j2*$y5YKiM9$qNZXT4)8tp>}mYeIR_FWpbBh*U7uo{k`A8_&Vf}>HBFz{qXQw}P znYXqp_dad|t|9ol-eg|=KK@O05C-F;k1~9(V47bT-1h8rdl0bm1|Fo=1&q*of%rnDf6@Hx-v~R`SA-n3)hViDR0w}_o%%*F~aeZ)x$_V0TU zKR52x=71d1Q;^6`vFm%mAEegphCEB3ZCG z;lQr5jTL2(tR$r+%7eytG8_Cu+@DhgY?f7ACZ$rtT|*oAc{V@SLLL@3JGO9GSUm>S z+X~0F_D)XXn3cmeQ)+-rE~Q1%U(uw;8=P&>PyWx> zZd0pKKHHBpF$90zzMrQgd9b+eJfk^JJ1KD?+I2DRiU5Z!7XO_IS-*}S?1JXeQg zX+?5>#CIj&;np^8FJFcpMhrkZa)eWMtedMfYF_Q5x zcbl|ExnTZXX&7m=z$9t(1AzkDOg}Eiy@I{Avl9nrXMIpj<%BnYUmj`NXf16C*s}@L zFg4AnXjo(pQ)~$Mz)7skb=7uFCUZf4Y!+EZAO}WBIk~Z2|#l4y7 zH6Nq%1>xc643CVG7x+*r8({#iX`AJ*%RP0#&L^1Sfpsw{ykPR2k}j{blz>4^YhM zOFh6iHCz(bPTyAyICPnL6d;S947z$P@HrpNgXDb6o>zXsli+XO%P)&6Du%W#Nubb= zKiR`_nZn~I4G-z%9eLjw9~!IKfPLpQYGY@JvOoH>mdOeOl5wxSNaUq(++~X99XH+! z#yP=1e28uXKBd-i0e25*C;Z`q$>37ZvfP9YAD9J_h!PgOytX!ub;iSy5f`rY zuh!m)xS;RoSUMzs7ASG!#-DQgOHo~(VEk*hyw#8PHYWNKxZkn#RF~C^et*R(Z>FK4 z;Wuu@*lmSmji&>5N`gSq01LuKtwfA!F3}mE@7PZSWk=h+FA}i^&Tt4gig&p?(on3 zy@g~TOo+qC$B83{I%54mJ((MueIe$o`XFB#`mcuRu7t+Z1c&!mP7=+1OcnbhHY}58 zC%$qhK}}7aCKuEg4)RqK4S;Q~u^wRX@@dMN91x}o0=0sl-ahQyT4UFD>%ESuzt;|h zT0DxA3`I)?9eSG0c7U0^xgoegNIis8jaa9cCQsDVV4YX@H|PAa2C!?dG3I^b=e^K2 zJU}ET(m3!%2xbff?$dBjE{(DQNe*52xBy?awyirPZ8pN zTl48c%(uLqhKQzIOx@m-N$sVI+yA8oz>MjJ?ldk@S-}sWirP1#j4a|bX4S&gm#}#uL={HB-d2nLLnLK_-f)spq z0o_&M=5oAR7C{?IgxJ0cRr#&HROYq?P|{fu#{6RB18_~P-!lS3@BaC|%Aaeh_Xo26FEbcQ=4bSLA~OrRRUby6@a^cNAAbrhAgj$Z=;k<2E(M5OjclwL#rF-j|?#stTk zB6L=UVRf+heU^o8Ozwojhm!!5(=j(MOP9d4Cr;V$x`w>S04B*D?}WsglOQJ3%&|g*=>L-VfP& z<4&w>U`7@l__@LDoh!S=hesm2fBux@>tbJ(ad4VfX2}ssD05 zv$SWC`lvl4PzF&A-JH0D1#qL4hX#IV5wJ%VyD=4z_yTEJ|}AXDXB&T=s! zBkgCgA>5$)TT_WNdTBnIL?w8Qs&7iKrjHGHsX<9}d#V?9{JE z$3cHC6AdcSeJZpFIPW%Y>T!;?3gBp09g)Ak?JSROr*(mA(R7v7a<49DsU)RMWGNVI z@0G^w@tp>%$M4i&J|`B2@GXm>%l79g`%}Gd?A{HbpD}X_@~g23G|qpMx^gBDgpWe< z7RZO|12nL;Gf!}HK(S4OSiz++yZ3HS0R0|%1(^3~JYv}&8KH}vDcoI4TySm)iHou6xUGX{o+iwhi@={Iv82pCjvUi%H3%7e1q!L=sbVGIV?P~F2Q zZxh_od|AkBDzRKktcvlaHb3uR$*7sH*z)l3@aW-KCNX80spm-O`)>f8eIsB%kPfP; zymG^daCre@)kCoo%O&v%p7U!m{BV84(*-Ryog-)eh7+WJSQmoW$?_<*eSGRxS>r|v zwN}BT2#Dusy5|yy^3O>Air&LU(zNZWdswzrI}s?+--eu?CXNh8LEe=a{)kn zO5IP5KWyI1t2xiO?srufUPy@>hNrrBbY=CX;;`3~JgZWWazq@nAfG@$j=_{1a*mLOa4s zgfwF!{S*yRb|WF5VC^%P$V*BM73mPTCD-#zkaiH^P4kg7B4|8}kODnV`0)kO-{;K% z)%mjew86H=Hw&tkxRJYp#ODd7qOpe)3|c;NGYv$X$qkvJ}q5OGLo^Bin@Xmyj# zmiE(gkRC7Sw_Wl%sN<9)989I%AegCQJ6IAa;m7}gA7|%Y6o;;VAvBA;`w4(-!e;O> zvw=vGaBjcv7F_?Xq>wEEo}Ynd9@VX|5ci9{H7#E}E{Q|g)l69DBh$aK!3Q83m=HAl zH|GQShJ)AtGkb-R%&(87|9^V~qS;wD0b6nkHsRcL6u4Wj`0uK;Zyca*nv40_k@JJ4 z6lvC2WS4OMLm`(0?&tH!f-+a|@eTif6g=?L|NVgfuUi9N`hPjt{~;UyThoVs(+g1C z*C6@nv^__b{f{-dvGL0f8vYd;y{bnYK)K!%rdXog@C+30;;};9gagtJ`dhmB|0P$n zaz5**`=cr#+9mjDfTi?ZT`L6!(D2lP;=@CK2?+^%5X?}79S@4$E~Zs*qXKtYe-pHk zPviEg#SSp)?N3Mu9FJzE49N2*d!1?XV1NfA*foGbK@H-a+g%2Z8L3yLJyi%0rC7HR zjsy^H6kuTq_jwibRpLnklkjH^UkJ=`X+?!5X!jY!QC5to6kr|sQxq6@bfZ{nY{z;^IXO4Mt!*aAP<)h0(X_fjYQPEJ}3;{3;BJ zh!Z2NZQzUv_;)B%7Vmnb1T)2eTXp(My@XP4?F-=dcPN@?EkAInZ`}F`pdVpSVBi__ z^hEJkul?T-ugL{I*a(<8@*Giu`F+;8m?-TbMTjZ@=4kTSF!1nrXKZXNb8G31Yisf1 zqD{+2(8l(!*L@%c2h>d-$Yzul7TUIN6L!ge?2s9B4Fc- zK-ws&B^f}z3xEYCpz@fjQaWG>)|KP{1Mjrk79@G;Z=@4h0{rej7bAJtEr1cT>b(dG zULJsWEGSFCBsm|zX))U&aLt&1ZS?|+OcAK5_z7^Vfup126LZD4E1n!C^(xanrJ;|1 zeqw7m`Gfag%J6~rZO4sFO_hV&nSZ=?`{9>g{#-~g{|wL(ITIO($I!IDyw@j-KY#vQ zZT5-#R4d=oC>b1|{+2)ZWgl4AnF^3HS3`G4GvxuELu?3?4kh$6iHe@1!ea>(L^P}k z#2OQzoK3=i$8LOl+*%e1;Pexce>}gp6J^NwRnK@NTQ7s#rsK<_gJEC_taYoO z9}TYE7HVaECInH_(`JT8cx?HEd59?pT(X<=SU~-k)xB3&2|Q!Jfff&4|KSP$y}EL+ z_bUKMtgmu4_-vSkDY5}ZrD1*vSl-K^fE{zfpH=@!ePY}W)S>{UZ@lP!9x4k1L>*jm z9|c``f-MEr1m>WucfD?Sx+P$IbTm&*sAX)oD?ociotrsk1+4L!+cANln^P7Tz*UBv ztP;W2^OR$7;QFemd3y$6S!TEg&BCL>uQO=wP*MbXOgKhxnb&Gch#_hj#3Ryf-$MZh zL-+EoRaybyvb?7Kmh5+aj#Q8s65eK&D+3h9GC(W8I z($w2YQsue2*@zIhko$Sc%LL#BGzR2Rzk|7f-av%u7r;eAs3iUP$A{1*T#Qz) zXS63Q1|Vb|aQO6ws98QJzX{r5w*sUskUWOKhXEk%T}Ctq1ce~qW57M)g~5^6ON-g){a% zV@G~0GL2#$8x2B{ESY!WsD3AsNOonuvb-8$;iol|Mu;} zZ|%0FEcmr{Tm&e#*GSz?kSXq32TkHXK{ZCAghzfsL4*C;ec;2QFCjp0H|u`YuKxj?Kk%-Rg{K*E^a%+G$|@=fDk|dO-=rl{DIFo`v_%Cj zKx2X0z{Mr8y}i9~X=`$VU z)y-unu@s5rlMW9J(aKD!si`&CtstqWsDJ_trMn+f2U`+4#F8~P-4h1o__?t$^>a-U zd~2Ug6&?Ucn4QlNabaZ5mjg32YYN6?UU2@?}L0}e+7L-GA zC~kze@0%vKu&~wnl~}BEV@OEIn$PJLXj-V>H+=inAC!ydmPO)N8WFMolaM{)T-u=VQQFCE^odzuO84u6s_;|H>K^q!n z(AT!b)*GDV--eP+^43l)!Q*pNz6eApt94E+pKE+oY9>J?E!%bx*f|qm5cd``YYGcP zy)S>;U!B=E>)I-_fc|IK`Da;WIHpXh1uwvnR61@9*PL%-U|>WckLGM&o&k_GV=1phbi@=6EJK2N5x^oG@< zEF~)|I<;6;Q4xoy|M6v&dc}GNFqVT=aXLD>>>FQ4e$x2F#MJ}02PjCEOCd8I9Z0}m zn}Dlp#mTTX)-~x1kdX|*5G((_cNfnHOr>}QD{(Ulqw1jknWhYHf z1gr8NL1;s#*?)!y(+uF`;=(yyuK*paIlfL6I!!(2M;nzhR!%N1nwb^4+S)RblBfV9 z>3G_@yQjp&Jl53G${8M#OrPne8CEWJg1GK44gtY1XEEG~ze2mAV|iJB_%yV7%Z`6i zFzmgU;D%owO{Z{wT$z~VeXzzaZ0QdqdauB`fu(JvBj_|by$Aj|_r0;K>?6=wO$g8v znk1Lvft#?KaAwV}%FtLb6EpJ@a&k5|hm{kLt^NIIs8_li{al@GL0MVk>Z-xZPuS$q zA$@&P5|WZ@lfq}f9(sE0nilSrbgmq@@q7NZ>ZlP@1zCiZqw~A;3C)w259~2^c6V&= zwPO8hf6bAJwueZ5Ul&PRqatkO~_gA?c#NnXf%#3`;=hSexVqqjYGu z868_!2ZIy%$E5g-X}BC>WB^>46@08(kT-3`E8V9+)0do_{Myqdd(|R^fsHL6_?7Jz za8;X2wZKzpH@UE~@8(M9GBGg$0QIS$fWAN{j%utJY!1KAm0NpU`17YQa0pX^yK)6_ z0FVM6T|J4W27OyM$H>gwxp93sDPItA|~#iv(b9_fRZ-} zn7fdlBu8wxX{rd((k0oB^D7b}jKrba&jk4R#>Zk1y}NVFY`*krEexL5q8)YuT>(mL zjj{>a>H-D%`E)rvxIk%06oje9MOYP(i&xjwbVoic?QKku(I6{&A7SF;>PmQw=H5D~ zE#{&Ro+Z1oGG=ej1|%(UZz9FEdvaG#JXmS-Q-D|Co*6Rj7EnlAcR4>Oy{`Kk~tl{hDcV2D4G6OG+bb5IvTuA}+*=k48Sg`js_zY>7Xe?py>)VRbv3*9ETw1b#N!DW znX!}8`1kZnnPGW?0u{}jW|yeJU6UN(N&v82r|tXdB0Zj+O?9_L<(8pku9Z)W#vZFc zfl!XQ*K(>0ig^=~7Qzoxw8iiU2@B>Cz^o*DP^z4phSqh1Nk~ATD7V|M4B^FjZ3O(A z5?vKIX|5@-m2As;{vEr~tta)IK6Lv`>$0Skx0_R7j)_y&h10wZAJ_|pj43g)I464$ z3+#asThp)v?it`G3^q2twHx%|WBnb!m4ko7HX~@8D$diz=;-MW&b<;bc2n^2@$CVu z2Z^}M+E-Z?E3Ulq#o=LLz;vZ9j6^kBq9Wrh-g*i0rbX7)@}{PxftzY#WJI$=e+BHv zU6!qsh=M|1AOk=R%%+7_US|%Xhg<>zDK5Jo@F7{Y)@e&GlQT2#Tv=i*1_9XnbKt9( zmq+|WXE)V(8HM1CAJNVXpYF>yTUU}zIFZerV3TxqbiC9K5!6XXMn+b^kPd8r3SPJi ziE5;)<}z3FadE}gMk;TJq3;@Z?z%XF#Sc(Fsmxe?{~irEs-#holH%feFj_%TwP7&= zO#;GCiV3#9yXACo0PrL(IdsaVn0Rfmd(g<{`{)XpFb!aG6Z280I+p3=kNPG7K8cb!J278GlOO#X=O&7 zaz?XLyrLypu$3UR&*1gAKa|ik+fvcFi%UdQ;i0OK$JXzitkl%i{sd+z!p~S(SOq%s z7B$~=sbXlT7hd0zVc8_3^(uZX1%=0e zbmrC6Bmle+LBPIYNJv`(d?eT-MqfgZo>7->1s-c^k{(3POrMLbLPs)8RJNq>!$Su_ zAtBaCVwAx4fW6<>=x`AvVTWI4 zr`=cLDc>b0w;=DENzdM%wWZ+iLpvT{<__D8EHU(9`JQ#=X6-IC@ojkoPt$k47o^`> zop15vlNUJ|pN)Inv;Zh{Umaxl*sH{z&cGS<6L5j1EssngE{xDbBXwCe5cnot`oCx< zQKx_*RRMD0eG+UntT>g5pM+8x8X5xVi9Wqy_%SY&het+6rXbXmkeCQqUiU}ak|v{q_+{caIj5wFgsNB4$Ks=0ml z+FutRDt;JwJjXb^Q5~(!rd|I*DIkVzczfI8u06a4^8&DLC5Zi>MT2o+YVbzP2A550 z0}uyd!@U4m4rl&SKYDn={cb)MgCk!Gzj4{fh_B8FOvA*vkpXFb39#++mklyGz?D)W zcwAmI`R^;#6BEk9ggW)6JTEWW9(b*S1s!9U)MdVY(VB#G-SEt4pf)bOL|OiUp&>wF z8qPlfyajo!>gsCs)Zy5z^Y^RO*`PJyWB3+Gq8-gPxHc^SCFa}2M8a5VxHdq+KfSx2 z64KJrYS{LYFi))`O%}+7j%~@LOslD?(p}3i;-At2Q(mXie!gE&s)yLJ3-k2<1j3{| z@bwk@1W`MNs68Xk3Ie=pr{(O~!bwi|7SfU&aU+Y@xre>_T#o4#&Dcj1GgLQDnDcPt z0*gyZ2I{|M;saw;2=7s%CDgP8!{y>mi@$=Sa<^tD1Qq;)()2S>hqpf<0|Nv1eEFB1 z1kJ6jyG^QSudYDbYT9*p8rL>Jt(%#-) zIRv(LU?k$}3)oxY_VZlarb2$MI$=HgpVD9W%k20`N%`&ky1T^ztw6gU7@v@!prtkN z3QHv3MHyyO=eXZ;dIzu%54kWy0)dfLV~ib9&Qvqh3vl5}arrez=7W9-MJ=tgsQ~L4 z21dp{X-+6}pLlNQBA8j3oRJ~xkvW-qetsUL6yxyY^fDzgGrzSp6QGiK#fOg`tsIEi z@`CDIA`ts*Y@)#YljiYeY;f!b!5Ev@WcQ|Zd0z3rkio5@xDRmyH>#}KZzQskdTJjr zYxvke5_s3~<1j$!nq{xQs(vYRtb?YTV2K=K^QHm*A`83);45H(<+Rj)c%P(>l7d2v z%g$(67`hd&)^}$FW@ctU2)IQ>N5C`cRwTV)=sLso;=NkmY2kYs2lU!%QQ z08a6;PCfQ*@0o@#GSSHTOG#s6S^&wlJPpB40e^S(F6q++xw~drsq1{x`AU!+V{*tck7t;(1-?)FgJoTFB{H9sq&C15+u=Sl5#1H@)P>>M8=h-(c z{90J2-rI5D9q+_h_+qJhim2SQT_uU5#)GB_JShJM5#MLieHN zfxErWC%|%&{jMy5kSxzY*?yem(sJV2F2L2M14Q zF3Fu_EmKBE2oR8yv$Hs@?742-==5}QQjuE<6Fa*pxa5hTlln#&@VKCtlpv%xc$_eS z0*JMPUWyThhENbMXexShbxKv3r~(&skS4X#Fj*-TkG zVY|nUjZCk&G!>PWYVX8blszFK$!Td3iRRSgks)BFfZJ*;hVdTiQc7}xU)BImQO7h1AOcW& zH8Y8U9~e`$lBdydKom9wOb7@*La1_h{ zbQ0)Lop89_{638e&zV(eFQ)RBfdnAR*z`uZMF0MMvV`h|TW_1QhNc7)1z}m#9k`3U zNK86ZEaVD=SoRy4PQ)JEw~htNpi={cg>~UPIwhYrQ43;a8r~y&I(r~e0Lqa=(96co z&dtH`fh!7J+j#lnv2Jr!pepdPG9>`9rV<=Qdm!pm@e|vnR*@esHL^x}fex!g$5B~n z#5A@}mHkcX-H^!|koZYOeS~dTWm;!;T`)=cKaA!h0X_!K6fiCp`WHp9;i13{mg}~p zI<=q9&!^|17+P7etO2r-(Hv_{^V$2-1z7QF>ylHSNB*QCYQxZH=?rlG-nW;V$Q-CKHzVS2UKc}N#OA`IsAZ*%PFuWFuwN`IjctJ9~4y6YA?$Km+ zBtUEX#Kk@pNH=J+0i93#8~V2xF*=pV5F-%C8jgmA8rGtOzV{TyL({ayfN{}p;kuNZ zaDefOMa9Kn?5BFU?CAVFX^o0~sXhl0pM{dLa{LsJ4sgD}3^&SJjNXNW96=7Lg0QJ> z!Sm@N>LOo_N%R^V4glX@F}wNn3tXN^bRhi}vD$YTnc_@8*YHmFs=)$|5OCzExs}2< zK*d@6X;D{KC%KE4Ae#FL*wj@%%l0N$ibWxl+;JU=@-0DKMzD0l80_!2;!^U|q! zK@v8q;Ii)}}Cfj741a|8QXsTcZDrvnp`ey0eZ z#3)+VpPO*YPm+(-RabM|xe)Ufr!5>1P6b#oYYo7MODAO)6yG7r?>KD&&ui@kGvq+t zq7P7{Dca%bWNr=S&!0cTV6f-k4T1o7coL~CX7HIw?~fV%%L+jT>`7fBoffzlp!<{B zR*RB34-j}%Py%_UsBJt>|NYwqgwj=9fv3StOVho6nYpTl-+El)R1BbmafTt@IH%IRe9jJkZ?%^dp{b#v zu_Jy*gRjmG3ZsmHIf3-oJ!Xr##>Re7xtA@731tNNl2|9re%1HfK)1_@hNK>(pMrw8 z`hqqPT+)TzxpG{9fizt1HL!;yac zhr{UDn9}F$U`ST9B)L0{1e49YG#+{L~kEW14Z-ALd-Z>8VuP9j#IEx%sd*+n6 z*;#d6-KgRxmB2K?yh{w1{l$z?3Ra7urx>_#Tisbf96sY%GZ25yy`%J##K-$8d@ziMW4dpo#}*<>gU8KGK@SeyRpU4eb^Wo(*SeP{;JX1a9s8$+fk#yu7C@92mZ&uIney z6%4)Px>^wG=P!UOyiu^#yFK#TM`#eGjkLW*fhU59e~P@1y7-=xm2oo7fB)y@jX)}j z&pm$KLK2$vERZ5>tuKJZ6l?&EH%jYs+Iv779WRcz>I;taogk;XbEXYtK!^@Xuvo1G z>iA?7I4|zT+U)EOhqv{>XBJMQ$7qYu5cdJh`qhGDK`dSo?1+6YnXA~YVB+>wR1_wV zpr&XW5DY`A=4_f+H(aRjTZbks4c~=sZEY2jC4rc5#I^blU%De|YeYO;Y=m&~xI06` z`bc)hkF9m@0v~wx;534mh5;hcxByzh5Bey`Hge$rSA=aZBLS|?$-^TKKq~MB&z?QY z^;X;h^dUJ}A0$(@jw2Hj3GU8L!N270Q9!^2`7q)0^|(YyDuBYkC4FI;F*=?Es63EA zjT$)8J6D#!eR>5hUuQY6Y?IaIpcjL#b$Yzdr~nXyyMWo z8b$7M1nfNw9iNaEO_4V50yYC&61J~@TqVsx5PIwRX)nV+j%Y0VJE7Tt!gm8KC?j|v zS$B5TZm+yCe?9!A0n21pe zgEKJI6^90XhxG?QI=};KODs=GN$E2ymKPVlcULp-76t{Xa#q%edvPp20JA`{0tbyg zN$K3Q0Ng_IKoUF1y^x+bmX?(n17DITA%Na)QUz#0JAhnrQZI5lPM4RT0+a1y)aZ*} z+K=A;3gUuQ*eFKZ(#YVTOvecC_9-jSt_(@RqmTZh&yv6}(7h@wVNHO-i-uIDO|T=f z8?WI;P#{*&jqE7KO6dZ152(JWsl?zZSHsN2S=iW2YU&%obpd3f`PWso%4$%WR1b(D z^OKqHjkpS08yZpoUeqFQ2u+A~#!gX>_7u;5J~27z0CF9|)yqUqcZCREdU<@u$&ZmCkDoug1v|)VPs)s)Hr=i4(J!jKCshnaI=!$vJCnx z2+r^0!JENRT%ona7|6Dmy169-TaU;rlw5ejcqdwbnN+G1 zF=f+C_5qbsH(<&A+3qm{Cq=P2zy?7FMOp zeo|9Y1ID{<)Z_Mi014=m<>fY=Sv?pBGSq1A0HBry7jpu9nr({r*D4u~V*h5Z=$%;oWRRiL&V1V|hp`=ki+ zlCrWx`5p!=UEGm(QWK?Eb2{!r${@G=Sd>=)uY?wS(aCuV{2q9XUCHYL`S9oEY9iI$ zy6(P8WlPik=he&gKksqJh?-;m`F-$>7`|BWCvg6Gi$e(<@ZNVnEVjtcD~RytHE*D; z_l_1rLjAkqN#eZmSzp6yWLG4~>d7XmB!)aI7y}%|4vF8;hFOW-_k6%H(oqynwS2C* zBP3%#>mLro@~1$K{()2Q&+@^WKkpcJ%8esCXURYE5rX%w+|pU*kn`i%>@+HFv-?RZ zb;r<2eWe=?gc157GtQbJ>SEjMq$+N^`t2Lz7Rbcl{CTLwOFuo?(RN?%Zy}5g4~~$J zV5H084K_k>K4_6BM_WaW+<2=>)_osl@pNB=^sadApmV<``o!=a?KZ@OnJz}^ zyA1^^67!^hr@|{IHGPHhxvzXJt;8Fh?g9*vn-#897?4#V&ZJp43E2z0iRkT2O5VTZ>EYxX#t@e~_XvccR z@kWczzW>3$MJ~9-AqU71>Jcp#lF|pMl1J%Tvbk8fR=r&8#eoB+3!{Oq{@8R|V?LO)AN17c#+5`>)|sq2-OQ!FK$E zlTSBrGi>*qdR=;CZIbgTRD$NTgy{RK^PKE&5T-*am*=l;WH9aJk_`$giT|@$n#X(3 zunbw&A+{a{OL;Xu(qWc3dyRNEEN0t<6(_x{GFni=4flxPdHLk>1})3$5ckkA8y-0+ zj(@IF5NuPKzW4;&haI6A|175H)iq{>Ag;nj8pfjdVz>Zi>TnJHcIgIo0YyE=8J|)* z1tKI(JRNbY0rUUb5cOYU?p5$hif@{?;aj_Eh`w zT6kz{$Ocn0{@I zS_L(e6$(%t4A;eQKVJmhR64YJ13^f#1ztNTc*mRj@Xw0dQE;tjvoc#Hs19bG*261O zH^)?d_T;Gjy8r8p-tPHXmd$nA-io}z{21Sec=b+kn{mw8H#`0!15XNuxBRHCNsCeC ziH+qhiX+Zj2LG14YBT(0cIRn{7useW!tGKxML(L?re7bMoARjJyKLQ?7Y(4yB= z>xhYlt6t@k7vcH5Hzhbslgr-LmBUrSg>=i!sCbM!K2^Rpw=n#tB_%Xr0G70YXg z5{oTL6mwn}PS^RPF;-oZ|1JJ$<2P-j(=>=sLbgv=lK*LH`3_3!z3T32*B&S9=<`4^ zYJ#7hx$ln>@<+(!9&hL&w^=lnqZ&fT7n6UJbL(76tN!n@$Dw4LqIWd0Au_`P`^q25 zb9$f{dQK<6=MrFQe$+9JeoC+j_zp76{~4$H`>Okf%$gke64#cg{B2~dwnoG%tag}g z{*1#RoGuwJExjr1^cw%WrU8Y{4=X7|C=54f;}xSV%Ls=yy32zf)Nuz@>ijA!%8)lJ z)3f$Cyfqy2*25NHT)->Bv%evJLk!tK)!fm*u+O}&>Hk7WNOWAYtrKr!O7`?R(&;~+ z5+wD@iDp?gKe~-XYm-e}Hq<==^L#|cx{czDV#V@i$SFDrm(9e~`Lqzl{>Fc6jok!$ z<6#fdo^9&v%|rNmIFB!IskS(549P728O7_Dj3*Co$=;gaols^Ckj9rb=`c|A1k$kKHVEj!^Hlia`GimW}R z>C5D$`ve|R^xtNOc7^%7rU`!2gF3~!2R#+1ze%3Fpw73}&wAtqT;U^qUawhS1t?G2 zTy^2Yv%;;^wtmXtQ?s%qi?AbO*eht!h{560C5tEA;M`LelE$gQ_V*ZJFzkgLf~7_vamxqIGtI0$r#ZbobA zjKlKumU@4YLgAqvPXhh4~4W0T)ho~G|(+zn>` z(`3lrkW%_B)->{9(1&ECFHUiJnnZ0W^|14|mui$W0UiXul}Uvi0qB<@{c>ooPFcs?I@6;Ie*Dy1$Y$7|f@)QoKSsIwjJ5z0-3 z-%^p1D*E6`aAnwQ`WC6RIm+H8$gokAbi?pDuHDS|m80L%J*d&Y7$P(c^1zEA$4!T0 zMruW_5jj}}I`lz*>%}0$3xvW+&8c_M?a3|^=%uM_T3v<5*RIeaq0apii?VB2TiUQE zE?n|CE%wv*b`RO~p^QAkL*gratH+2SD)zuRiN?x*Bj}D9R}sDM4TKgEzFQK?ulX(Y z7iv_$pXO7Pp)Ky5o^YdHDM&LPJ{GY9n0%F;N5K^$SN{X7^~_3K@eh^ zj1u>K!W06j7KlToVQqB9B9FD;!%n|YFFJ)}e-Ll9;j;B7(GB}7+j2*Yp|XO2%|vcIpUDjKzX?m!?ADko{B$Y%brH|>j%CdY4KA@vM%1J}^RUz3j zOgT&q(}5jv81OWOR{q$_fkjuD3;z$+8Wms|^5u6j4$mOYlR;WhydJxKhS_&FlAJk4 zbWx?aM3Cjf3WE51Yx)THvM?_gm4l8Q!D|`H!UP`VyS!L_AJZCtntX)4)w( zzP)Feuw$7Upl{cX1uxLDHZ~49IOWl}L`T(2a&t0ce8WHAq|GkePdHYx6?NRq-iL-iChJjCI zVtX89q6x2rM;~KmY!BTSAX5IAakxB<>Bbe@nd278IQUYbBixn?SfyJUb(X zt7BT5FT0)9Kgcr&u1aG3>n+W`tfN9QJ>DMIc_s} zX2t=rf}`cegnU*QQ6?xF)Q&Lz-AXZwkqwiB#*jX1RK_Zb)F{2Akl#LJ438*$F_QaV zr6K6?klp$;#{EaRYFv|}+R}Sn+KdE!9yLsLorP|lOLpgDbh)w++TJY+O>$K92|}6cgq43k?^lb)4etRf~$1) z@H4BI7t)z#`3qVOqE`7XzSsx5VHb8)P3NkLx!7D)$KHNgO!S*iJN3 z8>wgP-=~;Y#`GdKyuJrB&n~!+-!JcvDpPU$Zk&g{*gs3Zv<<rz9j5(_g(z{LHeCZebyJ=oK z@GWv?*R(Z8eagWfcY7dn%J)2D%bBw$dq;amN&UyY`TQw#L*nm8hQpU^->f`z-i@wQ zu$npXu_Dug@8o>ed-9*fbonCLajEyF4}OfAOQUz|R)h zBL^8x2-E9n`=u|!6?Yo_a0rKm8v@tAmKQW5>F0|At(^4NyKll;tJ5>oNL<9Ak6)j- zn!bbt3F7o-Hu^MlZ@Dq}2!W|VLya1rqz6+yY0&mReW1GU;jpuT@{-(CpBj-ROXe_d zA&LYS|J1!_2<^`{7-7ii1H24>_v`Co6$gQV)&UlgQW|eZxWBEY@Tq*?r_cXkVk%Fp zkhbsc#AgovhEhMYRw|r^--^72zW#f_4o8rPm#%#g2yKCtOsHy9|ewK}v+ab|%)rZozeWDke_tj}N+xI4VwCs}_fMT$>7qm9k7rx>DG)w)B1w;vOtn#*p3o z$~34Yv@Lg=ngf+VPo6w^@7V@2%b;uLjk0nzqC)p&U3iHw|7x`7Z7sUczTook&gg#C`d`iZcKj<#Ke$Ty1 z;D-IKCQwYG`rX|YEWdubveCq3EnG6%F7n6)I_Bh)c%3RqA0P}N*EfSS;_g2(yE!P0 zW)7#+ZJSFtsLlkrqE-QGbkne_Pi|x$`b+ie8|^W$*=N;9HlEZqlV_n(TjE#dH^rYz(^j*(G<0 zaHjr+tb9g>DW4#}cGAMw!J5(w9|-w!m$KK~h{fll+Hxu@f=dNZF)xGil4E}ig{OY^ z&FUK|Nt9OJ&5fqc10~CCD4wrZQq!`0%k8>??R=rUbVDT8(k|PkcNu5|dIs9D%tx3( zP6+flc1mzTJZA?Ein{tLXNDa}_P6@HX9P5XNqa~2NjA4@B;unP>kGsr|4koYJzMlku15Bx3nYijT7L}K~e{i2?$e7n9 zan7vd^XQxGLMEa9pq@%g0> z1)+&;l-M-hRRy(;iwf;tx=jLq*+qK-!qSu9Rhw0drWO^|2PAb(o29y0m{(u)j=rjY z$cnl*xy_S1rfsrzLxx#GinsW;7GTfvi_)rkme20?HdD0d4L?NBgv`|s^f!V6Hgs&2 z=5v*yERnspkiaGPt}0^ED;G7Fv2QjBD2BMdsPFPBp-utBH|Oc>#bdfVs#iZ-JDg_` zi^K8S8kX=+#zpX=XxksAv*H%uOZyBhWgQ&Y2DTQiXHS2cRx!^RFZ#?#9rONrm~ABF ztgA@nW~(&Xuja;Ql1?ObBS{~)BEpWy=x(@vkRrIVqGU~-$1d&2P^2b8DS`HJn5$Pm3t)JF1 z^wzzIuDE{&U(;9SjhcH$Hozu-zN~z{%hys;K&FlL}O%UA2BrIJcXOL+S%ai zVgAs3_cL(39$#pH`?5udGz!bucWUU>KjbP@c9<|=U+=xH)x{TH`cqz$+BX;B8{TFS z86R5RAXW6Gr@Vr08iOA1_8*~U|2*4Z9@bWW&rK%(e9U#R_glKXLXncaT42U0Dbs4* z@1{a_P6@WKUp?hA;XXqaVZ)mvy+{Jis&N`%CA@Cj!`csrgU@C^MhCNG&1!K93Z|X_ z_zQy}fU;rtP=?A73GOt^q`@}KGa@ZEIDCSR_D?YB6X7BV-9A$xWYnFky&-wgT0Qi* zX6iOT{^;xUP>=zLCsDn&cr2Dm~jRr&=6S z>aulhnAS^kOC89SJc4}`-6anH_%16N^{|n^a2|IRRqblsu_E&WZ+?9w;d0UC*RKyg zy}6my(bY9EGh?vb6$cToGppCu7sTVKkWBX-&OZr7-pVHxQN^0pN_Ki+8=#4p=Tue!M8lCqfvcE^Z z{M_Av>vq@1@BUROLM6P_Uyep|GYewp2`Nxj`}<{*4|wD zBJ`_?Mf`$IuZi@MEEo*!uqYwn^PzjZkjnEAx@U50FVIF!o%T;B04Nb6&=R&e6`W9c zP0Rak;OJAt+Er3G==T~ed9l{wm663V>;)j)K~4Mm(QWuIct?hu?!roLhckDdpb+jV zNz#?D0RH;8(}yFz0059UD@ogsRhY=zO!(!Oq^acDgVcw|kWWEZjm><0%z-l~O91)r z0+By`f-zPv!w=8I{4n^q|7%QeB zNc)?9b<)zOt#+^O8yF_TyIftyx>E{uUlkK$retO@yc4rG>8A)_$2!x1R@$8k2$9Z{ zv+YgQ47Jw6y!tB6fBx9)%ANjHXx9$HsO{of_I$z(VHGjW4)G(^!7>YAt?4(f{oXLd zk9F*Uitf8gU!QA_{v!ZS!F;HWAJC;yc^Yi0g5U*qbH_WlpfsgqYAi~8;;0ul_I=;2 zWbUl@=|3yp_K-oWZe0B(L?3gneby;G(wdV~reZTH8<2TlwDI@Z!s!@Xf$=8kR?_&+o*R^7@QqDyYGtt`vNkS0}wd|DR3|6DsV0Ubz=^fr$u76{&6)pBchV02R;j9a`vsgN!O3tpHk8>&ZIe&WN}@-aerxL`L)hfVl8Uz%oeY;bBPRU-L_E* zDAG?4TLC4CWn^Fjt3Wk#yd^8BE&-MOpz__wDT|&SS-^Tg>uqt}m!{ca3>U%k(btDT z0NfkcvDLFB?DBnQ%@n=EU|L$OLVbyO^|DqEZ9@8)hKcJ|#f8Y4%?hZkzkC=1O(}!o zDY#I@>QUX8W-VhSzBc7`<^nU`I_7F6R!VFCfXC~uaSzB7#z=cQJki=axg(oP|28H19Qw#O>$kFuY)otqF z&an*}w=Gn3-haeNf)?L|$8Kd$*U(|NC-I?A*oyyiO5Y#KfIFv~oh?|Z#GJa#vZH^c zSVg^6T_B?!t-;bF(wr>#r{jdX=GT-k583*#2deAjTxAgzIng2aW9}L{>;P0rB<6JjtHWf)Env1ljOHLrFq?c6q3%QCQA}#ahi1;s-Xj@kvvj~%`A8{pvH14t9%%Bqgi?D1FsJa4WRK2vgMFbYMbG59- zX?+rW20B{k_6w`e@;r&jKv(KnM%M2<^+NC7+HvaOx{IZtKlGlra$0T>5I>)57untW zvZAR6#5^Q}IL_6jUFWb5SqXk7ZZD2DqUCbF!;9ZC8454cAJXQv^e?o6f!rTHgeVS=Yd+a%7)vy(1P{m+JNloBj` zAKa16eN0q0pYXsI9$q}Vx4d0|Y1xlu>`f9gAO5AhniVYSWO*z5 zNBgpJ0LQ}d%=f78mwfqsprQmSZd^ne00Jl4K)mp_v2M#mjcSa4X?n}ZgSHFyYe;To z+98x(=k+ySW7TXAiC`Q(+tw-yHDX`&S_Sy)4>dGJPkqdqGFF1$Xb0FomF0?OycLvL z8u?AGO5i$Yb=O}yr3;$q4C>}UgBcjBkkyeX94N2+Q@YqegPJQWTMX9F7MnQ`LdeBV$BjDyF z{1s;PVYD0DaYbM|flb^oCGxt)WzXhw0a;BY4*hervJ7t?7geOx9p+Tpw#vN@+;1$G zktaj|VhMaaK#kneKINlbQ1P?cV79}RD3bjS!yo^ORK;Vcbz4Xwr(~v^)h9f z;~z-pD{-ojOxLI3%aco}D>4y{N=ck}lU-jxDLR$|2qk;wsl5*w=#O#a>yYZvB|M>> z^pOCqne_`cAU5In*woDxfzb_~ODmsmJmqVKJD`sqa@4YvyD$q&zsHRLN@5A`jcby{ zI*V$tGeMohqlX{Q&^^EYTz%wZhQD!^8LtFVH>g@VE}Y&n4{3 z+%BW*M=ndoy1ZZOYEAem;tkg;EHpEW#b0e$5ZV&{Xh6SR>qPQA&+MGsC+OOrm+09^>*=;7r>#39j?fbFzAJ&W;Y2r_ z9jP+-`5TqTYbzl7Za2D=e=C{G$vmNcq}PbOWbUgPm{GUqsR7@v&-D@u%=1p>ieKU4moqr#gxvAj%< zA;ivl$rCM$;%-F+`=-5GP|iNPwS3Z~ljt%sV`2YuyWhZ!wNC+dlvZZlr1ctzAe^=7 z^?~!s`EJ*HU1U36MHLmM4h~%9PcwVhz&IS<6*7ywZ|X7v9?UtYN$8bwwnoAn*ek!NHgSV05XO6cpzb3fQt^FVN-omS@?h6~;hwg3! z1f)|?K)OM?LApC7rMp7}1w^`(F6nL&G#>TO(kuflU3uN$Qo@O*w$R0K*iC z!2gdB76U+X@eiKytlofG2(z#6I0CY1p1pijtdjNKOGs#eK-J0nXIC@a;W+V=j=#C( z&(FesbN(0o?>cBVhm%SC-R!St>m2zIIe%iAc0Ht{8#a$YiVqIk&}#D>3UjjYmp`3u z#p{xhQfdlpLnckNEY{-Hoy$P0!YCKs?mkk9F<{i=e$?ym=25{X#r_@C_L;4NFHNq| z==?^e&wZ3J+vQnm0AknuXwk1r&`%E#t_A~wyuJDPT$ZrX*gRSmz_gqh0Q_qCtogPp z&d<>2&T>Q}*rz4NX*c=zHlZ?d6IXC1cD|2(H0y6IC=xFA(H|S(A8zX`p z)mJrZ@v(>-{)c@TR+ISFPl`;t2xs@*v{HK zIk|POsAs$AA#&(LzREYd7gv6~B7-T?@yD)DoW#nD1XRxGC7l$r)&$I)cw{=v+->q9 z{VT5++>5eOsaY5Rh=|*-A$%|qMh#|^(x16oPdf3tEX4x=LrJs{0H6|2@e~7K(*@^S zo=LtYxM-S<^G<4V`_&e!SUS?=UCgoF0Fj?rCFuWlLW;(#I z>~~{D`Wi3$9ZRD3$b4C6<}Y8I7^AJ?65~|$O}->7ew+EYy@#mweL#m9E($)rI*DQ< zYS<97N(mvbreKU%4ve6K7*tC;hxm&!BO&Ok2j9cP!}lNCx|sdFW9|b1<$K|o>HXd> zD#TRO=v6A1B?ig}R(v5CnO(nl2suCTSVW8XHV%kyb^lKXeNp0*RIjT4d_;{JE5m~Z z-ZD2?mm`zvT6fpWJuwAAIhfHF+s^mNz}qAToNjcE4MS2HJ)fU3+vVKKQ-N~33Dbf9M9`N2`pChtRpIvBcyyK8Y$!H zwx-5AcZmG+u`5P}BUOh4gJ|F)10Yc4c65kV{Aiglkf;OkNvmTyg{2!<H5m89UBt$N(fxz*U_(b~9*^rzCD+3{)TEssALc{d z^iJ~e#3ZBPzpDP5PU?{82h;P!CFj72G1+rnJ-nPjTo)1{xcl52;WASDl1vYyrx!UF zxs@Nsmpdq&{H=sT;0MtF3NY~$aHg)M6%J4Y^Yil&yjqPKXLGc)0a>lMg995tf(`+5 zqOYeZDJV=o>t88_v~JatvQZeKjgipD5*dnq4<#pN)S4qs_k3SCT!z4bLws5x+7{G9 zSi9YdbZ8%jS)_ySjk-78gS$2d$U(>$D z|HXZMFJfEh+0-X+*`S)&V?hU59kYy>Ud7i4CTUZn(xy9;ZaficshBUQalI zW@kkuwd@gp0e}kcl7W^`@W?FA+U|DZ;4i~4IHG;h+N&qp{I^D;*Eq@V4Iv5?rTeql z$l1{jr=6DlGsYvHk%g_4i^lT@0;FDRqJAIIy;Zq_MY{DP3#eFCkOLGb(Z~sOMihB1 z>jf+|R_StkIe>nWrX@uO%&%#OB$TvYT-2iu3Tk;YF_c?}hH1@r- z=A!ua7yg}Nd@Gr`9_qy)Rx0Bzu>Oe7r|Fd#8AHs8@C`L93``V8O&T)Ch60Ri<~;3| z(}$otf4tycZvZmfX*gESSQG#<%l70mo<9g>+oTD50qbx}rqU$TD>A^1g-C#0^pZoF z8lU+}Mt)^FDv{Nl^yK3iiCS`fjM-(diA@?z!Wf8IG5r%6B7uzWbiM1dcuen&pMW;P z*T!Zze5tX7PCr0WfMuM?5bFAEO(Q76>F?fv9pjG|uwuB(_mRM7U^yk(en^esu#D|xM9U9}g9mz8K`kO`Sm|pO`q21=!7Y5YosCgSfi&z$iVq<|IXz5@; zPC2_nGfOa-enZ>Q_K$hLXovwL@d_b5Ay;J@)WCP8w!Gat(~ZIS);=eGhg_2YozxmR zeZP~%TQCb$OFf2m03XM8qbq)STiXNhV-G+<^38T|c8;9bk|r&m@ew@fJsVT0Ju_%R z0R!uDwQKRD ze)H{34)3wudj>`i#CCeCyaWg_Bo>Wh($1RTGyA<+6OL|!*RB3tDf31%#9nJAmiXy< z`ME7h-F*avz(zC6*91NscRfmYp`*k(f%AQGl1O8cO3!JY89z{&aBb!u*ACcUZ~CFfLqb;#?QJeKCOQUaeV*K5gBJ$ChdpWBx+ z3DbmFq%dciL&tW)%urE$F@NKJu13K(`)0H7U;UVVAKV(RF6tXqo1kFMq`os=(YCqG z?j$j zxqZ*i8j^U*@ebkr3YwpT%gGBK> z$zCayA_ca7jiL1tiX>&ZIK@F-U|FK66kG3)kZKF?l@5J7D?LUeT66KJ`mQ|o#zuQ3 z`fQ0Qd_jYF#a@K0CAD5si6ub%acKb5~E>doE({fR!#s z^Yzc?b#H%-j}$4>j{I<7rCU@9$~HLbQDHX$GcMzN=KtJ9x1y;Fn*P9!7DzV<`{uKW zQE_g|NNgwd*-BDEjUU=DfdC4m#L1fnXNT`41hRc_9GWTXJ*=`@4?rBa+xIT{OcJV# z9H@C9>6w9EVTY1?C$uuKoA>*pC+EblvX4(Tb`_s*bt^~nqlrn5@yWe@RHn~^G9)1} zMHr=u#7fMMS1Y8lQ+tphAK|>|gvBKnDgS&7m$1P#2^+m9W%ZO|(KhmWl!77wRe4ri zyF`!@*f~x({me?NF284FgO{)-#nbb!H8Dnzh(Aam``Y;bi3m7FNXJ~cdu`Z+#!as> z#C=<$C@n2*m!U~~#Tf8SO*6+8e|OI9UDJ;Ah>B}=U>$-X;hG-o3s#!oVwQ0inK@~? z2Fc%Zq47`Vce5u^g&+2Hj+SChB3)qO?v1elZgafO)uE_121hH74ys&QtS!O>^9J>1 z+G3Y8884e4JRS~qXb}*6$oWueY42&eR!s|%ZlSqIS@oitX_#p~Zg7Ch zy4|-N*}#CVGf2M~J*^M@ad#zo$sXI%U&@fzwCce@WF$B59t|hx_EgfiY)$Z1S1{DP ze{);kI6BRQCX;_^oql*^M*}Rktfsg1yTU}k_4wn=Xy)7h=>-@chm}mkfKhBn91PC` zD5D%|)TmzY+H9S2;Rb!fw4*G5`I*}jp33_fui0ONv)7YpXx)RCDK4XzBvmHWPaZV( z<5%u6$+iEZ2VUD&+GaooV1GOCcMST*oZipV^&7|nBV{{UPg>!pw`&8g|B zx_8d1v&-MkL{6Q-T8pI2?{gz=}(xO}TwWug&UTY?X6e>p59bG`jMK=D~XL_?xt{JWiI z2!bL1x6m@#jH5GJE*+ZFt}OGFHs=pAX|CXU;k*eIvyQSL(zG*=j0gF;%q{E@{bg{uG9Ueo>Z zPLy7@R?WnvR4w5v{J2njYN^EtPocG1_Km|<76rX7FEziYuxcM38dA#Q#0F+gWJSQF zlGf=lMiFwdEW1^?8rMqIf2U6Q3S>yCDhNdNh5!tE1b9zBK&z4Jky*I}R3^E#8cA16 zll6jHlb3e^GouK&-x{LAF~zSkjQM&(g4Q-QvpPU=pnAi_|p<4=zyp~2>ziJMJ5bpy~4X#+u^K;8S4041nqY03BQ)};& zQ#KaXJP3VnN!MJBVDer+c#yMhFpHLb;g-zo zsX7No`)Mbjo(tjZpU%J&uIWx7C3>Xkzi6Ta50znkO6s0h0k4o7mBGAT?FsrIZuUz0 z0iDdfJijqi>mSolir5O5{oJm3mmRt<1>7$8M}bWcfr%~GdyJr=3%l@DjSyG2{-LG$ zH>V|SKF(=Ej|`a+n!F>4tN%NJ?kGWcZr%-o+x!k=3y^z?7O^$MwE-|1&gB8=5g zHzPcJltKEf<&pjao;>3c+fdyLzS=0d>;Q=}8z#!Wzt2s5w94Pd$%}x?B=*>OYM}Md z5UIEWd(um~FZkb6CwO6t3m2_O;3IYtj^V5)Rg_Uv9v}}IiYDRAUL z75Y8{Ns)z(X27^`P990DdKmh;djd|qqjnU9qMNfPX5yq=nznW{nkYIpO5?oGi^YZ# z(pQ|1O+#{Ovq{MLEf?_D!6hSV{kN1FgYdhe|3$tp4M)`o8~Vr@-&W2_oi$)fxCG^f)xz;(7wGx50gM`ipu!9aM0urYXO-;$Qpo z(CyLtjK+I-`dO*2dS4uWiLOuj@?|Xc-AMJvxV+KwvcXvgWLHezC>dmxHyHl~XubbM z^KNN4ylXCl7Nou<7WQDv!J0S#EnbX=qqBSu1WL?};eqt0h9~e>PbR{F8;PUi`bg3D zG3%ZJD}OeA5hW)Su+2>efSa2DNho9+u~MRvrC=u(5`Z+pYl>BazTheWgDUW@do@4( zc!+$4&;I3@9lv>lt{O5q)T;mYEFUxOVFmAu^BX=2zmFK12U!pLGrFIddc4HziwGmXOTOqy2x`VlOmZ8LV&7TkO zNa)!qES}p9l(@!(W76V1>qCCFRMfNjwz@>m$5=;aYT_##;4o=M0dQl#9tl6c)=C&X zamrUEk#;NtgEoBw;v)FS`PA$;p5|D*SV7!dQJIfxim6Rf#sj!Irs@d&69F!)nN$QqV;69r zsi+hq0w)#pD%PW&hzPrzfc%KKs&DlOw`$2>ULQrU=v=SRqkcFd(2GQa8dYn0QcdMT zbbkbjuC`OfM8{cX07{4!d3o0KWd{0>TjO{O zD%R||{l1MdOi7FmY?7+qba9J?*!3=0rB*bW>2+y&!EdP12wAM12#-G2^yVXk> zMB_k=@0dSQSqv zH^%Mc=5qlMpGMOm#|`M2b5^H2Q{8W4_o>mUY0&LJ0-DS3T@33wa^ose0KaLsXTp_J z)$sFZ6i1+qU|zp%X6k4Era7`#U2rd(TX`>BFv~}CyaUHH9cR!E=1LfRdVix$;%^jI zu>S4Gg*_y%ZX$0sw04)b20*s9DWDdam(|xre*^33>ie2;7e*H{m#yRHr->jEY8y-V zy}x-*EcIQFiMGZS78E_?IMn3WM4qrm(iT)3Wb>9nvwX$<$5eP@P+()Ljs0TS#$&5R$@h*U~Rt)4pmD|C(iH#|<~q@o$^o*E$n*haH*m5x^ zvDos@p11U)O#uopjB9!k=ZXeXjZdL(C5bclEG-Ad1u#@8({}Umk%EU5?p_|4mP44F!Q88U22`AKL zk8tlh+%1tedw<X|V^1t@e-O5a0RN5Qkybs~R~(zE>l( z2x&QXjxlj`Ta^!$k}OqB={!>6Geoi!v`i;n>3G2>+%X%XX_P@lVDS{)-{Kv|r+&;h zc7!{=`B<^1zOD~Pv*S$NqcW=Z!&&UR&u{CtkmDhczIDBksdq}5l`Cc4#o$z|zRmBc z%%zib&lkzb&;De46E~l6jk$2c7W@)2k+)d3$&H9R$EV=b3dc!O(4W$(23Z+(f8G@4 z77-A5gSa}cmn!Juw|3ucV|o{>*2hg2R~Ix0Q%=+`uS zcqpDg)r-OF?PsYTTs2l(RBwNt%0t+}I~{pt{o*mZ%0cq;K3r0m$p`Zoeaj_RzOylH zb;(t#?rpT`jmFVuT#QO|4&Zl}zCHCjn)83kCu_lh5-GpnA!+VH06WWl+5OWQNF!y|79yuC0338LmJ1R{A_U%Mz@v={^b~PwFo_vOi)O{rs zD(F_pb3x3mC(QTfN%Ij#JI6ct`LhS$atVa7FhSdlC~Y@+)De2YQ^$b!%qfgWzT6m4 z8$fqnSF|aGb0X6ObHRKVzDPA9HsJ?E$;iAX;Ai-6T^D;Py`*8M7Oes)qVb)lCJQ?% zO&fG8DFuWkjkG#VO^;$+@n3JfR<|d<*F}0yL&QQjd-bllP%+c&4q*aa@V#$6ae4Oo z2VB#00{LCERKq_{X*_HrS0YT3y0M|5c=4S_M57rzS0dnB(YQkn)RkS$C*8c%b}Rmv zqRR)mYuk;;O3L%rVIuw#5#Oa%PeU8nB!AiSUz*^td)Ww?PWpl)-%g@mjZlxk6_t%Y zIVW;=tvPiTI%K+;Ayh#1u;S(5GSN+Ffp0TgO3 zh&~HVPYDtk~KOv?mnXeQo^dO9G?L94H8JV_)%lt*J2_-F^6_~H%5gxNNA~Zf&lgE3LRI) z&;&%;#Dy>Q0c5xx48#uI}SRwEfn-D}E@;X0;7 z=?rR-Veg@ln8WW< zP~hgBaRdp$zV!0zag>T&q2%{*D^)D#NB>3ZALmPmOby>?MpugdM*l0?oTpBj%6!rD ztIt=ZRPpWWEoH=2^Tfnex`!G=O{Udvv>yAjBXoJLVY)6yG=w|@{d-iiMW`1|4%s-&5(|e(ekdr~=*)4-! zacgd#I^7ut&yGKQ?S?XkvADV_Df7!rJ)}ysa6HV+_gAuwDvj2c&b z?w7AaS0gYQ>MywSq`MKH#xy=v?~NrIp|b8yK4zW!kQ}tcpc8UsJ?l2>s<`cK!$M(D zzEZ%*)zUr(FK2tRyNZ#nXWxU$)b9GXel8x^ON&ZVC%#N|J-ujvOq#t>m?{8}oV-S0y0!MqB&>EDOLVzQiJ?K>@Zj~3xG1}g(|5l&s z`)_M5Ep#G%q+RReKili8hN3anQGEl9T!Ec~5g zd?mcYS&pwk0fMWc#r4A9{Aq?_35odIE=51Wvd*Sl>25I-M`b&-MBoIie0YEIp8Qj( z%c+@pOukym+M1kd8S?TR#~p?D zdTA&ZE`ClTj3~Ia%!42tFn^50=?;PcC7T)Osl+!a6WZ>MmDTM$(eIq# z(29D9?pbrqzR$IpY8no|)LWiB;%?G-o;A!A%zO@nchhG%KL)<%?7wST4Mp&O>0#b~ zfcQdD5fcDVUa@;knDZ6fl@oGz8b`3ZyOk0jM@F3SM4-ll z6z3Bu$|bFomC7KT6*I`$7AEn~cQgXEea8{$+z@GM7RoP=_!noS_7Y%|!N6knv8zHY zX~{pgj;AUIQm!F$Fo|c@a2-rN5k)S!{^cPEkfvvNg-gZIXt_N$)s(QDH`(f%Iy;S|$c1-5*jG(=p zET9jQ?9*7QEXWwWumMR)v9`%@<&ps4WfQug$Um6 zm1tmnL9M=BkWUHWzEuBAz8e&xK}Uvq#zMI{DXnjlD{KbWR4BL+whOsJ`Uf((uZ3n^ z@i&hk-?*-nfq!9H7c)XS|48_&UM0}8G1>U{K4|{JNnC-0UNsr(iuwX6at0Z!@Q<9@ zAoL2S^(w~r9yMy+3n=2aofiZOG6^)Bl~?ZD8oWM?bHv!1!OscAMe3;&l3?YLQx>ae zbF#VK$!*d-GN#GxE7@^fff^X3(7l5)9;{eMXk6aM%jE-NFOVou4*I8bIneG5R zYX|u%avvjcOtcz%4!R$PNWM%6B2*#gQ3v!zKhR#>XRyFm%jyTbe?JsbqF{kUA|2q; zzCSnk+KT*Fx{i+a=)J$s`-A`Oli--ee})?$KiT=8LmC||&;QQ>k(U1dPxJqk4NCeP z@fFP{qnf*Mv@tRLR`WZ0lqWHN(Edqz9Quiiw~Tt|x)*yRhUR;V3Q?mR21H^FKcX#q z(t>?2^un-QP-yVGt-7N=yDso-RPle*tDG;~M}7ae@?j4Znl>S|jk^i_GYUT*X8Qjg z$VML{=_;Ii)55;wvBo+oouKPG7i6B*CP&i|g``bX(r8AuA{6!o75 z0#FY&Y$k#$1~$Dk?}7%C!HFWA096e zO~@14)1Om`k!JxV^tYy6887IbH^f0RsY{>?6N}_4z5FoL3g^`hH8=MEJfo^ z5A&H-rY9}!r{6*tFs!p6j_@-~8#VY^mjemJaC=US$c-#w0(DW>FLy^@clSTMyLuTy z8BE`EObr=HB(*|5FacOosBs$f?TW@5QDA2|2T`1+&IZesgOE}V1D+ZK zx?v4W8kI~_&ty)5N{48?7S$dFcIa}UBE8>-1xqlPiZ6#*9n#Z@GD3~C~{=BAzY zfn8H|T-QsuIE>cR%3ZSe^L&l+e zyR2qFQkP$881bm-@s*=~_1 zl$Q`Cit04tk(v5NifBmfIm&(XjwbGDtuKWlgtU?gk5pxN#BBQLSnMw!GzR%F$D9LA zIUQ;?Bb1Dmgy-v)(0^5cD@hO=S-7fruR!=eItK9uSYn*fkY3r2?MG&E=n zS)n{FqMCD!uz(U3SX3z|afS)ePfWF77ufYborY-)pat|5rSb%g-EK5nM789eE8u#J zSvlcG;^iZBA=&24C7_z}HpoErrkW@#l8_2*AFXXK364CA0k#wQdVw2Ublkc^=Jpfp z5G8mXdhiAQc$l}|gpQ74rIr(bf0pJc7d%5BkK(dIqzwLaD`pdcmjF=|rD6~8#r^4X zwdKW1;BKL{Om~Abb6D^cEzFy~2nU?~3nCSKm2baucLWC$a!r(Uj#6t^HP#YeX3Xf@0^1R&{W>Ie%{^ zSZVvnAW~R*#F8fqO6?bj5rdWynqe#ww)Y5Qw$X-NB#tNKmY-5VY*eE8UGXb&!-KmFJO+D$dfEu-8h?tp&jg99q0!NQ zm1Pkl;Ui&uqUoM!?Zt=}sd(nucy!a&BXkxE*oY3r%r?wW2f^7w1+Y4F`GKi5lt6^! z!48lirm1{!VoGnM_|>f8+T9C$84JYfzD-%RZ%yd!m?`=&VVp=z(DW%sj!q^ST?193 zaB~q}(U|^*HO<}e(#Kjp*7K8yp~Tt)Tx_Cc}xuO6K;)xkXSBruD;rWE;dwtrzeuz^pko0AVJyEbK2CbvU zaD^;CBQY3oeSizak#dPzdKjRjv&B0@>mLpsti|eox}?FJXzB8W3k^^scaFpL2JP>`n zGt&A8d}tU}k1RsUX6R4ah08m4zi&`V4xdc#9gY#5qC*jO2k}rYW!G|HDrLO0{1Y&X zqz&DcGY^aP&8cYrNP^h!8|FV>kjl8V6#piGswfw6hMy~zHaO0vuQXIZfygU<SusbsRhQ z!&9Cwd!3jo$yoKk8vIexhxMAcyD^Y)^5o%ly}iwG(_vv}_cumU&)PhbjNb0)>Dl0& zEr!L{$*GrWG<$X6MuMGIL30?^E4DViZaON%NQ~GdoN1k=-|l;=9h+~MvwKA((;RpT z_?80Hy2OIxHp$H}g54#fy$h)|gNo$Q9uGvH|9(8h zuU=|yB&3RX!SnMD=YphuDIsX046M;fF+ev7LO>V<;{AwGl2!|$**%NrUR3eT(C;Gu zCkoTTw#Rdnd;t}BbpFgToo)O{>w{w>mZ2K@r5`IQO2?JS#p#TM)?kyDos8QOce+4e z`Q(!(g8!!%;KBIPQX=S|?%FtpL15el5Z{K?)((Z4y;&5^4vzw85Ce!q!*-g-BMi6h z5|^}G*2$%Y&9(MJ^Dej?#tr3qM8xO?^St8WCPH=4uJp@Qx)ljXOtBRd4JWz4T;@C> z2?Wk<1~?U3oM==f@6Cl!@H43CUC2=+lujRR`$j_$=rgC@XZs%sj44|?8pCn$pr_cJ zw0(RzW9KGuBpkl<V9_?r8c6`ZWpF5rzbOskSV;#K!5<11Ws+hw&vM4rtD4E zw$pcr0|_V(&_b)S#v##Q9q;5)&8??Vz?3M0aZJgvqLu*R7@Xu%7`MoJ!Rw`BL;>`@ z$PjZqpe|{n*2gZ8I2-M@Zrq6ValUq$PQa`2e3{47uoulQ*L_mHoWus1NkUD^3eTv; zEjsI;#4!^S7k4L#>f3joPUR=wT_m|@JIxErHnPYePn%xTe8(icLs&`s-{%8vJOCap znN9hG`S0%f7iSvP1zL^Uo`v_RN#YqAz4==g;dqhDL@cp?j&)4}K0ln>DbuNF8F%D6 zz%n6Uyc5{ILJ|C0Z};xcMmD9!pI(}G%u-RspP6m5F|;-UP`Y-XoVkmUae|(*VSqbg z#&harnW@C6l*y;uxVQGa5Os~twLQ*J3%0lL;Aso(?1)&~m~DeZ7|L777Ci{Sc<(>1 zo4wwXcX^}XMY}xRtwuCc*;)y2F%41vO@zFHB5I)~+Vz-DX#n4BI5-Cl>=L@0P&`Iw^sbLn9l5t5o^jZO>pVQ=!q*NzND z9_X~}vJDbwtABjkY5Dts$svY})rqX!>d&9EJS%vUQ#~&;uG^{a*m~>hw~?aiC7@gB zL9tl;;4wvBJLwh3*Bl|#zs7eJ`db;{#viXwX2Cu!Hen5?YE zM0B0ApRml@H5`vVg!nHyy(KcOy$I^zD+yz1HuiJEgvx>5nkX|)=*y@_GF%v|%u+~5C?|aW`Bs+|HR!;P zLE+W29bP&@s1nH)ZH_%SiRq#PKmscjQ-B48XwqjgB8kRUds6&k{pBmsitU@IVk*tesV&gXvxyG|9QNI^f#bm1(&SiY-ZZ@W2@ zOrM#h7{GG+4NAd3^dE&HF9Lxz*yda-BXie?QNAf}cHhu(T4*||1lPTXb7pNySMM%6 z`)S6x@j#1ceTitvfGjBGx;QnJxgS?pP2*_0?CsR$F?qcou_Pwe2Eoh}YDMnVm-q&d z4f;C@*l((>M=AVI#^nTpZv7YbgoK1@>?Rn?4H|I(V2}LH%s9e8YW>-q>WFn+)8+3> zrgG!dT>M-m*Sb*w?E1G%)Q}w^a;*`;x^R$-wgl$N?9+uyUelr?Y1^^T<|P) z1@J+9;E0X36}NMA44>ZVzb6lNTa&%dH*YsEsb=>@VpD=vK%B<;gn@yRcdOgSih5}3 z3GC(V(M9FuBY%I!aV@yLbfy0M_+vV ziTQSl()3$(%Qv^LPpgEibkkNNZc(0F$NnZJcR$*Gh7*J3}wl#trc!h=B-&7ZZm-W>rp_rUxZJ^uMZ>qw;`#J=bTJNn5 zt(3TsHx?NppkdH3<`8!QbTwww9?4+nUf#9erW3^=ywL*e7DI7I4c&W3NH zR6}K1>fO_8!D=LmdsFwbV|YR(yTosyMR(al)U?ePZa zZ2z|8e>}h&0%u3j!fNrSIjaR z6I*7;`sQCT2HZ$t^BO&FKvfz~CDdS&I6VP|NI&*KSY$A8>qUUWwCQ}&d%4{`7$o1$ zbH1jAM+MttzhV?tdL+oE{D}4ddzl{*?G)9lF)rV4pN0C*hM4YLW%GcwhaXrPwOL=+ z73{B8Xf-S#tWA>!#+2vm#%(^kQ8a-~C#z93w|x*c1)uE(4}-=qZ+8-?PMDCN{VjcR zM)hY2?Mf68L)X*;5rx%iQ%&zHCv!n6DU3z_To*NX=4OX$GI1nMDH^wo5jVHiU_+fZ-xIo?k-m{t zH#Mm(bRF8M(j{38@J=2dr3J33Rfy`2EX2%PODlVCS6jdescJhKpj1SacabHpcYpr` zcm)!N7A)&jRULorhsuQj^WQ~FX(kJ^n4s%Q*K}Bp-{}usWsPAWb1J`FHSN>t z)IZt<^Ablf`E*wBfOBjT9S4<45VA?9Vk5vT((@{Y5Wu5YaP7rjn+D7AO6+DrY?Km` zpf7T7@j3eWw6v5OS6GM0O6$do*~RYp1;2y#Y>UA}4shxWpDQ4`jQii3&H-rvM$^~f z;r;t#;a-EJT*hsTZ}~PLtJR)}oD_|~?#)Owb9P=EoWA!!l=*@9Xk;v7f=}Rpb(xgi zq~rSiv`Ns7yZi4E^0&Pb^jYqIB@Yp4`{-RdF0F@NmkAxTO}sELxYDkg_Nyr^RRtR= zl5vX0KDZ2Y@RFO8)1K}@+p&txq2DrC0+Qe_YZ9MzrB(u$%@$n29evSm5Iq0~$ZSmZhvH*_F zbCw)UApZx!R2W8yL802W9u(x zVICZYO|ENzT-XNkd9I!+DyGU4UD|UrWw4l~DmtK zjD5Eb6DawYPQNR11@H~}^0MA+@Y&r5Oh_v=D;vPCw;O%`v{R{3-C?TyvM8UKKSJat z{Vu(QP?AiPP0fdrgv)qA+rYrDuC#Pnd1cRt+@9p}RfCgzW5AZ8%{Q?>L(MP}&Xxpc z2pAqFWbtQ`C1pk2)^#uLY3(L-s*G&a^;(I|Ncv-8)KFRdjfU3ND;$>ffqPk8j)3_M%4rZ38suNSsIwk*0@V>` zuygZvk14Y7dwF{=p|iA9P)hs9d9Mkfq$_qj)@z)o zUEj0WL|(;r^#XT)NNujY?;8Yokb?CgU9!d3(#koa0X_i%H|?^Gh|vGtx;|TdeODmc z$Pjy8nCiK;Yx<*9n>0hIxFiD4z1IXRk0!jI3{}&u!1QA%^ru8i&-L;n!U(1D>)j)n6)}Fq^tZdM32&5Ph>#&?+QFxi z?75RaU%3vx^yJnUDO?6^?H7UxDh0Hod_41H>V!8Mm2WfxYZ@BP>Zz~dO2AtPU5n7~ z%P1-WjyU2*Lb8Xnz*@-!~ zCm;NFra}aWSSf&=&A(1j>k>H7IO==H`QRC;-;=!=0^a>9mL5}wo*zUau(ddd@zGz- z0xN?qG{D5;wh(Z>7$oZUGn_XrbO6S(9_PDv?-KgO=3hU|3iw$1*A=K`=gyDfkVF{0 zh`mGE+~vsxRpPYqPNd-L?MA{LkOxi;`iT;M3|e+(;W6*_`vi~KFqJQ++9YBkv0%1O z$s*kN3XuTc@!Yb{TEcwb-PQ840I(B|FPhY*-2sWw)j?_Lw>KKMk9;0?_-JX1><5jp zN5&PdPcZHOJ@U0WXRGqadQn?AdsHH}ZUHg^jpNv0zui5a&ss!}0-b|fXRD1uDVgbT ztggcsUMO$Kd9AfTRc~zxJn;fR%?qap^W*gpYrGYVqk^cxbe#dx3I=VTKW$&n%3fir zLt!cnuSnono^5^G)X z90(JsFjlJI9$Y5V@2X}DAd3BB$rA%$FO#wWIV)+#+6`#vpC;TmcZ(s|DP{78^dqe1Cx^O zuavw8%ZWk>B_8Xs&u14EHwDB{4*|<_MJ`XTo8SCK-}inq9fQr~#R!%YtCWg%Ssj2z z%mlAJQNvII*5vG?3Yv5;qKDj77)x>bEC7Bs-e9=e0+2? zmKn>g+=wV)94>wS_jLc> z%#tl8!Wg+Ksetn%Y7K3~J4;t?ZEyd%UtU!PfP(o;mXJ4vkqy40O4j^mhv_+3Jj6}h zB?gf-0{boGF>}fe<)FAC2eUw|*JF(cGpH*fP!(*3!hz{~`l{du`*e+d2e7vSLAXwB z%KpO08DrYn-cA%fflE2me*8xFu3V%Mu+wtDxRK^YgRp}Yp^;z`ZiKVKSLv#d4_4PT za~1CK-6c}+0m23R&eQ4r;15Evh+?gyIpD8JrNioBYA|M$Musv#nbx@EyHgP~*Y4+? zGVb*JHOAfog)>M^hC!(?knuH#bR~C`KgJ(VX}P@Kg>DJEM4opX@IVgGHA>gbP$J!B z+sE$Q_?6F6_YYo?Wj-8_11bgCAwK(i1Lr;_Qbi-=HIgg^hNJWgQ*Wrbug&r?y5ynb zTx?H})2YmPuZFV1b-Px)4Y*2ShL~$o`h4LJhLzjAqN9iKLWtWkyMyt2_PnlccRFt! zCcbiYwlT>?N<}z)JAq9Ber#y3g&c)Lnmv+l($1qei7J9>ytiKnGLGcK+7^J&O^ND_ zMDp@MC_wbPVMy-0_4yON=dJa|DxVUsO1#6}G)OGFAdw$JtqSFn~H}1O;Cf zlsCD{=FtvfBE_dbb9o6SV?uxzukz}k^KK8IKIN2ERgL|&vmyL}v-hER+sHOAUpnXa zN!(x$^?}4sFsh2#r408K3iacm4x6&D2){w0N_$CDMFoN?!lqeV|D3I0{5>CQ=(UCy zCT$|^7((Rq&RaH}@|oi}lWX%{XtM^*OGzO^V==h%{3nLxSapfCA==8H>WzI_`#Lp1 zcPX!(%ebatTW8X5$V4xtB5A*d=8-D8gl0JGcDbksy5vx(GG9*1!T+s83+ksQ zZdPX7^ozaAlpP1AzjF_g5L^+ei~N5*a0TS8m6^aH#|2$v293^C(Rst@dxpO$Xf&!0 z&<@&t7)BeA&fJXPYiu4fHl?>HPLZ7X(l?`QkkMKKpIh^Qea7v(owYjv>TF`*oEeX*951d2&< z!BV&G8KV@r#45|ED=F})kqZfmt4xBki?AnW#D``xYhMI+d9__a;)>OOw!tgO!DHqv zh?Np66B#F`V61%Lr2J}m6~wCEhc!|K(Ini(#s0t8e;r@`x~H4yO%nby)B=0J8fs-A zzW_>e<0z0pZUUxQooP3M+@UP679uGFk0ZlHM6(Av^7jfPqXLRck>}WT;;BmRBOJw0za&6p1ZZ2nZ15WnF($2mh9*o(P-4ak@Tdv=t#P>Ft#!dUug2 zUDUVy$d2xo@n-uKx2c+WHh&8YSl{@VqCDD_n>AAE_YJC$XzauB21 zA*-j3QNFD@h2}`v^-QHhd@zGoC6eUmZ&(E7B1HD9avYxQK#nNf^DACzUh`Oc`u8=Q zCjrq6g*XKh6+hc8I1Qf`d;6rwz^Fe6q21LD;x{r`+%mH{XWB=rDXCpJski=YrQ z(~2;xijYBYG$-tE$15a$&a|)VSK~>6Crbt{?Vil4ky6Mh@NY)N4}$sXzAxuR#8{w2 z#K@n~J~17U`wOG@UyN(F+gHWKMf4%e*s94}D3NTH1x`tlk!UG&@WA6tI541-FfEZi z_~}Q=UV8$twS_JMXjF_)lFxblUhp~c6E0P_GII4E4LFz{E#lPNC#Ed z!NkJGo>|heqI;0<494DpXWBS+Uipb1S)C3h=)+59lYLS1ry8@gbW~?)OXujMfpbC3 zPc6skUSD?Wf03;D`5NfTA4KG%89rtO@LqFFd9#OhPLdQL*JJFn8Zl-VaQdV3>zXUf z^7ca!U&)KRi{gDaO5@`hmqDC51_BC`Rk?BPNT3 z8uWK5IB-W@^kzHnIv#}!d*2{YMv_Fz#@fOxaKF=-zX5Yo9El8y@2*!H=AExPfhFW8 zU!)G(i}L#R=ZA`|RAcB3iMSia6-9W5Of8K#oADURBGiH4)bB0`^`?%hVG*@#z8ly0k4)E@TRgI}n6`70~e+qz_g0{gmyjtA!vP=#b`p!KwCJG(0*~XnC z`y+G>LLKoJaR){iEBu?3oY^v7m9f{N!T(Qf*B#a5()1Go0f9(FAyGt(a>YoEg$6)8e**hBsR=!Dfi4R0Nk%-$g+Pzq;v!E{1|*{-ai&(dWO znWq8?q@9|xpWcrxYS0@ZLzHL-lql+5mrbxFOb9c}+GTKK{OIrogvH5s?`SZ}d1e?7 zS>MUB6wljg5BEMXb4CQ!2R-MJbo%f+F}4F|*hhB&ESo2|PH?BFBH;to9>`bO-WDM) zeHIm?R8x4PDC)G^{EvXB*?8Iom!ghOW_4>&%1n$72};RBIy^|Cc|jETJF?E!tafb$ z%xt>n}G#v+gB8$@;g7IggaSyOlju9*3EZ&Bt8gumgSjTvhb zEns5jz+5$!cX(06(o07^@67f8OWwUg@EAJE(r~wio=~QtxSP`*f5M&jOpUeCH|C-K zap^QhZls5J80aHdPhhsGNyV*9wqH5JG?>{M%6Vd8oOrAK?!%A_@n zx|Z1n!Zigcde8%4;)If^Q#$%}PeT^(*_ghUC5Uk@vv;!hrMi}$|0thiF}o&g+3KK{ zoFhrn_mV;pUyWEx*?rz($tu%oKg1uN;WTixJjeZ=Nc&KvOeXVH*FVRI%H3GlS-eci zf$G|k12!TXWAzXIos4gDc}>2`CMSnJTe`|-b}l{Q8&9^=2~VgEZ{9r?q*U2+y*jA; zQRwwe&oReG;rrEJCc!%YdIw#XTlo$%k4cf0_&Iljl^7G7E~AuPF^j6New?|P=xE6} zhfVOZUsUKoWLVoKzUaNiD96&Tx=2}?l2V+@hv1<%vqlHK6F8Q40ruxX!0_6IIg@c2 zzFSgivWg66b_At~V%*Xvj8Cq}QCQdk$I__^t(D2jN;ac@W?00@TSx>^aHT7LppNp8 zHa;)OvHT%Wk{aq&^m{Bw?-QT7;HQIIOYFA2E`#dM#vT$K7gobp=Y6iJ&k_qyTzWe= zVgBdHNYLF-xjY|Z^}0>Z$kB8dEoo+P@d)P=t=PD^>Pgsh;-u%?4~yrG2$$p><|P8- z=uP+~!P{&6_d65WXSktq^UTO|F%`4vz zFxDC@8q@j($`#;kG>wEoK~wuwuuC8>OuEJ9*zbf|f)y`0<9#jkST=n?B?f(03e(-;YmYDrEht=PY2A{dk5Q!LC9O`Df9DU&=vL$aJ zU(@}x!p8YuO3^B|HjX&viw{O7KC8dgu=Ey}OkL}WMq zjK0n6Y+&$opc@sXr635L2K8+VFf37x8h zF4+$5Y?3zy-6$|AuAMWn?F$YHQk?i=0E6!zy!=y$qwi|{d8#V^GMIor%Ow11voMAi zW%!^N%4iKzQb!4PR(^`WC(G&^SzungspL?gO+P(q^~0lcdFZ&77yfviRiGcU8k#0ZUsW#YK1qrFMjr>L~{Mz*wW)@a^dn|Glq7 zf_$Xsia;Q(6h(%sSM`P^Z4Jb6rXpEMvUsb^J(bNNy4aGEX5fWzW?n2*8jS`GOjo;SgSgUe+3X>{Vt}rN8i>l`HerdObP!(9`C+pHHinF92 zNfPUeDY?BiFq|0O@COVVh z!bEE|e`vI)2PQ}z1ME1M8o~fO3Cr`0dHYmKmEr z@_$q}^Y2ijkw+*y6rRfAle^2(4!O43yw#7uw3d}q<0%Mw-#@o^`n{@Z&RZ6R=$T

EAf`5qlJQ1CVJz~C5@v`T zLhc^UZzg>W9Tmq5zcHFs`W0mszYPn%eC`PPw^^z7Yk%ZK>%YWYNkn|%yz+wo5ZsYd z3C2lsgjD$l$^nc@W*=7ix=5_k3y>CNC54WYHE?(G^t@7TV$i{hg)T47JC`btoskE7?vMNh5*TsY44M9B7Js zqZ24VgOH;d!bZO-EV+M%$_w<^SnAs5Op5-!8Jl$StHTL^N7VCg) zf*PPg;|HBLXvyD^)5&n!4K`22KcRHUvSVPh1ux1+XInl$V20>=3rIB-CiW@8^uevx%)jt;XI<9%}3)J zLgGfYN!CY|+c1rBJukJmcy!Z@ewVf1y?#g&B`Y@m+~ukiN#6Oh&V+tq?AX1EYQN;= z&<`}da!j2bJfm*n8&n_R5&Cuuv7M%I%Y47|nRh#Jl#YMW6HXsbG#TN^#z8fD>&v6l zRl%#v*ez@DTDsdW@MwC{r$h&hA`-bs8c4@(8)%Tt{k|N7Mbqi9xJi^4^$}tj+Ozv* z>#B_8Qf?JmCfz4P*S`YI7LH-XTbVj8<2fsA>ym}usdkTW0vhzIMBWX+`?xzZliBQG zUj@V6ACm*+i(#O4+9&uz?~r$0%L?Tg1J{$ham*JwPD;y9+Q|hVm;6^l_)vyIG&!FA zA5hg;>6;RRaYVZzXC|}>eKDV;&7A@0`$s$s!Xf>l3g|nEWL+Pna z9!*pZKZtvWldx_Z=B+F)pQo(ybtXyzTyhM9lRE7Vc{U28hzGBj-3L(j{bU@^3o;;;s^2K&M)ImM@wi2{ODpv+N1~?~NQw z7`kc$p;ot3IhN^?6agE^U3)Lgsh-+DdqRiNF;qto6MIBi^vvZw=W z*2jU4`;fFejEi8iQMSn3M^&h-C|~+Z+;VOaDjl&VT^QZ!)CHpY!Etsp^T|WRAEWx; zn#h#B`!T47_EB(DdpBI|pfh{Abdmrim|mF73#0O}Ipo+tik)~%Cbj_}1C}060c#~j zHf;V}AIy>ilB^LM?IJX+Sxf$({g8&X)Y)8q@^A0_wK<0B;f{C*wu|K2<;DiQMDw0+ zH#n8U^X)R)04+}GeAfqIBqXlZq~7HKyFcwMX%7apIooxWzF3;t1Y8nQM!8A$_AM#B zaat^NPkI-VAK?0E)}_NJEq&zIH#VS=!M8dNZm__mWh)PFa8v1jib{O;s^9H-FkGdM z!BUhBwE&BVB#d0@z$h9|}ssb8p}X-dO`Md?JjmV_mg ztTH1&Q*SXlJnSL1V?x_LPc=Lt4L5cF(YZqkc8Ei4_H^A12I5uT&&H{EBH|Y7lJZ7| zRAtrV$mQ@Qn$chCj$8mz!hvZ(yJZ1KHBjn{(sNuRm$#88wd?jR^9%J3uw0WLMMopl@WOiNRLDiYb zsQJ;4bo-}Q#dYI9piP<_FS{zP3n(#7#l=uc5l8v>0gvDWTc$QtLy?_y;O-8^(9C>) zxx(ni-iUea##&BKX~SDYGglR<_NhD0A@*!Z<4y<*;gasa3Tca7 zsS2};8Rf^-e(NL5OvjbMoIRlWQ?0)r98vW(gOVS+J-=lu(0VdxN-2aTtHqndv621P z0GPa~DR0aHdzD_4H{Np%*{W}+?3V_-t$s|KxipyiPh{-8j&{XGpy3|vB6i#GrK4f* z6_UE`$yt)-!IcweHB_nWFY`bojP>ki!k4nulK))+-rG6u)Gsc_UI|BhFga^*wcB72 zV(%gkrp`3`9gjvZnz}pFVkDts;(#K~$l4%4aM*uov1173;c)qOtNtVDf=F@bvy-<kTdpytbttif zz9U0ls<0jaN|jtbgoX1B7<09TO8>G0Lt zEm)9Mur;E9RS&&MQ!I&HcoDI!7_8;fuW`suY-?H<9JRvTxw`jo2dPab_qgiubUN1M zcJHMp8k3snmrh;X=gd&k7=E80>wRWj&vgsss4AS2F_K4W*%O52_RCt!SRr-(%0qW~ zALLMc&yIl7V=wDVyInEvs{2%QO^Db(5Ua3+JpzZ%L}k)_Ot8T_G4ylaPr*K6i5Htx zcgeelLdU@7CE|sC>F~Ke6guMM`eAz27*za-f&&q?Mi*k(yvH1ZDlm;@_jwWD$F8vK zS{X^23lY&6ducX+SC4B=PQkOaNcxrZwaABrB+PU@UHS`Um!57(oD%W{F~)P?O&!Q| z5R;h17$!6}(!!cMmuDSPu;kK|Bq0>IiHXhjjvNLji?yU&sB`qhT>G_iZLEOHAB^zm z_ZtyI0C`FH105c~mhQW}^h^PfkLs)MzDBkqM+|XYDlbzQr6ZbK!y_}NjNk{E$>Jq( zj*wcJ+c{D&M1CAs1uCeJjePY*DIMqg75-TRU>ETbrcLy96pUt4kYkIqtq!rh`UKHz zaS%iD^X`e3{QZpwJ4D!gCs4 z3!3PYas4e!)MfBNU!req_tCir^l(7EBvZQ?i@JZk%$IKq@^xHRPM3bmKhzk$Z)^+o zf%p-mqA~Fzu2nJ}JCsUEd~*paH$N>nV_82|9pJyrM1QSNJWFvUSr`Ch&D(|GFe*l1 z8P>JZmriN;g1|EI5t{9=Mhf7_$Rf@=e{;1!E0trrQTdLZq4AzB`-VtG&*GTkq)+63 z<9$Epz`!r@4zGw%9_dgPqt_#cnM3wl#zY*Bg=q=cicJeH=glB*(OVv81n#SiU-Dww zxthioPh6oNQh^VcLXM-19JirS0c-9nN9eIm$;~2~DMIa|w#F^Pgt30bTP94CqBO_N zXeJl;*1apF=sP3I`t}Q&Df{cXev$$dxrRURBEjXFN zj%?aV3z8=TvS~rtp{}^s6fMU0Wp&T{7{fc>)*NDADX#Ejt7WJ4!WK%d961h*4cIV){HdW&`v2lW)#LI(f9Ll`HQlob1 zan%Ts+l>n|Ba~@XE8Ce#NX`#$b*HZi=?t)2c=Q*3nXBoC(drXF`B&%~%`KVbi2{7e zGqWO(+IsBwXR54bdJw&wggYu7k0s77S5toSw~n3i`oyH* zhq>hkzYVYYc$mUjn5}B1)GSDAzg${ZDzy`tI#zb|PHgf!=zJ$2ubE&8$n5L1^{$wE z%Pxet(;W>+asSPzq7x66N&Gwdl2*k7F!Gvg}Qz^AGU&!qifgy5DjA62ge2>d%3oaj%WHLQ@Pj z&xK;=bzawBN1{QO>N(T7dz?dPSJ6!g4U(VmR~-JbKSZTp+^d@|q4gneiFIZ)qqlJi zGc#QIoS}g1{9n8NZ0R?z?mCz>ZNnZT_+EI<+geZ%<-Y8H+2}9=AK-oHybDE{&W51!wu^aiGEc1>~X6hL9EcZ@| z5mS{3Wop_UXW(ytwKQ=8W-I(VvMuG%m(bnpc55$6LV`1dZwdUuiAQ-iNiPA4vqTx@ zs`%mxl7(F)I>6*AS#E8_Y0z6Xyt6ynzqSazm3QmF5h+hyDD|Z>Q4LG>IINY6=A=pv z5qS$1<=Wtlt`Eu7zP@YHs{xBywU37IQ!PCx!XFDmp6d;mjcGb{9$*V~qj1*kx}t`=Az)<8P6)g%8Rl8vVUDc^dts}%~%N~0njHLd;F|lL|g;6KBk$Oj3 z^i=i>?ikY<26vqo!xaDJ2YH?vzz}E?7^~1i=y1x5rimNolT-Dw_PllGnQ3ZCHVeVMzVXUZ@jrhlf&$?l-lQsHjY)_uI#A zs&NSv#6ChZ^9vn7%hGz?>ghN(R}NLLzQT{b*HR?1FTp7@ioFg`OwhC+CcgW4qxZU4 zzp2VKzYd9wE6Q;8$ls)VFfF$^b8Mty6koOfLmUkQIEP-KPQHwOl$_{bkEcaz{%#ya z;(^v3#keBU1~qQ|m{6voon3H9Zz1{8{iWlCIrz#E+$zGKbCM#D#h3P*X zU%ru0F7W z$8Kzt@PYRJ%UQ7)v5{@GmVQv?P6shnou1Zud-o_Bf=jv;q9I;NVIfzTt@O#&5;0nk zIVG-{TT3{J-fvRzQG92UPEvedqZ5C*Q!S3cYE zNc@hGH?>fRnO>O!J524NURRt2Euq@Ex3UZ?c)d@*b}}FDgBJvMch~j7kMX;?GF_3l ziK%t6seHDws~bOG#@NB6RA-u9NFZD`f89y$m&LFbc4M{eL@2kj7Kr$NPP1S_t@}7G z%WQ{lq)DuGd4ic8rG!?n$R-&gX7yG`YqYI|4nW#H!=dX3RKsMh`nxaysm@>B7V=tU zehI6MIC^8_N=7@qxhdP~Mvf<_2q`_FM9E7uo12ea%CiS@ctmPHg68c>%4c7<3MFfV zcqMk)+-*kat%GWqn`y@~@>VjLWs$i_ohyD`=M4aE7Xfb6_-XS!)R89|5Rz-zri33l#xbf2S=EQocq?BWWN z;Fh2FI7A;qOfI}TqFMv(Opb{i(R}n6~;vsjFx6zJ0zMS5v!nm zQ!UDIJ5tv91L@FSYtfpo`29@F*#0EKoey!2*wu`VB6mz`tM~y*)Y7Re0f#Cgc0O>? z`D_k8;WVkRso(P*Dk}Zgnx>*G0D2_U%lo-)tQGSB=uRh zz&0v-b9yC&wvW&4bD>06jt@_avR1>X|IQ+^By{Y4?dwhgQhU?aIA=74;CQQif|Mid z1+YSeP#YUkne4;Tn`-TU9|eDS{Fl*<$ob(IoJ=@xzjeP~%8-nIt}O=I{@N;AdYgGs z?AF?({&w03Oh=C@UhQd)7PUh(-OnH;vjMc28cGv0&yb>Y9y60-BsP{J{Z^whNvR4N zNeu0hSgz5mx0k8>X|@kY969qj-=sEZN$pXhg&ziPt|uvHD}nrVc9QXt27 z>5y$?g`OrNer{#vu=SC>`;?L2{nbj8XA-1cG@ZRd+RZ}nGcSnZa0{60WRlPQW)sYY z+;4vTs;)-o#c6|lq-!~em3$@7q)9uF%xHKSEp7e{R`QgGUDY-g!$xf>b<8t{7^OK# zxamD|w|wz<-HJP022*OGgty<6g)MMd?@UOt(9y0C4G%LPlz`&uO00)d8Yj{{MI=Vg zt!0}QLnF9yCal{Br_Bsy{sR@x8CGMfhyCz|?)Gc+C&zn2{tpi+S2GB|>e4UWbF~E> z^#v`rvS!Kcava$gqea90iZK4pU;1;1xpwOp#B$}uea8V+L9fkKN8ps0y}xKfcm>HS z1M3g)m)3Uz$7|5vZkPOg*9R)v_Io*3)d!^MVks^wOkqn6=If6x`sqmr6jCVe zi`dXE=J;jodplSN=*Jhr9$j8V2Qhw5D!-#p$#YPPzTYD>M0N&Q{giur(EnQn%krqU zt`7#G)MmrjvL2nFKlOE-NkaOiXX8?_r(kW&lgrQrrfY&%T;E4gnAr zm9t)J-nONd8Ti-0YaYL=*^JHKt)=4v#vS8C9Fe)IAn{QjR{S+zX!l_|rSZ7iNW+RG zZh4$#n7iob7l8Uu#nP5dL;Cfoq9hvfOlfM)>Cd=j6(-c??I3&l#z z+dKMMYY^Z>XwgVSWu$Vf^rW>;{QBtBQ9^z~=H&@eHoRlyC_vb{)uW+wG}^#pfr}_< zZ<&MBCOPmpyP#-^Ax@k9UfVFpZC9)3AS$*m{DkOQImiNsBRKr0#JydIyCdQ2 zpvs!bp-D0*`gR7+^8H0^RRZ>}n6_$|GJj`gMx6y7f9H%@9VROskAE&KPp;~728KEF zbY1nEZQApZkm{qbMz*EC8HA2 zc(}2qS)J2zYS2b}yuiQ=-2B;LP@Z@@&9l%|7bv)XUXYyq`E)=4b=ms_I&8ye6x$MM zCD@xL+KD!z?LRyys}dBNW8f#?<#=I{{5xKZke4H*v8-6h4joGC5c|*rAi1P8a5N3S z>uF1r->Gx!WGR(QvnSwhc;7uegveg5i9bgQ36lt}mr_I|H*EOc%J9{%1T|i-eoT`h z7FcbTbi`Q_-ZJ6U&T|tRiti0!cX-D1kBRHirP$w-Mcfh>UtYWzu~(mb*(u zFKEu?jdq?xeWMkI^}WUfIDl?h`D!+ZBRvg6P2CPM$?1kmxxag&cU8|WKG{EItJ_=u z6_q6+-$m{xTf$av|1_d_Ib%JbUBHE}R`iXrZ8aXJ%y{Nf6z7DU)p9X28+yc;airGJ zhL%3h+Q*bu5Onss`rxuFSEV1%mJBqkMR3znNSBhSV!469JEtN|?n1Ec)s!Gsvh@>|ye@B(cvQ+nBsjTQ9v zvB+OG-^mo}Kqw!I_fAnh@srO}v>Ur*0gx<=nrj?7yZ`lNPCiU!L-Y0ZKKU-puf@AK zHu4tG@Y?!?9$oph3y-EXq_K5tL004@xiE5_?x(J*E8H~?#%>(%Yrx+pYxh*0Z={cf2q`8i!wT9r7*_*o$v3U@7T?u%9V;J3U zxrPOF!z>^CAGaST@eNL!fsq~-)4~#=2s(>ZkSdh5%b-RWtxgB>&nlpC!{#;!`T313ur@-5OsFV$#xS+#w~j@{HUo zyd}D7oPs+><^J~h@VHcZJ98xqTBX3ox{aMN>p5HtLDIdIv)# zlK6$CHC4%>DQPMbuQ_PO%8-3xQu@)5yBVTIvuTON|9N%xzL2CZ4v)}KM1@uvKd$O8 z{pJc=oBQEm(pN23iH z8yL<${0ZlKO9$vsSf=ouHWb0=2$`PnIc-<8i^!_+Y#MP)A$VSXuvX3!q7_t;iVLa; z_kUrZgtNq`liklzmN4#kLIgZl}=Zd_{`|HM{#n62vKbW|BE z?~S^(Y%VFdM0`IpAtjw}2o2AYOQ-wk3lx8u^sY_HRgt&w9rAmoo}K+Q*@&)@Q6R991S$i~yp!Q?SxK=L!8a%^vdAAkB`m&fD=bbSBrZokE?@yFh; zyex1-Yp}emk4zx+kWIA zAs)4Pmy~p1PP%q1rmEt}HK|WF^EjDH2NzdGxGE>$mnvg_FXgf zmlY|=~6Yh5Ew#=Ua?%TMsY8(361y?C>D!zydm2@t?k-;-G+;q7$@c4l_Se z*u%OS4wm&ji+pjugq@n6+y<=$m4*9>0}K1s`>;pnnw=Rg5TnB-gUW<%Gj@-9zN7^< z6rjX*RJu`(+n=w)Q)dBthdsSF2d7S}ofy)Gq+`^+PmBU^| z^DUL1e`sCWUcmZ%jJoR7uV%HoXY`fbgLJNSXlfILSgFfO4|(E7h_rlLi#))i#5mj( zdMLZGo~Z`Q`4u!2C8H$Ngqh1-$3;k7-S*zIcTOM<(SFEW=?V@#Xc&*srP`?cvn1HE z41s8qzMCfqWw7n%NVapc+?D@{$C9hVYSCzf>zUFy8>QWBe2FAv2>X7$_yH>K<1q@d zz~-BejT9<@6&0Qf9(Y!~;fn9Wi7Pe?RnYitdYvme5HfX5DTUyKslR)%i}XA{POOF} zU+%}cz!K$NWcF6A|K&(C%4A;CdW`n9-(}gG7!?+8m%jNiW0fOse3VY35VvO@Ap8CC zJot^ER_|8PypWU}KPA2;y(+rVTGph}POCuXoC<>Eyt{mBfaPPBEV%a_mP_~9`0=YL z6fJ#5e5B%5jqX9K#+^}Kn*$5dwnse(cTlPd^-zdvM&FN!-COI36+CpS=N>Kh9x@*L z135320L%krQoR9Ul9iZ5Bl{0Dz$2S-ME2UP)>=s51R4#cJWaF*4>yf$t$xmS8UFC! zscBwyiiBw&$&+`$fRdfF3 z(u-MPijtV_epw0aC>&WdH2fVN+JL#Yix9 zp|(E;&C9Y~>pE(We+Q)W5>?fFac}Cp)W-)d&g#8gftG)sDg9fp^GJV_b@-~M)R?PJb8t^^;Y19Ary0y%K7WCArRNa z)0A|$k#sD_Q+t*jD`7BjNLxTlS_%)yzIekl9_s$>Rah*J)4=6lXN{Wu1q z<<}EUvLLMLru`VjLpa?}=&AX4AS&$nhn9Ka^1IY>@Y0QlR&u!TB9|-?14_WAI_=B+qsmN+P zxmBY5NYX#h+46ccEQ&oi_=fk$Nlw6Y$KMD2X<>mZh>nW zvz|NFtT3VeKyPGo?MW7_33t^RBr;-q+qjF$wCfV^xSbJ7H;)GHj%5DKT@+1D!QxOc z+fx?Qy=bT0dS0dmqz7wPZR$u|ZFfOyq=!P`a^EqA-ISr01Wojt;t;n{6rJ`f$lCje zzJ;aDW${N^KqP4Dt7Sa<9-H;O;%hT!~Msp)5XDRt`}q>OrKi zDY(59;Sh7V%S9s|>`oCmY5=aHLvtdmf;Z|#PxZlm84~a6Zdyk47L?QFE#lAsOp;v~ zx`XGGV{3(xgAMI+Wc)Fq4v+La=3s=H;>m&WF?LFnD9)4$)$5cM@+z^qnoGzC6=NlL z_roXB>_OhEYZl%uSl-BaS#6=bK3{bI=}M8ze6NV+P1A)Q=7fQe22CNBhEThPq&I)3 zCso?ytlaa$&p6>aPIT$P`fm!|ohPrxW+}c13B~Q&{eg5dI|!&IRaJ-O(!~W3D!aA^ z-))YO_h{`TE?m5)u<}vD%+{*bEHc32Rc6D|)Ms+8-eLGEn$d3Rs(Kg zqxyd>ZnW)OkFft%I~1J1@z-{9P;&0>y^)Ct$<|x;bI!+-i_gYVPMbdVu(@;gt8>`7K*DZc|1(UxarvtRP>Y`68G^x0PR4*`6$X-6N%qjjearNMX$cd}n@%?3(lV?6(;wXZ z1;kfo+1@;G%>*l#n-!+N)TjUWsW=1|w27*Uc!GQ=4xs+CjW*sWI7gEaahGY$0xpJt znoAtmYp`c5m?4tjLB>B3wnf_X?L?{b7z^CDs`)EKCPx*1=TMj+zPSuPKC!T85XVF_ zfWn%3JX@@Wu^XBQOU5i} zknoF$-oPr+!J1ft@NcidU#x=Tatq@MC{Xo`BY?9ct<39ruKM4 zPV=Zq0%;W1CEA-g4b503sGbS-a6=i6_N_heg9u&Q^GfG^NS=n|PA~Ht**SX*8)&%@CdDo#eyF*P>NN&XghU% zJ%wgA9xipUk=T+_NS>nM$6$|%@nmgC)G1Q72qrPV_F*<1E6y_Xkd~AxA#udHQyj*~lm5Hb;-bxHg^VI%8H+A)fg|pc*64WURH2GCA*aAX%(U75|rbjfAK(Is-O{Cb}az8<=1auVl zJ3a-`H!5x0k4z-^q&A#*K%Wl6BD__JU=+L;c!3{rkp;ISToeR#AAxo1HGO3@mW)P% zfMY=R5gfJ76yVq(2q^+BFujbP&LYHU>nKSxu?2N|FV?o8v8(%dovBbTN6Ku21?Pnb zm|5D!=OIE&T-n!WTHs{zew6w!QDVY&)znEJ;{R7$S415 z$7zXMI(o-4$cqjl@HP!hb@Z6SdiqLvBd$Ve1dKn04O=#eA?hy-XGzRt_PD$pK8qh0 z?ZD#MZZSo5AYp|_Gm0-x4yd5L(9!y9THPLK#pk{?^Lleg0eQ=qWurw;bb`7n9Yqw+o zT16r`{1?F!6R|D&f7p0>vDWU?qglJVEidiw z6(n;)qs09RU!WWcIp%7#+Cd(79=Vv8SAf6j#V5191VFRxE&NC3?bkP}l}5zxI5`|l zhNBYO`BIZ2f5k#fFX$rX&Y_4V{m4RXbkISfX@TS^w6=S(@I^umqIoKQ0lBv+s2vg5 zW#A6bI(V);(K#XS%$PM-_P+hiyf*BGM__#C4?VaSh!5r%5D1d9&=25r+( z+ebCV0c=x3jDl-Np3T_YU-Po*CkHbF&K-b>9}GovqN{pd>ngxB z1|SvgF`{$x$t1k{YEvyYrZ}Kv97QF3kr&-YnVPAy)?3H*iOxHg0N6AI;M_6BQa)06h zcpy^{YL>e-4FSKo7f29?c&ZZ~-;PGgkh@GQ4+fTF1CJmUwVk%wKJr%Q8vhdEtk_65 z)4`arPdGRTU*g-}aHRp=nGE~aNR)Bqcii%XEM=ujyY145m7Wx5`VWitB$l8>{cpOS zX>LI^*iVjU!p!}?X`*$S(z2M8zR{8P#mN8*V zcBzQV1D5?!L_N0PN#S-rR^=TBTGh`UXl&`?9(5T=;%_}ykkWKuR%>ZAARAF({3baa z&tcixF)glxfVO|n!}kN4>b>XvYKM4sn5Bca`G%KBI^o}@@)OJzY6PojPN1xEhrC3# zU##^uT@O|?)9`tskY|Z`eE4em2{cZcg|Pk6@-6ls0ePBvb29D~>i@Li$lL$rqe-wH zsc@AdE_q;&MObG?zmes`gOUONN1u!rUx=RtBw{IR>)y!?1DXlY_9=Ub4fw zZ)7yd5w)`-9hg{ZQ`Ut|+$zfTRdMa2S2AenRi^k7u`mu;-b9_~B%OA*K6|#9!8~`W zUpswoPPn=pmUeFDL}S_?h#cjPyi+LLb=O^rxAvF^e^V|@Q=(l(tkGtK*x$y0HP+)? z1;ATuecoOup>Jh9FZWb0?7%R3DsQ%`JSS1srX=)gi_74eFVlQ|b?3S`g;6GX!avRD zhZc)Jb%yDqNodT>Tx*}4fS!1B)mk?j02t{Aw~Mm0Q?M*Da^&KJ4|<>6e)62a@S}j6 zH5(L~kbD@`cc}qqjYqvvX8nN<&ea;<-h$;<5wdsrB4*3&6axXZnJXJ77q$P0=As$P zRNV0AB81)AopS2_|7bePfGD@NZQlb(4oJ6KLFw*NL`tMPhVBmOW*!BUQj{FJyHnCp z0qG9uP`acWzvbTF`{%Bm!9yyeQvteYb!S6>2Lya|o=v z2ef4iO!|F%4@pA=!r!oPNtafv$0^_|cPVQK^tlme;RW532t&qS+TU(wc@J7#PJMnr zh|R3wkfK~XC@AKhkI-FI7~Gr_P;MG}LPIYoreS>%owZ?l_j=7}iK+Mg8gu=zK4$)t z7e;jK-3-rK7z5}*U(7Gweh$#LJ!1ArJ<4Gnf)lek;zhLSjBfM%M#ccx9OUx+sj~!9 zYv8AV8BsrP;n&kqaO92Sq1?D5e7rM@WF;bqii>H{uO?98Fl*K&njSRf6p4`!Uxk?F z|3G|PQee_J9h7}lgKR&Laos-m_rz7-mneE&$+H2P2}ycPoLB02M1!55DfYcVaY~Y2 z)5xVFmZqL%<>}11o;x`1)bslQJ1WD607j<)b;3KSKafqqTid%j8X_}=FnG(Yg|;rF zoLrBXwu5D>`dkZ7@6Zw_r*W07V@pG=dO1fuWc zE&l0Pyu?u=xSB8TMrnCIT;uQqQsVt)R8dX{jXh&;vawnMm{yHHkRk@n8m05jjZZ9= zeK_5C%K~BiF=Q8?^DdJW#-AFm8ZtSZ(F(-ZuUDJc~>E=I-1^PzAuZGJx+g z>faBaA?-XkoHc!kR8*}_6_ddF{rkIG8jhlnkXydw%j`R-so~5ORvYtl_M?3U)rJ%) z1M79U`8|REl~wg}{~dnfZRSN3jFqPE!We#S1&wuD*`VR4%V~AB$WcU?$4yHuaX`RoyPlk{~BN8`>L0&5V~}v z2Kd*>%C^q!RpgWGMm#My@qeERTwmW{3yfKrymO=4J19IEwG$@qR{vm8D3N0Z+YsO$ zG$YE5epH2P@Ij}xwKw{wxN2HUSBey%KwK%6_=sZrh31siNM>OybXue?SO!&ux8n5S z^QocY?mOq6PaW^ZCA<)BUof*&R_#0G0{d9`Y(%z)uAx&$3j}}8^U+%3IaKTVtNIGxwfK2*(%x2 zS@)8hIGEKEHYqWV7<7A%hSK}3a~nM9aJx>1Dm{nXAnsiQeic}gj3Vz^0PQxIYJer4 z^cwhJB0R`5ac?e-Fn)tlo6GO_11cF=_fV0r5C}74Z9%7N zG0}`BifffL?sM<(mjJ$a&e1EnosV{R-p=127{nNBs{E*IhiheISEo3Pb79{Za4i(X zNPbVHQ9L}9o546PkQH~dUp2YVkR1&d{83imOwojv-~zi}sqsynd!0T3uhnAje0$}z zCmC#pZ4g$V_NWHeRtEkuNGso3ikY5Kcd5YuDZ60K3=Jb}e#*r;(Gruf7-CiUKa0Ja z?qvF=PrgJ-ETL}Ea={D4Rh2iChtjY^o}s)wAZ`MEK#vusYnw17@zN1S=B=@Wy_T`d zgIV0(zd|z<=(;4jBa`%GPan|IM($R`cp@V@3`TR+;cM!pmUM~V2@)8cbhLv!QlH0S zwS_ebrvPvVM={&6>L8N1Q8f|4=JOgxR`${SMH%dAn9?YWfSX=-`m~h3Pan4Lza@Du zof5js-<}!%3&tq%@c3#92<#nHIq+RmhcWHLDeZ%-8laqiq44T}|C@LlOJwfhraTB~ zEf} zeA>D;x{*6&lfDe>mwJ|`S&$%jiZ6UO7Q>|T>v@Q8s@Y(Y#uNO-FW%2*1RsobvA_3A zQG+^*^qxPF60Iy}vGl|CY(80N_Hl1UlIzTIw(%?w+Gv>0okzDIWf-W-0IBF4Q4V!c zmvDKjAX$8l`SZ>NcgnDH`jzV9e{v-F#qfJ(r|({i-K? zUJSzP9useCgUlUGZs{$=1{fy<=oLpfx7IU3>kc#theRFK;gvZV{gNW6>_b zOoB@l?D|RDShJGzdb3FQccN5*7uxzuOdv*IsO=Gsq#x?67h(*a5}B--VYTO&nBaMh zE#U9fOoihN{rIfeQJrylmC*}n#;(i)G8Ms%kjZ!ZUtU>4EJVIfeN#|fp5ZJ`@0m{r zF-+R=yLy@+k>Qi+Lu=S!rnZyKwPf(|)0hxdi~eQ$!vc99pW+%_jHqKC=dK}eWs__n zuM!ZWJmBaJMXL&Z%wA%8qQ)bE*1b=2FQC8-D}qPX!P64{wzB%yIB@2gTqC)+*a3kJ z=f$V)I;B|YH*y82d6fX{Q0i84?+s7T?lV9E5#|zkPfo&?wd4{FXd2SgUC1}8;8WVa zF-!3X6bRP+&?io5CFTGa-j3SUV1jQ@TUDDuV=2ip&*DmGWk!biZI;FpJD??-H%e>u-xf zv{PAGnkUE z-vbyDPKHaFvV>6N^(tLWL674IroUzwr#}!3r~{^+VlIchPz*6W}e%G_})n(sE^+Rx%&YR!8G(e z>U1hGL9mHYn@|FW?L`!#A|Y`H66T<;0EJ+MRCg^E;T`B{P0}e+dY__A@a$S1s7Uw> zK1ya`O8@vy`eWe%lxRpK#}+emSnCL@d9>A{oCYatNF>S+ND(RV-y0YLdx?GjxR<3c zd6usBYi+=G6?pBjcj6R;WDX%XP=}0GQIngQ-5708`*iG@#Wum2#eA-HQYjYet+`cc zEX+SO^Xrg65mdgC#MuwC?`V3fCYtoh>mIq4TqbS0S40-on#EA;Jj3jIhrx-r0yeuwW8K7T3+w`w zHi$-sJg&)DfvEKxg*~r9;loB=&H6b}ChNd~i*n3-7xFihTVYGz?laGfcIdS3=|ki=tLK}E%H zESY*?3B04r#-u|fif=Fx8e5Hrtu&zBo$*dYfD5~p%6E`k4;n=j%x1>1_O8;bY5LT& z7)vom_Uu(MWifb3On8D%=C+1ME}osTw5B*2ug{P^p-(wW>hlK#H>p1*{y;>w$Tf!f zij)g%Pm~OP=+GL!bm3Yr`; z5}n6faL{Puk5OK;BaHk5)z@ahz^J$JfE186^!>uhqum=$B(fr5p?uu@hU9S$?>5C0TvBRAnW{rKA z5riT+lwGh*%xKBEI#N$v0c;4L^o)J#dx)5g`n@9C!WOsj4ZXjCpRfWH`xQNsye^%} za{jp};k|#SB!swkSEsgpAP)wSnBE;E)V1vNqt_GqIkKH!WYTXn$-8RH5yOns9BWRs zP1u26KHX3`Fha9_QmD4l?fUf#HZJBbCC=LsWv@{FR6aXEoYKHSuFWz~(v8%b;_JD! z&3P5Qj0$3~>00{)AtX+|j!Lg4R?o`h-wFKoyF!u=A8Zi-z0+)x2f3gk_|;cJf`Z38qbUMd z#AbG}r4qCAPsfHhfWster0mYcmRN+bq#>+ao`yuzr8++ktBpsQ(#Z_hJS-s~;MELT zhWteN%ncqd6tupzKdqF3(I56Y=$Q~yKoePi*6J~iv0}35xtWZSXb_%b-#p7!1Sz-v z^d^HWdOKN@P1K5!n#*Q-@<=Qr_H2*ADG&wX-uf`{JPax*HuNbuH}}#Y8@mA!01hHT)vzLJDESM!+w4y(E1DcQbec7oe-z*1(Wx!Yjn z8ff`znTyaGc&$=vw41UxKVQvkl^}_01wXqg5UI41FbX0vY z`DBzVRBEkr^y=q@cs5r70(zWtmIz_cxi^wRdj2Xf0lC@u#`g9%;ho069-Zv3-fD#^ zWTP0$rs`TXd>?&<>6l+dc-wD4y)qRsl#WGEVzKVpuU=R{+pm!tEkH%Hl(6!nS%vuc zOqDcp=Cz>^Z_$3?Ch{(A;YLql1GLKdznTM}o3vzyX z8T4Y(0FW;^yXP2#mQy&Bv8`IqO7?b7JdHp84>U?9Ds5nk*ThBrA^-8c_%_H}LEZV*XmWe3X8{jMLg z>?(XgTrmFfGjVv*BnG7+A>XGy6?Oy~b0*Gxa?JDrQT(zouC1E!)YwfH8Cl6~H}2m} zjCk38_+@KBk)8B9^H_bao0UdbUB%Ff$PWyEigq5bJ7tWNHs}m|I^hSVBWexoT8#o| z>o-r>+e3(XJj0DAi--0Oi+3QVuQ}yJxlfRSP)$E)sbJ50;w-9vIff*vltgA!V&z+P zYP@&M!^siyKlN0XFm6BFDu9bk=OaI)(c|}D4+PuL-2_t!T}}f41)XeA?dSrpLFV&+ z6}NQ+E@tb{4)I?=)$*xiC}8RsK$vQ2uOwIiz|7cJpC?g@xVtF#37v7;on%y(+*Pvx zvMBPAuA)T2eAeU#d_Hik3Gk&0^CDc{&|OIqhl7`>1F3tW{+IHjyl5!V zu8p;iO%ot?B4Izov$A^<0uZdG?bhncSbKaYHU)TbHEXiUeEanAWwn(iHU7~ z9XnjpwZ~<&9FrGl5!(V2(46mXdT|g?25}1Dj(Qwbw>&Gas%0SiX4ABlhI8k~MvQBYTADbuCk=sMEiun7N7vsC#7;G0;69#aLWa7-hGh zO>%AEhg1TbjByUDQs9O9x0vuj33yL+YYy2U*Nc$i? z`6w*bdv|1KPfec3;L1I|Dpba_HGCq*mV?BkG>HYb+qn#Ulp|*^!W$J3X$uc!(02UR zab5G)uM^$O`%c1M-3VJx55lhHj^Qy9T3ZnCqrID+5wCQX9U~;tn@l|mOo#j4X$y;5 zZhih?8u?=ErLz8=B)=!ywl`{oGU7D_jI2EIPki!4jkyi7~a607JGzA&v1Sg)i$hX@HNs}|} zFyAvHs%E;(!X3IAIPmZkdaaemTOXC^jK-=bXBIkjqasa95*Se+c)O2MQn@?1j%&+j z|LS4lmR{M19*ixv;RgVU+vo45d zZm%L9c>1=W97*$^Y=cIb^V66xJU!lILx2r`cablpij=&Kn2_X!A8Q|wlD0o_QmICi)`Xae$Ig=4YqA)f@zTdOvtaR zHN$&aa{fNr#S9$MyDGK(kOsm2`HP;%&|PVU&(h()iLCS}I^7qHkZkht5e?4yS=6nr zvj$)x>AW0#svuNW)AFT!+fPdFp9sDF-wXF8vna&cD#Jrskd~53JzP@Uy^*f3T_;O( zijle?(}u+xwYif33B{Xu#E50w->R`IXM6k)^JdW~#tnbX?mBE@JmYMu%Ds9ff={#3 zT7A>-h0S0Eh3f`=8NR%+>&(p(am^p7*fIG^3^0`=$=~7miq#-s)uP|k7Kj?skktQK zMxz3rJh^`WIaVGtp_N8gwo(FHVC8VzF}(&fu0=pV{^!y}P<}J;4lZ+`J@OtU7s-=UDHu0f8lR6axk8|jSej+k<(Ty z?hXxRyJ_j|fXBzSp}nRsBI2CB zr~;w_1g6U)+Y@1^EB~Nqc-p~Dn7pQ6qu{3!mlT7K1DBZSB1bXs&SeLjJ{f`v{HaZ? zHVe$tc@pBg0*x!~fBjr^AEg=tx}Zhc=UUMA>E{4zQowvB(-%Sz*640Xq_axuL$r-@(xwh0W|-U_nqOJ2rvOs!2g3ft@!+{0kb(VsGOAL0+!tEY;C%$rRk^+dw9`hER-(;U zZRrna1fzQN-sH|3s8+$VZRRCbygiY)$%SJ7WPl-?h1{)W7Ivm~%~fehsw^vea=vmu zt<`truZ~_mMW$E>&&UpzP_@>8PIou_Kv|$-1qq+%7>mbozFk(ZtVUXMzGQb1N;_I& z?d3dO)8>hMG!Btz8JOPqd@AePtA84c^G{|*K}RFObPT*K)d+)yFrRM6xCzbh!PV@# zNnorSV|>=5$WBU&caCHI$2C&o4OWGSoYq@)OZebaqp1`T>voe^nvgicw?0s|!~^L8 zu6GL{#zz1rp^zDVJ!sH5&;mNn^HYp+WUTJc_H=8=g5kx8E_L_JT@BKNW$G}rj{Kb z8-HuFuPpMx&+ti1Pa8a;diYXjh;lMU_B_|&NW|(ds6B|*M-V2fResifWX69-dTiT1 zeh6A)zS?w!X*`@gjS^#p(cw8Yn=Fw?4{)w}rn8~%uC|l}XV@ZLRqyMl8jb5iE|tOi zdR}{34B`CvRrUtAxR`7U!VNni1-)Ch-bf+ls? zcC%p}?Npb@>zD<$f_^kj{;)%z)%q~TCnmjjUPi}2{Ks!M-^jT{{;428)U${pU;A=? z?j6L`$;~y!Q#;x#<-{dmas2t$tQi3}L9ZN5?KO`AQ5?_Ccbim&RdZX-TU%rMRhS$q zG-gn^I-U7|u$is*XE2K>uF=|~E>L55t0}PQnezMZBl*97smop5H2cT2^9#vI%Vq0e z>+zINDOtA0qxKUfrGX8q2*>NaUA9jHLfb{Vo(8c6a_=>bE`8 z;iRS~>mO2?VJdk$vt6F`rxm$!0_u=*sp=BEOtSISE-l*QjnG-!7)TN)?DDP&#hP}r zx`p2+#-ORcuG|~{%#{jk!IBO-!FJeOtA?>zgrp{KVaHcZrA{^T(2sXbFG>UYuj-KQ z`>Z}#;Ov7(BJ)XgKjdzP8U6V?iV2Agn$e4wV@&5UDH%`0Le$~no3CAr9|%%fDK`IX zuUC;`d`X(OAmY=#L`&ZEWlDqzgci)hDPqXCx1p*U-nmZ{;@o;g#}d56A>R=U&Q}ZT>bCW!d8SFeTNbl z$+_vB1tofb684s0w-@u(m|$J4lK$i-IiF~yMBGO=$~>jSA9XeM&}|yWUYosQb)Q&Y z7{P-ogae9lD?&#UQXj;shlZ^XySMBq9`B|n5pfSk8mhCqmA-H#A&~qTLBK0Zpq1HS z=LRrLi9>W)SX@VvCkL6T;3oNXkcW1G*=RAGvz`n=g~J3fjWUmLnJ3%ZIL`&?EURh|ksGcqq`b z1`_oxl#xi48V&=nO!wD}a}l8jw#AqAP;&5=@(d*RB+kX-Ob@WATD6Pa>(K(ImEe%8 z&aFL&a&QZpPG7zZt>gdIF_q z9@+}uo^oz3ZRvhXL9WiIa=L^Oq;(x>d+ue;i?71ivuyU)Zff|kXR{28zgFdxYg!cJ zUzyh(h%FJ-RU`aD5UZK%p_@aZ+1>Sd`-Nf5%|_-0HwsdAV6gL6_dF6BM>c*7rV955 zsOE4}gD)A*XzH52Axo}xXl$Os|9mt6EkmK4L4G3+5DS&^zX2J&pwlWd`Ob1$0*<8( zOz~f2B`ltIj1;l<8SfKqzX9-d6tE#53I0p^uT*Hr_uBUhv!{RI|A3UqR{Bb2br3cl zQK*s*aS%~OUJsLQG|2A+%15IGzp;@fR=Y%3(wDiL5D&)5E|ouaWp*Qxd161%o-<4* zp*L0#Q3RQO*gVmk!KlX9-L{FYfPOfv{5Iz4fx(;K{(-`;xgH;58rFt694AZfKE;FHtMG;FemiI*m>#uY(KV8O&{ayB3#C`7xLa(R9IilzS=$n&p%1)8r9yu8u*2PvmoC+}b|Lt;*#z97m4PZj6v$20ys3%?1xJXPGR?Fcf*N`xIwg?6w-@baf9 zhP1zF^&|(FL=6=C@al8#AD@+Tu#}5a~tsI*7%JINLY$fZtc7Mfn)Ha{R6C zmq%-^R$jUsG3sy>3SbKcu}&|*e>vUu4foVyUO)tNBGBY)pIWAY+Jtl^5MmP^9M3)wfCHnB8h3T zVH>sPgzhATgR2Z=~p$1ocd!hFj0VmvAp@ZWsB^~l3y+bJ$|u0xV%kKx1^XE9`;Mz5jCN*Acl5;CDMkb=v8#X;@w7AjyPt6qI|mIeTrkKrca@( zCNsHb5F@)V=y3MFjTzx&x#R5onINXRsdLa%K{S!64e4-LQFq-RD4%B3>T>V!#dF)W zUJSp?n#A9vYpNdgZTqkx>LkSy*-Ad2cR!!dNfcDw8&~TX?e)N?r=@B5IELzLg%VRf z7C4h5Ekl&#mxw>rKD;d%b`qiCbn@qibZAQuj`=g-_!U-yK;#VrqhnQFPaN{Xs#biX zNknt`n(p!|bzyde7Ewaj8oBLG$m@PgZ>Cd4_iZ4`AP1t|1n@%oxt<5~<;|n1YyYt* z8oscD{hzR%;9qdT(!C;M=G`8%@!niA4W7*-KwGo!-8m4EG(z&6w%#xC1U({ws06eS zWv)`xnZI$X$zT%Gbsgn17~9#=%M#k z>Mk|*&e9fy=b(g$1C#Ph&}aZZ_>Wy*EW7e5oQxg>==6sw2_OF8CiVwObT0x~wv z)=?nG`lL{^LPp~iVCnrRcE}f2ynqCyqjI>3RF5(EWD*te5XqNF)!sANV%WFkugq)b zC@Z@#LhDPC_}pMAdOoF`3e{Tm@lA%hArwmqjO<0#QB#&6mCxjH3@+hFY@PM5DlGLD z?>|s4rhPHTDZHCUIY}H4__9El?|vMC&La@%m}?sJ`ZS>#N^aPwWN{~DG>gGMns1*M=H&# z@wJ%O&x?zSbmOf1JcL8ekgV(;3)A0vne0RReRU!bBP-sPFXOhI8&$@PHlwC1)O_IKLxP(S{mhZ@*=_>d-KyO}H)t-Qf| z@Pn*m%1gqF>h&(WYk#XUn0+GH}CCAME}=V8Lh3Vx5yPN za(S6ez@=7`zwhm52h%wzbas7!^KUUvnJu^o7ZGSfZ%0J%MPxqW%s!B=t3Wf!7iB^# zz?}Q&5`1b-mAiVtmupA`ae4tsKGFI&IV_a9*s+Df7&RTAPz6T;AY0 zJ6Zotd?Rvscy`L{!ac?%`bTx}*7TQXrUee$mI)Tf{=awxTyJA@0fWs0=jogE5LSW? zi^e7<5K7QIy{J6|M8X+EhW!0UgV#+EAz`Q|=b$y%1ru-^HUstaQXfDMlEmca+agq8 zZsXg&+}3ydF``2U?;7{SV;03tw2j2-hq87JblQ4nW&|R|n_@f?ZphwEe|a-OvS3|x zr^xC@SI=?Po$7`9k)pmjyJyw+Olg~iMlqvQzKYMaF_MdFGU{DegFrjo9Mwia793^$ zEJ=!pjC-5npmdlat+AT{|1nS_~WcgQ2&eVr7pBS@d=!Gz!+$_ zAe=Y~Df4zxoMlK+t&imfNNJ0G432nwXUNF9rPjpT361gJiU*CjKkM4Nya1_mb7iak z92@ALNz-!q-(*DGsqXer+WlFdh~iDN1pA1t?Y!S`<9XbvK3;f%ahl3OS$n0|UE-?E z`HCJzc^XSkHX9wjWe7z)T_>qqF{*w^RZ7|Fe(?<>VE)q{$!N?Wb zi`D|a9UmfpeIsBu#>{udr`n1L@qHfnO&Uyi1&3DW1*D|oG|63){3Ku z8)n_N>X|A>1O^{lAU+$aZPi=%ATxM+r`)VW}X9Dy~=_ z5V@^hqVJPky!Xva!%<)Q+!3d_NMXx59P040IWUVxBAG++JCbMd$?g4x@?didxQXDl z{bCwy`E)v+cQ>8Zw7Aw8$>;#)-*2Fe&nnk{APP0n;ZW{cn%8kDg~%jjmQ?vhBqrfX zmu=$z@-?Ck9y?UnX7)}ZNiWOZ2lt8~e^~!wHdTzpUMOL!{j!fiGuE=H}=T4O73{G)@w#;^FV4-yZrJYb=*Y(PEM*%SgQ2rf}>@&=(6~Dl2Oyo|6_K=j@Y%2TWnbrMvFwU+7 zem;YJtc^L0^G*utIR`1M$B37;IxVbtWCR@>w{Wa~V+=3?FpLK^s!Lov#QQH*Xt0OI># zdo7C1eRL-7C5W|@x|=)z4@8S`S8yIQIn#_r%Cvz>cB0&K<~NU(o~4g>o`*K9D_G$PWcpP%bLXfX{c-s{DBNGxZ#)*If}ng6H_{W`sM>ZO{K14 zXGvqO*rYJ&G?c(wRxU`Anu4=)B(}0W(<ssrv)HQXAfWzdhMvmFi*3^HOLlpa-Dc)u(jr9RBWQJ%hl*nEdAu5$CUaPIt0 z-0MhMuIc>%-+r?Rls7-2qKK1J<30sU^CZscwx803s;0>j59@)zz~ap#ReJ>dZHloP z9rqF$aPwg>8(RE!y>-mO5@u(j*b00ai|z6FI474%pw^L!UFM0f+{QX2FD($_J5ip+ z2&(*rib!%J=HN>mfE#WGNFo)SH|-9BfYpVFuOH9Z$-?>u@AXbX>QD$Y1Uh&jYtB{L zZ2eNQpqx7Av1~L}Ly5HzGLqts@ooT8`acUN^toH0Sa3~=7ut%HNXBG7Rz&a!sliBl zPJ>f~fx6LL*=Z-o8#xqkVpU}~WR_sV*~-awyq%1r4+KHJ zPW6ZIe7kak+*M2HVJ5CLseh%1NzOMSqPP};F5kbkbgFbO>xWa3QjARYjPvSGS~Qo{ zIp^J>G{xOJTt5$L;-bhK9^hirQZm9=p-To~0~7Oi#f1r7(3weKs%jQ30XNNDbD>@L zY9?5Lnf$W5*e?#Nuy_Z5=W;S#Ie9&xDbW1>qnc}M73xXtic-Lf=n9#JRK?#kbjqIg6$@ydrDX#O<)G5Oj&9Hz7AnsI@A>!kIO zt^}kgKgR&;T3r6cafF@Vl>AMF!6`D_rs>QJ;4m23b>;gpwUyb~2TTxhL~w&}-u8xK zQEZFZ&{0pPJjIhlRQ}f?7G%zt>g9J(rini#ckfDlqM3!=%Xmph8s#I&{Vd*3t)Tvh01lv^6|#6&o<&{D5cjmWVG>zOuDO-V0%xwC$+auRg7r=h}BKY|V&s>w&Xy>^Z>ikD-CSl6QV_~Yd zZkG6AZAO*7>tH_GFL2G~467wxE2-h`y)l3^LL&0xU3#IYzam^(9je2hDwH8<7iHtf znw6w4S(um9^}`?r3$=_cytgc~d*=2R>P>jEFue(#r`MXr$u9nZNG8Em>&98iJ?36> z3zFx4v3dKfn7a3MRl0Z6x~L6Qnb#s$MKQVA9LhbS=MqHJfx^D+WN!TN9cuG1!yIK_ z+|1-m`FHBHS^|?NS3ICKVcN|4ew z7`5q-=MtfCN~6M&1SE!ckkv~2Ik$Eg%3UMxCZj!Z5+di4NSxJ6I03Po=^zd%jj%yA z;~wXDD8t{kGT=}=+!;ou-RUQS$;fd#If%qEwh_Q#0Z)Vbge+FTC)puX_|U~Pt{a1T zm2qsveHX&kDRjgAoBe(iJ1vDrX@R0kL{jZ-^0DYKTS4t-?u!Jp`BDBRj4bZFyqKK2Vvd9l4i#f+@=J~chf(Y zOOI(pNUVq;BPpFHK_MorAnrlgFvaxKiYeYrMA3GW--2KTPvLlHjuDzmy7#5t<0i2aNz51a#YD54l3(!YDa|vAl8$GDnnet4C)VO1 z8Mt;^GY7a*o3Wl*y60um6#+=?onO-mz2}-CfeO3VCUa<>{#?~HB5ksmsm-9YT*Leu zJ|i2&DsqIw5HluCc2NaSGuQH}9RcScc@uRn^yn$#FI-cDM|px70gKQ4J$7CGj#h`} zaqV#QEkcUj*Vlx39(;591UsreJ@`k#>2KE1z4XCwScj(XxQ}1m9`w%<3o=kaPr8FK zoZN`hu|Ta1_Dwn8#3Ptt9<#xNHU8Me8>m0}ct5d8^yL4P#xi)xeATiFXfS0zJ+lw)N=-s|HCKlAq5ynpMq1djo?T$&FB1FxAD#4aJP9*hf(LdX_(A;hL#TT_#nspyk!8!spviB%{&87RnYWTvU>r zUde!(8J?#(7vjo?71z{q;f1|Wi_0)2D%Q6yZb*ra7yke+`9s+u6z+GfViT-RrpTeG z=5ubqi^V0|v)$kV#i;V@2{AZpN9ZQyr#U|4+}lsX(%LY&dGCS7NYf*$x??^yKAWUK zC@(bjh_lF%Gs%3YDw|R`y~OzALuKB6tN;U3bAAp>}Y@<4TL{KD7X+N0BA#rpB6SOdYGAR_zrA9L@q zYZ5K;<9Pfj@_IV9%9B72qqCMSX8HPlWT_62xP-idMg7;RVD?fA*7(pTNt^%7`TZ(s zTXUvi%ZIPzVj-rrd5Xu-ywmZof=K9jhn1rS0F%(Hg}hQhGWbeO?lf4`?Mm3u7^Tp0 zrxGk&eOpy0Q12M^G>L`5$8{3lK=LMD$)~=Ghoekc1N`gWP2d@r9kMjSHY1SN#q2P# zABR-qKXhDNHvWXANn7eYKA->=o;;2caDACrj6Kz}U#58aaa=jU?6=<*xu_gp%tTGr z|4npY48=!kNoPpuaaGQHPae?Sy$;!jE&_%$Rr|66cW-)GEo{{qjpM=ks{QSg)z0n;ITcQrjehD4Xysr^!#W_riYMr#BK4V6rXjnGg&GNGMBt9+vm*tMjKX4L^4g+lyhpmod5KjgpJQI?9-2V zHEt=Ktsv#SUtXBf`2B`+Y*=bLV~K7gaDE?Qm##6e*LmMR~qJT zN|XA}5~cY|79Z!cEGsoW@rbCJVd8I386KLYC}6?f(a4j1WjZu#()7oI_T;gIIRs>5#X)S9tBT4Bg40I`GQR56x%4s+?v<@V2;RV_K?=#wIZ*{~o?|&5IRuId8`w$WnfC6)Nx+ zTr2$Ws-g3=GUVIU-=78#lN2xwEeH$)v|ay;u7rRglPp3{tB2VnHYozo&Nqt4#^1+v zR_^=ok41*_r+6ljls%RCRhY8JqocK`Qc+yQdk4MR^&kX2ES^hvsi!rIL0kHOr}XIq ztj4hY#6OUotx`U>yX|>_RhFHDxA}W#b(5JcJvIvY4Y?&6(Lm?^4PV@XW5d%#cZh=9 zQP_84NY(A0$-=yE%BI)d;tFy>l$xW81Xu!n-$n@V@r;?_<{bp@TQYCJ-m- zxmYdXVtNDnWHw#hAQS07lvm1to5AjeVN^`s4gX1XK^X%W$Lm*Zz)VQ!&(pl zV^sPYqnJ)<9+567{UVp;LBba2r&fDGOjZMhuD!9{8O)kl!u{?_U)zf>_+&M$EZm!D zdMnZ9rP8M+tgAyz@@?i6P!k(Tae=uXL55s-30x}>|iVMJi*4(U)jB?Up> z+x`CDe*p2rJkMPBb)V;P*vP(30FtAePqnd=x^cv_l>Vx(E>IgHNsiK|Ye}?OG#!*~ zHAWmC;~tPH6MpiuDgpoc$o#w-rtGXkSML!Mg(Tk)pKYEFI6lc`R9?TaFYu@xL|SMc zt3TQ^$YZsrybihNqu%RHN;{-_)UOspJB2Z%oMe+dgWtsk#>|z91G~`@EUvmCp5|xSY?z1ncH35^v^0~_r}l5=oX&@89RDzM zo`PL z;+CabZb??KA$$4f#hs89&lK~KLWnQyN$@Q~t@#&3jlkdb^&~im0!UazWXghX=7;Ak z*vXcrg;SXrB{uCLTQFW_U4Ns~vkL6f;0VEmJQOt$KS5Edvs&t(OPk^dvJw(~)$F$+ zV;wIkoQ$fJpKHq@1ENMWA@Ak>gNa?fxH^E{sa`}u{3LKl^WFNFX(k=j27-y zkMi_&@WeKc=IcGW2`zT!g4vfZkh?!k!;ZD#>N&3I`ZMFNxDIk4j!JT-Y+^JGl#Lq0 z@TqZpu7P{LIj``l{%LFtwyEh?!!ib7{o&SKKR+n3_BfImGz=VH%2YaMF{spA^*BRy zt5meVLh~@LsN6nT&Jox2Ghx7#kSnz>OVFtcL_fuo7Y!ZSao(bDU<54(V4KkBL3SD1 zw%`;@3B2o{%wo$RIC?RMAt#D!m3d@NV(-VuO%mPCY*%go;I-3*>XF&K;+G4;5c312 zOV$^2ke!#R{KW;NAD-&2H~n!?j6{-imHN#dMTOqm4RY(d5jPwyD(kfbQ(u(jNU_1_ z9;B4~GL}Qj19emQJ6!xnR@dA9+$j2&1$|!(22SY+1*r|jvsavPu(=E%lZY(~a()qxt#_a^E1?@! zI@&afhRe(x@;2LM>W8+Pw#WF!Vc%TXFHa7IrVxHl6&FQqGJ^60=ZEm0bB2Mm z+exxYg~_=^%G%SA9<%pdyJHgyldQwZ*$bMoHnw&+v!*5e5bEK)4b@ZF>YOg2Um#X7 zrg;ut4~Vnn^VJfC6%}KDe+F=9N11yyWmawm9IP;M5g{6(8-&b=_CSS?y$uxhb?K_& zB6?w>+d18L;vT{H!REs5xfg0AD=;A0&g+X-K8+dGz7`5=clt|U&*-)8XRt)A&aw)DOHW|rYiTc4FsKFnV~1z7o)vZ# z9JL5_rO_YdcH~e#lUlw=;-rU!gt@!5>I;lZHHJQZzV6n5I=RCf$627JzE0Fh`H&kq z@!Cv4HM2Qa?@3T)#^3Quu7;|1&^o7YyW9!PpK5(+2t##Ox9X=as@_eti+z6OOBxD; z;dr^V5*GX_#)?yN0K=l-C2w#PgcOX@2__Sk8Y8RdH}y92N)$)ltJ6_0%yvFveugfU7m}^hsRH`m@D_!D2(GWedyJH$p8IF)WQfva2WKqLL`5aOXo{ zC4rbSb$gnk0@g7&&*2=Y0+sy8Cl>4q!ZD%99aMJw6=)SH#+#^(9>RFK>P2sIh;KDbjdu8P|U&uewZqdCdQZ;!MfW+oY1Cp1a%S8r1q+~Dj+@1!Zg zLMGa}h7HLr$@xe402Y@V!JHQM@j6NrN>C7`a>}Aa<&&#d4(I#f0#ujA#qaJ?1T-1bAspa+b_f&XB~ zzkaZ35691P#-DFOcOx3HMoYfvJo=TWt`X>z46@s0fMxLgg!3z^OJ@zZtsf@$V= z25**HuxZj=x4m4O!RH{`&Gyk^F)k6uy_v0DDqT?`SIe0H_SZQ^z zrMX$vj#Ul;$|X~))A!t*YA=bQY!$%(2gm8K_YYVie7$|igm;l*eB7w;D4iKUx+8=Z5Q|9kghU!^ne5aaoJA2ghc$-DvT zIb0;wz+ljtZfkfkLaA7#7-rtHS-5{$yh*QC1=`5^uL|5i@V5bJ!CE ze(?n#b8fy+P{%4dPZ1`PoTAA?Q8%8Vo9-H z^Gjf*=77bvzDSt?S_b&*%7e3$3K&i1xA^8>B?koeWO%;!Gae0^yFMW+azz0q@6+I_ zHA)0N(fiw?16OaLXS6A%qPXyl^@kJ_CMNSP(Ze+A_h2B!B>r|h}I{VXO z$8xY`)u@~1FtbHzb@6pmYLyD0lC?i_j|Vd``4yl3i6Ecax2b^AH1)K&l{--I!or=2 zMlFaO9O^1LAP2}M)nx7@X&8LyE7g$z{M zQ2DZ}7D$9MB4x!_;XJA&6*!+LE_IYOfDj=+_$1j^2#a?-V(!dC7xVd73ls5jiRCsq zE|1*s@e7f`9-X$gnH>#-n^fG=MO@u=7}5OX%-Md#ansDbS2G4(vI-a@&9>$PLKVIZBP{<5vf{ka^Obnz2}Mzdd51owMH7Um>^OTaVN;&ZCsW&d~yqhgg-DP5jH4+o%9ydOeoMaR5scK+!42uD3kjb9uVr?G1oBu$-& zQDo%hYsWROK|&<(SW|M|LpX<1_O1pc3G&iS@ zU~jv`&ZETjsgg|tb}<ZS#iH`>N5mS|E}#ITin z8(R*hkLJChFSPerAuGcQmxG=vt#Ip$BkhVv$l5NXF&&0fLat)OXsi}2u3|bM*%m2X zZ%WH>_$X&>@|_=X%aV&5P1)MMJ%i>+%cpZ+u@CZ1l?aboJ4yrPw$Lhd?SdHIzpPYi zsUh!CB>65;9}N57a4RRK1xZM-r`HhK65OD%#UeFh08!{tVS2@OXM#&6T5zTrmpO;N z?t*rlS*->K@F$7|wg-hno4hn18hVCM!H(M*Vvzlv0Q*P9`o@bV79q)1!D8#kP$Cn- zP(9~6n&aRV&f=t*nUsUoM@Loa_?u70+vEf*t{a#Q_%{@^-mn8#srt`R`xN9|8T+BK zv?i2fBXnv?FcKbkte#}WhCs=fW)i(=;zEWLSC%W-7IDsmL2=tS*4hoL%<{7VQ`l`6F-F#tyoWomX$p_4 zD_^!?`UMx=-fHQhF|64~Y;+rNgl6~sKYUAI!{Lt$qWv4=5Hh63Hjct$KIRyhHZ zd}T09H~Gan9U1kUuX+yF1(OUo7nGLx;S=RE^Wi^GY?;c$y0vNV@&`W3|4V117#(P% zq!%4p5t{a@5_MUZP>YSuYJk%ZTL|?>=r73pMb6<^aQe#zC4CuKe}u<3f|PUM|+HdGEg!jy82#!kt(qul^<^u=Fzi zI`zLR7A&5_1l2W#{wQ=QS?AS31(p?I)6U9esKszJ!P1^CMtZtaptXzjEkYtve3e$) z5Fo&#%>U>S-H6C`kZ~ofs6CUIRM z8Xj3Pvs|rELw=9Iid=H*9v zL$)?6&W(iiaPtthx8Q2R#qMl9Q02NT+M1YuIZ#fHYfMDgC+n6h&@yJZGw-6d1FGw$ z6P_V(`C8mM-RG8uFvHo3WIH$RH^)F;L00`2)OU*>Sh&;dVuN|Ny4Y?(4Qkp}YOS{F zo|z!g>)Lgb{os3UE{Sq`I#C^xfm3ePngbw~hLv1+^oHUs9|%S1oYuB_LykpRqxo?- zuSW#5><1=?A~>iDbip^0V4S0u<_5J(Na7AOrEDVrZVAcS&g+j~LC?ME{9&<3nkrwL z2AsOjr6IK$%vBI`$%sq;IK>1lZvdBWTYN57e7u>ix=uHUebW6mN5rn z_<$({+Wo|&@0!3D?wwk&Ca)T^jIn7#$?HW+N;bly^A$(#gxc18-nQ)^2l~|8y7oKo z9Fq=_q3jKIya@*qv;gwRKp?6Zfy%n%R5?4Z-}DkB}k7@Bfp)eM_X9PK$b zs$j^Lqg30z5cq8St$vhyaAjJ(psM*3!2s@mtb9>By9{|IOghE4d=$Bf_CMLMYkvyu(7Fc8oEw!H9DF!MSWU{YDcI&pgnVL9pRw-&4u za(y_c1>j7k;VIIZzG5Lqv#(INzH5`qzxcZQv~Ky_$2Y>@*LJH$IBC=e=zZO-fKp!J zWjKs&=)0X$a)UjrZuT|SJ z^s%@#lSR7ziXVy=V0_Rlk&YJVvgycN7rz&;DmK$HX7)Fso*Sh*Y^y_AJv-d5YD0MX zTNaw1JHn?d7Y#$Y_CP)Q%~>Sg$o?X0q3w};ZKX%18a*WLPHawyZNE54b?bL}+L`$^o z`<{W48=>w2{!a#~@s;OFqclsi&mYwi9t1~NH=?X(D{Z>Nnjifk z`#a#OF_7zIC}j2)WKLd1306+CG+@J5?p5usgXmEF?k=u2BN$@s<=fxd+u_>h`b2iq zJid+@iF9=E7#AMFzhX_(km>5vva`a+CDMd?ieY&Q|Xx3vd}cM`1clsnQ-kWX-na9+C?YG#@>wPv{CW=xlw+@Y~P zJ!9JD!I$(!9Z;E7FlO1Lkg;>?#n;(ELOrMd=x5qOd(8R?Kib7i|H_T!r^BrcfRtz3 zPJFHGAm)~QrjN6ZjZoy)8y}N%D9ZS>YXVbBiY0$$hnhqAKS^i(ui`?>suGf{k~Y%?;EB2Fa>knzF*igeUjMvR9FC8WSG^O_f;CXsZlycHVbup+$OxleU0BnNA!k9rD&_1?pJ8(z3h(Zx&m3sU(%cTSX4A5tgL z&rt+QF{pXbfb)7ozK^sfvibFN+SRK|eBHx(zSDQUy={ZNT-xe7_Y;`p)JwkaY@r#9 z;dV*vi`q6@zY1B3EcLC6W0-?)7;o|V$ry30P3lzoLWasAAV0fgAEV-Fmz2a?OOfgH zP^z_lg<)aq1!`i~{u$3k{NOPEVHKhNB8mfP(%#Pcu^Ma~X3nG|ORC+HcC){d^%Rb+ zleYal9bJdZ#e@Zf4lCjltS zlgF->QPh@ng^4s%e#}&3hm|C-8CeyjTdi0;#>nDlRmE&c4t%4`qQgcaldzogxRNFlm&n$?IQ&NWC)4- znlK9{6RSjvt49v2AMc397uB|&v}_8h+Ym(uBw?fHq9iydL#-LvbzXgZ>e;~H-zW8S3HPsVgN-rK>AFEOr1y87%=#4$v78o;ceIj+ksTGK3hH8im z;u)o|?mipS9s!V+uUlkswmIy`$W!`oU4pM}eU|GTEq#8By}PlyzBuSsmJb;Cn{|K` zWVmzJ*y35taZH~4nt$@SAqI_Y>dTasou(uRK!8mo1W;jp0AZc_1EDTkb!i0X(rjkM z-GG7ce}A|?4Vu-LfQ`~}hr2`5ge!-&m`v9el-gl9D(jn>s!qH7TYk4HSDP^m%3BFvRDzGh!V2;; z(EFU-zdKf_MbR{z=5!fwKwJV(be}AEhBDFv?{BP2l*!K7Sy2v#$!apkN#eK&g7g!; zkyXg11FWTPcHdz5*c;Trr)-LRC#KnZ1=AGYBx-#LNnZs`B44ZTU4|Uc`ReA5GbGR} zvTrgEj^82A%1#$NZ(wnKEa~9d%6fu4#&b*OkwkG7`*P*-w_I#?gIWJBy3U=`#um)~ z?GhMmECeO>j}VkXr3?AWeM9kzS?}iU)7IhAJn~J}FhX8RJAI+WQ(B(r(b_LE45gG= z$}1dl<%-^up{n$W5b~Q6^LB!2IwYhnqupkJP@nj)?;q@}Tba>h!LH zmh@}e2jA*lN33#Dtb7|5$V}i`VH>dm4(-iO$OE0q8fWm6O()A`>dY!bM#y$m2 zkIg#!*syagRX;+^?060KioAsgM60pEP&5Z7PWz09NH>h4aFJb5FNqfTQMD%4s>%AI zTwY*qu)E3xPh4UqlaE5L$Tca@;;C6$j z>1bm!qZ#J{tZ)~Te;|GWzLbOJL~LN<@M#sp@r(0Mn;RZtHc;m7%hVy;g}HQDE?#P) z;hV~nu>9YrkVo9YN?obRe(#JFBA7lL5K7v?nAi>fnK;%1TAUVee(w6vk8=61&W3s8 zx}TLlfSKG=%JLw-?=As@p#%LKG?XO}=qS6YbV{sHhc=-I zEVAR0#xG^V-*BEUTn8PJF(Tb;bbvjIiSw^IB3>@`~}&B zh~bk~{_Fc{74xl4ec4v3qt`UW2#wtGI7hewD|}6b!5~D@(W`Fw?X-*Pnf5+{;}zgx zg{?ekRz`cJrRXhpU4rsvW|Bsv&C@S{yy{>={yvVDvTs@;3QMr~KX)dOVg$+<9JLt$ zJeK8%dzRytjrl-6k?zHjP|yVCiqDe%N{kgvKeTr`<)iuuNQ!KRDsn`6wZ1e)qJvs&307RaVo5L+-N<6q`Lrdy0(=Tbuhi59ZySJ8#)pk2` zlQAhq@HTCz<+uR*Tjp2rpFX#tVtgvdYN45cvQ-%;z0(X@9g-Ggn;TJ=j+IP@4IgjQ zu?YHTFeDo|1pheFwkFMa7WcDpQ3?fSmzRXIZHwR>(cdDertnMKufQaqCyyeh;4C#* z5=2>oTPGZ^5pkMAa#K~SCc825y9?{ZCCF{ z15r;t%k{CcpfSvHGAKXqi9xp}Dvc<$CYKfvywu-lXw6Af6FSG!4~MH2o%7-HGw(*~ z#+{ayKYhgp<0t!6S82|CV$zHDlf%!_m=y%h;KlUz3=}xD77*GNN1U>2J6?kI`&tcF)f6bbgNi&cR5i!n zDUI5Z4x3xYd@Qbve53N@2O)9WF_LR`PNlPkY zCV-4L$L8VDa<5xZ#Ip=8oU9m5#Kph)E;q`$WjD811Q)HkljRy&Xd;SHBjPcm?94ZE z_|b}FD-&%Mhxi@Lg7zGqpsnQzg9h{y}otsuMW6 zMN2c1S29X%e82CE`jg?ChN1Ty0t4V(_2~9I46^oB>odu3)D zg##E1e$}|fMG)uU@yF<=iZJsa*ukJLqyraJa6F!$EQ~@xp z#1o|<@Rh=)UFQB*~Lm?)JKO7#tG1Wh; zdrG#?u_Tqlh=fOMlIUuI39?5pW*$!;ndV5f-efyC0wd;YSy46eK{?L2M^a?Kx z_2*qFW4?iv&BV);$wEmM7`a2#Sn{oBsO7Nt)TnK{(u9@FNQ9?nD9a++iC5=ikboZ{ zSIhf3m?iBO-Q+liUJJL{@aK$zOV&OSt?S|!pPimrsg#%}ZTF=dKw($&&-4-y@`M+X zLG(GSjuMN44nV8dT^76}W~-D4`a;uN^g=Z4<7@UaADP(6xI41Ootd!iv7$OZy0T1= z`)*(!RdF$O4uqFxutH7e8(kc)!3wHW78R|706Pkxv_*j=SL* zhF8u5xx~e=dZo?aj3BeK-5=j%W@4O{uMELk;zbP4c4b`wCJjiL%*N1s5T)c z_j)5f9Nw@XTgf9FIZX!AVmVyw)!>~DCEr*)N4`&52QOUTUPrvhc>*dw66gDS-(CJ9 z+BK9nA-{SZd@#0+lG}-%oDh}}+%$W!jDGBpV=a=za|!SX z`J&lqPy~QPtoWUt*&H};#4et38?59_a*PJdi}oIFLo3O`0u|WbUypJWKij&6bZ4)J zAMrQH0m@`mv>x+#kCdIvM$JHk3~=WpK@SAhr>6aSbc*3xxz>{-7*v@Li-H;@9}zcD z8x|Aek%e!B6jor<6xYtJJ_oR|w;pEufXmad`gm*J@T;}F%jYZKY|)*j?kr4#|dcBc==RZe8x;5e`)ADoao@S z`-#vZMO5@j+(E<0wWQtLh zF5mb%L3XwK;mlWa0yDDj^Ozgvjmv>a@xVI$Pcs}*${**^NlY5cG2F}R%m-YYbiMWi zrw=64Tdn7yKgU%%CamRdLDLDv!VZ8{iNUW*yb)|eqas!|Ex1ByAHU%1ei_l+Es<=L zlIgt|c9&weho#mXe(WlpHAqydr}-TC@X`pO)K0hQlmN-xU3V;-5*7P_mFE!O`|S6f zXbG;?Tq_%Jc^=Evl%m?#O@INP6<+{!jlRyx2+axN3$|VGg*T8QbOIKm=;R!Vo|Bxl zfbK`Q>V(vx*E#TE{*#@|YF}Nd%DOoqm& z8ry|j-4wr|1ej7lAKvM6RlM9N&alvK1m3im?q5##`&wYOU{$G0X19Gs3*H(^>`^*? z5)WIjGE8U<+&KtMU$HG4H2xjK)vkb zZa*3A3(!P{_E+*L8Zdct9w{PEq@56(gBvkp-i$`6`w(k%gZR%ngjn#AU7A! z!Xf2s#?Q0C1LY9pa{L^!-g$x4Di*`b^*I6`K(Hb;q3|j#u-CMl(cV)A@m>)R&D+M& zNA!)VVS&wT0&I2;E-G!rOUY=dF|`DB|_1!fe&tXor3~lehk28;JTb zBoC|?;j=t-0^@+wW`j+loh3Yr4OqV zvGh*|*kAxtDH{T>0)T+LiI4!X-&B+1?HZc7boo(r+^iX&S zk$i}wvT)cAR>paQ*RvEIdP4VCJg+W$bBh#;DL90#DH(xbk>G9cbYBHV#MSiW@rP_S zqVEnFd1`L0n$&ZRRKK94Gf-F$G%8C8;(>6a%FBz)hs>$co12CvD2jD>zyB4dr%Yx) zwFi49yREi6FNWQB_xu`VtuiaIN;E!OgH83>Q{j0!ioWJw_>be`#ek9|i!YC%B zshih1%Fcm&-bnK6DMs##Nvh4vHk6l<I9H#P?gz0gK^z=zM}V{}p2Sud6H0z0dR`Gx;oL4-7Zo`=t4&(sRgrY2J6t zNNYjcu4;w8=TcsvxXo=DNJp<$G`almh}jES0zq+uI#gI`lrq`+F$hkVB(hdGHKDY~ zJ4KHI76c)DU*9QPETvfCTW1`F=&8-d{vY{jvx*a8>^vuVDB`yXKTUTg!LwTE zqN}YGM{CC$<0^^>cVBI%i&FRf!5diN;N>wK$!*SjOeA#+&ZOrQm#thM($Qug3bIlV z6aB7y<3*=$&-L_iRGL)m1(p3Uj*LsWLzGdD=Oc=vJc0Gl7vcEEulElEivHn$rt^Mb zuSDhzFMOg<+E)tmkug=`sM8*dZ|nAx4DJ6Bb;Y>qW{PXiB6X4T^Hw$xnkg*ZWPe3n zkzM`@B1ToQMs?Kq=(D7)JYi2l(+FlhA$N2Avy4}s4t-;m#GqFn#T6Q-D|302hz{=j}x*?$i)9hY79oyaetMi z3b_-91?s$a16r&tN#ycECve1v5GjqGUO+BGr@$w+80 z?I#Jcn~%`JS^i&XUEy_%7%bK8u?ey#G@Lfsb|fm?nmcVt8jhF^v9|O0Y++OEdRyF>h9Gu#DH>J8Urbm+Cg%{7PbcvvYrq}|Wl$_$ zYGVw)Z4w>f-aMLsB`7^8emR^2`4bJ-1H@mk$gn6>VOkQ7nu7xW{iyJR?$c77tyfCr zO|sLq-o7JBmN0_jXkg-jRJ#O1AnxtvFGwFluE)u6&=(Zzh8(wwA7QZd5#BeAY0wTI zsOeAFd!Tmf#k3hl7|oxGd|eu2Hj)#F5g!j_*JQNp=B1|HUQI#e3ne^zoBUGW!~xJE z15gI`vvi!xb2pH&Ol^a~upOR3I$z2jxf zB)-lCW7j@cw}CDBw89_$2(oZGW6oeMj1r5E)@%(d!48_NVN)NxqzwV~2u|AN#12dr zLzgRItpUg*pHOrU35BJ-)){Cc^7^Mn5jYssoH!+Tut*QSxUK!xyA-B*2j*xwMFMEViGM;EgXaXJ$## zv}4yKUy^g&r~tG6R1VhvAjO1t|4ep%q$RbpFNo1JUAGoJLO&u2lG@*{%Cfvxo93F0 z?OAD&2{j!Y^Jzzj+qCvqPKzQ)v?D$W9lCrIx`d7-zh;dc0UfnsEDyzj^l0mwaE;<7WZJLeRG$(XL^m5kUXPNa%3~~OzIP{ z0QYM6IWZo;ePX;ZTnhVX2hy3WqIoJ{t#r(dP}Q`k(dI)un7h0}Qa^)e%l7)Hd}a?J>aJS?A45Mr)5x-E&!cBj%qJ=%BVa4?;RRnB3(SB;lC60k6 zx2QhMB?tpPDQqG52*%gOniLD0LK@YN0y`5GeBRfkWZha`hu`6$P(4IVvpIP4;0KTg+WAl9QVYv#kVK(h1m0gZ|AFvK*%FO8rm;(Uav z`iAe)I`Z7nJLWj9r7(-V$QwcPe?;7{B1enA&UT=c&4XIoh}`!jP)#Sc+CLZOjP^GS zDl@0>>MG3aJT|j zWe!Hl&ZmP{;+!y-=k z_lY-XTNp$vm=7P@{`fb!8MV#3@C#}XEF@BW9$*>Jf`5k~;8*LNn|=THCm9;+9p;HW zAuPV$82^z4b3Ci4h)OgrQDW!2U^jB2_fh#9Xoa*@;*Yv%0)aWt#uIy#YqlP82iZK6 zxnibp$^#~ep|FcJ;USR5ocGNK;n$A+7C>Kh+06pJLVc?oYJ>+#2wdX0 zfQJuCaQetPU$52I^+O}Z_bM5uvPStS6b&6YY%M0P!_%plsri-}gN}kI5LF(74+^fC)osF^$Ser<4@BIJ?f`2kZ+j#D@YeI^iAb{-YLuK>LP%@k zBgY&Pzfd7X5@Dy&lU~(Lx{-^Q&+ROo^iUW`jCI*-{4+SH9$h!AF3({i1{e2047y(p)URBq439bvjON)2DIck(F?NH;(WGnZ@2`X8CNUHihJLTxxD~aJ}v8= zOV!`GID8GIIT^{yYywP?rc0aM{(!;)} zhGVDiZcw_ylc8$xX5vfWI2sWnfv);|J!!wN2um9mjWV>@|MiPC#2P5Y%yljy`%ghKb8$V12FaP%XnwKBb z(wv~7S7Frs3OlR&!?`GI=_2Z#mo=C=1XqC<(7|sm+Wqe-gwdQ(V6B6DU=VG9d}5g* zZevH#_U7dxXN4~HFQ|F=WEnOiNRg@@YJCIYu-Y`Dc`tq%=!tHMhFYec81By4EW(Ck z8=c$G+DZoe6|R@2-!V=<>wGh;ht@w4s?{q6sZ8g^#}CDH9D1v7!6-I=*zasRLZWXs z?$`lr(r*r zXx|+?iwkVLjO(=svX}h1gCO2!1xlIxeJA=7xmlj=td7^cIQvw~w~o+S+;QpRt4vsK z#!N%zl?N)j2AU)`QWbX0!F?9*byZ$?>=X3KsF2E79?4PxU`vjMFR4aWQDob~V|o{A z7{gqjOsi)*B)3vA{Q?d0&FF9V;3Os+Vhb5X|AIy2_j1$G>`ZKxY^q-NtXfVtg`%WS0iOfxw{o*@=5;Z^ zQ!eqZGmO2njg@Wy8sVxpPP~KLFx2OI%Jgh6LNVw3hRb)MGGtzxU4bs&SWu}!u2(1n zq~?p7VDpv7N;%AAsml()Y|j4q^u<>zlsjqOtk8D9U${58%Z5{b;@pDxfZ_C8a0y9F zl-2jS={P%ts|t%q>&i1l0mi~q=gC<$4C>e)bmFK7VX3{Ewu+Eo^r6#sDlU6+!MC#O zaG;S-nR74BNJBG*ou*;89WG+Bez1tI(M@uOX^eJe=2!!?;`lTV3UfjA>>>aJqr$Eh z+x~>ye(Iff<6)TAy*msU;ntmiPxr{vnktJnDAlv}OtJtANNS|q_){Y2S^Ta-d)gZO zn;@`ku+KkTbl9E|5| zvOGcch2Z{TX0HYdgmF7WTMymv3~V->XEg{?=86`FAkguB#y^U{v$RhTzBDWYc^NgM z*wa@?h`lx5)msQEvZl^Fiv4Piqe-iECzx?=s_{Va&8So+tW$+2$__@TQC`HcY>6Cz z%*eK?6U;y_?hhA3_M%asffCQEH(W%4-;|)(fB)gs6*OTq81Z!W5guai1`XY=-%Vs(wNHL{oMfLRLGbUr?=KH95w2&&T6!)rNoV7=k z>{4=+eZ3tRSIA$ho}&fAM)ZE_m7@Ht#f!M9l&zs zaxK3n2R>Cz^%x`jUg^FJ;#1a}(qT1DKykz3N%ggQ2vcy5>!c{p8_?`LkE@}%pmn+9G^QMKGpE)z|($dVOrK-O1iO^9+2p1V<3p)PP z9n{9qnXNyqb6RKBtmC;3@w54p`Vld9@Vl%pvhQV8O&Ybz{^po{wGcw7>MtCWw-{Xd@GIxMR7{r=xGgtRDK3Mk!Of+Hn4 zbPU~H(n`3`5m3ql(%qfX4N?k2jHG~+N=r&8sOR_iKHtype=g>_VD_`0d);fj79N|o zR!=2`2^>WiCZ7WB;Q(5!|3X!_AZ5 zu_#>BTM$u!@Wn^2CM>5R-!FdO#IF$qw7V>zQk-?0*&ajst_&o*=75#PE4hPP!@Ds! zTqnf=$qJtE+lOj5G`ZMG-uBEG_aW38Fkf=U;fY2Iq0R8C{4tM+jdcL4h%~Dr?5RWz z%s<2zUoz&MKtPM}t>Pw(-d6FWZ7;OBDRlBLv=8;n?dc?&Mk%qU&pLfpPky=Z{=SGr zT=q0@>bq6}dm=MP+rKm1XQXVvYEWTx)bKtBFtRwaMp8)M-()#9(6hW_P`n#|Hs*ES z1f>?J-e45C8B8%2;lMX%k+gy@RuDO0t314vlt6fgTA$k07ur!Gd9zj98c(G5==CUe zq>15%Xnc1%j*6Tw&01#G46?qFgO*BSg+%RndJcv1Ey{;|VmJlEVWc(5Qg)KJ50LKH z1*k#6htcg$wS1L~=r13?>7eDF;IU-2k znk+p=`9=3HPCVkBVk`fkJ+{U%uW!PNkQg~@w?Y)MJUfr_ggC+Uc?Qg?GlsFX=8R%k>{UBQtN7^Fv>srRk9f z#VvT>fqp}Q8bsam8B zT-zd1^o7-2Ew%9;gvvrOhMsGHpZHMxQx(@M8=}12FHHg|GmCgS8_Ch7Ny{%akImf} znQK|7&f9s)Y$&i=^L#%$`M!^RGAc7`evwp-e-ZKJ;t8ub*Y%nfi_ePG?{pI<`5=!D z{=)d39YSS2_tNg$!FR8b$2n=aZg0_bNR`PGQKSF9Gk3wmmI$To$=i;Y9!5uIDh6!XLb_$UeuVb%O z%F8clfmd$LEHphk!U+l;|txpW1^x$i%SaHpqDn)=$yFtyIf2&ysrOQd^%IT zEXd7+F^4@`uN9pJcYMnEsoc*n{XdhJ;IS3Cs3$Bd(v3z(1AUj+u??G-5f_MaP@>Y` z)cJta*)~o+@S(>Ru-7@h!m)ts^Ud4~zmI~3QPT5orWL|eb?9^whh=&^sOxG&&W7{hT3MY;^lp8Ng1=D@%-l|{ z4{erkW^DKR&jm>F7bu@RV9?LFa$#hy6r|hI0EGbxnPyH1GU2vvv_?CurSLY$@8cYt zD5Cx^v{>d2gVH+x_Y!z=`Q;AGc_v985Otd4ji7rYP&4-%VIDWJpF7ifgREztAY&$L zP8WgX*K~1<@r-;Kz?C4l{G-F-T?Q})=BoD*`=wSf39xc9TevSXR3WIJt9)zfOt@xJ z@~XnJE?IHttgSt~aV4vXK=|E1?NaM=Col)uv!jKp!FI?^cgW4;lLA%XdsAl3N&O!` z`iNs3)xWAf*B=#yU6Q)evf|~=6kuevU`GsOA%6I(N|>Z0Y2}wO9w-^mGF;BMg|s^3 zu$hCx$ac4C<89=0v4NnKM^pymq`a2-krFCYwKlIwnZQeum-&uySm%3hExLjRb#eF4 zzU;!pQFQdPiM-CTkI$hf%Ol4^7+KcE(Vj=;k&XoTFOR1$%v{lUZT~ZVI%^8d=W$M# zhnAev29Tm}NX?5m-xQ~{E4k^~Hr0eo`>_>1YzX&aUCn$iN)Ks~A|nkktlfRL*nLm* zxF|65+ftk>HY5eXt=5#IqArMG{$5je|5tx?5e|MgyW2-=9lUO7&I=i;rlX}84WMAh zKx;DOv~f=aEPT)_;+_1+t|FsFRO8v+;B#21&gLTWh{p)^x%yK2w z-(y1g7l$Jl?#rX&nPIMq2>&gXiuP!2yi?2(~Pe ztI9Hv+i=7kn)hfUxcFC7HB~wv#OF7R^&gF}n0ri#vs!hu$)ZHO(E`^U<@$WE&E9(L zU*2(;h3DLNEeux|zNTtLrP0u8gNrmwIc~czmBmUZ%HvbE?wyY)qu{{b0%XJ2j+cEr zty(yb`u-*B%p!g~7~{R|bm}LgBxZ6e%Ys)$$p&4mO>_M`e_`N!$wO zSZG=oIT7h^eL`-AO&F1*|91ZU5HyW`P|2t6gAr!JFjlYtBb|8s&f}5|G`Hz^ zl@E`6;Uc?#p=T=+(5;&t%t(4;@CrA6dw$sUbs&Ul1>F_v!Px zSGfOU!bJu9*z~=KWU}Ka6+6u8qfNYAm#O9&slyK z!h_vMIkT?Vbm&J+1H+A2X6}dCJ#x)#RyIHeM0cYla1U#X*C`wT^$D z?I1*qe(`{i3o{o-wK0T!r#$B01bpvebB*_|M8RY0+uQk*<7P0a0d4rgw9Jocgi3mB zS&APN#So(Oa{eIy1ASU)cKq#eC=a!Na|e=k=%m#iSHsum%#rjbd#T%^)ZW^awf+N; zcF!$R3;{7(;t~48&v8U}1_tyYuMO81{&vc~!mmSk7z@&*1lLf_A;(1WHbxr#`jn{k zHgf<=ZTUCds|37F`rq?^Xe6Q}K6(pW@I(aHx_a_Ii3)@Ea$OoPrJm)P5~8_o*KQ-+ zl%I$eoAS4$myBakE*(Oz!Bmj+kbjN8IY+!IoAB*e0REBd4_$(-{vhhZpd{D@&HQ0T zA2giB3-78+0(ysvP?5keh+aq8R5fbCBCa`4wB)Iv?aqGfp?@I*PbEdNe(aT&$F0L8 zIaoBB&fq9iwL1S@behxT4_Fzi|KxWa7d&;+Hohn>!<)>jft3vl&6hbP>ruUvdtoxc zfr---)#zVUSVLx{bV$u^mg1OVzunwAWGt+h6vkWy%$lXIma?yl#E~emcy^PkB*$5$0eWO6SFauPxx$Q zKDEbPM{u9`MRA;JMi|AOFd(9Hwp0pnl!Zu6@+~5jxh9)Kr`2JqG2upI%{~62WR2#n zFyR2lUu*>k5h)Q94oPQ|Cj4*HNhdpq@W_!G&$=(PY)-V_XuPUx5vI<%NgLYceQ09S zy#Akp^$Z{E>U+m8WTILe0~+;GW4IAcbY!3EnyzN^x%&@;A4jN~fS0hGh$?N>VZ8%ZSqwBZ{y~@07pRRF6(i1j7))FX zZ3_s6mYi}<@9T;ZGH6P~7cu0ymD*5Zhj)H&BL*UL(j;sMcXu-Q>aTG!P`HS0((z() z404ho`u@+5CuaAfm#4`0j|>Hn^=FQwIv5 z=e|2DNGPC?7=L$XIh3<)WU>P~lb*wj9^;HT^L6-|&&cS?6wBw7O?ZF0=rK*q7>A*B_tT3c@pA4GP$0gigSu^!@ej5{Kizx8dwVbTU@ zB$?3|2B!PT5CaK02UXrs5b8Q*H4S=c1FC!!D%#~~Ll9lQ)NbrQFrYO%uOjWfx~2b=F1|R z9jAqt@d+Bz#)gO_Zq6SsJxW8ZvZ2r7-S18-;0Ad zt*99321eEhlw;)-<{H)Tj|s|12v`IenE%o;)HQSC4hNB!ZP(?x<;N(hr6}LUAAO8* zQZsu2f1A{vVzb+qCm?OH-@QP{mt~Wv*~m$SKVroKP9w(!E%+k5PMm8RUkhAcKN z-bxD=P$CI$@U1sx5aai7PeUWQe5;rZ9+x4ef(2un2U7jGtr>Zw7nTt=u?oJKDae4CDL)Cn05NvGCzoz?rej2mENV#NU7&zw(mj{Jg{H`1+ao0%1)R4@o1%~w)lad^EBTcj~Nijt~zVWKI}-q43YIyQ~d=$_7S5_q8ahS z$^AwJxqoMem$skd5bNcyW`o1(#>4^1oM%rlRTpe>#F995$=TIgbO@E`YTS1mARp|m z-kgws!)JTNR1;Af@+WrbHHd=yj{FTxiV@w8=bU_7qY=>{0J?gpM)walKsE%2~FnVZ)~yM*g3DJfhu>0)+@_h7(@%|?n9f$;uApwrgu9GHw z;f{dT92oBpcYf{esKszgxSnr*eVga7Il(Iz5?5DtJrhg*MI6=S?ysPA5Z$lH@um|1 zF_!?okj6VYEt#;AY@O%w39v`98PVki$|le{E}uwlrV~6)h%;G&z5q?hyeud|qgg`B zMDOGzKo0VCHr#Tge4SM@By-$VldaJtjNR#+9F-D^8AB8@I&g7(at3U`Hd7HdPc4L( z(BG3^<0(wY)rv<0oe*yloiAMd;|?N*Y*JzcK~>|t|2Jv;7ln0M;#CL(#)(#~Z!qX3 zjf<=e1jSnYsh6HDhDbBL3|cvZQbhJORfe28uO67Hiy`)O;k0=YhqQKBTIQHQDx__=V5Q`mBZ`8)!Yvu|rjs zKuy{Cy0rY87Y%kmzbdR4cMd1tL53zg5A8Nu5+F>>?DD_R^$~4IPV58Io2{C^X@w?# zers$ECV8Qk^x#MIJp{LF6-~&@D2}A~tH$oL>HsEKKle$PEP)-pt?x1pO4gbXrChy_o(BueV-DNV{B<_wo`4go6f?x}RPLk7eXQ_??aMfSoUZ zE~SfZKV$+`g`#Q=ISepK)Pw3pid0>eGpTnU8#{Z#49Q-i^b%!3iv#;Vv5v=}`o(~q z!(LO3Y?FatGz&lCSIC^ludoeGi_HkdpHcgthaJ{}q zx5SHNL}y2w=qCR!+J=0}iJN?nvdRHQyy~hb)&HwEUzw^dv!$g~qcxvu>qE7-XQ0(e zB{l8)^6ERpwDFHb^+F$-8{0)cYrwjxs58~pQZ(O1krwl+j{BMp?e~vNw^Poc!r!r2 z0h@PrpVn}P&5+!7Rt=EaJw)16_h-e|xJ+8FSSh>aXD?m?osq|hZ(-DjQB%Nxjb%wn z0@O0FkqJc7UAa0T)aT!}J3U7uriE7y()iPnTvRo0QWHSLCeZr1hBl}cmtDALwL>aW z@3IA`h2I{P*iZnAQSqc&BsrZ*znq{BwKyxGl})+zZAc4GL_`JkjvoX2-lg#B@c>T|)r5=Wh=t??^A z{Ptlk+q87Hed~O)=Dy;sEK$fybiry3q@;!%Q!}C>2=CsklCRxFc$iQM&o$Hkc``PYJO710?<+JLBe1gE* zA{8-RyhgG>^4liKOM~2ciQYCfc?pEn-%a8!VmYK|wnvIBM^^>o%D@N{RrF@9a~b8@ zA=;oZEyW$>L!V9MFo9yBFJoXZ{`y%FX`5|6U6qszzCH|*shKKT({(S4!15y4f+2Oj zq>o3nxNf~Nq=(F+oRHpv%F!G}lKOy;Gv0>1DnRxL$}FZP?qL6-(o&cN&E;`$C>1j9 zB1$wthdbFq=m~e%B||^H7R4$q0tt}Ai(C%n>ujcF#*iDMTtyt6X{e6Is}n(E#D>8c zlIt5Q^S*a#;+S`8r#Q2z8o`XI68+JEQWA;wny|U~_QChTD!@Qdl5lNO&_W28T;{Wd za=HfsK$2z$7ycX;fHEWV-W)0oy~k@e9(OKODO`z_6#rgIYU(5+iut?}~7koR@b4%E%a^C6(? zf_gb1ney0We{-HVx}g^0`(9hp6XMsJCdTwSr@Tb4+^F|3FUH>six-aR6600?{78?%Pr?!Z~sKgRfBNm+p%hngq@+G}!K8zC?Qim$(Qh`xA4+Zt>=}HSLe7|6jN3qZruxhFFq!m+ zcawGU;!sb^6JG-r=s%?hBLBk{CVOdpHj-Mx$a&IHa6-3aQGPJ`^o9L50PPK_Ds`ys zgX|LXPt*(hj;V};`IB;y-Gq({V+d^IOiq7gJW5+GZOq}`^CN5m2QC{G_n51o$a2rUS8{UTlF&aP`hzC@)E6*Jh?P3-8RSUf93DNH z)t03-_Gow=QY73k?#^KZvjvo*;{zdsd;QD9&Vx?r1kGxaqiK+iB8zaDvekrL*Cre~9XdCq&hYa#vukxFBLX5Om%;qf9-FB6RNQVu+vi;dgS8> z*n;=$+omDYxUVnYPaPps(Psh>f5Uoz)$XMga$4g?S9=95+hm?<&N!k!M5y`Y!iTFt z3-=u_XrI47R_c5qU>t>mGk)w)BMLzB{Ecg}v;A@zm|M7ug*;cpb@BL>H20!6w-|G7 zv02v6OJu=}k;xZ7WWeNtd(P))hWzh6MIaiL>`9gIz5pTINNzY6CcTd zum20lxE3R6F@9MwKiqHDkZOHs$cDo6j4HkMrjQG>VJ{PIC^0R6G$t1fCR>vg6(pG+ zes3HI-TI#bMp*L0!o&F_p2EBylyQotm(p-)Z)Cp7nF7Vg)_y0@Q|)%iZ!jLyi(u4)~nB)hcweA$Mml=gYA3KL32pMT4e3Mg+R*#_+upz%6rd^re*>>3q6 zu(<}BiwR}rjencKc9+w+;~s^XAHmSz$F$$$2HF~rik!0yoInicH@RpAd{@carF<8q zBQJAb5V#}V9PBMWDeTxFbXzh=!pSOt=Vi#3=9hIczHq}d@QnL^*GYk8dw@2I_KV=Z znX~qpQ+Pf(2(U99Mf8*lT>{sT1;NENGk|A=ajbYJ zp6Zfa(q$+2@6QL@-h>jeF=Ih&L0IA`Bjvm4|wBWEGPMl5s*c&C{#M`?6 z)rRn31NXCgd4KVUEUzK&YcE$51K#q_F^?tKg}?2Q3)fR_+y+}&;>Dsz6X96Y+NvLL z+M>lzHS(FQq#_KO9^c|Hf7n&A@k_4&q0Ro=c|`LAo_EGJ*W3p1N=@W7nnkxzx zm5zG;J1b{LlW*uCvHN4MC&)RU&-_$K3lgR_uPy!!Q(yvDaE*|QPK4%V=84855<+p- z5r&Xn_g1**-hjM!Y9EEkjo#p$bZE>ZH|VQQMZVlXZ0X0mLt^7>$A<1c{zmbqMmfpG zh&YkEW?!>Vgo9F=+~mM+#kp!QZi5?ST9glbxLFFARpcGwnMIC4tP~0}qfaw0K&eX( zt(l4GjHcQKN^9L(KuV0uW9_bDB2PkKhYv*=2}K{$Hb`vMro*twG=jh3&?g>vqbVC^ zF}*tI1VT*0$QzS`0J-GAiR<^eOsYL^({Vw2lqps^7FT?XlvG|LPJF`tU&iq2R^0>N zzV4^yZ&Mi>ZgJIzNckI5r5-H7z6HSz;S0lM4}>S*srR&J(H}A{q5>Y|ybOB%iQ&WL zLOE828hSsMwO)~`p&CKRF*7qQ!TK&rJ1cgDdBqJy9OHd=_X4mC1|t>aP+TQ5Skra` zwzM=psW@bc$;?5{0Yd&(=Pw;r;sCDh%W8-x-E;rF*n&hL+;imIoKl<5K=aUYWNeMV zAH&UD>Va@E7ZwKMtUOcZhM z(w{6En2Jy1|2A0^hiKTT6rQ_-#?xVmIA4Jc1gRin^&{+4!wUcpW8htGI(CKIF)vc%vEn;~|xcf839W zb{t{0u&vGqxn?lM9U~4`NgySX?ziaFLzho?WnDiY=`(%ui|)+<`VF<^7X6&f?0Wch zH43d9VfZ4ko!-)Bd)5RU!(j;4|_rweJ$($~-Y zKNJS%QU0j7O&eBCs5kuZd!;+#ZTjgfXj2(FSfqNKP@cve36gjW&+_r7iC0)|zkwaF zd<|>gpvbh)l)W6!CdhE?l%~o#!BwB@*8;nDbg2Po(h)p2GD8$vHEloiXo!Y+|Eo^k z5uMo!XWcQ-#XaM!7A-;dQMT!6R~!KyjJ}ak&uv@Qu%2MxbXKxAXtESXQhMq%MfNfY z+X%u@z^WR0>$nT=l6<5!oq#jLm0HV0`g4>15%F7N;+CbHnP}tQ>sw2&i$KQROF+N2 zZL!Zn=~0vUhz)^=te~$e@XPXDsC`@`7Ecd*c_QlkjD0L}-e5)kQ@CwN$)~%<8S;nF zd+Ftt@fsf@yO$bQ&lHYmflKGca&;0rU03E(!1b-tHpjy(}J@>_O6NBEJ4iChe z@WuJDL+%tO0l-X0Dsi_O2TI~@R3zIsWkZ@Vm>~f_KWU11!AtVVK>ve=DlSIm7EZf} z6qS}B^&gpJf7`mM40){Ko$wZ4eeNCEfPgPN-MuVUmftgMCw`8@Y`tPGv7p+$gD^sr z-g3;j2gCp0KcAMZ@T&RC4n)XsuNj-~N+*;YkZ z>zzJKjNjMRU|eKrP0mY9o*c@|Tg3dJ{i=ybH_ec0F{hZ2hEzu=YTNG{=vdhn6P&PN znT*gLn@s-1Fx_Q|-3X{Bl=8cC(%?f;8b5S2+r0!BXHf#wg(05p4-z;zjY-Gvfu2dx zSH^Uweoau>R@wWqWI{hG$)t*$8@|QD@A5<+^V3d02)=;+#I+hA0#>JXkeC@V9R_szFhC_JE=9RGw|T^7X%g$nna|ypfg2HdyCM zc^^O!MZ}swd6v8ldt|cb!#jZBZz=k8M@$R%e?kK^iU~P?TrufF<`ne8M7T_~CxLQE zFtNSpRYfk6)+$(vD|9Y&4S9>^>>8EDA;R?x#2H@T3+|h3wBZ}fpN<37c2@oe>P9Y$ z`qp0YJQ}I{oG6DD1Q1;#C4?RaaQEnd3j(N7f$l1Q+9}L6LoxRZvf0slQHkl4L#d8u z=8kGa&cVXzZ)HnI6Ogu1ww8~&Y7tKK)x&Ri&0u6o20lT>J*$@qh)-<&t;RXXdV+K( z+GT(z)Fv$}zIM4wPS_8R7j`WY=J zA?iYuqvGn+X^JQ)6XLk(0}m@kMqE0I0mZ~jLwV>;@@M@dt{c_`fky;C9<}vwyf{Fp zw16}QI)Nz+*|5Kfa7=e~*Yy}hQ4rujYvw`g?sI865 z88Kh5TT<*|cfYn)5~g<0pGuw_MWkohIzN<>CgiW>-w2x;2e&_!?pwppA^%xh9-~8= zc-}Qkz&(Uv)x5R8Q!b8t9jkR1Y4nXHBN;Rla-1Dq6W!}d-{9b<`%men@X|v)3(x%?e-Ld#HZ`?(4!c>p zkcNuqB>50!OF8~Fjse7N)pZlk5Ld*g2k*^yqXoRPFCGc?T*QDWU*}!}2$x9m&$X%% zKUw|wt1_R;4Aj71BI1i)x-%p;x-wEyudB$op16Z5_~7OH%aEN>?G7jlBeaZF^zQOx zq_ig!KN+uRVhm3;cx z8+0tc-f!M#X^a=$l8X0p3}^)n_XZ0F=p|RkDoj?Y36b?9l5h8kXip-^jr$hvB3&;L zS<_>+Zsju)vVVLW0uHd~G(g7yzT=+cQef3vjFdF|-dG9R);&w;9Tl5mE2ANt zW-9nz1;w>b@-M`V#}i#4HQQh=LDgopqe^f7bU20mxgeSTps#{Kmqi81FBZAJ>tC=P zQXm>Mum2Q0z?=8|0gxY zmVaCs+XG3EbxUcj>`rJfv)LzUM(bPrh^SJ_W`Wt0cw=4Or{imtQ0yf4;kRa5n`yrq zMap})nEiyw)qGx#PUsb2^9eIGc?07E=EOR*6-vF|EP+}wt?4fpb?u9gq0rxd&H&NmNF?-NU|V2%E+j zW;MwrPC%AM4h9dXomfvGaX7 zL|>M2s-qnO>!7dj7xn!2Hy{m;fc$;W8N6ThZJPJznHBC{wHt`2JnFgCOIp)Maf@f> zJ_nQ8dPu7$f2#x78!akxi#pK|T7sq9lz`Kft1&ByU;p+F)V7g)e&+-hufvDr2tW$5c5%X+dK6NN+M>Bv<77Kl93<3?|r2feerC zv$N-4S>7A>nV!(Nj3SuSU+1<|^9*RIF%~ss1)`S1(=iO**Q| zW6?=9qHGE86HRcMbVxMj4gp@eAf8{Kqx57$BzUP@rzKFXG*E^dOh`TgDl1;$RfoeoXDD?J7iP8 zx*;~lw2=KKXGS9)1zI9f;YRAuAzjrz(EqaAh`2w91NaW9KxFGV`yhU^9Chg3+nKpG zlx^i%r^;d|vgp)T4Rgm!GMz&o`MthXq7f<4|3YH;2bKV5xJkD)Y}8N*6WXQMJ(A*b zpe!I0Td?>2nN$82Fek@7dg)Y&cS1}&te;!>Wr4Y^LiaBl8i zGSIKHNF%hRM9ZN8$@9=v8dFjHA(<9T8^IF~Y-2WpyTWgn`|C0*ty4>b_RbC+uU%%! z`jL|l0!RUPW1e>xuVng2*DITTGX9lFW!R(c1d%9f(j7kbD1>jazXH}Fs4ddc9)pAY zzI(M=^l`tddJrj~g&-Gi)Vll8!b=0?Jle#ybSV>2^AQ4DbcYNZ=LS#cYaWfVO3es-z`X>^MgYJXVKrM1oGY4J0JG!q5K`WWvE{4!|r zlliQa>c=$m1ppH#cId6I3k5y&%*P*GqKki~OXTc{ej;NEg$b6L^(l@Gr<$SEaw97I z!TQ$bSiN=g45p)XO}e6m3TJ9YN2B$j-WOoAQaBa(OAazIiF9tq{niEPqWUKX$Ijd& zjtfH$JdS34P`7h z;y;HMlHMt8e;Zsk<6@uqU!KAod{1jWLRy{a8!>VM3c5(!$X~?=0NF_zPr$^u%t|^| zrg{z)$qf`ZrfR_Chk2ZO0K*>mNULxeprhz5HYb7zwVMDwdGyM@XBSWBg@7!kPDncP zuVXqvId-ry))TLBxqcDxOWkxq^m??yfjfE6!ZAu(+q9W#Ir3nc7NhIDG|KiMt_r=5 z=yezR3P=AU&1s(Wp-4KU(ux;pnGl!pQ_sOjamm!O@VN_p`);X5dbGpuQQXIDr+J3* z2Qc*-j8Gtok!_+TZ1D^5eDM!-cD3k2A@uJ?U`gAO*!Xc5BI}lMx$W?L%C?g1NBTuJ zfVh{r=lWqQo=iPjXci!_tnKH$LoC)Prp6jOd9|B&+(B24_&5~~bjME_gr+FGubEcm zH<{+;19nTt!JdQhG+~0MOw~8e=wV2(sR~DWL$$=qjPoOs;o11Q1J1$UaVtM}iL@R3~ zk(+C!$!kFEFpH^%EGa*wf(V)qc@$SznCd^&cm&Z#MW0HfQ(W@6qUFBA?2+KoH#xgm zCVlN_<5SzKid0?v45zl4Aoo#m5dtxi+Kn|v74JfGND-TTP?9Ck05b=e2(G5Su9=P) z`yATjy39pk$d!kV-$$ra1pHFXGQ~?Ew2Ri}gj;Egt;x?KD{WpQ`8#f_%ld)%%UX4# zy(^?jUou1N!qOOBN^2;z%LNP=JQRXz^4A$Dd(n)mA>da;$ErN8#HvLS*@lPOYj+f_ zh;LOB`jI*?GF(KPNyze%iM1qI784p`Pd{!3E>loWMCn&zot&;v)Jj@Umc@aoP1UA3 z0xJeS3R9!DYRQGi&Ok$_!`TD&I1EQXXS_boYb@ynQ%{p-2vIpy((w}TQ8&InlyMjZ zzN)G)BT+{bKDI|{=pwRn*oVT-@$pgZ%Z(|rI43I(zuIoUXJ+Af_{-tDWG~sUQ_$0& z9EC6yE}Bow6ta4nU)5xSB1)DV9$&vIV9V+!=Gf+y*6^sxb;}Xy%6g=Ok*{ex{ zh@GrW8VsSB@P~G1(O*h&NP8NAlVGAUqW!RGgY4V*nT2mfoU++{o_k+1Yazp_lDR8#H(ER)e8cOkN0*lK<`LuM26p<_=%6B1FUw>|QUgj7a0#rckdMR5YT2$QjgT%Kz@d#Rmb> znH6Q4tcX8zZg#M6WeQACO82DIE-?SLx^U-Vl-Ky@PeR@44OpJd?Gcp}Xsuy;ZXz&t zx#@p;fp3BCp_fb1o<;Rdt^&%>>YO7P6G*Pe$OkLzI{B#gH>8VyHf=dLv*Nx)5BN^y z``6H85{%Bc~P?=0Sx){)(yj48;|=$|IkWzS$2#M-@D)jBm2A41e#%|z;~1PvTTSl za)&`#_^d?(`Dr73IB;d+66lp!GhJG$7x$|*=)2x%WExR}euu1Ii91T)t!VIQR=tnm z4{$;8=K!gQP@vq#{>Ncyyi$ywz|yPdQnT0+ir&ZSl^gf9kYct&$Zk#Ug$BlQHOWrgNJw7jQTmZc7abKi6-RYu^Ez~pO<7hM*L%&|IWAXv9-^brY&oj*$$X!kAScV>VXO--N{mV`u{OLOQBXm0 znxlQxh{qb91?=inWXa`gC*5n;u2=qk2eSFoo1o6aqC!i6Z@Rqw{V0@c!qAB_ypJ|i zz(6E1dw&Cba(!p`#J&L@tKG7Wj?Pci-3^8}dnFPOF;}2=dnHU7@=Ko4!;w|2`+)ny zFu28$IY?-GG(pS~j|A#;iEpTmwK8QiK}=KM?+HwfAsJT4r0y#`hldMGK8`=D(j_$P zePGctcWH46p~byZM>Y`m&hCEgfG8NNIXr8|(Yp9%ktRzTZwf|dv|Vsr;?4I6y+z~Y z(#1N=5=PXk7&S`@@@0hwMjgo*ncIb%v{F4k%jLJ6Vg3|D+)$!ctFh=rsYumuHbJE| zM~DuAYL#z|!i1qJmPx?~k^6iVxtXnS%;TL^P@~R~w9=xBqx-~uH+~yo8>Rh>6ULLB z@8l-6jjSL_<;ygNX~2f7BmahS0h6Q5k`0u3LAw5Bkq5G3JNB+@$>S!njgsNZ`85N5 z(o6ce(FqZQ;D)B#+$b}WJJ|r;KHX9Z$0!w&Q)_ddSo?u&3ixfw~q3+ihkX@M0r z51d<1g0?AoRp0nNr<8r?^ z3{x1=H;szPAIq;Q_>g5*NBHT>almA4nuQC9dn+ zj%oL!TKXsp$2yW2Qa#{X7q#3uwM)X+CePAk8|K7WEkE{;Ka5z|8xbbTh~XQaJ7UBN zaN<_iIZR2TBcDMz@(}ks)A5iF$mL0E+xRp(v4Ajtbc*BE*pa;|-ST*f4bzwaXOdB# z`?1j?605ConS~PiA7{x&&+8k-rJGDSf*4*-PxewUs-K6;b>d=(c_j6%^|sYZNSZJf_3Z}SfKXv&HUOkU+KduurA@=?05f1UyCOK?%M38tE!xGf~^LMuHHgqKzWd3sKi($L_8=L zqtyW25gdu{yqn9fc<3s-Ww3G%@v8^-{tFT9XNyqZEi$Lxg)kXmny26<5ezWvh`G#( zZ!_7yvrrbp>2)n~oj5k7Jgv7T51&5-_EO?h{e_=00$_X@tVyNfB63!)Z!!SeAZ$~8 zZzdwSVg07t?1uYtk8J^|)>~%pWCkd+R58OFyr!4GKiAC0*J|UD9A-x6GdvP%Fi5jx zztF1WAgXemz~XbuL@B4{BMYKCi5DIV0B^e5QguGmaQG_D@^(zpgG90jv(n)oK|%jC zgEa*HNUADzOB?Tg zyS+8yg@_{urxw1BCqRp?2K@7;NL!U|&V0Npo~I6Y3?f0}yN;Hu4~L$&L^Ff2oZzpgLH1)9L?iZg~ES?0(obQ?)ie2eEz8MMo=YOMZ)+pB%}Ih*dp$I>Y^vW;}Jgy_eeOa>ptIxtbZ z4JM$tlwB-lS!V(Z`PwE+EWb9(F`d|XpOyw#S|Fd;SRrne`&Z7?xNsY1+C@;OG;SkD zapr{|&50|+GSPs@)9c4i4R;U>Betsv(52pWPiA$B=xfbdf1GL<3~vjcrCF&G#UBX9Lw=Hhgf= z`N#p{Z1<7e6Len3tm&VVg?&BTzlN^UNiXP z{OTvT@pl<(ClNm9wslxRA`GU(u!Wv+qKijCbK{mjo<2CBZ_N#w!$2XQQ_H*J%ic zdBl!=C(c^>$W()M*a53MtBwW5Q14o`BfrbQirBor{TKU`$6OO?8~x{wh}V}xzO)() zx1xLh1K({t=;}Y}dHyNm3qs`>Q}H9ihES_h^63~yzc(EZbyspnN`hR z4b7YbP+^R(_xOcX!gVri4T^W~d+tu1dpUwEy{9={7V^)v%(YIshB%!~A zby_e9nloO~AD}=ltFKC?5QQ{P@Fc$8aRt3Yx%^xqZ{$7#XKUT-56Gmbvl2DrDPO1{ z#UeykKvr!$pZ0M{@hy zZT{P+K|3TiBRQT(vwG3R9c4{l&Fr+j*hihMY{`pq5E&Agnoh>=?j9aqNH>k}m)q>n z4)kHr(pswPa?1~#kf7P`()rXFPo$%e!?Xxd2^aQGFDZmTiZfF9_*$)Zn@&w!rpkE+ z%jUyUE@8!=?~>0&eJH4&>r`fr9a2sZ`CbBQyA9rJ@~Kh0!m%ES_8-8n#^~eiUaQHoc@SaeRBth99|ibP*zvr4O{0 zy70@&!&hl4VTSMGf#Rc&cyU8Q+VPVU=Wx@pn;(pfVzyUc;?#gpcrQ|US|ywj)9gd$ z_p1Lqo)iLI*-dc=e^-ltT;eeA^Dh;+SiU5D&M8_W2RpwoP&fSgWg%lgt^jD14X;ZB zb7Z2Pv)M(htc(}>(Ap-QiVcO71AJtBywlpBsfk4RZR_h0cSr*RARItw6)VH-qwoi0 z%gb!`n(%cS0ljI(4n%V)gx4lz_`N49AzRhs+Y^wM@LTEW3}sgb z1lb|MCWs-SfCubDlM^BuO{KSM@#6%(L*_XvPku#DRMz;}wKN8Go!F8BLH$DAG*jGp z6H>_K{}r3113*VHL+)a39)0+u*()FR4Pe@!I0K8M!*&>7w6>>)vmWs$28+0-EG6e1 zvxOiNnQ!?x+6oO9A9curvn)!Z>zN$bfq7`odj%toiRq^`;`A2NA+NY!-%3!|8w~or zqbg{7SF*m`z7e)RGq!iKZ|+mf?0p1!Wh(j_NO)yoie z_a9LFQ{hD@MxT@O`aWp`?uK;yrB`+zTet)Ityd*C*v{Xo_&S+<&53TSC0)>Ik2B|) zd6|iR$ZF-A(ZACIXL?7bN7op|f?w(NT?s%u(yemNKX>^NRQVOJ{BPelw&>$H)h|4S zL#0yiEBP1sQPZGY*Z6ozR$1pVy2A&^ANZd98XMiB(v3*Z(*n_=4N7Vva@^UR6GbpK>fuME~VybskT2t<-t( zY4ZdUD`Hyh6y}i*r7k6fZSCEx^0q3Qeap}=%Mt%iP76u zd0f?jo`wKnYdZHnJ8kY(xxPTNAcGb&jC3T~#EYtvL&hBZ;dHO-c1TN4)vh&Z;R*hB z(?w;mHpFO5MyabaBW0ALAl+!N@N!^f!vzL`rPjHg+EqqkX*qXmL{>J6W$j+u3YItraKs`CRHQO8$9&$3{1a2!x3Q~F~z^}0tSTR#-Gz3 zUl8>zyw0DTrXZ|e4}lNFW==sQk2#=^*;KBF`XK@ z&fvJr1zobWVO)kAk8e}<7UcCI*_uC}-CJi?O1Fzk(c;z zef4`EWeg|;6w>5Ug0=#V*-N_Tg6rwEKkK`9SP4&B||Aa#hLyAh=u z1QbNS?Rmd{m20kRU}iu2UiVtRb?pVUFjD%O%CKXxo47S!0yqVGpH;(F_#mpkZa)1< z3xGqXiA?_!=m9@Bz3;a6X;!%9Q3S_YbNTTmeGy2enRyUo5;Jkn7{EQH#kGFeLRZzn z(U_7f$kC2{vRMM6&axR1(ekLqN}IhfVoS!QrQ&H~5Nr7j;a+aiExdW*Z{ik(geAlu z*NLG(wX_KN#TiO)e%7LX)W18}v6}+XtNk15xz5$j6LfM9&HDaQ({JX>mXaKZ!_ocm^R=!JrCw?%B`55I9nFq1cmGw& z3vVd@{{2fK5o6@NaH-MyvxX|zJy|m!zS=PdEK57p7*^Dm&B4r81URgqEDef%xK zuP`#+{g638QTH9?w7cmdFpSKJGgMotMTt=Fxh^=2e+{>aK0>ST_kI>MUjCdtBp*}o z_J`x2!4?)h(78Us1|{iSr&Ww8fGOayIbZt%5;~toRKj9Q8-7D=uxS3KHC2SeAIAHy zLheF{RW>kx^)L>lU>IcPp0Ww+XDY2;VWL1>|F?EBrSjC5dT6GBWyAD&todgavt;iR zff}DfU>_ZY>pxL}SVO;@c|*8NvO(4PXcUj^Q2WL77Puc6F$}hJS)Z>(UFL-VTI4C7 z2+yEaB|BmN-#sxPzT0`|0?r=6cOw7Fe8|E9zwtg1^&J?+&nB~$aT<&WlhF@4ESemz zoM0h{oKIryog|wd0RFfK@H{J2ckKXh>j)KXH!l?|Ob8d#i9axc@mCCQ+=HG3#@Ft) z_8^viLwhGwo5~V)igRUnw#4czT&ixKTx;G}LA}kTXaBsDh76MO&2HBGze2}65_H%1 zLXbqpDN%ziD+^tcJo20Jiw^Wfs??%SG^ox-1Re*uldkcAR-Ht2flDyDOElR;P?u$< z3_B?~^Zqg2ZkA73XLtTCR5OiffS1CurTDD$0{}&rx{n==ehpf$n9N#R$@Igme<~tL zQ?-;8d^!z?TKb!>d%ms%7sjhT$?K9XQP-uqL)8oqI!P1SmCyH|ijAX^&C&Cz#}2iv zXU(%HcT%I+_S#LPK)GbX%YUZViEKf)d^m;GOUL8%Oks!*6K2dd_QYQx5w+8#kmoA( z(3nQJ#StwOdDGcCzI8}n1Pe=aVofb5fZ}iUh=sk|uzN-Cw@StgVe^xMC6ThYaY+Lh zmh1#6R3`wZux)<41NV?9anr465+e7t>ej zV+c&AMiLkvi@%Vi;AL`D?}$Ez1;p$*igyyn;){K})xe*R`-vnqvB&LPBr8{<%I6*v zO=5?s)LJgR;$D|%S4b9Vf)djY4idvv;P1>S7AES)AXcj5-ND-6 zEQGux2*mH>HL5Mq%V$emSTrK-Eb1J&uSULJ6Av(NUn%*MIwhWVFL7r!AIcyvT>hHo27 zS_)DtQE`1N7C%_0{O8Kw6!fJNy zSB(%kakqJ^18gGF+ocvz-g^Zo%3WwP<7u(gwSi~!p8N-)otgB^Gzk5ow<7Y?v$-B< zw$5uG6KiA5cBjRMe*PuRM8TH%W82>C1hTYqzV6vfAz}JSIP_&T9wiV+F;dJlLuKGcI zGUsp-Tg%_E3lZ$jDfpY*?@U-#@=e0uR^fqXmH(cZly@()hJ01Ix^j0pbu+`tg%W8430NNC=Q# z)kf7yfbsCq7Q)@U;GIAc?rE;47D090BYY=w%Vi~9SlO9&O4=yeAse>GYR|>+KzmEs zxes70SVoT4Aiz+FI_dedJudj?>h%+L8rKU#yehculbna)gvl-Jc&V zK^)=!Z$4WnIXgl;h)DlM^BO;>-!k@ma_MCjOSI4poBcDG_NLNx!|1+97gpKx2!q`A zHDG#6HZU}D*U?0V_wgBeT8B) z=8Gm)O5z+JaKVDt2&q$5gQu~3HTQWqOFi&a>V`gXDHkjYF*2su8?E_Yy|_P#ZRrvH zQ<#@Ya~5=3 zT@#eq;edg=Mx5Fgh?sUUI=xsU4^DDfXADJ`Za7RdVY5!=B#6f16ZW#`l^2Xqk+^;I zyESC;7O8qr*Ywg}LgaMlHLIDx?Iv>?R-B+$VpR0%OMn_6QX~?dYR_UZ)s$Z1K63jP zB+jfpq^T6tVK?=T2G50`+@S77eEOv1Iz20Cubj&Wkilml-p^@UGc3^0tjskVVwZpFta;us`MFu}lkBGH2E zp_X}pA8>c4KTNMD&=?)isI2`iqFUtS6Ykq&?C|x!$@wPU9<)&P3PveeH$5KmduLB_ zSQ(;Z&_zAtVJe^Y83bc`5XN!IAczb2%XrA-osy}`w~_|>cQ<1sp*IX+z!y1 zKf2UFG>)a|bbwS1cLI?bEX&y6khd$^3_BO@^wxJYO%|1Cqo|m_v>=SadqA!kEDhnx zG3PAs?N|xk?YE*vO-O zrzBZ{qwKkY{gwr6U$yn*IkHBwG_W{lWV|KYc(QN}oFpHYLVs#Xv2t|lnLCL=Uqrb4oG>8T^3Qv;375|d z12B5onGi!$JGcYO3$8;4tlCN}=k+cLl|lXm!7fB!8f9r@dz zeVvrcXC$PmGbXiuxaa`j8H*`W1?N*d8AmuUb3T}}u&|og*?#q4`P|-z| zqF`SrMjFLSJ4i0d4;<~5?MVx39fc@|_%T(?^lLgQ!$_qd1Mo#mG-0*|mN6c&71dF@ zGZfQC(`0!}A)r@N)4dHY!Vl74<9=53V)#)TPg-t(Tb7DE}T?ywvT$G3v+*DL|B}F zSo2_EPApaiRZ=U6O>x@k?vZXgV1qEzragC$?1REN=9H<9C{M&&o!5U6ME5eiG=_G&TgZEB;xtwI_#o^!S)KPi$Gr<*NhUEmHvN>K-f&)&sK9zo^@AH#p;&LWzY&1FErjk0>YR#)ehF^Kb{C zr(kJ)TrX-0bifvwCeIpFm-%V-cIV)cfjwbT@OyBDXN)vUpv)qrH7yof#@Kw3 zrLwXOJ*=aJ>|kcYTeSv|f$6Z{kq5jaa5Gd*rTglc=|;io_r#w|d6%f+Jw2|Cdu6g= z$+l-zj-Fjubm=<#8rMd!ii!+1lPsyNK0)sS$z)mzbQpJKg{0^+JpO@nP3dj5y)GM7 zJ-N{>;NIuD5z=x@VA6fl{W-1ly<0PiFp}Lfpimq;KMh9!@gfl5oJCkbr8w@!1H9{K z;2{%_XGOdO9dJ%msUxkQYNC)Jmi|1`!BOVVl}dI9p;7~3Vf2g9vF2qk+Q22J)B!Ax z?ReX7t#b@8E!O4uDb-fU$3B2Z{M*av#pDShEW@TYrx1N#Bu*`yy^|*pd53mPFZ4IW zYYO2O(1zhQBkg?TVvG?6!V$SSrC#gxY9Dz$tJJ;^LjM1d4E>nFQbKZp#o zpXggFoCXdQdzqtih>F&0By|?XFAHVs=8k+sL}=L6d~& z=}vTpr(@juvw!VQl z*lJ&UT$f&)X?|CJrA??ZRq)4D9^WXI-bQ z`s@&gue2zQ>)Gy`D@%^Cj5N7U+ICqNVg2Cg?f|e!thp;mxaNNDBL8a~JJzyF*x1Ch{F4-blRm-w^Gr4UC94r%p5$&v_>Di#d5ZrpgSX zP5hQuLGl|Oh9^H0Av)r{)R>c+OmCY#o)(O!6z=NoIsViN%56js1d?jamP`k3X(l=r62$RR%9DISy zNO^a;^&zBl@qpY0a=d;Y0cxrif_iyD?L+t*uHTS-ff0%@Y&T>zmfrGbot>S%+#(}V zg~#xuy8ZZLIBJ81AfL-F&&h|JB(Kn~cdrbVY}NULoWNdoYHP`E9p6zwrS_4GIMXzlSj?z1>+O(cOiHId$H1{FcZ{sequ)HBC4=S{P>(1?$*(oBXbH7C7bA$wx& z=EPUvqMRPp-5a^A46|p4JDJQ4v-1BpMB30sTuS?Md8t8`>iMoHLIvdssahz3{A6e@ zeWnecwEMDh`4i`Y6o`*ZjbbI+=9TEADk8CL*o!o$+VEAmd+e^#rF!IIIqn{DOb$W8 z8+$h^wSPXA?S205#M>G`NX^no^m23(3&UT}!YYL)i54H7y=ucXrCoB0LwQ&^jzw=V zbG%_EOy6)r9M{SifIrpdMrjZN!erZ0Rq|#RkGs2jRyP1tq9F5a^p7t5%omuY6s!#j z6L3>U8(`m8+1PYkeFU~y6YIPz)88iIWcFS&P$<`n$J)wu5&gfl9(t+pA+gZ`ruEoZ zJdhn?6)V$ml*iFrYnDJ^877-hpF#cEo*zGzp2l}F5V&WmxU`c%tS|ia`#fx)!L-V` z-1A_`_Wr5>(>k=<|CZe_O}p3@q-e-VER}s1L{Q{#!QJ1Gg9YrAD2X_ASd19aCLi{~ zr}l{llqYJb6sQ8dl$SzZgC=u(f)#V$Tx}$=m8sGk6iIVbzqGuLm7V^6p#gO(bQ|AU zBlYXshf2-oo7`jfcr5OhI1WG0Vzh0X@htL5#+J1$Q2s*s+Ew_yj)Y3@V5Unc9X&cM z|M^Vy5>j?ZXP87H(sdJ>GuEsWBeDu_A(}cU!)Hk%y-q*adBEjxPF&8lkCx7rCHegM z>j+?Ix5-6vUNnk>Ws|w_=fsLAq^qNUzokZW8BFFKn*(t>kMUM|i5&_6@s6EKN+83q z&q2eEM+YarKttMN$%UPq7m%P+@|TE2kGu60ijhy4f}#HK^=kSX@R9!(fTz+TbN4qC z3HHJb%PEfSovUpq#sGoD%|!q^?!H{u9{2=k&0ZtCnar-}xzNG!IN{ttWP*t_eqwM|d|_yd0)+R29*m5-5eQB)9jz4;@cJ z;9*bo+OuSlQI_r7SjfN4>sYh}k0+&7D`Ae^%}b(N6AnP8Wf6&vfjMq2Ok%$aqbe@1 z3^Cczt-ktu#`wPcI?o4XwN2-73fnK=S9ya`EXUuzIaY%*rFX?@3xAAzky1TTFbX|& zUtrBZGi@ooM@Vi>l#j>U^4t&CWftf&jL*&{;vX?sG_Hfu`g^O?j$@gv2WO1h6W^tl zd;Y^8Vg>;9ky7~!2>YS4DUUd0f6g?c7+Bj}Ughjl7HlbY*={?Z= zRT_`)YIjdme$954QSQx#tgobKm6K2Z|9dq_nPV_puXtD`C#KCkzi@Eu zG7c(ipHQ}M{HqGG2t8`S51G^<)WGnVY=MphP@SG@A;{$M8*-KAt#|>*@V&+9&j>$nM{YQzsKSza zn7Ft&!8}<|T?B3_iz+)nMWmB(11rD zi<$+NRg(GCPBn_XhwN$h4XqGvat6oBwgP+&SSq@wHr=_`1uybV2{gjd+o=+_bN(z@ z2=)(7yBJ{!!lY>Q*3I1D)K?e@B2o$QvK_#ednkWpWnphH>214y{};sWI4xBbxhnh& zu<2sA+gWubP&Nvb=aiy!n`pEM3aiYlAQD*1)VxedrY z82%Hb8YiLdjJ(&r!8qFS{e|=r19nw$&=fuw)_}sV&3{s}aJajFN-r^qS<1Zp zRZ3S#Zh+TN##X7&KQir)-ASz?Q{?$blEBxq2Gd^nTIFXK*Es*=MrZXbc;Ist@hqfy zW!DQuv(cuxx&ro~U?AJ6=zSnCe-`}c{(c8Wt{2nd(}qp`chbA-PYhyeluk=Wp_Cc_ zk|v~!#ji;!H$#S`pB4q!~c%;sIJ75aCmST)f%;t=!AKzgj< zlzWBTm{kgVjbo3-tt!7!f{5a%Og?rPcN@3K10eBYOV}2B&$oII|HmH(r;5$mH1mfg z9CkVQ`Dy9(zmKO;+7zPGLCp&`>$v3gi|bfc`DVk+UC$hsbKIUPRnZ>R?&Oz|l|6h! zO&xuB3}d>#V67^{1>mgL$HYCw(X5B*}w zoB$p@{t?oJNkMUo_K|{c(RgQ{314EjVDP9ANG=%bv-L)qo4&P+@+JnB*ta}LoVCe` zCRpG_cUnd)^hfpHU?mw9aWhhEuvCwFy0Ci8+@-*zNo9+&LeOd#g7~T?v-l5}lUfqa zH`claGicm@>_06+0b^dZzo8#Bjv#SAf!hjPu(I5Vlus9a)V{N)3)j-aB*qXZ z5}bJkQ^7EqPA|fv-JcC;X0mmRlkM$YOw?83>GHJS$bYk|GTX5eSKhnH6*_B@eRE!A z>wqPAa#Yyr>@x-e_hQqw(kS-c!i}#li}4hNiPCaks-FfzDuUL`c6y7DGixTXDDU$w zd-n$6xbh~Ys=(jSy}~ryqNF3Eas1T)*4woYufbetCjtnpfzX|-;^>@6>Cr5*|6&WU zh;tw=!<}d{lr%Gz*Ljpve%SOwm3Kd6@;K+G;j>azTC;*CFV4trt|c##SHeL?n&r~v8@V3A~?(Z$>k_+5sF7te<5c@k<-eF38g22e!MUadkjw& zp5({lYzbGY31yTVT+llJVS81fL4y(ezLaP`)`Y`7T9CeoQhIUhJ2QHM)uEcsfIEj+ z3pP`OU4NQQ@EshNy8c$fW*_0-KE?(`^c0W?jj*9+Q%@KlfLM18CZfa{5ruv&_zy=Z zG;gR-P+^~j#cEzM2}?GwXtd3u$>;AlEy5?1-7n7bDd+9%_ict8I~2Ix1J*v|%2F5x z`z+HwaYPA(CF`^xiGtV5=xeg@hI2-_2s@5NLM*7hknabqh?hVe^v%Da3-5904~kEz zoC>vQ2^fiaz~W}+p89n}4&{UW_q1qhAB&AoV*oLax~@t6Yr1HSrAiYuRR-=plJ+IQ zetqm>K^ygnb1OFk^Ur{Ai+~+X+LehLh}>q?OhjcJc@Cip>>mGy{I4{~9vn<-Ckbag z+-X~S2x%)hevCgZ!j*1RAScp>YWbL|omv2)EBV4mX);7JND!Fy3=vOaNmESKoLr_M zDG8P|m%>FXntYqv+>Y3dI``RJ&{PX=79t$j1 z4*nJVCqX7cB|3796uzQ>N69fAcROu>O3noJPmz<%#QaUo%!cVXy&RGs(Lp@kD2}1( z-tCtlr5A?&#oa*SGREIW@x`-od>x+c_^U5YJ);<9!^`pBTm0+#gODNfg}dMpI;=(B zq4|K)l`b7u&Z7d&ilPZm(icj>&Sg|j>g74tvIzG>XshNW#Nvf9rt|q0=*AFO3rxgt zj9rO=Xy1=LhlwPlAZ|Zp5KY;~Je-!>D}H!m{^b;z_kwOS?$O%~nVrp0$x8waayr9m z)xUL(P?U47^)d7P0?`(Pz7&;Yd-+HRME+>8a_SBy^4IZhaIrzn27y&#dBS8F9yud5 z1$t+c#lT+VZx`RTt_OmvK38Lz8;x2*;x^h8rOS88E+TYKAA{l_dNF_t$M)aytr>Ca zx{EPygL*BKm3e@)5Dj81KWq(yugw0+*U;f!GY#fQeFnw^>Wc4p%lf_>WeG|?6 z+FPz6Fa^iE9k{{vG27QKB9+?-BrJK-T(k~J+bQa&ZfKWoB1%;tQb(;#6 z1}u)hsN-MtXQY_c!Dt|MSS!BakZzRd5^xH9BtNa+g!6W4b^XTEb)@h zcd`r1=)L>+HdM`!ex(pIql%WG7_WnQmMgU4T|hJaMJxhMrWCM5 zt8@1x5b-x9t;B%zv`S5eyW}HacIQ#fShxW{mpCR9J&4i_+qOZ&- zr(c}9!WoK$hg0>D3Kiz<8?ymzH|?POlq3;JVHK}_tCxZ#vlF4Ft@09S>C#H-12f(; z?03PDbF2Dd4ssQh?w9iHTJ9~L{T&3v+->B~r-o(3U|Nhk*VL#yj92qZnFViWSWLuq zVZYxAePUt5?5IAW9_^6jt_-j)DgHz;IjSmdJJ@Eydyk6c9lYqDhZGS2v*YPMiCH!z zgOR0;t_--phK~c;VQASA?xW_$(fQBiQ@Iq~T>+cOIKh1abTbN;6j}Xo5xaD4`8?__ zHbreU*XM>m(~!5ySON;~(YvCfr!LO+u%z8c*W-S#Y2g;)#7~YCJmc_JJ(3lfD(G0; zWMdL*Hd}Hb7>)U=Q_w@Oi47xFc{93(`$fAzX5ez^hruigThiT>OBjf#jCpRr6o~^Y zUt)#aF~cDt&dln0zfsN;s(4Rg?e2j?Vj+4UATouzz_xNGN=R+Si6)fWZ z9p#su*q!ST2DeQrEKaWpAnI8^f<3zXg(R(P;nRoruL+g#t_%%epaMyT1@%})_ja6i z!%}ffq(Ftmz&)qDKpnE90k(Pm@lBdtE8@2x^LE=+&4V1!z`i{$T%tL zZD9Yj(O7w<>R=!N-pKex9vFpAZ}}j{t5{arH#%gc?kw)G_#P&*?mA%12WIqow{lpF zir#8^b2Caa{ITJvWWhL%ik10PLk#Z&E(J;}l}p6Sx#oSyyb-)Itx6fpjA3~3eXX!J z!~$=0|Cxo~%W{+3ECdZ$#@@I9ekQIQd+F;q5SbZ>Ouerm$e4a~Nji$Ur$)I0chbza z4uq%+mQw?}U}@v#zHi!mv3&AC)(9luvpOTa{U4iXTuMc>U`{X=DMS7QzB3@^+3lh~lvYn}(SVPf`N zpYxNDgd+Dur$EbjM31%Pl-2J1ILv1{vV|42A{Z6nCfjmsi9zy7IYNw8c#mhnxp*u5 zP9v9(iv1v+fe=ACc-7X{#qnz(vve;H_bS>!bbbYa>d!FTQ4Hk)BV;Z!p&q%EM1-L+ ziv?(j^;=-EAO&whA%t~wAAp?h6o#OEuo+a;XX2DWOwvR_ZRj0*WxeTxTG%O8cv&fF>bpr_jVOUS*;vfbF-*=Gt zUa{K*d1#g5iwP3cQykjLuOoD49@MAhT*(5?0tg&hMl9Ex`#2EZi3whHvoEQFtx8=| z%2T$l?_Gkgl~BL;zMqDT6@T~flS~J~?SdrT*VT*A9os#dK?7{@m}|NQBNhX=aD=;Z8PPxwyarY3lvrUiaSzy^4zfh@J#O9^v+*Z_u z!u|hVuUiVSdLa@+V6SPldkBV6R$6jlp4tIO<(NF3c4U1KQbDxc5006#AY{Ipgugl; zR=PUIhlaqA;<6dE2vR+=mf;+C@K#gUVDM74bs7wRoAELoSf5kgLtL$DPS$C32(g+w zjtN7!YOSp-Ac#*6S0cCAgYa1M%v}rRP`26>3!6pYqHbx|%_Xv}FWE&Ddp-1o^W`*9 zxi!<{_Kn(nv8t)4}k=0x#Hnwyz&E5JFylz32NPZMo{b*YJ-O$vB%#_(A|V&cDv*xhw?u< z4U|W(8frl=|FXNRTX06wUm3FB@CHFx;hrB1Ybs(N)r7^?aBhD0+}1CO5-%^d@@r2` zfnfx~1mXlElCjveD>M~vl2|N+0&H6n3g!^~Z-gkjMG;$ekQAvo&!A4h^!xKz?N~=ePk)%880~!597@VTSSL-a z!F6>4oK5ZN+uCbDrpFp;Pg|&O6th?IO>5eOL@%R6Rk}zG^;U6c|Kq3;dVOKP@UxsN5 zEm;S3#w%6%|2?c~?w3F^4q5(zvco2l-gKmO>b{%?pWzIS#0=j=o3cNz(|uz)P&CmzSC#rd$BEy$6#5M* zyt@}pjTpFig2aaB{EWju@JJ(r4z}h`M+fOKuutI*exo1ClQ~LExMcFJk=BJZp%*6Q z){ye9^zrnz7~rPn&rSbqc$KeV1>gZh>N%MyiLvxFIZio|Q*N`2X)J|H%t&m+<#iwe z*pH?~Z5EJ3F@HNJ8V|vvZBxDw@_jIK;`dWnwzYGY>_EN0P2m}+ zHQH3P6{nvj)MJ5b-5byJmMVeB`#Fp4T-I@Vh;0v!wzgo=qo;55M4S#`6rm<=0wUjD zGhh%=`mOT|Vh&yp=jNW*)Ip<8Q&;ZcKSIO4TGK%)pM+***&bhvVhe-8@jjv;tl8I zIWMR}kU-*yv1!bMlC2ayQ$pWZKHjeZ>7oeQMpX+ej`ruqmo>o6&-$#)`N)D6&fneQ zXov246rq>QVpGfemv-G--)3WMo!a+)!!<4IXsN!iKrqhV>;`kmGMXiFmb14KbD;qxwDU{-2S$v#vtI zSq}t%LzgVEP-3b_=-Y%W5qVMGfB#6XnGF(iZm_W|Fl>eAs}k1BI*f23wZb{-?ZIAn zv1M@wMx5N#v{VEQJvI(7Q^&(%Etl+|Tz}}Tth^F0;Rvite@1^)&c2c;#(feym)?1O zH&cdv{LQmg#*T|6pwyaJyGNYz6T-N;Uaz3>H@#Hgc6lu`kFi;;-g2t@#W|J1Shi2m zJPdcc7w9rv&LL)EUhN&QJ2p>VepE+4B#eqT`&2!%?Tg}e_Q}J%#^a;CE~78vK%2xb zbjcrguz^yZkC#_-p|o;oSkW4}OmVA^7s|`9sR^2zzDq)~ozfr6QG&@PY65M`hg6JA z9v${PAO}eC^NJt?#l=%tGb^L$WDQmz-d6d)dlwbFj!V&)D<6UAseWqXWRG7T|xiEqd+!En6{{vz>CX5Kv9ZIS%g3*BoC8NJ zBKr1l8BRQ!*vwMmFOZ~~SJ22GgM?>L zwtUR=nitrpu)9w1w+VmD!jqHw9_-G?V}UC;^mTMw4UW=e3wrU3t6d7lAy47$=nHX? zFx7$dYG(OWdRro)o@8NZ*z2TyAiiGK+&YP({(YD5?H7|PPl}TnR&*o5w#b4QjKf?1 zamqL?TeKdKeNM*^Z>>@TQPDODby$r0fH?a|Zb2GK3}BCc zm`Ug{!+H0)sJ=_D<0=@`tZZs|+dyW4ANX2po{G~N2r+Qd6yC2`Fh$`$d&`waOEcZ9 zTgmYo68H*(x4Kg^qwZu`7AUFaLD#k`CJWl>LjdDO6dIM{mI{J8v`%Zgflwg$@dQhG zC)>ZbpIKXx<5Je44%J$p_C0CwA<*bI%uG~q1V_B3?KM*VGZ-$u8w%mR_){i`d!lOSC=v@dO77@a zE5o9mzm#ZrpHTU~o^v5a^8dm)v-;~P+a%ql|!xrNE%rBE2dY$3JZ_#d^aqZWR(3}bhFVka-#!2 zbq-75x(leytt23^i_ZSzjhcw=F z4Ww^alXYj>jf2AHDyYbu_EEs%s%O52$ISZrPuAru0UB$izNacBuulaSG7eAM9PW-% zOdehQVVF}@j7BDHB&Hdde>K)AhL=3Bo746(Fo1?T)Y0xXo>EcFYl&@j=S z9@rBKF{ZZZ0v**M!BRlV+`412OYYGQSxhERN;w1m*}`n&cYW17JQVI|VBlK>4%-MZcXNdOMC~^xTk^6-0i;Ds$bo zP{U+Gv(=W~1tQ6|4-eE$uT_ql>w$v8^l~_SD*)xXr5GL>Pk(c*dt1xTqC4F?ePBj{YF%Owu)!RIo=H~ z8l`q(`EeHm={#$3d&$@Pyo)I;#iV!5HUSudDGtjDW1&^_4b`E=o#i3oXW)V*FQIcs zOj<2((op~0U*kS8cR!6X$w-DO&=Ra(J|zD60D&4G9+;nBBNAd%;jSs(h2;yLq$%@< zlnz{i%ZYii>E2BL;1Cw2;2^m-4~mvMebxw4zeBJYtdap!=6%#oj`(5^CC7Gfmj7UX zz~wQC5$e`D+Xh9|%gtpE3SY1>U>mG)Doo4m=@$t%@TLBEzskLJ)lk(ZI=hcJe;`R=i7&IBR)C{GS~d zNFZ!E;alDJ5eOfX^man=gz`VWIh`(dH>Q0;1NsKvfHD(%NP{QV^fxUC z%yq78U>?)@L_@8sWnDPE(-OijFD8(%7Va(2%J&IYy^Ky2COorlkYg#q?DgUkY8K`O zKcN{ObaNm|grZXiLKC#C-}Iqy{|I0vGR{LIbtD92pd88wMB=!GVzOgEJP&+UsO+;} zHrIcTXv^ZeN0I*w_U&`)N{In@_VwP5=q$1r(>ldlvJUeT|7<)h7}G0gM+xwEv>x8O z{07zX2begCCku&F2$Q+}`UZccS)8Z4E4>LgI4bAF>Ls6iTOhmh@A5Zpr_DXag%1P6 zRc~Ca6JQZhX7Jjtfa{*ql&^Kr-1qLzjtueZ{B?rfAD6~$5HLfz!3$u?-II~Ced|NO z^@CB#Fwm@LyzifSeo+e$SHK=g@Fa?y9Mx zW>EH@$E+t2u8;j;YJCe5Khs}&?xbQB>sI9J(kMJy(cz~wr7Lid*8|KI2gK?-H|l<> zHUM$!$>5%YJ)*a7u2O9AmRXAhWV25heD&h-I?hq^Ib!2U4K{5`=si_q$al}Gb1!DB z%mJyRl;>xYhi6|(dD1Zse$(K&7*eyKvyM%5`QnAl1|+KN@%~dSxI~Sq%=L$TIwtcN z@1^9AY)h=d4&$2v1$2_DFTSL%cG&^pB%_dF*O)Op#3*0+ z0|b3M+f`9_$lNuA7@nM@^Mq0^p$HSr=@yIz3t&n#X_-UUkj;OvXr{>P{aX~Sy5@uk z60(f&a2cjtlv9z> z>N~$q`oUJ#NMH&;uuN&6SR7Q&AjjWuH|LC<{h%GxAlXHOQp~s9JDC;vR!HSa&;((F zNv;r1MQV8(PdA_C;iyNwPR6ckGXYPZ?IG;f4`?b~KW_rQ_g{9t{H5Z#vUg+_Ix5X7 zVksAEwp@RJdd7$s2TQ2Kbujegsz3U|-34D59~!`MBfT4J^j&wcx=Pbh|dL#`kWw0)>UP zOPn+0u7n1A7e1!2H(Fq+7Jn?}mKgF`a3K1|UvQW)B;Ms=qg$9_q}ce(>Vw4rtUEOJ zLuKkJDF|I_+15lvev0p63S?Rz>qNp%fcHJ(z!(tL$fi}bZ6~odG(TRyUJTbB(%AB*mk5eA&@Z@!2cHAsrglye6rv;(I0ZXnfZML!u6IJu@zOPVXZ6EMUjRrm72ku*acq&tojT4w_pe|z_*;ZyGfEn@fnaOmy?8{ST(`_N-pm?vFfH#Z*D_`)iW#;U$}iCur17P z^_bm}UavW|R&xCG0j?=tQ4-oGeU9q87k1h?mI_JvVXO#`cfjlFz5#KH0=7re(cl z$qVzOiRxs8~S0#}cA#FFh%I}aYRHEciPM`2y-q^X>A(5~umw66^Ah!{tkEIgg+&^ygwtCSsS zHViOT_;-f{xH<19D>Wb}gwLJ4ZTSIiF}Vf<9A&q0XFrvJvj!3tYq6Qge#a{7jt>xF ztiTB;!1?^m66nd3DJ~d)2x>2Nw1eyEgx)8-8wpnFk-hgk+vgjq2ViEwLTT~K{nu(Q zkVJ8|zo_vQRz5?Ik_Eb&XAqP0Ivl3@^j4Y z%fq|bG`1`zTafhnKzf5J5?77qNHnnoNf;fmr1W+Yn=Y(S8KL^s7fGnX99&Q0o!RCf zdn;6FgvnqAe|dh)&J4o#8MfeIOK1yY+LO>suPx_E;KDuAJ^+8QuztIzYLE_M%Kot6 zN+k!x9K-(viY`q@sHd>w?zY|~!# zo(5j`z;d6uuP}05H+GnSV5@dj2n}@g_b}&Fhxx{W+@HGvxS)~`{h=eEahI73N`Pc$ z+PGmJecU{j7)u+(z8IJSpg;@}K}XZtoL%=K>1sXIz_+=MmNs5cl+*~X@h`z6FHNEC zzs^txeH7xSG3$dp37~6hC``@cp*t;eYb|2P4uf6L#~q(~HcNaLkCt@P+SUQ)U&FRf zw*yP-jojJ>e|?bb3$32su7c4R6@7H6n+8{HpM7^pyoA5_`-pWG zyink(!3V!>c?dQrod$A}oc>~q+nZGmvX*&cWFLTj#u*p}5^vDJqkG%U^E%HHQRkUJ>C~rWtxw*P;6EPOf&l#6fp5(lV2tfnH(hN(2O#F}_{L$`c8` zqv-MHc0i|7oxJvB>DXT$0(E13Tm$PkR^ez#ap;~Co5EFVtHwC0#5hwrDmWg`>zK^h zwoa+4*wDzhVixuA6lb96EEzVu(x^0%4R>OpSaWr|lA`I^xc?h6UVEh00$Cs5>CUx} z1quX_;9a=+X|Q==mXi)8yJfR3OIjt20(6_>A>bYRg@&UOWq1hXjn%W>f2#te!!m5UkxvZxdBc>l@bPG)Ub==ba}^ITBBlN^sB&SmW_N%I6f=zoeS-!H zmJTf>GOnncB`b{a-r)4Gr|8u|DLR?Q?~eA{2h>hCdIJCxuT3_k zD-c-H*K6H6P8t-Ue{T#!9YB9orDv6SCj(47bzi;IgTOH%H6LVv6!fC23quojVKL7V z%a89YwBejjwJYkV^TV`i>KaYUL9Tho-u(>J>yj|Ft1=lq>a@LRn&Vz_IG39tBtID1 z3H@*buJ_4>T;g_6eurR1p_>MkaaD2uPj(XABrnZIgH>=JV{+;fN=-Thyd&zO1U1iL ziKr-wsT-$KSOarLOWzZCA&i*2o>8>geZdJd16Xs^z6A2w zOSe-iu-1>}VN0nuL*5fg`yjl$(1a&+r1rE`F@O}q-Ff2WI4a!Y`C>A#&I|tjDX^`P zm9PCDNoO4u<<>^=cNjooKxqa<0i|o00ZD-?g2d1r(ldmVG%7mxA}FOG-Q69cbdPk0 zNC--Zq@Bjo@T{lUJI5OUEFt`G!T#>-LeeMQ5KJQfzS3}qO(wW!)8^P-n8tGeR>q+ z!kW^&lN0%+8%{r=zFIr~4RStG!X)K*zFYA`fRb9CntI^)H@B3C>d1aOSB5093oDIq zPy7z)^ms`IS*VSMGEn~-(0X=cxCxf70^c6Q%_<>7nL*ACkjby^Y4GpNb!%_Dd#TGn zEhbr3n*^{$|5=@ES!yv0|LI}ZZZ=Jry}uKxjA$rzEr!vc*>tvR{mVH4oBLsdm-ug> z);O+~hD6SSqyX!#{qMypD95DIIMrbZhRAAlt>b9` zRUO&rd6Xp6u!yh~m&41p5XkPV^F(vos_Jq6`cKL{f_=s(_u*ZSvBt?CH!Ikz&ns- zX0;bB+Hm~Xt{3V%wf;;RQNTL+$z5P%KZ=6or6EyM8fsB_z5p#_6W@#NgREv)G1JRj z5d#9z!Njxb>AM9Cl*=f<@X#V?5Ngr*^*I4tb~12dv#(;2(NMCu!w<(`oky`so;fNd zzS~Wzg;*CJpKc{(_RhWTJ%>FK+wiiHKHU_em2rqV>l{t>ZJNEXCe5W)S*#WNo`ymX*A;KWc8}RPrd67%}Z}y%`ev%(K^jgf>}6E$Jx6`n7o3ob`@#S zpMrlJj)CvveE63oEcSa_=z@@v&H(>=6HE8qU0){{{x*+$(7H%Eh+gU@{NWD#4oN%O z^e2E6^F|MlfnS5YK5yKlyI&|+A(>Ze{PR-0f@93&o>XE#q$sjraCjgO_y1=Qx8=^% zs6?%)HjuGBNWv0D))SO7e*}e`#!wbtJ6Jw&0vTQ!^vk?9Z{j-*m#{o%RQ)QfBaCP= zCt0VS8&9hxTETP4 z278U^DEFj6-O@^$BOmY005sKMr|Is25=9qROGZNicsd*}7DQmw0CIIkV>|1la4v&6 zAA3v_#<<;u$BJqHl&MmEnmTv?~?g)^tpGn6Czm z*|xVREw8qdPF%G#<=ufrdHoBSl7T*%PQeoeq646$->|mX1YBLSbN`p0o5`KP2OFR2 zfIUcb`LE67fhuwXM&Bc^K?$zNE_yS|>t$YpqxJ|aF?InL|JsRSM458u*X>3n`*anJ zy`hgFC4lU)W?C+PT5hkO#hQ)%`Y_C|3Mqdi7PLm~SiA(o@}_<~Rl-Rfgpc)(d84q{ zav*1kGZsrZ`SXBnl3lIfLS#uhNAi02BSk~t@xvoKBaNN_YnssPvy@%K(e|rR6nMR) z%G@7bY2@yEX$yX5zX8LB)h@r&4OkWt8k+^t@b%=LMxxvRsL}cV>K)t(g<`#f@KIS9 zR$Tu((}~B^DWQdG6NqFn8V8Wr4*Vg<(Fpu5{AOAvQJ~PAv4MXs*W*1!_BOQsrKKX>t}jt-zw?5rKA>GR`{C4c5waKSp>bmHfG^P6><(J85c6 z`!5t+&@mtrAzIV&bU;Z$Qo(la`iP*Cia1{DR1#DaB)bL^`|j~mm_1Y8IsR?1uMLY8 z8~xl&ifuCf)pWg~T@E$4{n+=AHoobQJ=Oy?`cFb(L3c%sSs_0ywlV_n?Z|x^MOpTS z?xu(U1fjNh*+0f5?c%uZaBM2LGKg0#(*JgzxBi8`2Ko>x4?l~a^tr)`)#5#Bjg;@erkAm`d~r)`hqd8rnJXLQ zs07=G?+vz`C?5v~w}vYzQ;T)2e)SJ;r%U6mGy}BsSi>4qBR7Z_EDS70;m^k#fd;`+ z_Eo3)(>%PE5mZg&yMB#_H9oJm9LZFJNe#X4d(OZ4M+xy~vi^j) z$$Bz02%Orwi5qxnnuZ|40_X3St-*w3Ml`+ufOYAAe`-jY=;cQVj#V0UXomith2A~Z zI1#mKfHz4bY+Q6f)6kj@hKL)Cu^ZbsAe1#jwjTIqVIKMONG7y_Y!K0kmZmZ@`jVnbR4lDd1Z+N2UdZ8t+D2C%ET8o@s11LQSNcL zPb!+2zhm%0V%=sS42LP3J9UPK)iVkzSDnNh6+!YB@H!HqZw&88(Ie7wYi<8AJ|>S-Hoc&K$2C@8{rzD7fRX zn{Nu1cA52)!zK{%vzw0pDmf}?SG$C>$rFCj9x-g^oM(t!^aHrOczCzNfiK|$opxR7 zjw6WW2-Y3;7fK_Mks&iTG7KZq#Q|@>@zzESV5Ed(?~bXCl4Sd9-b!rL0?r>^yS51+ zuS9lnrJrP82Hydz-Ls7|^5-PTw#lFe0SEZDjK9kVtg__TNJe<%_7n2#c zM*-09QGF)SP>}_9sua(YcG52PNALCy^vVe$1rpPi6N^v&5{`vnp**0O@!iz^3h1h~ z3r}ZbN*f-AY>SSvJ#s!Nsf8vL`x~XJNtSCw1>Zh4ae=OJwN$rFZGMEG35fbe#X5AX zekf1nW>2(lXY6t0!{mb*bMo%)vFrg3#sHc)NwwEv-#Yig?eI*G>nag)`6{Fu;PVUl$|vbZHs${VvJ`fq2{Cz{a(&IDXo2v4 z#}(){Fpt35_&Q{8MZAWP-N4I-rbfG-2+w)1mL>%!Qtj(*)n|MlpXr?Atiu;Iq=X=6 zx$>qWpnLVBWTXF-t<5V#c;Gi@>bo}>=u55~{D%_X=x!ct%N zuepLv5xpDA1yx&}NZ}@TGyhptmlOY^2!9)fPP;=$lKW|Hf8N(4_cl90SAiQ(vqj9&`(C1K(nDIEc3+Wh+ z_{T$}#C&ms)YvTY8=^xU^OA!1?i!u?6ArNYn+tC$3p}rMr&B*zxF?M$xmjJ-yXGCx z;f+E5SHcR|ewu32fMHw((mG)t1G54ImJC}Q67V#JrYLlIK-FgA8v?UqAZk8i`=|v- zo@iFPQlo|;SX__d!1sN(E5lJlA^=EmBK%dVDzo=NS@JXH+IGAF@8U(I@rcL#bGa+T zD^T$4r5{)sbkV&vX>_AaZtn$ngMU(kqQ;~FPrGRIEuq^sWWl+NEXg*}M6C45lK9U^ zbJ>5PkgN*>%S8yA^u2DeV4#^Uh1-f{I_*4=eR=2OLFBZDvw8P^MLS9UKt-`?JDoP5 zw5nYzW^}UXsx=LLcNk1mkA8L5ba6K_3wm7~-$)RDh>c*bbB3{XM?By8G$8S6w%g22 za^AYO3G_Bw7iiSRi*PbF_c19Bz55qhL`;sdOn=|okGGMq>KU;9ROTxT9(BIw&gg=qJVB6Wnr?FA>E7iRj1dXMn^5=C zXRm`gIO_e2hfg$**3&L|PJS;me=$Bb{ZLQIk}!`_+9SN;)OyI z*8GO-1z`y7&28k-DC`%XUQY4`+m(<6vv&>n^#Bs|Zn;cMc@-(PZ#;?!(^m}QWc~kU z2F9?|QDQ{6IgY8{8B0w0G*?BcIr0^4u0*Q2(4-{i{%WL|tS_i83Za%9V)e@tvUNE( zbK%Lmr0qeL3P@Wb5=DcM1x0swTBXlDjNm7d$fp9Mz$#St6Xg~u(&#x z8Tb!chU96`KigMnLxSE8?~usY>R0dp)j$Wg z(bal0@5z-lHOrs*1^>q7)_by=xivN}trw9cPuZN?f3GL-AbMmOS}mQFuU)_73EvF( zY+Bz!!YDREluYplvOZBD-sU8{L5eW&pTQX!si%Su#=MhEKxtI5W|s1?i~3K(g%F|s z6IxYHUTvS!mv4TD?8CedKjXk|D_fnxTV3%{j;8~@@n48N>4A^vo~4{@G?KyAq{>t$ zIOXYg8Qq&Z`ZZE7a2(v-l*)`I!GBFom_p(zv~v@th&9m1mCHZ9@w!>1k5L3%5mhrAk;k*yU9|^ zotcOg#AT67*Blb?|D9!9lEc&r?U@RDM@bxdI-l7uY?aDEI0KsV82Mk%`RPLgyo~Bw zm?bBm>++rF9-?RMz`s9KJdJU?Y=f3L+>6Qyes!axCN0>Yzz7lhYeGH;*=d(F8P7Kg zrwg_0X#Ef?>_jWQR-Md#3+%5@y^fl~?c%J?fkPwF!E@-+MY5aFr2=4b_+%r{^4+~RCoFt9vN=HBS*@Axp4bF&WF%z8yX)B zGQ{i?Vq+eFH3i0+p2a_>PAr&9&HS$B_=GHA;eRXY12px4y7-paFX?%ALNSPJi%J^n z7U;%cYuUX0TP@IW%~wd9Z@F4;4k&XYM-h@AI}lrVR@;`iB1qv`M$9{L+@1|-mqeR~ z5`xHTrqa_=o;FFNoir$}KOE(5R!d+qLM3?C*Gek&h9+4u=Az93Od^8u^Nn`|+8;d* zZuFiov1Zmh{@Z?8Bz8C?MVzT$W=N?G%piR7QKEt?0qUYw`w~@u7I-{LvP)fYtNySK zh}G$4eS(3>yV|>w1O~Zo8I5TAV*wDogA9NS3mMYJPP#bBJ-&R_a^vZTWaZ zhI`dM_*E!BFK)5Wi2|HLD9ZV?7m}5nu>+vd0u+ZP2fQ6RJc70{$a}@Lowc5;X@kOD z&flOhs+OctI;qZM2?=mj0zV6jby0V!NZf?!Q8!MK4B8dwnbKm#&hQG4WnO4Z&@)G% zzd>))k*_)i6s8Gne>Kw{l&1mTO49gnYCS;Ar-*%_jJLmoNK+uL=QI#3)dU_RaZXgi zJ>DR32L8)GS3K3FTWu#6+mM*yt@=xqG#+JWJ3^r>JuRvCqDjS`JCDzkuocir4MI{@ z__V|CFvd#%PA@!cOrxo!qb4T%r9jOF@~0>yoo;eWpbSf;tR{fmK?-peEYVP1&wLgF zw_z#1Op@zz&BR;v(%Fbg5-tsT1NK<7t%XXy9&AN zaMt4$&neq&GRJV6$q5kY?l_qCD0V>%*PNX-44{L5A*JUT(c9_rTS#*_S1fb!TdxOT z#Z)zOo;D4@iv{~p8s2_~&~^si)O6SrT+Z(h8&XZ8$#{46$`6hmfw!DlxC|DO-_@XU z72jVi*t2Gk`xr8R$i$yndk8YTl%aOzUJD{qP6+QuOvDkZ>6WbCj`hIu3jR;3NG$BP=+KcQpYy#z}6R-=J5HST-efO^eRI(%kq?^J@CHMH0)>s(e_<` z?UaZgXPzD-CqI z<#b0wpY;mZwH3N1OCwxZ$gE4fCgHVmAKv;7wSXSS>k~^`-|Ru^Dby~W&9cj&fQQ;$ zaE{j#7WnkjS&ykRM8kA)?awp?y1mA; z0v~QMonFMmXcl%pxr-uFrU{AfZ{@(6hfBL~L#vSbiVFL>*I2XPu-$(ab}qbLaoP}6 z!h*DRTa;OZEi*d&octMd+OB*p&AQp_Aoc1Vo9t&d>_ilWSy=J(>R2;Qe~m5E#iN-) zD$d1n_ez?*r(^t$1aH1hz;8rV~Ym>OEBL9_a!rp^k4w9 zHFV32z?Wrwzqu>lC2u+<@5HNB8DRiVeNGYk>tp1zAYyfHtyL;;nwxEg>Q{FRsPl%u_`pHE|WcTU; z;aZ6cdQ6LxDHFFjoaw#}l8s z$XLOWdsJD%f`zltfrsA~N3SMM-JXR=aI3l923D-o}2xg+d#%A0Su*H5EFI9ty z+=w9661(Eq9^%L_s8lZ$bR1J_NsjCrkSktL zg6V~GkZYxp_T9#Xav~uIVA7aoWcr-b z3M{&ZVacJ+G}M5-hqxD9;E4=~*I$IN;s&B_!6)|#AQ-Ju?tMRpkw!D*v2O#&BQafH zQ;GdexHN*P*p-l5IKg`&3X<1Tq73w;bM_(vf1t(?F#W)D-FXEt=d;%boYr zp~~#Icpvm5w+PA6PR^j91rg-)zIJl10tPlFt++!%xE6IGuuvex`SV7Cm)BkAm$+jn zLE{IVisOQ|Kk#mgsF_Y+VlhfA|JCPeAl*No0$QiO+cY(>3ptJj@)TV@-oh& zLuP^PYN0CiC8381-fOWvV_$9K>G2Sf88{ewn%EvS9q%|8E#wfDHFE{?0 zk^``=wVxKbHG0jU(A?!mrT{^}xj9x#F4qR(;xIQ#f@G;MUl5zhB1N<8XhUwc3G?r| zHrZGc*L8sc>=)6h)BUe(7+T0N$L<=$ioHXbxABH6jGc!HCVH0mJuEt}>AW+11^+j{w8l#=G z&JIC-0u;n=&Kj5^$QLo^W97+gIbqi9xR&q!1ZHGVo8|8;jKK`k@WRN|BI+*>V~DY2 zFC7jx##*wt;_l>BHtvtYVbPH?Q#$}P*X10e{ls+%2m^+-Z~rhq znT(>a)4Tbmk-kivCLGR{F}KDXyTx|(J$mm;h4at_-Z8B1Lp>EFzd5}!{pawUnR+%E zAs!dcZUJh+(>+3Y%?UVEKgu!32tZm8sGQA0F%(Q}q_g4HYtf4#QMzf4i2NAcT7AIS zvIRJqUz?GaEw6{CDa&1zRUp8g4zO+*UaW!;+Bq79X)u9-b0NSoa=zXQmT%am2GxM> zxnwpbc;!ec~HLu5fY|Sbzr+hY%yL<;T+C zLAGQcx7cd{N{VNA>go{Rvd>3&Ft>=FHSJ(0veP+a@v-yakTinomIN;R4+}2XAglYR zZ4pF^o_`4h?X`tI4Q*=g4VavL zf=>;Au{75IgaqhYjyKC-fuRB|W_58{?sNE|mlmeegfQBuRvD_tY_;JM3E`^`+Hj`BK*}YmPh%`5|Vv@W=(>9x>cWv&4O1EcM@ot%r_BCZf^XY=TemO1T4evdu#g8Ns*$l>JeaLgdx8UQb$>3?0%*Yq z(M&%L^L8ANte&lD_|eXO{J~^a*RTstHCieT9oaCqY?0xK34MJDe;N$wx1hKHvG)HOu@}xleJS-anLP#06d%-=h~VENsL>UH zWZ>>2e0YIrWs{n6CReqm7SFGjswe`F5_dwet{yQrCkvTf)Z^+RI{PN3KUASu`1Lnt zPE^0BcmA6y)=3d*le}3|=w95=?{-#1Ia;a83ziOSxg7_`Wy^Pi}u}i(3#i8YqC%=_t zLJP4Pauug9d{$te1JE~3d29wyp?)_I=o7vdb6Mo>lm)TzpbUMqL;Hxoi1j1S&Ro9J^fxWkh}$AQUJ0FG ztKRRy6n7_t71xK2B7U41gmb?>SXTe@m)*5z0jRn^4sWU+UBWJRe>GGG0Uk20_oZdb z-g?rsAX?;WnHEny3-%3XxKR>z>(eL+57^7ay#yMT!C1%<)g2iDo8d+hbDEETmftWa zP(~PbMVT8DC_=;qwSo>az%J*jdPHH1IpXib1EG`>NlMmp`k((ovscI&-6akiGn5!c zP;V)2xpB&Wrdw}KdpV{YkbD_|vl+oK1xA~D$IPWIIk zq(Q(B>{$dvoUv{rox>z;oTY)z`_QtA^@#^a2BSLH?`}BvLWVBR?7n@io{W|^^?9hT zr>cR8?oGSphdZ2)?vtx~j^?0!w}v4F62j+ZPJme=&&q*kTGfI`>F(});P z(#?$*Pz+N`H7kP~mMk_cyN;9iT73#DfK`WdZ_+;5cLM%fHFyzjYa#Hn?~Z#ZiduAU z{aWvYJ4fw{LEVq4#B7g7f(KY>GPD{4z1FPhag%%$)R+Hj4F%`7ZEN7W(SE1xp7tI` z5T)hUc#6(JeI^6KgGx+^ki@S-2ye4jrzt(UGI=E5ybDbf2OWFYc|ej6wN`d2CQ#%r z-MwoY0G!!FBI#ZrbdjM%{dob|Q;TS(?Pp!$DM&whHa?}v_G7$?HO=1dT^{Mh0g~j0 zsIB8!5gA|;Pg}dLKmpNp>Yz*D?G4?y7EY4xSr%EDhW5JyLT?5$c}WJx?m9j(*oQ9v z`7G83c1NDO6+w#;{sad3KYMw`4mSdTBzK-P0kE0ar5TIHkb8A$X*@JiG~#$M&I;EGOBp8@a)NmXJLttD%6Fe`02zw2 zSRs?M<)b00_)ln`JnP4wlnmaBUMqb7Qe#km;`r->NdpMpY)kgkZ30GIP0??E<;pNzt|J8M$D8ondYBE{C1?3Vn?WFTGzYNF zFcs0P+}}c~@qdMy;Pe*e$#oPqwT}t?W+8j|_Kr5w{S0DE4HjfTab*h?ux;CDRKj5x z3p(VGX>rXLD4B=hQtZvuwqT-M+!{DA)ylU7hrc`R>w_NnYoF&oeg>d)3^he&nQOpk zr~$%^Nj-d8QwdB`VGu8U;7zbU7!rLG_7Uj0qEdW|z=+$io=~}?a40G3Fd)f-SqI(q z5dU~m%ss7teqo?|geyjE`>XbkFs6{9xRgZd5}l57$CwsGwqdVmzX%uEzIlS5 zmf%A55iJ~j$Kpr<$PU) zH2uP0^>G`RFfpaN^#J>p@Fz1uY{Z~yAXj%kVpg4|wU8C=$@EXuRx&SCb55m|7~=L% zw}ijfZr10=or=`PjXfI1`~BiPP41~=F9q82b3M!X0A%C_^%u%DW=@_Yb7wCpi6ypg zP2XxNJZaW!N*_ycl4+rbjV69|5g6Wx=offyr*k`OJsn6ROZ3yC#MOH;&^sLRsHPGhkC+Q-uqi0 z)LVu>yoyhuE4XR+mJowC-bA*OgL@nuh9M$76%Prr_EqLrk%j`HHN||1IZsO#G{|{Q zwBlH1C8H>4zKceAyqrja+ga=UpuV`RKOR4G2E@QG@yi7uj4}!t2;9?-XZl&LguK)L z&)lreeF(jpS)Ho*Tlvn_qgG<_I&yqKPjG1-tgZ4!8y_jzqguq~z`(8r9i9BJ9)tVz zt8LV`fOKQli8lKL4Wc?5@V_!qtIAyAyonQ-TkWhLH=+a&lf9};L!R~~hy5BY7cQ_8 zG}`LyMkCjgI~o9qm*J{WvIc~IL}Ol=08R^hBD^)`5Y6UO$NFgi(ogu-7*>?vgit@UL31AH%a2Ck}7E1cyv?W!TNnFQs=5Mj@A{beOAy z3HtC6-QFH1-^yjxdShjT$ZKQFTSG7(j+@^aaw&6#3fdKQ@`-U)!%+Ra@#U~}Nwv>@ z`wkqKmme5EXlm=SMm9Sxn%(OHokcB*;t~ZKFSe(}9PRXO@J8kGfCfCo%kpPV-U1gO zm_bd*4pVPAQGY1!tk1)iQhLWXGL~?u>C0DZosv-!Jfim-C@LyWc)+pK%4`7VMCj=! z9L=Wp1Kw?Qpzt2R&XL{zatuJz8EFJit**Oz&F8at98`LF&G}JftTRNV{5{iaoAD#a zPlV`15P_`L!xmO>>N*K83O7zjk$1cS2q2@!74?k}WsEH-ffnb@^N=5RA^{-eg0&Th zHE~RR9a3GWJs;x+EqCuS4Z5ke(q~+fV9d5}Qni(?bkD~t`lkZFj{diTkF&b)v**~h zy8MpxsdLXjm94zYU=YY@Sb_SAH>=Cm24RrT&j;*BE@%(f=WX11%$)3_Oar+F4yAHl zSGi*IWjiX%v4?^6RO4QQff0|9(xxHBUIs-wi`?_^hD%HvzKX*>kk=$O;Y<2L1Ehxo zd+=g{?Ee)EIY&LuoFOsJOQJtEB{HNWZxNz@-LHTY{&vofJvGr(BG*`zM!#*BV73j> z%<{@+5WgGV@-rlFiT+|lT%?S}aX%dAMoi(@o@)k*FFZhJNxv6yZ^t)FRLocG9`6i9 z#uu^t4hxSL&-8w{GAwHxJwIXb5A0EK(SV8sAv3|l+~ejiK1ky(U&!7W@-uXW)&Naa zAzgM&iNoQ{x%OE%$RC7Tn?)U0k=o+)^`Lr_p#v{ z0EGxcGG%tFza>aaMTEHJ-XWQHwE5K{Gmo&vl=Ppt<_5sJa!wbPK9!!L=pA-ED$8NP z&HTjt=l)IGYzsH*(y(8eK=jBTaf6V2TJ34yu1dVqZVp@i#E1X7Ff2~OS~s%nA8?wsQ5ig zUXSO;-B;Y(25Yn=Gv~ZnEIgK4M9TWA|CdxFAY-zA46_yy7p=TV-sCu&kFp|&9n};2 zQSOP0G=_CCrFWCSDe3OR3K^t1;?2m^uz{|mg06fmJKK+ zKJ7g4cw?JAAQk{NB6-VHB&*o-YG91nX?tkhd3&~Jr)y`=-u+X%Jyl4GgBX2hb#qiU zLH@-*mV#RcM%nZJ9yC~{I7)E%F?wQ(X6fB8z*#iKgPnuMpH86W=L z&Yjq!z0K$~DN#(90TNowDaRk-j95WI&?>D&6~P==<~K@GnDM>HOYZMzD&|5vD9%S{ zeG6F1#rrMiXXKaA{5YT=0h`P>Umd$Ty+lqDu+p!+o3g~1qjF@l4knoJpcGqqmMmrj zeX>z6xt->ughkb~_|*dv2aR}0Nsat=tA5`zl1eK^C9(vG|c5!`vGITh2g*$xg zo%RbT5>=m*ke)zbX10H&Ip+WgN)&1RxVbqzdgZ@BJ*k~@EEyF<8LIQ~Ux<1_`to)qLjt_@az%x(V4~-;&&awye<= zUY5h#pU$!vaq5q@7dVlU*x8{nUTYzodYPZEH=8P3qz*--0Y8_KRJ5a5H$>Ptimq(B2m=x^($rOi*nh=!XqOOUj?FL(WMCTVJWKkln7F zJrjn#;<7g8iA)wD|KIuvq;Y8oTt~XW8DH9wpAzpfu0e(KM zfM2kPMdh^FFV6`8E^Zk6k+U)A^zHY#(H2sAF=mhv_Xb_ooDktc4vzh!*8-y}ie=(A z*=CalBPi18D{PqY_wsMqup-dS!YuMW!*K4!>49?_iV^vSpdxar%qKWLj#zM7`EYt| z?wJtVC~Nb9D1HAd03`hLm1nsXSrMOmhis8rn4C%6g8j_OpVGM3!4mhA&saj{A*Hkx zkEMN$3lOMvvc2mYc3^Rn{Ut1+EKPw00^ds#vg|3tY$b05I>O>>o7n$3o&QD3PZ#QR z>c=5p0Q>vSq)c3VuyC#c3W4Te`5X?5(H5e+nRZ0z2gw5p#|x60lhWj}%2y8DW5`~& zKIvHLE>LP+Fb2J2RZcPT&*Z9m_%)iMLn zU0qiv!;fZ<$H7XSMX-iKS6Iyql#E4YjMfR9 z5PFhNCy*%wBxrno@OPV92#M}M@>C~ujkAaU;oU>oD%Qb}?;hnrAmqt~>P}EERR{!i zME`Yak@yEAq>2UI$|1pXSmPY4R|dX9I#(PR zbAC6Mz@CW+kw)TqJ@h}x-ldHs-oqyX9f0f$@`L**)P-nl@aQ^agA7Fl3hIq+HxQ`8 z5U0;8_a@)H^P11S7rGj#XwmfRVKr!LM{xKem`6|RG3T7C90P$N8J@LNm9`w;At z4kjPA`9F2z2uM0iWoq7x+woGkJb>1@?Hqp8MMV)jT`Zgp2=JVWXTQZ z`NIcmfct)M=vb&rhV2U%nsTdySzCRG^@cL+J<~t7<-xo701{~RPQSjdaD_1x{mqQy zn(mdl(__$%017qY7&1Mn4JgXk+TOsZ1IwS(Il$81QlXtb`O?N`hs}yhx zN#t{j&trT940UU|0}i~Mu$Z$HH%L&q)s@+(9!rjTs~&Z)i6gcjjtpD40iv^P`@rNAin2P|fjFBdPZ?t@ZTx^ScqR>Zxp%1QZpfc7o%HoBcN*Iww!v0hKM z8>e)iSKT^zg4dF4!}51No0|8dL5Ww_yVDAg+B;-tDNJ(KXF|JugGF`@98NikB@oDM zW0=NT4x%UmhXlknKKg&Eqn+)veI;U)H1YKMwz*&yxkZtwMFBG+6nXi!-|0Q!t#BRU zW>Ztq(D;BElx=}}WLevvE$fO-{LeoBFv|;P#YSW* zP8W~E#b8ofv5Q>F96N3j;>}s8Zi5mcTXLLjB5%C7{vvz`$fe&4em)Zs2>=aGG#39t zE(;8xMT!9-dg5&k2N83N9gSZFORU&kwz}V<10Z6)dp#po9&OOPL~i>^Jm2oySb7w= zD~aEolIqUe{>%%4k;=EIro5+@%j0e@pQzaUB3d&-T9`j=7&iE&K#<9drvYeFUVh`Y z7I%;=6@4%(2!!5)hxs;$f}Upvi>}|;t}LtEXA5)(JMIrwsqAY!l3Bnl!{0dn$CW^% z;^;7M&}fn7epu{V;Ou4K zLQ+Wv-ftj~vjsjZ8pwjitw|T3f)`!29D9l|$Qb>6H;v zbkWDTq>Ju0j+xtSMxL<7h+2j4_Wvl|-2{V(7`NoR6!x-d3nGxbyTGacd(zE-;T&z~ z%A^_4$%M3RSP(hmiPDr`(AG{r%~wXRK;6G2H4XUQlj=0Z&Ax)2Ya|txFTHL3YvrUa zx*7MdB9P$vQoqmMTC)M@Ir`r!?N0Scz8zTrRG*{rGfmYcTC*h6JVMA(Uas&Gf7?sd zwJ=9$up&Qw?_3O)OKV*QkDC%sPDtbgec2{F&m{gRaeR6D>p}5Ht~(LvGxzOFo3uVl zx8nW0Dxcn3$P#|9_CCYb?keC^3px1uW~zwT+!F4d_^P_>nrJ~W2y4Co?bu%>Y zMpA-nN+C>oUIgg`Q)RZIo0*KwRHGKzim(nHiEn4SiAQ~|-PawxbVxfU8q*(amB^y4 zZJ~tKM4}ZXVDX`^o9JJ;K$nv#Ic_f#WDO#MceWOQM8n(3S3d=ME$_1``ad|I;jzK6 zIvA_QmT?GOPgYGIyjtc>|NUpVeZG_Ra+nkd<-hluhI0501RgGcUZ{y-pBdrQt-vC- zD{xlx80}u~TT@Qtp^3<{A4Wr4BbYtO#Cw3rpyRen(btYiG7FoS3silFHK{5tz6v~I8a(vx7rqpp z0x{Sl8GAKd1Qy*LmCB-24FeLvrdxIjKcvcU5}?uGVuK*(FV!+*v{)WKEg{z9G1Owg zA6L@Ikkd;Ou`zS)4&rx8RBLc*h){uebK}_O{0_TUA~j|$qRH*WqcYNS4YHF`VkPa> zYlXK`fPWNt@j6C~r)9FxsTaXVwky4MiDfMgG$M;~j`q9ctob8qwA@U=66GA2<_Shy z?^s6IQ#1V_1tnx$kLa(yJg~5K*sJz;fvqb`&YNYOL=w<`?@YT7qY$>}-P5R9B}Ys> zI5F_mjDZy~`sci0vKCx9L74`fP+R?*KLoLu;p=UpUH!NobJc)DL`KQpU?Xk2;_=^+ z5`KB#p8Qh6qk1AV7ll+?%%AhyiC4=Z^}mB(S>jz2g&kB)GHdw5;d*uze$8=$-B@QF z))&rbc|Ud2sUUgECn|_^J;chniwAp4u7GRar0T2fY&$``bjyEX+FDzUNfvBS1l^le`Iz4 zH3z1qcuMmRT{g@Je8Usp?S&r`a0&<2trPCm05}m+a7;Xn1L$mejK{4kq+L)_+~Z+& z@p87Swbm-LCuZCI%Yx%b-EiWNB5>aMW?iBcTVzL}#ecqZ_RLSvf&@>G_YcD0^nPqU z_WmlTQ zF<`UBXMtgmBsHK*Bl5w0#W-LUf?MUiI$?avHc z^(8A~)8sRbW1=EhqRxfbv$vsn29FXON`|O9I7@KE@6xhK62EtiJZhATMFp|@W8tn# zvX~p=si=)~-JwT@%RZ3ZpopYa0={vSg^2#=z2c+%C4QpAFGbP&?bk1=$d?2{lClLe zKaSYYkQFO-ujJ@l$qq7^a7)O#(zy}*sVSa-Ebge@e@u{&7}k(7_^HI$g^1Lv5}L(F zGn=fTjZh?o66RHpo5wxHE4b73WRVypSUkU9x-nsCxO7%dYO-gi-!@70tJMJiCCu{& zuO!mT@Kq#VX&N6twa&YQ=X>O~%fG6`U>99X%s=Gu^HxIigcyl_Np4t6bHF$M~cgU z`2_WL6!3vHHeXLE0iB|QrP|Ag-CK8KT{ED_5JRiLkKa|F11j#@2`RPh^oU^eEc?=@ zf1w)$hl{FuW?IB{@yI=)OTtSn!aFRX4w-6*YkMBanbOl6(OuMY-hDXWv|;R#iK*9x znmR+1@4lr0qEy7;d_%jv9;1fP4E8*b7%z-RPLBMZZf9LyR!At6lXdGOb+$} zz2TmEBf$>UA9cj;3XubZP)m}S@Su}rtyU7bj#k610}y@MWq*WmXInswC>9S0d=O-f z3^&iG3yL{}zK$GDe6ImSn1Ny4nws>EjEwIeTsdl=_lVK7bol&XkRyQGt>8ZOr3~9p zRmnojb9>WJ&n|5%jhjnIgeCy@#^Ka9(&M^BHKn?I^UMGq;#w+tYaKSgR_wj>tF2jY zT?jR_S^fhe9cDTpC`+3R!(LCc3M|BY+JucKHC{cJ*t+m9Zy)YE z!Vrg?d8&4Rvw}{}e?a;-0NETYb1-I7q6mU4zOt3Ar`k%a5!jq5!9=$tveOfn)dol* zO_odhB{}ApO$Gyf@C^xyYE#~Z&0L7EkFydS$&GY@_IuyNph-8o-x9ENTmy6Yfkn7N?~+@6;2%U6OvNJ*ZulCEU7K$|b*HptO;; z_nH2r&xRtGfk(3t%9%KpP3_%7*y8?2(|N~J{r~U(b&$d#ndgv7W$$y2Ey+q8o9xXo zLiUW#J2|pSS?Aaz2O%>uBT+a;RyHXs86iUT{yn`v-{1f0X1rd{^L$*7>$+bx>-WIS zG~7*0bD4gD=XsZ2K>a#SX{02gCSXB9^h1^R>agh#-`L~|nBsqNCy0++ zP$BCDDTwwhCZ@T9N||?K8CV#bxQt!-tqs_N%W;_jpqYwedc;X;@&I}r`@)oyB?NwJ zyulH+J9*mH?jtOX`|)S&AOFNNqKebstb>{m)cvUKWiep{6{x1>iwL|M)Sv_*yZD}9 z0G#AeWy{i7lAEgGw}zb-sKjObq452v03zH$Of}&a2*13Y${gQ_&3JjEVhDK{gL6Gl zVF?Wiz*9W0C=3TbHlgY3Ui-UR4U@VqQks*|fd2qgu(UGB<&geZ|5sPv-RuS}NTm6Y+6c5lIksK&60hcurwuWWUVI2ObBEnhLHLLnQ0x z-rStje0Zvx%kI1L_IaVMaM#1Is~z-6cuSFE*$>ufjY74ggNMCh)lX58o79Rq7W{3; zP6u?i-yaKv+KX(N6;4e=~EX z5kS4{>gwmmz=-*6&CjN2AHr^>M%<5HYEYq)`epKqluHJ*Uv`ol*MRnlkgUqCMSeS<&@@GP^SP>V1$de07U zHfboN){j^fSDg~dz{t8F_~6L`{n?8Y|y>!@;Z8)+?pk$kNInWe*vj!X?*BX zr5cr%k$8JAyZv(Hz4IP+i}EYbe3E<6{Y?*lNaMO#EYE(Jb2|&q!O#+n;bE=odXX+9 zTFW5AIpW2GF2Hd)2zHcRq9s)9{dR92UEq-=1`j2t>B0=z!fcye@Vxgmq`AwU)1X*C zR(=~X)S?xw+48=hG2)~WU=dc4xm^cm0>FH=wY>6-(@q^-o9mx&L;AP7XepHxF_BgfKZW-G( zpSmF0{n>M(HvlR<22BxL(3-F#MmZD*iR!8q4N=$fnla?y6rT1*3owb7*9I#a(5@y` zlk4@k&gT=0MJZ8Ok{8wSlffH8-2M&&VB-R&JyhY?DsC7;~$_# zCP)?vZUU?cR3~Ew1ztqAEd}-5EP*4{-6}Tzpz^S_hq(>kn9bVC4saM+#uPmPoPSbV5ju9AJt(0Q8_|Q6$wXi zX`IvTN7g~R82|*MQR-x*;Oc^Y=q@{|9j6&{MY5A$TLyQ=;QdV_Gj#B4TKWj@&6|wg zc*n?wDE>NH7kIsp+^8Wb^|1+xh%5IJ1Az;;P_L)Awu*C17%n!r8yF%fsBUpXWix1@ zQ)FMj!+s+>p;IzZVREvx{WScG_Je_28PumAiGQ8ouP;6n<{~{P=2(U5x3eg(pP!~j zt_m+3ds)5{4E<(cB=aZjV}9>{ITQpuehS3O-jaC@TtHWn)KF`p=biSmA-cag?Ei{@ zLi_7hpkVZpxdU!w81k#2+yDFc4#fEJzUX}D2tOW-mtWj+WdZwx==v%@(+?yREgC9& zz=8>R=+pWnBu$0NF{dsk#{jUY9BkB@DYei=U87CMUzr(@D#i5q_B{}8yUZO(Q-FJs zgv7?((Uu)ur$_`u6D?iYGqUT!Gg-^wchlu+HeLWRT*MFle^>h3s37Pf>mf@}*wrC; zsrS136!oL*x&wuIv<8!&U7n}03qSsPpA7q{KhJH~{usWC(z82P70R~}h>YT{FZ`Y3 z!<0`n7V{r~B}kd;^?#n0v1c2(cB3X79~@?NyJLt~Efp>_u;UkNJCskfeUF(m=T*hL z?Jayx6C2L1|f@2-=&J;|)Z2v*tIiR`Aj;YuR z+ZFLYCVfnL`u=mk5EaMp zdZvI)x}-G0!TRxm-6aM?A^D$6RWwHD1+Nc}|21H3COTmS7DH>H=;FXF>5lOsw%*~N z17K6{;Nopbu8zk=HcUtl*SXkbI?+ zcB2YVWn*kwr3mYp)%J9st5ZfV?d|zMyti{Ey@R(hhY=OwGf8XfY z!(kaGP4@;#PfIVgVcQepVHIpoWzmO+HPg1qThsv#Il& zYdQbGzvwV%D7bSmzhC*pUA2SX1rGe1`V9Z4f{wUJ_@>*s4*zMQ}F zO#TAd6i;!sV#=7jE!_k}ov^JX6&HYF_<5fh&>sjA*jnGLe(w+P1l(+W%FHOZ^JTnH zFNzYQ)=oo%KLjoML>fe3caI)n+wM+Ui{I|{n)cBa06=Od*uKpvn>ot{4Sat>d1wZp_NT0~Keq$g@njCAH{lw<QowC0kBCMAx3F|-nS z4zlwONlw-qF7kLM^k592B7qjOE|FC{0jWOxDi6dn#MBbM*n^T02LfuXI!v@85fbLp zoK>)eW-7*phVX)^d}gPzzV|{c`=1r-gH_IuC0Z(U_Q3NB*QsvvZ)duA3TOQ-G6pIX z-x^L<;UxYu$2-q=GM?A}<5!BX1e=Jf2v11(2f+AZ?Jpi(I$ z{!L~NazU7$OB7IEpeg{ReYUd;TVy)1v&P*-6i>TZelfcYx-bI(byz^ZEc zWcH zo>5Aq;4jCO|LWehrBY8!n6`OM`$RcWF@yKUXbkHw{J~r9J zZopU_TJ=bWcqM(%`pH}?S7p9`5ZJ^|q%mlNo3-h}*xn8utQNXagEru@y8yk$fhd=F zC%Nnn^#+QtXUbT%|Nnw#s^kaWcVl3<&}AY2wA{d4B5X-`{qqtaQR+;0)#+V>@* zu=qL9T-y>t&8e36M9l8XK@t=FemzP6+wG2RZS?oZ3MlvGmwuYvi{Vx52{Nz=wRBDV zh)d$H?5qV?bwW?#wet&7#CdX=OP(Cc)2)!~%V7~Yt+!GIEM}s^W17}^XylnTS({2} zFRocBqO;P^=_OiC#d7jaFnQ-SbntLlc*V;e{l7x!M6w72rwW`&szLq0QMbV2m_^AdO-4rv5ezXEZcE^kU1)R; z2fcl+VWluU2hhHTS1^O33#fxA$(OFPyZrMS|hKQ2p3#GfQ3-HnW|~W+eo80% zhF<%zn8`_a{c%(Bk&4R}Ek{N}pXos{)8r(4i`DM3m-5z4k!U;(x`v2jzZjf64;I1f z5dkj%_=>SBFEhUs47Kq4G;Ox_NX4!xvr((BupWZicYlUSyhFD3h%`sDj1QH2`ui)- zB6jfuuIv{8ft1gRD|%M3McMc@M6%S^zSmCcJRG#zQI^APPr=Pw{XHFto2A3HwygC!3-!rf0_ zEw(@77*{v^GHcBoVvMsHbh1l1MG76?0}YeWeZeZNLpqXgOeoCod|dh%YlLvS>LQ87 zYx65`+z6SP3eN#sX}8nB4{kgv>9ZyzIr>odXP+z--F)WT&d7EsbfvO1%64Wk<_d@a zl?)$iZ03%gMidnK{kGpRp56fbQB946ai@s2*+GDtkSGm`gQRe3cWr7vP`|p&wiHSLU#QhM*{`uVr3kD-JxbHBM?LD57D9pRqG-NpA7 z_$1?8!xDeS)wx46T;NVGdB*ado2>!o+Pff;yKxz1r?Kl|>cbW;f#D_e25-E!SiSP7 zOP3Yc*RBt*W-n0fNdKsx9#o-~9B9JXc}z<(#djBpa$j42uKKW3enmZH0nygQwPbVY zOmpXw8{K3`>f>>*`H&ixf{#Axqyd>jvUG&&lZ(NJ;cc*Wf!PiXR4A$bef)Z0hx9=cU8< zz~2zVUhNhchd-B8{bqPI&s^zmJ;<7CP^Sf;qrZEt5KpH#Bz@Ka=mLv4O}S5jb8AD> zH=ZW+b@`O>TR@JIgLl+qmS98*>+OxnSyE{M8%+=SE&s63hSdw1UeXSgY(A z*Qv?%qxf$5ojMoF7oKT#mjAi-x z+8RWGuajx6Vy<~rs3u}@Ad0;_u)It0!&t%VX#Kcjl)nPM)OiG9k z|G`3jN{__0Jk%}Y=>x|&(ZPlDL!Te|(plb{GGgOoCKA|t5i_YBC^R5ncMNT(p){C=V^I8NPw!ov1c4`ns{xp(XX; zf0kdK+Jqj8yy(S{B@Q_G&U!J3H!zCH7wP-M@PjwBhdYWFcM|m;7T5%H-Lay;|A{XA zoLaHD1W>DM<8wif#$~S_UCeZqEd}r>P}hwklrpF} zpaSlKmCoKgDvxMPPEk_iCw}mx-1d*SB|o>o!mH6$E0zKdwL6>lUz~Ih&#|)ClxiU*}?bfY+$Xle=i_1PVz46cCo%u@T21l6;;=UVHcg$P zxTvVWh!BxCt5?`=qhd#iYF5weueb^aPWHP37gc0TQG7Sl_May2DRp`3&OPlsfqata zPs0-0=<`;-uixJ@Q-8~LIXabX=3?07CZZGTSQSQ+u75l^ z>X0Qe)q+1W!h+_9=THfzNOTrT*Ww9(-7V!UsY#=KiHjF+rdU(5yacb?gI*aaLbkX) zRep+Nvg{8N{MEkaWpkJuYWe6Dr_3?{v6s**R{i1$L@H5Zz34@7z2ZP-72?1DWzPVLSO9skb&J=u)OV48S3&3oQF=GI z9d5B#c!FS)QhPpNXy5A)N3s}PF7X=x_x}{TLI-fwKYPP*G-DmggL<7WmNSZi++tO z{*23&g1DbXn9|U0UmxHs!O2a$t_Yr?2b3LD$m^D4$OR?Ai$()Q>i_(C`BVfGa^w^gy3BpA%=6|4X;d}%xeT); z7+;=PAEO(5=PXy4^YNYh>XocAm`TzT=d zbO?*4kOa6zCvPNOEC`;p>V?8Pp2&zgRr#-kFzNmg;|48Kk{>*^4^F3tzzLiidM;;^ zRd6SVGTdKlCy@RxEr=Ds`Wb0Rf8GMk)uxwZD184lQ31sfz z{&ymG1qGcU^aguA*CHUJqd(Uy-5ro|IH|VfoY*m!2#6#8eUXh9N9th8J$bV71RMW_ z{DOR_Bj|VlUR<=X2UD2UZy}4flTt1Dd36cq{=!eGG1-kC;`lPK<)J(qKQreTL?}9Z1Kac#1k7ubd&*gppl%^nOoc~t@zwd- zhD-FBv|HRtbE|4|MF=NzA-24-(J2|&6Rf`uSj7{rqsc`6QJg0F87MpHt$AG1S z9SzxHyICDw@IODZ=X>ksMNsD=`Ror81HE&IweEQtUIRI?8JZYMABWbM$!I$WD{$#ga>h3!y_*x>FoSnegY)5uLN1WBw z^PywfD=@TZnD>ZaU52iAY953(Zb1B+N3XP4x67e8j>$Bu(lt)B@MS&WRg#$^SNo~h zV-{EYGcP$ZFvE}u%CT6B^tBs8ZW4-lZ`3Xo*y)%KWZ(T3H40IU`m#rxXWEA1u^cPV{~V>W)3eKS_NDlv)Se zMEW}y*b2hMnbMkYm~YQ|O-{*ESQcDmy5#FEUi%H~QY0Hq!9a1MV1dI6Z8~6qK_XJL z1}t15eiGBG&|Rowj@6K`L;3xH4V(ebDldU!h-iJQt-_q(OLQRooZG0i0kNf(y>+u- z#dhbm`1h%er#-ji2^_h!#KG*!Vy!ACL!-s4LtWrwBybq|+Ghm*P^e`THb3qlbJa8! z9~;k5vywWz+mTp<;*=T@Y+z#vz}h1L%#}mGljn~_G_59Bz_|Bs6d8=K#Sa?MY0UCu zE?I)n2NBRXBk$@WNin&T=GtN|>3H54DHHM14K$>s8$3Sr%4~-bI5KtLmZ$v|_kzHB zi`e45>qmV(q~%t9ci$3cZS~amR^`ZQsw(+8Kd?fc7ev5Q7@~`6CM1@F13;p$(-D@&)oo^6OoT9&VopD{35?1 z5@Fc{^$pT*o5$s#+9f_}-~IMB)fd)VA8)l4BvCnrSQ!2tqLTdJ^L%CsmZTkSJ#U&n z1@>lIrRb4^5l$-iqeYMH|+_BQ?0$_k7k4{-Fm*>wg>>DR_j&uMHvyBsjH z@aIJpGi8YMyC_3NM!+;$*gXVhbe{i82P7r_`UJATImaSqV(6Leg-6EqKWJz%fV`{A z8jnq;)?mD(Pj|hyFODI75`x%@-FB#_OQspao)6S6nbSJp*t)fwAqyNbKK)lI7FLOM zXyK?Yo=kJaN0;R8tm_WJ9NCS+FZapgrnG|Ux?e=Z%l5%JlWXK&!^2UMd^ zzN;!4xe+YhQTj1u>)f-YJQ~ry9Ixs2LW9ebNx_NjH zf0-aXONIi^$=SZoe!#|yQ}?ZI zdFkJ7h~tdY57If68#YBXdi$JVLWjWO#0rRf3JuoGF7V<+?7XDQ%VC^z@Pdp#^H~7{ z(WD3~i^MrdbxR!QnnHtp_gVAsN$W-xjoyRe^*0>UmHLzip1p&)f*_xfbu2bVMTtMo zUFdpbcN#MujC}&oG%mpc0ZfzQj*<1Hpm5x$?iQYQU!tD$%7iL(85zFWzhfq2a9;4K zbLu_!(K{4zoJDH)!>uJSj$=_APk!RBsu_O1HG>Cu4p*`z9`-m9S&QRJmOQ9Gz zIeY3?;S8WO-TJ26CCzl^!tU&fdP!5rvN)Wa|aX<(S31V z%|lW-Nd;}VC_ql&U#09zm&3q?Gtw8x&HEHhhd5V!8wiJBF>{8a&Jn$;STZi=PK_fpY zd7!V><1RRah&ZvI%#G`?L^U==uG{!_hjQ$a4{Bss8dZbucA2(W50iOh(4J2whU$+> zH=P6BZhJr6U-%%7JD!l-_y~cO2pV^FQFKV6AJJi*RFD=|6hyS z2WkhBklq5d-*~*ON-u(HOfO1AW;4L8{XN zk#eghNC+ovJ!qEcgV#hy(@c7UZ5Q}e7pRb?b@%_F;&5eKitAR~vH_R_iL$*thS}4D zlA}lSJI(E?qdUs6C6zvB@_kGd?s<8`0=oiP|CsUPAxCnB^k9p&J@sNqA~t zn71B^fQ4H`zT$4DPYf$LNO%7<42yqgS;VUGlN+R?I99p>bwYx_4pB)f**SjO!@IEL z9Df#o1s8RU2?OSf*=WnU>kQ0M`BB>%_lB-Lm6&||mzhJzZF8BEn`ZdV@{qKGRiAO9 zehBeYn9Ho_zMJeqk`ZHxUHryYTBi)Yzj=#+U1yt6^7g=6rXKg2 z1SrHjCiSnS-8B}+Q%8?|-^>xoqFHxwUGaiNN|CFcfv(sF{A%ar2HCkLbwM>aYTwGj zoZe7q<+iax9@k~A$8o}AD%Fq}e~{UQA7&}QP?0&Y0(prJ$36UY-F!yTm7X9p@s|z4 zuYRz(^?Ho)3|A~C!!6Xk&a?mrU*5@6vv5$W+GxE|w+Tq&-}uN$U`t-NB%OE%bXH$G zX<_A6fqUw8n=~aM0m^?SrOSa8SOTOJ3!ZN{sZz(V16Bu_R04avI?!-xYo&zNg#k;d z2PelULFcw(O(Y(%$QD1a+2a$il)um|80Vv__~a`GJbUp+E>p_r7Nm(NP2~|wVX~Nj zOSbTiFe!HgEvx4*0O`_wuX#ki<4=8sTSDa4hGx1#1tcYud>4T4y1K~LQQBm* zXq$A+t2C&4fARd|Hc(?C`AS~n`=E5|q5zWfHnl*GL-6aYJI+9azD)4pD>0Wkc(4*L zZh?2C965RP9I1g_6@xn%|D>LIr-7Heq@r6sVF%i!pm>EBMu2HfB3meRVX5wP>KuHF z#oODVf~bi|{q%CE4+5&LQoWf|!+^YNC|TN~b;&|E^=(IP3%`5Z$6pFJwddRk#($lx z+iAJnb=wjKX=O9#;O@7pN(+Isyq?yVE;mFlh0CU+=OqCYmGAR#ZWisG3gz!devH_1 zoY0Lb8zD*BI}>(l8O2JfgtPj8Zt5zY{Ax7fA0F|cA$kIP=4nM0XXC4*6|zq^xtJ*q zvGP`>x8+@(b}3o|3_LBcQpO4WWEaWS>_m-lr&pn%8`9%xL}KK+0o+SU&<;CafyIwmxg@&WoMz$kLl77?UmSu3$b$4h+-KaI&%b0+AU&~01q9>IaMqE4MGvP z`$$_vfN=Jx_+Geh0NHnQ=jXZZn>@QGXJu13h+Wk$cJ0-;A>FNDra9+@d zR&p@7ssbIHeTn~K|6NdSs}+W^7@`{Ox+lTve_+5i0@o{S+p1JwK+s4atqNa2(FYGU zdjA+R>ctfW&jy|?3t=#6F`wMM3#P2$I_uw`!t1NJ7^2b5Ep-#T*$M>}+domQ6)=r- z#y3C}RftZp;ucoX{TCvvtigtpHXT2zAC^B?jpE2J%9>&f;BWyH5Y=h!sI(jv7OtmW zJ=p1X4Y^`fzKr&B{zQG_rl%tczr-4)a}4P76U0T47+PsK$!d;)JPJ!O8+=GzD`z&o z+oJkVvisQ~`U@J0Qyh#J2-SF`Md%v){B@hEG}!$5xxyq^HR`G0oQ0*Ls|Hwz)5huz zA;U&7!M^!t$Wl%9CJ<%ukE}T8=R*%g{kXndI=Vo0=ZlZubPBCArPlQ~&>e`*>Tz?+ z_yx)nZO^xC!1Q8%HyNO@)IQD%qyk1R;M{el?dYaJozm=gL&S15Kw`teL$xWh#|dO5 za6&+EY^Uk#Jy-e6-pQq4Ey3l<{lHI3iyhhO1(}HPlZ`N_mrg!euI(I_vI?N(hKn9` zqF-%%8lrQO(vZma%^f&#$NUFQKt@SZrn6S?)DkQ*XHTbQ(x!q}Qp}?6xV#wlgwgDN zM&%{=H|Mw@-4TXK;^@uNn{yY|g@>pI(eIAtYp?TB9eC#OzHtZc+6m+Z$LJzG{hFSI zQ|4xpwM%1p?nb-%v0>w7h8Vo~wnMO;OHbNUaGeW|BYrR!9RMF?-41aBcgF*ldZ~K} z1?F#1}`+SyaHgBdaSUgxVujlUlH?=hZn0rE(5o+|#}5&610uw=U&)@A&f z#$|c9I2rOiE0&dm59*vMHX7_YV?c2Y0d>giGAC+uy04w(v=?bXs>FgXYkjKyTxp4a zixgdWGx_9PO3@Xu1jkarx4k5!iIiHQ{Q0ajhRKQKzNg5RCq4{YwE7&@$q+j~`N~|w z!^0Qut6U~6BbR6WJfREWQg+jdpz6*c*i1BC5OsDCed>Hxp(^5!tZlaltsd-c^4rx_ zAZ0VO2~C>M3&L^4()*~=d9$7>T7Sp1xuV3%fX$FpToGF#m;O#B1Xmxgj556>wdmWl zrUb43dc?InL|qcikxUoeot*U}KY^d$&;t}V7=aL4)XxZs$UK_-5R*O?nB>4_hEeIO z78;k&{6DH(WK)3gBfmL>PL}H~<9#8#i_ull^1Ds?RyFanT;CY?XYn+hLV;J;27qyT z+G@LL8HRi?_zvK**iij#cO@C#$rZYS7%vhpZ`SHAtzC8MM)05=f@Tmg#{h}US=)K8 zf$nP=q3T9a(B484?fY`~ae>9@qzkJ?*krxbCOthzGQyCHBwG(qjAqx z^0WykNvAiRqO##+IL$xAR(Wit0AbDb$tHi3P>5Q%;D@$ueb@z@$m2nN@cAb(-oVEa za)x}aI$I_kiYDnFM!bZUeO~HOs1ibT@6~;Ux@OD#46jWGs)+V=c}MO{8c`QX={A4S zksDp}C`wP1?ME9hrC`W1XSa8O@GY;ci;1XW0Cg33ctM@lNPhb%)lsttz28drN2wj$ zQ_MEXia$UUDXYR)(_RnQxH;^~sVcF_xovpPm@%FQ{h2DN6v^RLE4%1q$b#S&Gyo1C zb=gsrbsn8Q**WZ<0loOfRSwxx|3WTHu#efM>n)mIPnh#vUKz~_#52bsp07mYCDFJr zg?*@97KGVU?mcrkxsPKD4z?@nf(B|CCBDy0`YC~xab%WNVMQsJzUfqZZ-}*_Q%-9m z0LMgr@9TuTE7enxdGDrnS3+rT?>wz^lX{=9P(!i2Q}eqVYN5t!!O^SCdHfoVn!hrh z51meJFu(2zIz*(14w+7vQ-{k)q>y*X>D;NlpCw?a?IiiWuq(Hqit0nr-=l#fr*i}m z`pEYZDdyno-QR5QeuE|IdRK?I!#M_v}{(V|$6hB(}cU$2WU_Z$dmKqb>y*fbP}_j|cIk2k09s&)tnK0^6Vp7$H#ybK%@UiD!U4 zmYBUKFin{}nUgMZ&Nd4Nw3^ZL#$QcfGW?eujp&dUivFdXg)nfFXl zzeB$W-+7!`V;F{=asPS4OvA1$Fr3v4ICp0prbX^+vrAA@S9FJfhB z|K!6pk<8z4lWt6KDVDJ>cFx{uQ{Z?pXdG)TC!ILXe?QC!+|O>**qoShNV2B{2c>sY zU&3x(=(DxC1gu$P{`bJl>veodvYKVl9jTZ{tV%4IQWS{(8lbBVR=Aa% zHcob*T9ShdI_>{n&<%%lrssd(25v*&+xf|$pIl071XOLf|GYS{#@VDkEY7YXe%X1? zvYpK_PJZa|EQQK&%xk9w2I2+)`8_AquoGl(PAkU5dxHsvlFa0g&uqU6=5Y_j&XNg1 zKl!CEV7)%7C$V_FMd`soxN6+t_CrBSqOtFIUtbH&54u2^fHkO#=$LJ&Xf;4peLccd z1_ymA>iQwTMir7UZ>DH61!2^W<|G+J-`y6rWj>|eD*c{)(L2&iS;g5R;&bWSvil{DIChCN(?`{3)R8MXBJUXLBR!$#mg$G_L9 zDcBw!T>HA_hj+PFiqR2=;K)uL~B61RKk#Mq8?8HZUcu{YK)%KGu>w=}U$YZnre=?bD{aLKn-q`NK3SyL)T1Rn?Enj{vaVbJskDh|iim)iHl zd)s;A?te_Bt`9)h7hgMH4ly>HMZGYFxQCB<+RqN6__VWobat60!Llix;mr3?6u9H$ zyEkx);icvdkI;tYFk)7hb)ndov|w6|j(iYm=9)dF|3QcT&+PTTNmXE2@BVusVH__F z#|(Z}z9P})#hed%(Gr>!EoaXsg7=j- zS-`lC*4)c-fT2jMIziL%&&=sG*XluZX;2mHkUnUd=PLzM3Zxq;`_$j2Dxi}R8L&BJ zq2ersMJ6spU2-_0|IBluK6jOnTIck*nBO`i@C%vl+$u1wCeDauBK7dd<9DAL?8X|K zPkpE0Y$K0Mz@!u(nCQH>!!X-2f@J#1lKtP-O81(6y4+dGH)l_Cl^22$3qC#js&NJn z#0z-sm4>h295DH8yU-6xwQ#@>t%5^^m^Xv8*r2rPNHj77ZAq;_lGy_|Uzg9qjW;{| zYb>nVLvaXOw(}c2B94-MiNkJz-Hfh1Mrsv_*aGe-!t(J!`mN@6Is&rn+hI_mc8)v2`fqoP7bLlp zwZU2iBjRxK8BG95jBxNmS2P%l06#Z@^gzFFuvl;@dj;XB$(nl&%h`M`dxi6w=qPX% z=PvEm0so)G*SqF1VS?UUQwh)1Ad|NT4iU2t(7`DTBEw6Zx(pBC1)>eKHf$au!n)O z=37ncwBQ_MELl14>PEjeneHmbBwiBLK84}seo`2crm0wxb>2JXE3?4AQ1YkyVR)+i zXv7N{11cn2cYlQbq`Zm-i>>VB_aM+z6pM@0fqV(&A6sWr;%6@h{JgfZv2->?sYBwzR+=L&$r*|ilJo;M zH$LeW8b5bl_YIMsr@&I*j%a461h)%S^!DtXan=9J@==8ml8*_nZIo$e?t}~6?c6NF zOT0rWG#CfR?l%^&DeSOb@A_JGnh#-7<6Hv$diKZQ9GVUl=bKq6NM&e@=@A!49lvQI)moXXWlK8t!Ed+KyjqNX271l!uCCcGKq%l zTO=yBCp~ARYcDrOo^bAZwTy$?c{+#nFY zo1Eib0)2H@!T!-rZM?~n?2C_>xBH+IwXL~jJ^;@_bamptt_4RVVkR(!87*K4BZZK~ zrNi1C&}_6gM8(yKje>3&H%bhF8!(AmW05ZaH`YEt1oe_ zq9Kb`F68ydo93FtWI&(XyEGZ4E1rc!Y$hBO%VaO05e4pmrE(z@z`sOluc((fqr|+9 zn&tyR0#mML$;lmbPmW1N_H!94fwW_8T7bKp?e}Yo8(lmTS^f4OfH|k$dNUWCC866# zbGDWzQBLmiEO$NXfvt1lpx677Ch}}Gg}VPgY7zXrliz{4vLzUut}8Wv(-V2O><^&K zoe~l8im%szFLC&LRo(SVa5<06D1WfQLZ(0Q(#@Tf#`l22?4svRCO%A>hb(gIRym+K7skj`{bz2J};~}azkVa{Tw1!A=BHiOZ04gVg zqs)zNRjsGvs5q{K)>_)IG1c)v)99+m{AUXgn0CMs=fs)Xsjtb+*!hD^Ms&yavq(JL zW?EaYq)x_E^0Xn}D!!oCY%r|~HmUQ|eTLdFmsGjv{_(ZJFu`M;4Ev+Z;Xu^?Zj$JXZ^iZwCgC5Knt>Eb4}vre7Lb(&n^T8-7&}x$xGF#P+Gst)>62 z?+*Yk;jN_IYk3z(M8xNO=q)32O7)XY+g^z`i%-=BtvG7s&#L@(s;C8>e4kEMQJ1oa zQv2M2UT_TBM2X@>J9)=%sW0?hq8IIrL<;#)^QQE1a(PVw7TvFGGXXx8%WpP3SD`Kv zljw`OQza=zdChakOf9B9$+tPTv+AY|M%OfmpmqJhokI7(Fcm2rNAaaI7SSoOs_-4n8A)}J@J0b0sfqNyj(${ju!2P_hT?>o;}N5RF7XHTy>>n>(BZ> zGKfRdl`N4OAU*-hr0tQ~Y1~uV;AZKf^*mm)U?y%^M z6p1MIS*{&ONwH{t_N80VBHQI0a=^9EfZU3X+p?M6!%|1nKX|#{+G&-Ko@N&aR=knK zk7Nnq8(njTaXG~P?M|%dg($8=pXxjE`%r|QIgU?q+EqkHLrcokv?NS&P-(cwB>m6g zV=NwB@^1KT%3<{P&p&8w6mjVzbJ^0sDPJt zb1o&|5J?)AWv?#z3V`uho%~-|mGgz?LJ6U9c9-wcl6A8Hgs1p>u8Rq<3L6aXD}!Qy zNnOcz<9W6l3GZ!g{kim^*D(Zp6F1%%rItnw7eD5V!hr&p(Ah674i2n#?{>jOji@wv31m%dBQxJpyeEt^5r=RvTQ12Mz?bZ~nng1mUf( zmQ2>bUH>izOD zEalx;e89d%@fAQ%*NQRd{sEYg0IOwP0}Z(5I8?>;ikWm5R7Yk441Up?Eq{_T{Zq^u z2*Vq*C3p+XBsZH6EoH2}Hq0!vdRNA{u`$LCs3B@>vc)-7H15$w45QUw)DT!S`h365 zrBhl@ZS=s$F3wg~{{T2paDu*;ACAQ%Ae0dET5)o_tCZU9+vt1A_#(KjQTA!B$e7sM zY`^!3PxV(IV=@+r^1}0i-IPdabKT^QCFT(L?^qwUy*=aj+@{z+Nx1xXR`DTCAj1fE77`KllmV4#5U(dky+<+uXVy#vK>KD1^#f)BpGxkuS%e*OBx*b>-%m(G=i&>iuZ z{0p7UpN2hkAv(t5itratP(06$yJ+Xx_aCq2XbAV@b$5hLrHHBUe$TJOqxgzy$PPT?IU(mQ86 z;^Lo&pJrRQ9+}<9t@Y9;@A5Bij~$=Pq}3FR0?- z=E${4`;>Ub+Er5#X8)%jt^Ml^ymbL3gKe%Z-!Suzq1QUSbn1JNFDvK`-gWM zuQvhO^`$c%OuYoYwn1_Fw-a$2iEWPa<4^YUFWClTg=9&Jn%D9apA~43Gmaa177uz9 z-Z`YtGmich0cF8*8uou|Eojg!MFJUgc4x(aW*o8bzN=O`9QHJQhaNTQ;SVBEnyRjz z8}>?_aMa$!lt0~VH2kHQWl`?DAj|32<4PR57g_`AFKnb*v*QX2cQQ;d%}Bqd{xzC>Sl-FkRYYEWn%qFP~#Q7gUT{yel(+NeyK!1tb_-j~jT5VDYUBE8y$x#9+jq(3gfyt?l$@+mJ+kFkIEOm5fQ&Z;(_ zGsDmptbc4V7%rE4SrG1bzXhu5|7{nNa@;QE6>Pn)$zKn}rWuC%_~HL%B%p0&2%ZEbdf-a8^!LZJ=JSA$29#eChll5Hyu`@9#f#@uzoH|8m^PYKu*wa`Dwo?LT3Qx( zBbH(txUjI|b4EF-p>E)v>PAXk3l>Worb-bd2im!Yx30Uv+@DJ1>;B*hRY4K%KhV{` z16_Y4S+K|w9~8x_IhY)=% z!cRB;cM0ttg>rWa-S2ft;uZhV%Pj+1(YJA^PxIP7JVGpD&9YT$3Zct(Y)_o}Rp=#J zqyQ!?l@&Bj&52vZCulL!BCHz%&KR(x?gmF^#I<8o3rJzQDwn}lPon0Y^d+>qNa(B> zCt9$nIZaY85x!7ay@tMAv9=9 ze(<%XvORp}!qtB6No#V`jU-KkgP!BO8`t@0)qZy&vj_7p@B_s*47=_pBy${h+zjQ; zPrOW5u|$ad&_87>(|usUj`il?Bzy%5r%0G?UqP>L#mInUC!-fUfhr-BOAwxbI*x2S z)OZw1Mi#y#J@Y|<{T5+Fvmy$-d#tV5HN!paID>avPX*WhEb224oW(X5hixVSsQG`N zxxhUGPcZtZ=YK2tpQkH=BUok3z*wI>Xwtnr8Gy1yz@TGx@h91eBrx2EEm-^OZs|E)I zaD(8SuDL>JW$-IL(Dja7j>_7kh*1QB&aG6&8K%Q`{`*7-SeBs?(LSm}P+&g_du)xA zW{$_wnN8=Eb^84R(j3l;M)ZdzC}ZWs>&|S>l~9^Ju$i*Z;hnULAQw^oO|Npfyo!91G)2Y@{}U&eMx-h(@EmY3~KPB=o$3 zOK$zVr-Y`Y6E_`63R7mN!G4)Lh8P%vlroT1@Cm2Wl!*>{E}Z=7SRur3&Gez}ln>^r zf*bvUqwkMS!+k?zoS5A>Zs!W<_CMQp2R!2xslEGdx|bOW)Tt77-bXbio@*2SWWnbQ zV|=PQ_~lk2IrDJwEft^^HMcDdPeRvp5Ys|I?-bB#DJRK{TCZ$rqf!*Kd3zh6KS_zg z?fK2l{_2Ovi|by2HF5@3{M7&Jw9Up-eR@#j*26DzF8_I83mSfJh<|@YW)$`e*}z*eQP~JBlH=C;`j)e_vdQJc=M()EoMxq(1)O{(y(Y={arXZ~0lrDQ6exad zM)0C7%ZlP4rYiS5Vo~w?Xpk!}6c3(*wX~X~ab8H3rdMG-khdYQhaYU<3VZ?h4AhO5 z@dn6%WPFwOyb)=ly_(rA@$W)kjI$&#O5~Y%0b;+k-W0 zr^zLuLz!ec@`+ss5B@`!M>Qvvnvf4cpq)MYA0R_xT|8UNA;#X)eOShLzRb+;dCQ%} zsNlytVXVQ&9J8CL=S)eB31I4Jkx4A3!Oa~;jPB}_bv3G|iB&{ZOm5?;L7m+@9e-&= zvG>-5u?d*=&^P!vY*Y&9lMx8UJ#HyoqYg^&#r-gc*A(Bqk(MrXBDrE*-jB^R;8sR3 zX2s`brGn3WjEIVDqNXP}J~@745N z)`=+VpT3jioG+Ranql#A-JD&;rd^|;;mPXgSdyF~zO1;U_13s=?}rdkvMjLeqmU&1||yD$fdP|Hzm7Pf$}0})0u&D zHhb@4G_cWCny`_BnjHj#7CoB(dZPpTSRFZTE*jEa04??)jQKXm?nn)OJ&Oq`&tjw) zz1P6p#7WdxaadC3rzDV{lwi!M>;N=oJ^>~Du?EylcyvqiLo5uqSQ<4E5xgo2ogQLk z3h`jtJ*tT-YtD$Age2_-`F^1l4EqTojD(BJrgf%F9#KT_q7*t`I64VygZ~|{VYfP> z@>FBdD?nf2^Yzblu+rYbw48yoFl91afQSzDEwjV)UEumLy{~aOu$7Yldu4vJS36w^ zQeP4WhS$L~p1Etrwl~Cts?eCRK5?VCT}R@o4y1T0{NN^)!4?*l`EEw$hm~}c$$7X| zDiDat!|7dZp04?U>z3)O(08rm*ERbW`%|331OG~8$`*|4hR>jHhpf0nRcOU8YdiS1 zeqUmFuR9O`1bp0?YAmYHJ@xTAIu#ivAEB?vchLF|(jP>V_okK07SErAY8NOo+NY>3 z&*S^8Hp~Eiye95azu7J4srl)hT(0CdYhXTGJ=1HwrnwsIOg_GlkwXW|7npSS7g~fK z-xee6Lp-08GGa$z3B59Gnak&;Ebo~q9lmsflqX~yXV69}ScL`yvX9n8kUF}T_$*ew zHJ7!QK~qSfMF_?qdU0Wf0`LxbZn9?;CF4I_mV0D<$*1kN0Kbh#O-R_g7Xhq2L!u$q z+|rVvX#b!}sezkfFh(${L4L=B<*wA~4n$-H+^6hiGE9V?d%ZrA_I%o@|A)^x&fzq| zeFUvt1sQM0WjW#DrdX-0bzNYO(^|1Ff$%I1aCbCLOmWhbUsdAxbV1A=$*fLkd{gT*K2hY0wiqHh>uKIpN8 zKwOo}e~>JB`JJkq;*WPwd0=ADcwBH3rg+=Oau77E*5#Wm8$MFgVduHiD1JoCqc2A| zIi$TRwA^KkyeD_sK^A?Qd|!hCL}m2}GyPDA!Y7*wygZV`vy<1%53HgB!_#ybr3|Y& zp9Y+%2xl_pVJ$(g8$~ZbTyx) z_wQG^$S1b}3r#!*iwiu*@3c=L6dMxesZqkVaITw4R6u&I!4}xbf<=KIXOpswY1?o6 z4*^VhpTccs4V0q;N{TY0nwu#IxKw*r)z&Htk8Chb^`KngE)nZ_2>-ZGRoYk9+JNuf zD9JGXI@ufqVUh2W6MR<#M*I5n8Z{qntuC|1pr{?oS7GbdPQk>Kj*_Q-ks)XNqZNNJ zZucU8bJyZLF?AxU55-1Y$^1_!D{ZpZQ|;F_Cyl5-W#iYlss?@z4M78 zEU%`Y{T(DA{fwNAW-+IcdJn~pR$-}w%;Ep;fFMguxQ|a$M0}S6<7L`FF+$ zTzYPlwRbI$gR=;c_S~uRERxyFgkiYXxym6FxE-VWB?ypPRO%~Fltc>@@Kd>1^nK0ax`;I~TK8i7p zJrpSf!#3izA9W9o0i~$VE>j?&5`Sr&$!wf1ZylQW`r#PGix z-d1Wy_xoeb|JEcNDJu`yMPtoh?TUfRQ${yy7=oDFSA7PyPnhOUokj7N(i zUb>_Tm*VzMtUeQ;DxS6d;EVZYvFj0(offB^O(*@MLn7RMu|^B_E{(YiHIy|3k~s+F z33(ZQy6*$GLq?t^sTdX-hCd=>_M#R~bn_MUK=*rHaxs%!TO>;~>cau6kf<`39PLu; zOnrFX@hsPN{(DT5MDSG{RP#$27mOP!kR(Q8KME;$|MA28v8cG-=DC-*f zJ^W9po|brNVS|e{s3-l!fSQ52b~L@4_9}F9c`#?r3%17Z=uY4E-rhe}tJ83c;QM?H z`0@PdCUq1JVq9$^WQge)iN3+*4QKema$o5O1|ZCczeePK3K&X)#8aKLRvQ6|m(`HI z$D=@_INdJh6W5QdV>CEy1FR9kZ0#)4b`J?Uhl_Cj+Ltj#gV1rz|K_UHo}4>_H&V!? z+od+%^!Hx{d7_nKzi2sEHeyBu8ZT zz0(1sey`yN>n5{LUfFw4;^v_o_}=9y5m;isdXa6h ztOe%z!{cBq;wq1Ycm%Tv_2-~&ofDbZ3uF=2OI*L3(RAA9>37CQ;s1R0O#T!9vYPz# z(9LmXo%9XB461)r?cg_CdTki9AHa8_S@koo`6b|BRz?-S7VsDP1nrob-4t^X%F9`I zPO-93)Yg1;0yeKURxsi6(o=AbU&Iltoi8|=GP?KGvKbnYK}QO!xr=~w2iv42OJEGha;9YB?pJ&?PcYf9A3k2r)WU=5(ci=Bj^24&?puyp%M&XmQ$ zabqJoKT2S1*x_C8hxoB#D;pB779CDUV|c5@@+7z!Av>`-_`ASMof^P}quPh8J1(oI z2(f&NUW7l5@Wy6-E)pUuEsWR8O9zf9I3WVI<=>PidS(tHSx; zSla|ogT~6}j6g964K5EZJnoy|%={j2`%2puOdHSgnapd(DTb8VkdOK^HJh6wLI&LOb zt7H|H%xoGEV9oVZ$v^gjlc}&N;DRR&2|U&4!hujyix1SFF(_x4J}gzSdiMM>cl{{- zmU#AEM{T`*+F^_0M3&xhb#nM5yZQEFl{(x5*_AqD3PLd6$m9SWwF@}-E#;p)a%imh zdiUfOcTwR8IMY?)W zCt>t=7B5~0Ma~HSFt_l&;>A=@PX{wooA=f-Sr=VmO-oNhwb39b6U`%ExI1nJieQ5c zfY=IHXFj-sFwmgjSnj@lbS%SvQL<+r=U!KHpH-Z`NoMEtR25{15b)L7f^QY>P{>E+ znvJb1C4pr@Hc?w=qgoLd-CBe^CZN;C!8$p1H?AhAR92#A*WJ8Um-5>OO->y85Bsl zi}`NJj^bMj?3jG}8i{acyHsQEQ#33>vT8|F2#wRXx^I5QtVh0?S7LTEle{jjG(@>5 zujpQWQ}!Hc+`!Gj$=i}VHEl?)x$u3(Xl1QSUd7s#5)8p|MrN@I{eN>b7yh=v z_`7L7saEmbSiVrpA9ij(;7p7tFqBIt3;g}*Nxxa)>vSQg_lIZG#1b{cC@!I~JiqPK z)FB^X(fCJ?%>0FYJ3_GD8;|*kZ9uur%3Ig$O@lRp@#=@UZ;~?I8?@xD^H&tnuEjj5Dh%*SckoKVY0V~W8i&CjH)C(?D?(`+e)dy+fNo6N`(Mauao zgHQ1iF9uIuMCaiz6wv61>?3N3FfECBx6GXk^f`c9rPPotXKi%@oYF&utGnKUJ^XSZ z%fr+Q+O| zISI)y2kVmYcnLXknd2+AC7;7b+b|6SeavYp z*(2BHs%NnK2hY7cer4No2+(rh+ZQiRIEN}c84uPfhAFCvwf?p`-Y7KVxQn3K?mz|n zJCy%ThrV2in{kO&rlow}cY^%3lYVLD5F2X*c@mfG{1prI#mI`B!Oe^%h%!p_GfQZ( zo|K@@fqiwE{ELf1ko{TfFIVI)>jPD-*kiZYrG^(9$;{-oNS+&1T+Yn<-gl zFEP0N`%?h90q=a`xONM0;>Y0`r(vb;C9s$gCAP|Nc_p=03UZo{+O_GggHT`{uO{Ym zup}+m3ntB6*J7nN+?({Rk4Fa@=V}=lN*jm7Tx0&kOoVoJntWPc{>%zQuq8GGMXj3 z^U1~?TONX@Rn?puDp!%!(q%?%O0`Kg+`laQzoLNZ;^L&D*G!_D5f!5<-Q{;O)c?(Klk>y{=zK^E4Hzk(9v6{#@_sXvn8#I_ zlUmBI$_LFaUhicR6mRvwv(|E5sl0fTK@pbfC`Z`#cP{iBK7X@9a<>K#ueAOua-H>m z;JRq$)#zmKJ1ns9TXW4~@qlFouHzD0u*joFjqK(YVtZH9ntPLp&1(NOE%NQ&3)onAx`l@2t_425J2%Hs*y)>+_M8vLAs)G#L1>}*+Jx3WB_9u7lx z{iYm!`{7ru4iqb1CRhR3uGc|kH{nmlfcgZHiF4|!4+rWPEKWyi)cb5ZQyWGvzDwF; zseRHdz+d&NSic+!j7pkGkP6&@g)0-yCK;v^PV~oKgBC%c6sZ$PLP(;CQNACZWS8wY zAUop(Sc23xLP59uHI3cD^lzG)?25@g`pTz?e<<3ApNd`)Kqc;0L%Bc zY5MyY6GvHp{W&=1>O2k6e(wx|&WTB#0c$FtrJhP7*ib^hxY zVJp&W0WiIKdTERsg_@yZ*N2ZXXLvNb2)~hE#?qUBJvit#)FtowgP*=4UUP>T>}7oR z2I_JI!mDyM3*R5XzaAoe|EhjU%twxo*H40HB0xb{NnqiH&>enzi9O-<1406j4P8PP z|FLHsY&qMBWq^O1ar&N%m}q|<)yjM;zRUQ<(Qni4@wVZ!*4|g%?`aS544kaKmn+>V zkR{m2t?x~W(D=Vc&$sHA$-G_Ur{+kKWmG9yhDcv;h)sS^2M5+S2PgE^p~3CvN394l zyI7&p_3w&95MSD*Ok3KBN2YfMH9M}Y{q*=%;Rb3O!}WVa=&kT8IhP~IE&td(fTky9 z8$U=Zdsh{}LulaPG(8D6Gr{~6sOahOV`qq(m0)_)WoMYY_ST1jRPZd4c>GKE*%nMd zoTt+KKd219T+7)FFgH^iwp)R}MG58|6mDldV)R+ux4lmbTBu(za_J`vjDFs$7@>w!{rKATiIbH!Y`aGp_Pi8&| zPcC0cS)}Pb6c!8^n&2_#cVu4qU9gBpQY|v<6%`RD_p1?fWL4gqMKLf9A)O&MYv|cN z_19h{Z4&I+{`o|l*wfKNd*)wt;;V0W(IOF#KKq$--kt{KK>pF+%P5e3tt<1R3k64K z1S?iL#5$Mdr@&)p=Z7_CLeE4>DYsX$bDNC_KFMLlTdIf6^#B z$FECYWwy4HLB6z;dq>`+v~mq!@^o6{BWI{miig!6w07Lr*|~~JCKLt1#t_U zeH6SVH1sPUM$qD|hHyJAM^e5Mtxnh}gJGsG6fq?m4)0?vo9imp%LJlTH?LMEJjcC;BLol4&vbdB>fjG%* zLNo0c0zXpi@ywK!5!CyWJ55L!#1G_GAFdAbSQK#eS_rL9)d74_gpF3?`C4y$HW!=e z*yta;gJVztX_hQToKT-2ot6s0g?lD`xF^ZF7;(czWKB&#_33=+LUPz25d8r9Gt;Aa z(_Mgv5|?eXW>4GcKX9Zm6gUWdwXmIlr{Ozh5)I3&1>c~5%VXz!?lQ42q?1u)Q{PSJhSL-N<7JsoPs7YeJb53?befS=Lp-f_O z0xSJ*5y=80gf?E)fpO)T?=gELXj*C>AOFg|UwecH)XbPH-;=-s9t5PyxmmlIqYbh5 zu=p6$SLQPGnCOe}tqm01Kd;wSdML*+MxJvyT3-hBIsxR~0Nn8N!EZUpe}%KPAnkjU z(Z@VuzMWdm+UcAug+4c1O67N%EeaKZ;Ll~3!7Z@$#%s(eS}oP_`ycnZu4nqgsU$Hbh0eCrBuFP@zqj=-HNZbU*Yo|uh#=kp*&qGW z!f73F%5cooGB-vt0=6;*?UN+yp^-(n@9uliB(86`beEKQYwfRil<1fsCKEjJSP6^{ z4>G>#gPEAD6~6tVObRA?G2IVrvtQ}breo|}rMT=k$|<-dvu-_2{-@NCGa^Kq7JY8` zpdyX^2oIh_#fE{Yf6;?Xcjq=h;{zUM{XPgx&YW?g;jVYWaeN}z!8lma<{<-alchN@ zYq^{D?xs!<9w^O-Xb*!tU?sVPFAh`;DCr@zbtg$NX=ZC^ttZbl6N+KaMo`f!8hx+& zfr#{oKu=`dO@xSnZ`!+Gf`FnE;)xN7bCQS$`VjK~vFPm>z6Y0GHo!ssXd4WWJ4BYs z3qyi7Z0}^#WPuetT>Q2;L$e5?)&jOsHt z(SyAq{$8jEZGf0;W+c{|(PHE5B(fR5sJp^b4WWlR+P0iNFO3JC!Ul5B{g2M9{Og#3 zH=*5@TmZ0N(+IERK_;hgp-C8KlNUT@wNFnNrNesiP58XdmD8$u=Bkr&J^86Cb1%_+ ztzyBsf3@gqv0QV8wv-~}{!z;G?8aJkAmR@`qsP8NMy(iPg3G(o%4I0L+5tJix|Phj ziga5zCfm`IIYP77;aa;( z*m*LNc$weLjTC3PMB%aNpFBw`jO^nm9)yyR9yKuF~!NYXrnc>=fx(N}&!|yLBrC(Dc5``xor2)*b zM3k=Dh6^^Aj>~noEX#-vGdg@ok2!)iyJ;Lg#OxY_7m!}ve~`OQakG0^gU=yPbL5+i zhuh$GQ|hBSt^))-k|t(h>CW-9nSTjpX5*fq-?dL6^WC`v#TIz~QO52Ik`#(F>(hm4 zg(hI!3^wp@D(#bD&hZ0U;99a37fX%;0AKm7eoz}b4sV_>@g(dcN%Z}^Z zT__0T1vS$|2`aVaB@R9?&|iB|zG@$x{NT-98}Yds&^q}cu(f1$T>>=E)yIMyYF&WHZ6N#E@vvev zs53?|${V~?W3>(4`Vz`64TJrz$qy%$|Kx!@HXQU7C*c1edmsC%!}a!x^8vcks0J}- znEl)Y>amX^MtvED?vDvRddxiZf0>O_sPc*qGmyuD{(JM&4l;8G@xlG`zrTEjylwFW#2t>n97%Iugn15C^{H@khnV z3m5X$E*VX7d^|CLM`EHE+c556L*cQ_-Q$POFay5xUd%ns5LK*t!$IvAC_=TZGcTk8 z|C!pRj5CXII9XhN3q~ukf$%DahAv+CTvhJIw!rD+ERb!UWlL#bwWJC`wKTn6m2Mm< ziR2~!>!Fdww3-#vU<%La^~C%8H4-w==1%;{Vw5ez2lShDZ3$M`+#4$P8HjZ0HCu{Z z+pP_^O-LX${jKz`%sTL(H6i8s;*|9|@Yd3CKmT?} zA(3+5K7x5zYTlX@DTXlUG}f~$7(7%c3)nq{eP#`e&q;a%mQ_SMd>K<7Z=T|mkWV6xr0*PON2+QAi1 zC7lNN8Z=_<%m#hs(=68+YiV@L93~U4!XrzQA3L7Y>+fZSwRC=AKRak!Su^P6;b5ro zfTNmZMafVP&Z0i4dvePPVyzFXyNYsMeLyxP@!Oa)Ewy5cc4xwpdW4UNTwVTeh%0=7 zJt!8{ArW10@1~bDo|bOwpwxme>PQ*uq&{L{l1pb7g$QBXD$60*!i$Z?zN2x%q#bWx zA1af4Qp5-E@ln{|K7|$IL#eEUL^ANGbDd8-l0=*%n7tYI92LIZdPnnf_O7)#Ke&BP z@A{=>lL=z?cAw9a30$mbuw3Sg{9MxhzF$i4?j1S9yo9}Ft_TUYEqB;^C+yym45f(V zb4;JTUqRAXTDLNSGkwiroujtuOkY}tsd~Om#U+ojwJB~nRmR$Xb5a4{f2lR#M!!#i z`vhjEMbjnqq);F#xYN${;yOH9e)}`vx&yT=H(8_eVP4?x6`mndBSakMLU}#SCgLvT zhF*VbOfUyBcL@+{y`^-P_+deuYMI*^1Sdss^yt>e$JA}W5{_&~2cw3NM^{3*6y8yPUWL;js8+5Qpv+c$xL@i7q%p zGM;#~x=%t{65Q9zO{9M%kaKgUPV@Nu0!A5YiUY5a9Du1tT!|J|D;57_K|moj{1$8_ zQth>?8UPCd9LEwIBYql&Q|QqMFcmWMh7^{KcLWFlWHlBEzxk=6g|Bx+kfK;ca>Q}? znB7AeF}*T429zY4Bn$-@nTAUkuR^>b0y1*I0UJ59*?RcxC2JWt-@lnpq`7qUa8X=8 zybok0X!uyqmxlaXZ_rOpA6{14UW^4 z;~~;s-|;y8GGD{ndtX5ahBE^DRlb8={F&ND>oJ|txp8Ej@bJAaz@YM2qRj5Na#kJ0 z#Kruv`n?o~I9Di-n9+5@;iFl6T*i+Emiheu#U1_FfOvUx*xDGN&93i_n9Zs<6@?`G zpmK)$Sli^MMgCnWF8Q;(Ttffw+C2UsWR97DZN;3mm)>E_jIq!Dr6zQ_qz# z?VN5xW`Rmv&(o0Pf!XP<71YCfWYW7)kM=L-4Z~DZX?=mBle@XZJzMtL(QHOy zzg&4xYIjAAOdk2a)pVyHU9KCFU|#qD7!UCXCbtx#E!#7(TZirgdEMMe%z65k1MgyZ z@$S!rjdOgl4bbm(13r&nU+t@Ek8=f9=mFu<{STU<=kZKgtkD@NzW4UhFd1SI-YE2y z1dEZlma?EQiNCCiik$6*X}zD>OH19z-2M#+<#pLcylT*X4yrkYhiu?pEgIoiiMfsr2{T^6q(cF*20l zv;PMPu&l#!ujQc`FwgF5CwV~I(wim2KW`8&4L>BAP>(GjX%5eSbP!L{$h}ag)c2Fn z^iRC2pe47Rf$k85WlDb;>)&0Q)&NELKY6y>)bU`2>x%IS`*!n|bCMg_zFh22+Z;er zePR+_YMETrw}%(1mfmf_2;PoYXbK;p5S0OQhs0OB-_HkTePS#b?wd=bJfB(_C;>QE z_3*G>UQ7?aDi_ZV7F>Cq?B`#Pd6&QjtX8mAM`QW#Xwu?_5905)O712x;3NEBC|zWc z`4F5d5%Byi&$VawcQZZ2`{=uL!bMGKFZkq_qz>qDW`$m_4KjJmWXK0g9X>FRqSg7= zkuN>EuQ4SP0of(;`v|nAqk&5{yps8=f#%Y1;fZ|Phon{?-~rCQ7q!()Q0R(FKVbk2 zWsq5y@Jk94L_0|S5v!g81>I-SO96{ueC!hqO}E=-0Nkxebz*)#7bS4f(_DZsRA7kI zLa8+PfOKP{EblTgJ_vQKIN0rLHD#t<3KA!mLFv9j5G0A(q~p|I!>t3ei`=q|W!fQ< zU4&D8gEyTK80(>t96;@+0WKk8uiAN$Q$;Yf5!L0GB|Ewn|94&c71)1Yx|As9w{3A} z5(zF!!gD2kz#2?aVXH-idE16xRQ#KhvfcumpNWriAD8YvA(3B;G`1%cf}>ZnHW9q; zx@nrFsYwT$q%GvxP4HH?uHYg$ThZa$vBs`g!PJTQx>+>#f7>9JhLsNNd|F;-gUna& zk88pSz{28;5IzOwKMJv*cRDBvz;VG}G>xQlK&NHQDi2_{Tj~UPb zi3(n+zLx-`Qadm+2VRdI|%b^8>m0nZ6PLoIV9n_8JY*e(@umKi3=muJf3KvBv3maVNoO_W?t^DwnXQE7~P$`)w;{=6h;!gCdaXXg}=Y zdHv;!7Az4p`V4V0M1n^a>L!Qj=)nuN@05NU$|d8s`QByxwCC~r9JALHhnLxsX}>ao z4Z^Nw3Kjliwhzfd)jRm7aH z`Cfhx`Cp5#7&02s+7nX5OCT1nG{^uYWiVXp=RN+QHZn$_>v$1-u*J!_tiPo~{Ah{* zl|x`Hgkjuz8DyB7*>l0Wr*xbxUUqJE+3{keD3zs~;6Nq?gF>I!$DvU%C*bJczPtFi z%zFd25%?!$Fs%vChHfAY^r*#WZgNyDhF8Zdj4K?t?!D)2Cnt70^K+1ncrkfDqCBP{d1s zy3RDnm_DurI~%O#F`A0AeMwpxc3iKGK?0E`%*i?)z5_@^21S5&)K}nHeORMFg#Tl} zW^m{uOL6g5tBFyK0q71JtZ~=YXOb=r=fv5#o?1AeSsBHRHQF-3um;L4@~%!_HW1&o1>BY9uwT<0-Sv3GBE-om@Z<`upupt|<3N#jsc-@I)<( zar<7Ta)fM>VXUfR9iE(P*;EaGrCTp`VS2UF8^<=IoQHJ?$+=kvZb_Q;%kNt$HSXI^ zolQgAMTj$Ij8~u28$p|`yN ztPkkMki^zi$@yizRfgHD#w7fOS}xUp2fU$IC9O_d)39e+i=J9|S%Wn`nHwdShRs5s zl=##@>)f8zK@A^8=ml1`okb5n828 zoxYzb_xo3SQ_VvRLsl^0bkYFb$ygc9S1?9RBj!3vY^t4=_W{;ya%=TUbYwP;)ci8-7`Tx`^u;Lt$>bH~w3braD(T=m0b0)xK| zLF?j>r`SNO{k`pZ5PTI$$8dQLJ zQAh+FlPT~}JP{gP_Yb30y)x^>(&CH@&^VcVkhC1doOGRt3E9aCc$u6Crkm?upcME&y&Hj~0kPuo)lb&lTw*{ku#cDEIOUOf~0DnCmYQsUxL56Mh#b^=cnc(b4t z_gQ&8s?yEd5$p`Y+x;&3wI|uiA*bUii@(RthVPK9L-t~aU~8zs1BL9PGjoC`Cl&JDP8wcyc=F>BcX9v|p zyCGQQ2!B5ckIaiWS`Ps&?!VfBzUX@X$&up$3&Zpd#B-?KH&15wfz7+M3U+^Oi`Mh0 z_5}T#vHKTXk}`@ia}`{&mZsmIl;yjNX~3u%QZYFb{$B7#LeW}W3K~- z_g=OiPb9obbiSRa-eu4iT$a_r+Wf@}A~JVVNiG=pf|Lh%HnrcatkS!HiO_tkPPKyA zG)d{6(mX|*M~@?}mFtn$%W(5bYSamLZMK`c=*m(E_a)+Bf@q(robV(L>1q|Z*4tQa z_WG?!vVa(FsZe~BsBKH;TU5Npc|0V4u z0BvZvSg{;7lcTztoRF?{Hy~e~}-nm)! zPY}|`Kl`B}JJ8J;a?5cAgJ5}@)j?ysI`oy^1gWQ(>YWEH66jTZ05SBeU)1Q`Mn;HI zy9v}+DDj|aBNe={G!@hIfC4S~)(d^X9_wJR|C9uOq1cN^SsQi=Ii|gUJ0|(Tx!kZZ zMfFi8jz3#>%7SyD?wgw8HD+Q?j@h3VTU<=YS+cX4^9FAy6x@F>zj)V30Y|&P-_)VXnI$;} z(xF#`0t5N4l&HXs6S4^D&_&lDKJUSMDkb{$%UK{wxZ3yxLMmeP<}dttzLHU# zKcgxFqY;0BIpzok@35-hvH0^U0#&gINQ%GQa4-Xt*q2Z<0A3Dv!c*KJ-8``IAWU>h zDu*$ONX9-8JqeWd!>@4>#TxdGGTnT1Z>RZrLtKL*>g~;>eP@a&-EsU5QPjC&#f$Bag=o%TUd?s=i!SWp-ME;&54ZEjFqDz#RA`&c3MybMCj3VS8@O3lZ)PTput z2LHv`kxN)voqacCpxht)4Vmns>wOMdd&k7!WJTzXk42Tw_wtVpK$u=YTrJ;rRvjl}9TWK5(^AZ|2%6RtJsP^tQFM4pq2h+ZkBN zQL*Lbl-Y%RBaiW-Wd&qUcGc^|J>VP+AIud5E^^*}h9vQQo9)oOWS{l?2Of;r5!~m& z0+IBW^?7)NSZh#ok(Dgaxk)l}3cMYcZN4@5K*~RR)XN9dlYocKYI^6x8z6l%d+mej zt8mwl&(%rY5=zy-5)FJ^k7PFQJ_5{9jKoGjwN2z_`{iRdj@mpY{exYp!>A2tk&9b8 z-~~V3uRn*<5&j`z$q&9q-hXaZ75Vr{dfO*uwhzaj*x_?Hs@@xkU zJx@UEUb3)~wDd&N5p?F0opYVh|L-JPia`lqNmkH{@a8|-Gx&5bmu3Xn^Wh{fk4|&{ zviz9GK&wWbfn)mSlH(9qv0UW+WAEkbuh1Dkb!8I8?F1H9V>MF;b=iA=-x**0l@ zJI)EwYu`_dpgjxUyb-S-O1Ta%vtb*2#eQdnQSmEMn>f|j>>1EDt#wFMpRz$NDX`e+0b+(0Z*qX7kf5MtV07Y9IJ6Nk?$c35g@#y@BL2gTZ6R0O(Krt9@> z-#Rd?MMAdZ=YH9vK!1;P|6GYhUldUddTShJ)}$8$_F5T)oM7vS#haBt^rDUu^%U~; z%E~>jn-}!$<_K)P1f%{H{M?eX9DLnNoK+}uf(pFvykeRV>yhcvEu`=|TlyAr0+8*j z84pMhXBeBG4h50ZFXX@Ow>7R=}W1ZMY7L?4~FEN`^K@bagVD z)rxQH8^3AdF(Wr*`A5l9;|eF*3|HBR%{|w@7-B=~&4;8^L%_2U3TjWq(Ro40JyPrrzn3K!#()Xrx!WUrsODer0on2x&@uC#(fs%c_T>?^ohg#8X`q zq(+Ej5{D6oEklS7>hL+{SbTqhgdC>!OvU!dS)C`F5K-60cx?p@qJ70`f#k34BBXD2 z0h5hYbTKCvbKNU8`eadbx@X9DT6%)_vqn*tuIA@5T5&Yc;W<8jeLI7=QpRbJHRlX- zM>f4K9EIm$5jPJdb$Nl}7fMX_0C5DDh(a6s%!XD+7qu=C?$BBa9SRj@$89!^LH7-c zTKg->Szn;Kg08093# z0N|_B%)}poCi6bfjf8Ohp0XkBO zx1$K_A-In>w~1)wp(sVq7E%1AZnk0vK|EjN@%)b9jn4l=(|I^j-TrUpY3<$UL^PN3t`sv-b!YDTM6o(eL&=-`_vLDd)cL_qeXt#XgYp zn)5FNkG-kq%e`o{AVp!t_L&r0FO2_qJ%bIrVsiU{s9Y1wWx{^7uq5M`M`j3n zYqO&-P^fg|my|T20W5so%)1RSvNI}AM$jiOCv^ToT+j4k5v{hqO2aO1!V>R_hB;CS zHqR&pvJ8qSR(u?`7M?i&U~YDU;jr9w#xY9-NVt#W?f9x#ZS#`^N`it7>jRlR%FBfgSP@u=IPP?0f^Ij^U7%v|AKX5J*tDz&HpvJe?c60($sw( zj^@BnP39K+*tEz{6!o;?YhGLr@ovG4mA8F+?to`{UX9eZgFpB8)&3R4d0hahZko;? zXbLcdNMAlI4%Y=FwTz_!!xHA}`$%jajY0~TT6kxGZIm+aa8;n-VOjaooY};BxSXkS zSvi0!f&=-LOYDRHH|+i$`ehoEgP+vwpj!ll8I6?`TscCuipm%%sNlnoOWmMhI}(9z z>MLWHy`O#9@8Lp~u8qUYmNMX@QaSNh0B{+{CZ=e|H_@qhWk+*NKol!>NSJcc8tAD( zJ4ZQgf4{hs6?ii`$l6cP2(afNE5bxSPA0&(hhv#%23;RbQBdj4p3KkBpuhs5gM!O2 zJTDzDy?`3`FSK1dCTuM3{9@`E!Hs2FSMR@2NH)HtM4|S1btw(q zs~#P>wTu|xa^M(89Y=fos>b2$;LX!917kmpG{d8hxc?EvEBaochP#|6GM8>hTEt8_ zc}Q`}aX*d!U>u7p2-eU6@MPYbxg1;Z&z*(PAwOtzCTqWf?SHZe=Y4%Pm?Y15a(G}h z6tZ-o=z`h*h&&oe$U3_;@{u)^4 zL!z({ojEkhoNuj`royitePe&#zXFKR)*x6g7n|Mq-&M4%`-h0bedsdg+9)fL4z~If zxsTHVj$+iQawWfQ1DWD|e+tDxDMbI57YT`GH{_H>N0Tr#*1Y)3w>W=WV#eS$>Yqk4 zxU`{=6AxE+&Q_vjxL$ExJKxL!df=UgqR;hK=sk{Ga1&sYc4+mUAXQOgC=)q`Rwe!;UE z9ldkotx@OP?!Fl=XP+Pnlid@FFdD6wl1C)(M=PyJ@q~qInoyk%4w@0DcilR6`J`Bx zlB37Mc`6Ph1x>P<##7OZo#dTqp*EAM&#@Pq|kPPk`pxW%{{TXO@mZ7`34`?sp;Y`ptEF&rri z#E;4_aw$Cm2R7-@NhIoEitaqLX~-W!L6 zIw6V!QCPlVwt)g{%DV{QwS3@R&N?+fg0Of|dk>F(seN4Rt>XLSbl^|=ceHX%tLTpW z?2Ekz?-FmybHS7HkAY~ z3t}Sz+Cot^qL&M0+t>QUzYyO2sP+{n2PAFpP;aZXdBVqc?iw{6GURtMZK%J`s&;?G zeYk6>68Yz$VylIg|CpnEq72_?;_k4|Kpz??{vy0*aZn#w6ZFqE{lIV}Lb(eN2(%{zG+cuLCfiE^VXF(d8G(iv-! zW=zZxS8sWg2d#@4l7eTv#o*bTI9sSjwBW7Mr)aj{MFq~)Xpy+=l1uLI1h;;*)GafY z;<}QW7bdzM%4p@w@UY6aD4?Hs4$2Mufyfd1pL#GH5S<+3f>^QL6od4{lTWI>>>M<7 z??PD@T2`mgea7Dw3e2|B*_YSf%RvnCy1Jq65IM-Z6y`QI6SIHDsE@qlq$?zgpw#=Y zG6vJ;FLz!LhHee;RGda$mxu9OfoQ&GQ}0uY=X+C?@2|BFs{ghkMARMyOO*U59U zz1C?HPq-oTp6k!V2#oL#5&voBb#E3g2b$68X0^xSt@;ioiG12KPX%AnZOj;zLSKDQ z7xIes**J%+Rd`Crkj21XsI;F3iwa}B6mLJx-pZo&+9eW2oc!6GaEFXic%olq-*S;{ znJ3zCRf;`IGvnc!>A^v1QD!0s;o;rTEt-|J4Q zUnX-+ib>FqPOi9^XPrBMq9z=+Twz__gt?8y`m1F&35ozV>59=5Y~PpTPfFL^!Hx^b zOylEslJfIJEwVV4OexyhscZ=;J=oBlca8=Pn9?eToc?uw>es~G8GaGmBt-I_#1Lr? zrBdNsGV4wKjfw>&&=&Km2yM)9fen6ZQnrP{ra<-lXm zu0|8A*I)G-?9xh@+rK+4iZ_UL!~n$S zm$2o97;Ufe&aJm{a>2^ zL<*9Cx;*^T?k$mbA|r!?on}%-^R$4Wv_0~b`vlXqOPrzj?frphlgE%G8GPO9Urz&) zYaYUDGR_+|O+Wx}HFezmrO5fpqW0#twA6pDc~bIew!l>96Xthx5Diy;&h+5(>{$j0 zENW=)cSX#Jo>(m6yyHaDF3CJkT{&%9V_66^HNz60#)a+1a&I#g>7@qn1H>C2&-V}X zL#QD7cz=zb80o1SpmMLaf;?F*SJAg##4vJrj~)~S>~sgk`GkEU8V#?Ep~9tRg31= z9tZerC{&0|*z?c7n| z!*o4MD+^A9&M2$+liZ;Lx0SPwS|1yPq2gurU_2`o!|LfO^IAz#EIk5Tpy~`#`g+FE zvAEvtPfGPB)t*fJVn?b3I$_Y#Q9KjzLEqPb6Se%1w$*ymMa3?Jh~RoWZRdUik;_q_ z3r71gd9TSE{+Saq!JMxt#*+S*5(g=BR;fHG;An1GgH4XQf&H0&DD7ruUnM%utxz0K zb{5iiiu_vFsmInTy%rMkSsmi+c%T;!0?lYx(j0Qp)Lyjy5KoqT2GFk*J@l>qI^W7* zBhjB#^}EssTIV17z{AvC_ek%uECWK!_0)^ef5M{D55-)V^4#tD>44C-2SL3VMG+bg z);7soGB@}|eE=JsW}dE=-)??|6D`Ikd-W8f^-w-4n$@%bOE6LM%Iy}-8zB?6^lU`q zP_b8)UM@~U{KBO!6vO-W?1Ny_61_5e`Y!!nh$ug+ZRMMo$>lSxNaRrTVa3lcEvo-2 z?4X3f-QfZkGXnk8m~JmI0AQ)-h~XOt(`cr@&_Hr0q;spGQWu*x@j4z!utDx$CoBx? znLNzapgv}==pA@L0=8eq9&6-fR(~#Z=I}%ELj}MQMOiQh(6p){OiK#nQbA&j&>!g5 zj!t%wHdeEc^~?~qVn|!>q2Gg-6pXR+k{udx->wJWT*4^YOS79zdKSIakOy2(ymA&E zxd(r#<#Dl8Kcam|Pi_UR94FdZR!>*ssqBz3sMa->sfXOO^_?3{b0g3I@QMbfce7IV zd(>ZS6WS?6G}uHBS3Wv=TAX(;q|c${k3*iiQZCRXVbe6pZv0A82mk*Z2M<(e=VEWL z>U)^G`8(3+AXPRM%3u<*(hWc(Cr8P}$wL$f_N4zIgYZ;%+anW(!0Q%A*`hzCVVuoU zKvG=5?Qlm5l#?NW)n$%_G&(Tptq}PU`V@1U(z5AJt@tM)H}hB0j*|A#edbY1Mh5^9 zMoHNWVw8hn@Ey{fYFAiRrx0JY7fR7P5;KqwxFrC90H(z^EUM;5ft*IH=}`o4R%gIx zDYyf>A<@Ny=a139{}tTGh$!*a>&4>okP#sbpNthgSQ@rI3YUPYJH+q!t))r9BZLlV zn#9Dxu$Mxar7degKu!EM;8^bv)I_m;u@5K%AEi5=gFC-u`5Sf!sEMYNGzdVDV(xc- z(vg*Dw%Rx~oe&z!}_#YiEsa_hZI1`jN<3AvV7W4}eC zjUFegv3l0P35)Hx#d8F36S(rgBbThQ=39aF=X`k9tLiUQNb=xG%$QHuH!)qXyZ0CD zm%GkA4x!eNT(#CsL=b}Y<~k_ zHBs=9=z(IoLV}MA)m-0}z%RCM@2#qW0Um1Md@|phE>UCm%O8E;9qi82i(?`UNOTx$v{}(oI8Sx%FxO6c|e~Bg&5z zS}d5lP~k5{Vv}4|mF${{tM}Yr>1XS%Q|2-w(}N~f6B(K>3w*_Tk%$g-!D6->Oxce0`Wvz3h!~%(vevD z(Spf!gN_bq*)($&FJ3D7wQ5B8)2s-oleTbEcf3_Dgb`|}C7q;g_(}2kI4t-35^MJp z;yTabDGixwulMV_OaWul2 z^Na74LYTGA_qFk@e4$!JO#{HohHt5suL?c*);AL^RK^Vf7{x>m4U@a}68v3WS98)5 zpmx>^N2VD%rQp6i@1`E<2fhkm<1mOb9;{-;HN8Isl#grzbBnLPaZ2~^+(--vXb2(# zKGICaJMw6i)wk#f&%Y|88R5K-PCG``g^(U%0gwketQjZU?3 zb0Tn!hb6(IPhqZssISKkR$S{nBofZN%vH*&Iv>hLM(x0x zlarniASpPZGkntbAkBD-lYnGUhPTD+6pp)FN5$v(r1cNF*; z%l8Zmdf&?=Cz;3LM|FN0NLbdXDAYHq|8|Nd3sp_*t-ldvzIUU`g10OeFV#HMss3l} z2yoSu^0?&9_>in8wn0;i3O|cWJuEC)P>m}ZQPPO|;f0zWgOpJEB17+`z?>GCrLog?O6yC9 z#R03St;RA>1-d85*j?U(%@E=5zap}g0oNR^W}K-;hqBP?1D^5gpg4zvh7mfjBO_PZ zOWAs#h->Q`n%MhcF&ipcn-?=9Qzd&46XQnxA^PEA%?>~Y;;OsKKyq+?>HI1A_i=Z2+%jb^2>t8Dk9+2@0SivgxxNYN2 z)A2Uqf|eTG>P7FTUoX*kI;{@8qHntEha_&kD$#uNSimyRj!?aNDSkn<{4eAVG~w6v zOT?a$l1R0Oz`u7r_A({U<%6aK$ol&tpMh>~CYrPgVej0Xj%d$IS$w)b1-te&;i%V@ zB%T^wNs%dA)Hs!5`#e8=6=gy)?}-jMj!dHPI^3ID1*L{yo!#~D+ec#N*NZi1UA2MY za_K!VjZ#L;F)6lEFEjGKi*agt*GbZ;`D50;c4&B0Ue&$U(pc!RL;z*(OBoU>N z#il0EwIU-ZYn|Rva6O65P#qkgVvtaXpci(+rpavR_IVMpMql+w3wbjwbIbSXV(G?Ic=rJ{e67hJEZ1$M^|Jm zJM3NPDBc(;#x}V|DbiR%C7*j=*AF4QRcL#GDEsNGVk0U zvfypsaVz#6D~how5}TeG0z8(TY9Mb*3b#V0NWO|z&J+;Ek5kr-p=@lWQ`XAS_Dls7 zfLu9!N%K#z6w4i_`b0JADx8josWWOKFwKC}XN^NrxVii~`%TiJ)`V<3b$YCz`J%#f zFE^e_QJ+2!Q4qmpBG+VFw9UP-P_{co-=B`<|2*G7?sqG{Mer_)pACyL}>MrVAQ7EWX@ec(odxN4drC3bjs$xLK_mv^Ex*n8 zvJqvi$!EE1ywI-V53>XG^SsNo56^#Bp$o&OX`Nab9BelW<6DLE^?~r2-|S{VN`Y$B zA85=z#inkm%9x2sQ8w6Ku)g6Jj7Mz#jduaqZ8x!7XJg4Gzr%hMo4oP7;y$!gb^F@A z&k~bdwvu)!?o+=cU=9)p+>C!}CKbN*bcLOz7ZA#(NR%F*C&F1qw*%}agW*ffB#!j)e}9UlvZd`uqM zcJ?-5_%L1+objMZ_=QxGn(PXgstXx11(p z5-SkaVrc_F31|JsPPw~y3gCrq*xAKS)`b^NEa`TG3?O?4U#)kEO#f zlhy&X8vi=YR^G_k$@FP5OdJB7=D;v5pC8yN2kz2TO@**%CQ0Xv}2EYCs^7o(_%0fPSq%}yXKpi zH2$y$hrHmCdc%E(hudd?XgtzPuFjq9&lIiX6NeNptYFF114@L;%qS40I(3JB=G)L? z7t$drgn7Ok(6nQF1q8?gEU`SohI;?(i_3vF7j%ouqL=SEL2Xs&bwEk z+{9)dtz~9jLcWJHbPj1lQ|NUW?vF9ngMsl;F$T1&G9fq8?Ir9Sq$f1fG8(0nro6|t z%|!n!zC5sJQLV_he=vaBx>q`vj7~5nC&cyfQHTH6i^3?JZr8`JLlO6?p83wtUoz$W zGNFnE%~ZRyk=dO>MXEmqUKGEFz?F2OeerpfMyW~HfE_klywoqyu9IVfaGu^j6nrx- zbyGjm%XHFH6X=5YHEUgi6Q!=71ds3%k(>I_Lci7$+p^lvr;fmK%)*{=yFoP_o7IM5 zky_xA3Y&(;`j|RC$c!=c{-o+OkX7Gk6L?E@>IUy1bYikzM&k$l>|%gIj-x6kV2Q8W z*baa<%`_}bp@ebjTO?Td^S0@PmQF7DIqK*s4|gsKf}G57v%|3(4;RBRG4A zI%ZWtF`C%r%eas)x$hb~%MNCoDh28SQk15nWQ za82CS`~8*@4UjB@eR_NOvpku~o9X}x`9vSGcIA3NK^7| z_1!#lvjd*B{*_Y(87k5;E4t`E3n*Q~&Lv%T^i`F>bSPxAbeslA<)lBxFRBZ?BV|z) zCojjh>a?-nkUd|t>J@PC{IUS&Q>X`X7b_rLQsnBH+C@$%{xlh2f*%Tmx&{TQSYMi?7pqu(3kEE6mx@ zbKD7Qu@Ig)Pz5q8EuIVuWk_DGm|Ebs7X+fyl!`vM{2DZ9i@Ubo#V>631C0O?&~6z} zj2J!)DuuOW20om#X|N|9>Rsl^E|aYxx@jKv?yDZoI#^g5A2w5hkvwT88>;3sH0kb1 zh?33V(v2q$V^LMw*dc^nXSK=)6Qb%p9dOn!K|(nJKqJ+zPivf+JPS7mT7$$JpEx~J zU=w;Z@vRoTUuK^fAvL7Niufp7lBHm%jzoON9bYjA6q=Ig4}(Vc4&|>NhyZ-5SkMva*5K9e@;Wftxl#L zmAPHset@K0f-1_)P#@Rib1n>;&4%6huvR7O>Gzzm zXa!@zu}jNZcPcB?=l-=}v+p^>MhQlTe)66e@wX9ThPPo82W!u!$Gqhsthrlkw{reK zPHfK$iOrQp>3LAf^}>W5D*LI~8hH&@Jss=rgGeSE(hwZTmVj$k#Kx@tgyb&2lY6 zHx~N^V<=Rk3W&bT7;#kT3qlgtSa)c|FZ|lkzud*bEHJb?9R`*^Kr{Hc zGkrgjz*FI0^RBO#Wz~P~7k)KUX+|tzJ@v{j?cqOVqk)gCk=>7R@SdAT4CpN&or9A+ z_ihx%eR21r;Kei8LvC!4lYHsqZlS})2u z>WUKGJ-iGTA9kW)F;J~`^o}ss_c+j5g-KpaKds;F{e;8W^DHR(!!KCgV?@zM#jGZk z-LlhuM|zMpVpU(P0}U>794DlIhp+$Os0G#wknqv6do}r{#Fw8jq)2(; zKFHRa4*Tx-i>?qq)W_Roa~7nuL?rVGPfSX|7f>>8R{chTl{p#9%nzztMKtsT#x#B? z{E?6jCEm3iS){MyBB99;+oPs4n)?%`4w{~Wxd5E0>1-Hl0`t3{1O0Mg_;F+-QI!xC zLSL`+edD)NpiUm-nHv$Tf+?lbs#fLV;UXizdWJXDTMPdB+?C}1Q$aDSgY!1~k`te; zY9#hDVI9N3SFjC2yvvWruUQ=sxa)w>cvbQk&&vV~mv@M_3^wY}HwcC3Q}6twDj|rw zWxr@QKJRV=118rm-@1ivR$N#I?Zcb68ay32< z7jr{UX!ktkGTqa$x>ZI|U-DMP4&EHUJ}&zwn)4CRLg+scn}sOM^9DB+64BV52(6{F z^`dlxQa+-`Jy&j69o!M-i?*WQTD!>w4^dDk>(1BB5~flxXB+W8O%W)@)l!uYYh}#% z!Iis_0yF7)eBVbU8-_3kg4@K|>gb!~{mi!uB>M)i%SmH2F^?FLNKR+7B57&If&+rT*GGYKWey6{>Q&EMctjpORu;xb+4ftFaj(`Bxnsfg+Q-BV%IpEt*V*mSW60?a7Tj ze2Mq2ZVoFM9h!M5_TgQJ`obS^{v5EMv%gm-y3I&l5vY~h)-T3ddce$<@e~`FHgiwq z6F~98ZapLhEFt9F&oKg^Zu8hzQ{APfUZ89%5K0bHw-`X(%(%UlnFReD+9P8O_n%%+ z=&m(m-7c8VdxEPq+#THplaLV^9F&~C*LvdzTwr)n9JAahKxHG}U93Z_eY1+8q;z|a zy;Y4&Ql~9-R!U2mH(v-NG+OjTnXmYsOBR_Hz>@r#ZOr%2gol~>o)2mc01Z;CrMHlf z3b=wYzO(8*891u=4ekY>sQG6zXSj8bH0#dh!SN996yKt4bt_7F%p?^8?=ue~Ebyo@ z099T{(bwS%un)Kaq+$slJ_beszwHh#3rNxdM|V#@cD)Y$Uc9|p+37Dt#sQh{k@gn! z34&<}LkR~v@Vt1D%<3U{vzARFHH zwWr&=YW=I;&&FzbK&Hn2p`sfdtA0kwzNha0@Uw>bAMut6?5)~ft(L02%9{na?3G&$ z%+8v=>lqZf(G8v=cpz4Dv>woMQ*w89doSPplLOWB6)+L9f++dV+U0wR;ktbVrk5@M zJS9?+Pph?XedgLn+@OzrM~ zB-z4G&2d7jr7(=-A)a157cmX#5MW(>9a7`sxmBhZ2c?*eWg8~Y-9VJ31Nt(n z(RxVV$KgBCO_IifX?veU@IdGf1}DgfYC9xz>Mfh{VySmLz2GFzAGDFeDQ+q7+yPhy zcE8o5Pry|dQi+M^2BBhsKqDMZrDGtfPK&t;od-Ky01%b{2GeIge9woIjnVf|QEY4m zQ&6bikuhq4s$c6jY%EGS)uV4ThdA)dv&PzjK%sv!65B$3jc8_cM7!&hT(jO%5q^Uy z@z!qFHQ2(ei#G9d%3yL0M{4WPyiR}JN-3_$e~#F%VC!e`XgCT1yhtQ2}&(Sj+HC-e$~!hf}8L+f;k^O8%FP~4 z*))tVuI@|$eUb=kA0`$1kLS>ylY`x2UU1*=i}Fr{cE8-xIJ=bAp5jXvd=&Kaf0;oq zK?6q}>92HHn@tLoy81`nL3+G5_4CrqQ4$&C!=^)!H6iBf@HRkFRxB~ts03CI_e!=v zcSsH12;r;t3IO#2r%}~5t^lwcIxf{Tcms|s*M$q3Xqk|8qG(iUpAgs}*y|IPkTMka zozZ|~W*Zi90D@xyQpn9le15IC7?Od9Z6;#yI#yr~i1#1o+1J4^hfk0zQ1!#AK#_)x zs72ag2FYXsHfsr!t>MO705-0zSv|3=vK;{?lHu21wx>5Gf|D3Es4q;@lzLanjSACGgp>9N-QXJUcu1x)BX zeURYYn^mZa!O0OZAjD;!D05edaTOtoK)n6bXi*y7>343proL>#)WHP{#{%9%@5Q^hc{=IqTA(WNwaf&AmF%bJlQ-7N^{w`M zxCY}%bcJp?LE%PJ_B8XX9q=W^DL|(ZDNVcN0jUrNa9y&8NCPxJNc-CC%|((9?d7Jb z0>f-Bzsl0mhrKw{($w898=_#t+pfP5KiItQps|4@>xy zZtI-XdDUlmT6e;(`v~oeZ2qGEm>};cI2U$Jyh6gTVtbXC47u*!{4d7Hno#qsL3op9 z{>GC4_fZug=WD<0MNLF>*6JAqu1L-0$^QG%>{8|-XH!=Bs<3F;C@Z?zHQYzXF}s^{ z-|iGwF*8YHFNnz57F_48+B8_1Hr@^O#)cmU7Y_A>Yy%3l81wpc!|7u{`sb(56Izpk zsdpGE;q$!t4g(;6Le~B*04{k{*FDGbR7`iNgYjOZrN!`Ytep%Pe>*U%XkB|l>%Asr z$Dk&K2Q$_(X`B4^M!`JM9I z@D#wA3+Z;nU96lMMqsmN*o_qu=!8V638}*t;O-+9!xv?hc1gS`=21?@9=y0-w+d{c zi9ux-au#q5h58fl>HP{QnE`EECoP{|fsae9CDI1jeJ&NmfzZ#n zA?Byz`p;`y$@#{B-wWJLG-TTVF!n^wNg!tPB_ObOIh75#xyjgEHW9+uBaL0+a z5X z<`lTTZtxU+5RME?#gfr9F|gW&n`io`yAZpe$@ke04O@xt;C+Zkd#&C#9-WU)jJ~Nl zS%=yl!je^sny1)t!{SrZ(XJt$8L)m<8UMYNAlz`yDr21YMv^W5uHCt@lwgv%d-+`$ zpTKp!aR*b^LHs)_U8L|dZy7e4-y5oYGdJ`iF;XUF5xT%W}5Y^y-!C7iA`cZdge;& z|9J@F6_v%;mU@SCv02S4R1O!dGPAM9;aMQ?dJY~opULQG2L$N9-V@$y(F%Rc0Njn* z<8eScx6L`ua>NNUVWb}bdg&#`P<4$5tA>bOxM|?E>Exx~EIgR=i&}TR`0u7*X)^b% zl;2qfKw1zi_!~^R59y05w6z6PF)8&f5685lEVR#a29EQSn#ZJ>?gX5%YcXU?;=i-Uv`Xn8atKE$w69 z)zhmKJLhQi%z6G#_w>s((978z418jXIvfH4vy6#}v_)VT&_38bZ5q)7cWPm7c&UY6X^Q*D67Iw1a(e`2wvp{rswqDtg5vNr&U%%Sobi_|8w9I!;S zehuXOdy$du2(@m*&D$G5RdCNBf8Md57t%1Cj!fjk_n=Y57#a|d& zm!T**D^dq}yi}d*^^U!#CQ|ethqYY#Wl^s?W$O(WocD!59YBdHK6%q1c^4lX5_l#i z^pO7-ebs9+Bh&9bVj$1UdCu`GEOWYXLC0(846+Fc;E?=ISEzYC4{qh`U?kV59KZre z;9mz|Gs*U}e7T?Oq!SDaXLhqG1wOoI{L+{d{uNp%8@E^jM(@tO7n44pgZo7YxKWzI zotKu$_od+PN>q*_9_sxNhT^>?GIny7c#0p3a-OP~aS(IbURqk;WwG*c-mkR7Q zQYSDqC+v8X>Fg0hL)zb`y9B&q;fwMi?5)l`2l%>2hR`#;8|8aAQR|Aj?3GgP2B9h5 zZ&vI+dNu)}YoJSgd44SUk>=IN4fQYSj$ae%A-+IvlzYpw4w*kl8G`1sx&fC`KkFUO zz1sncV$}+@=#;mY>d2-(fo-!yTMaE@O@8ii@vTA}&GVwQVP4oCNdrp|_i-zTg4TF} z(lR_OF)YPqgKu0JPbic_d<5J)AF}K!q8;?w+RO;Rskj`(v>L?G=?Zb784}2+gUoRD zs0YkBwKs|eEmxtz9XOvu{|^tqnPFhwssiHAIOQ~dNR6~}=e*8o)qNEb{twOoiG^I| z3+}D}##jxD_{6a@n&@$9N&fM(99-TnUW=~)(LM8;hYKPcnGMdo&_g+~z9!6F3P18} z_Q>#)l%yo}>du*@sh4>$qWn|P#Pa7&Jm1%f74To+>a@K~wbvqN{#n-HKBYG- z@x7VPSJ%+0c5A!nPNjaqU836zt8N-qDRweqvAu#eUJJLAH*yuLQB8s&#Wfn?Y41jE z-6_8Mg{`?t!RcE3YcJX^)XTR^V|gtTtS9;XX4~Q-$GH*~eh2~mI6}{{yDsUUJWU!4 zKCgIoDNs5^27d@bi=kd=P*pVT*n#{&xciF5DQnGdGx(~{fUc#INTP*(9KvDI@WpGp z3hccb%{nCu4ARsKaKT6lpV@o6P)<;VDQRuD4eWKN*&=0>U;~LSYT}}ezwa8{8wc@kOM?*`=I{;|DcBq z>E2t^?B){fOKp#h&BS>J`j)OJ+;iS^BIO1eq`);|Ztc<{WofD2_+It#X*owKR#f-* zKsg~l+&ne$;{G7Xy99hn;~qmZy4dWI0_z73AFo&cbc`=vXJy7BwYgMh^&cxQ@8O#3 z3BB+D>9K<5>0hdMR%>pmQ(-*YtHqtoZfJ2{^^6roo77X792ATK;!*qV=i~rCG(mjV zuk~BqDDpptefC{L^O~l3%VVTD(F!@9f6yUaQwIgMBG@1+6Ll&7J6MJ^fF*Aar!qpy z_KchOaWlwr9p~U)aH9jyOhP&~Pr;^jww*LX6yW0Tn-Oe-?*?%rwI?^et`4G+Gj3Es9tlf+=&^J^8l&lTH9B8@GwpmeCM&-0=~}AV+_`UfklU zB>+|9Vti9bS{f6EoiEgt>S!>c92}$12b)DF#S)!=YIbbft3G1L__xXI6D7HZG zqaPccz{7o`;p#lu{A@q_9f6C;0zocrj{_GNNQ@XXhB&L}gQ;G0q3@8!g|lDr-K8Dp z_NHW$z}dTVSot~Q93r!kX))YBbywz;mVK$NV!(~-c@ zAZKVB+3|u=)Gx<-aY2ls{(-k*>TQCgaH_a{wg0TU;jic6)1R;9f><@gxQjykA0n6d zPO)DJitnAxZVv6PV1lMKu04E@LXq;EW!dgSH)v8nX=aHK-R9HW+RU8-&yU0-n}rz~ zjApBq8kJQlpihS9IZELNE#K*9*E;xf-F52M)s^Lsj@6t5vkK<~OOvcmbmW-3WP>R; zyo#yrC#*eGzix1qbSUq^Jc~-VX9{+bZh4WZ(j~A`N<2!=2YwTlu#}%QdfOj*u&X2v z2>7`Kw<;?L2!NxNz~e*Em>T4OSmf!N8{qK_^@+OAzuHWx4*v%nZniPNrP{%l6WVyb z9aRSKvs%Hx%w#9+`|>ZOXA7pyjKkz7Z_=~W6CL-6Dm}{c9hi=Pj?{so9eq0T$r(m<$PY9n z)^m%!0tJOg=sKGPMBMIT<`d&R2@}DeQ~z^|SYS5aNtEV? zti;1@Sn@Fc&kw?rm_Fc3N6dV%uEu48b3doN^K;FOuc}ag)OJH5G1xF)3$B3B@RbBG zeQDp6h>70`K;?-V385J#2GO&f_bl#Lkb8hYr&mf}-{7p*4q!6{e8Kr?SL!(=O4jgC zPR~T>1)CaMCo6F7qqxIM>>%~_?Se02TACpHClr8?``jS&{QG5acJJqG=`WvlWgg>Z zEb-4H3HTa9hPvO$+h+nOCrS8b(~Lb#;((I#H%-h6E?+bN*j$qUU@0wo(~@AkT7&rd zOx)A(6Rc><*W{KFD8J>bj`iM-U>Vq)?cgtPIupUfyNUCP_o467gNCn89Z82W(?tz4 zN3bWEvQ`YW(Bu*_RMExHF|WbmKV&ev4O9tO#Z_f-PUBDerKB6%!c!gjr?C7Vc-BBd zZM<*GlFa);LkNWq>mMvO=z{uc0d+Y7zb2(IhOh;8wlI0!WeMv#(*u^<(9J~u^|DNm z)5T3tJGfuICv~$Kb?i#^6W|D5@Mq9|#fl6Gc(nM`qs&=~DKSE8GUs+ImHC_MRVaP9M@L&Qoj)0v`@aDwpr{LFwJ^1Cj>d^1g&?Jc%>F! zBWV4uD#l=+Sbuu7y64ImCZFZQ*WC?#@+&OzgH`2uJ)6GWieMfZlxRhKMiEBan7EM^ zHmeS7yAfIcr>W_X+3ujYzj>+0yFb2Qer&gQs(% zXf-lo&L+;C9xT=|bK1is>6xTfIv)@EgrbcJ!3MY-ljwNaeK5;ti5gFGTuTM7J<{v# z)Lbc!&h19EC$K%I_`ngGC=InaI&K?$8%>@4u~KY=ErupKz9qF)M!B%`z!S>6TXReB zvn{TyM%TBo{>)wAW4Oiv2sU_v{_5cjSf)varE&}I(TG8sn1rQY`Dpr+FUG3#E+C@u z6Kj`60~et)O$JL;(C@*CQ(hd7rcN;deQP_g9V}`>QkaK~hH%&ep9ZZz%7vPpC#?Om zA#RKrR!(@Xwj~f*FAdyppLx&Ae?x{`>Fdw5Zri|CG4=j&OD=i;-yp0h#k7oN6Fv=c z-|l#=R2ib(_2X_^iwwG|ywoY|xZ@QCMeN{HO}gYQtb%1(Xk496m@P@vlYvty$*cud z4(jM!J4=I*?^;|C$~RHzUO6+ZEVu(0rLW#j52}x>03%r4d!Z%L4)~uF3Ppe5l4CFm z_}!QeDB1|+zA2}Smy=Sw-+27C&~B!_o1U(uIp7o#uQf%x#J*P+=@kW0s)buv2LM6$%MCZlX!4v#-UO5$H z@Z5`N7L8jUbk6Q>)sIs7B*~)}x-a7BDR^6-{ajy@K^kGnqu3yh=L~`dhIUPCf4@(# zlk#2cR8)b*Dg7FFC1y(tKb-nqZRZXok{(~{j0bk{Zt>@~53;B-NC8(6?Zv}xfYA_O79?7qS9LzfH5jm_pi;FTb~)g7LaS zfqXNPritK>P{n=6PVaqTdk=ilxbBLYN3kDkwV+fOb@KlwD1sc^DypJy^5k3ZG>V>~ zRGun{cJC3YVc6 z_ej9eqNXj}fCH=%F(MSX4x{?dMIR#JhKf3*?W`GW0ZFG3#^!`1uH} zw;uB*u>=J*m!{P0txlXn6!iwPQa5q0t@g0Uk+^9VXej5!5^%H08Oo98BGWYU*Wn?hZ8JW2teR6sE~h*HQFw}McujKxtDEhE0>qqXw(heA2- zqayO7am|eaQ1?(-el8RnJVILro>^M|vb&ipXgZ&zRJjUkkE7EfJALC@mADKFnWgrI zc;CN}0CotAb5@sru8bJ#0fQX7Hu~4l-Xs&tm;m3aDz;SJk2t*DEP+G$Xv$TeH$9QZ zulp+Uo@n#C`--tZ?BX<=%seaE*K_P?@16NRZ1#v`>iBuTBI-srTVH;=>4!!W#QLME z909lWK0&_0L=ie$a1jUX2)`{p4vd2ti9i+MQs8h>Hob5YbucbiF{xL+SIWMDutuOp z+5d@-Q)T-$0U+FA(dSh|;_ZGWz*b9LwqpRYCqfZAe|C;vJ&Kc%6%99NcaJ3P1fgey z)7JYjiNVACOe*HHqWQ44PymOY&|8qGjnM`xG;?|B29}HRZqySJLD>|=kJckVv$b=3 zr!2Kvz|oVEp)2?=ln>47hfoV;k&JD~vXTR`74NuXJ&W1MKL{Jje($+h==Wj~u_!s3L&3&#sUNm%3L%aV+(^)t~wY6`14=s!s%gH@Xd z=BWXLPi2T4sPmjW7F8nM7asW~i_FMkQ7n_SYiW1$Sv-XAXklLl#iz>B6jQJ(20{wl zqVnEPCI|}HtaZ7r(nZx5Ccy5)r*qNw9n3niu2)$-U_ACjPvnLMf1Lw@fh)I%5R-~U z2lWStl}xhg%3@=MXvL?NJMBPwbI1pI>b8dNxv?=uAW1PvE%HZUw4A$&0eHy zA%g9d0!xlb$sZ4Tv!z>#x%cZe6|-;9K8MjZXi_aP=bd3HM2U@dqt88h#!7I*1b9g4%F&5Ld3)(voAD3L|C%VI3B$lrGqz#(iz!mMY z4$EBCLR;75>wEXU{N0RHdpp{FCX~Mox@Ng$Oyt#giQZ$72u<(PO3wt{N z9^s}!r2pZYz;pBatoBT>c5#imIp(!n%|*$?#!%2&J=4RDBfvG|Z==m0mZ0DZ043Z< zb@d4e`LeKx^ctRnH?tN<1}s^UKVgKno%igfuK?uc7de1Bt*B^eJtggcZn%YWXt1vBF(VdaQNRhGslfeYp`khF{Rbc&LjwiWaEcsXfzM4(F{XjCU`+jrbPj7rN6AB=%nBeqkO$34#*!QVw!L{i^J*Rp+XTC-B|~)KQBr*%LkF ztD-ut7{<@OZDo`8)~84~L?uOqJMbfes$`+}Hbjar8RGyqDQ01whzx#vy137NNw$d| zV|)jWn93w!bRql;&&9joUm5`4fuv8~Q{KWj>4njn@swS?F8n%{w8-B)VCvVXCvSjTqs4H+t=;|_>@WqI~_(0M_RGWSfsDR^kg2!U0x84&e+}`n=cFnb>Rof?Q z91j&VQ0b!8Yj))vi%FL!R+;D}yAT;V>q2Dt2RA0fdE^K>{rVIz0jVLGD+b>*cgLiv z4-UnE@Dz}X)NuTzP@Ett&$WEs+7*hgyPfstttY6$VhTEj!GFNg{x#n|#DZK+Tl`Yx z<6QF@QIP?QlANZQXPWScBy@Zu-=>WpK)+?ye;wqE{8Xb8PrQRAo zK$H_VEUMKTv;%krzusHJGoQdx5yFA)oI93yPE4zk_%>EFz~dKVuiB`y_UU?26G@ zg>iQOLentYB4=HXV^DEs+j;L4R6&Dk=}p;%HWZ8~P#+;xfx0vLAu-a>rtG97G*8`h zbd2X1s37loYi_{cJdF}xVZdE%<_rG6ld9|G)0(Untdl7+f-i7qW#D4AOOjfc0}cdn zIzdt`Pw|#bxPMGvG%v4lR8LN~5%4}b6B)K3iUC3;;Ef>#a{5bICr}z5iPSA4}>7QTtLr>*o;R&jsA7i>R8O%f@ zS%A;^-S(XU}BF=JoenM_&+csirO-kqoOnP=(=lMFINx1 zZUWu2qobrxW)AyT$&d$5ziLKdl)u>iZJ)#GeZP!WWA8SCkU`Z7$oRO%5JmW(?0ZOl1L&*^O%_ zwcZhA*&?MUr%I2}|KqknZ4VRcvS#9~>Hb`8Rw3GgyAeqUh$-mH!n2_QuSzMoM#F** ze+}3?5P*YW=>GLr6&FBGg0TyvcLSZIyZVO&RK;2(xo@KM=dM%yupkoXi`M^s)Zi+M z7}1TPiTR!Ox?J;CKkIbuCtiqX$Eu?FV$>V0AkayECSZmGyg+9ciZM{56F*}w_Cs=y z3;sz??Xm>a(t#(d!X4VB)77^^Ubs3w0cI=xaMo9!$j^jen7oX1R)KcVKg%qqDwob7 zl$+K+^2uR9P&VlVw-WR02;ivF=?k3nK%62D>rV^D%G0?{^z~Quan&^67!rj;SD#LG zSybs<2rZ?`Z@TA|6Bi%+>Knx*c^OJHrBS9T#&m2B{X2 z<)x;z=qQO6O0xG}!8~tBZP~zUg0aKL(;{k+z`nW-S2NnK^-;C6Hxfvv0I}Q<3Hpo$ zqCshIVmB8doZzu3!zeug$MaSA^Atg4xA5BT%IVPm4ib~Yp^xs@S}O#N$l6?W;Q?ZR zFv**q-?v!b>cmD{zf55lX|FRuR9x~c{az?JZl93a-a5!&1z$dnv*%#QF^Lyaz?8-e zsR}=M(=~WwL~FyfIPk;{fvlhnpX6$b8vYJ4K#<6IKcYb4re zUp0W-Zq}V@o(1~oL3*k-)D9o7V!xqux{%y1cg(B54?QL5Qi}|pKl<%3H8=;N@O2;c z&OCoxz)ioh7ihLtrrd+^j1|{ZWolZGS+_r1n@q7F40BrX{#huGrdJE++mW=_6AzGO z2AdQk4LpB9gd%8vT{&iaSKq6NfOlBVCtP}Yjn8Jky1MMN%7QdcJ2*XqK$|rwrZtgh z9J#A;LQY3%6#Dt$+QlcnxZArkd%>M!eqb$T*DSJ}b6t9}^&8p!9elm^c!FYzfvTEzvpHFNLd*v-(o_Noi-g_|qzX45n zO4gAA)7Q*dKTXOB{JBKms>C9g3RiTSsIG|dKk?6{>j$r`!!M9kK4`@IFN}esS&;*c zN|vFA$!lMSEC6`}u=a-WID?cI>ukXCqjdRqs$C;HK^9LCl5YBgb6LXI#jHWV0XxJTe@AJh*zX-_va~i$<*`jq= zNL(=Ji@iqB8bR-Klhl~t_Y9@;|3Yp#Ds|e)zG*KWD-k0(<2F}+JhaHV-%5YTKkWhy z+_-5K&Z=+V0R3fV`m`woMC^ZAj%Gz(IWlq^rz~@OgC$c4=R*aH;Vv6_IP!)mZVwBB zQf9n!?LT0&)^DQsY!&Vy)Sj9m&SY;C-#(`v25lcq^+5gW*L`?L zyK>^ArQP(DxE2*t0zG@{PN1y$SDCWlSo^jo161(iqZiV86Tj^tE^d+qP`Rdsqrf=S z90X>qv?I$lS)ZHcNN9D+`+Oe$ht{6dC1dtFxzYcv#u%~aUe?p0Y!KVAd|_W50Bp9{ z*BuAu4c(1T!1(${-5gZaHj?IWQE{0Az)H$JF}(7+IVHA3@1OYkN5|1l^fJ}+>uBDC zoFz+*KbLCh>03X4fwCB?^&@8*nPMopkyYsTDe4Nb61sXZgtJ3&ZrS3U*!&ON%w=vk z#3n@SM6F-A>tSo&V-6SUIsBtHgQ!<*0z2C06F(_kXMD2hF ztfx7ctcCXc5h^!cTWO~-jz}c+_J&oV1)0D!1t#@hXcqJa3s5mO8>$%qlFapI{cVz+ zBGeRhTmRSXJVy`|DWV~MoCw7Ayv!E?wGuDT1X!}YpoOzcuyDSap!IkY5}tY5DAWMH zylI-*Q1$_0=67-0G*WT*XsW8UJg&^Ww4n$rKv5Jm|xfxMyZEwDt2APdzSEE z;R#ubY-PY1K!LdfW&0E?3AH!evF^K>L%=KlJZG&TstbuJN*ERPm(__xkh#vq(!q0+ zzyAv*g7)0YT0CG`{i~lX?y7ZtQvy#z`Cmv7oJ$4jMPpM6_n!rSQ0cmUeogxA^zbg^ zQy}qe(yk@U3xlO9<0p#L5OWZ;v!u^*QL{FlxNCVDVXrXV>VEWdRmn!b7W8BqgR#qRvwPgjB*L&rndAa5%EnoBL1POxaJ-)rxEh-aTLZ7I1B zlxx5z6N)iTL4I?;wl1yQc}rRs6w6|1!t7Mc4Ya_8q?}S~;&|k_X4~Lin~rJBAfwM_E=uAaEM>6yyza zDEh40BgItefjo`wJj$bXKnlirbG`HC_gCH0SG_IxGcBwtHs;4W4Dge{`=$M#!BVE} zOf$X|y5jb4p`15Co515VMgl}X46DW!TDs%m`-Ge{Y|A-7TNyc(163#3D-pT*DgRi? zArSh>6;R#mD+}KNb9WaHcaSLlFHS%d&J1pQj*-l>wqVaQgByy= zkQeggd<~cF9wkIov&Y+cPK3Dbmp4APTEKTx&U*_T*QLvkWki+mwQA#c+t+o$Mq6(^ zYp{#B&r0Sy<-btd0pbmkJ~;hQ|GIODWuzQH+dZCxaJ#ONU|*_-5!V`8hac?se^ROe zqzTr}@0z#+(0y%_j}F*=f*;#Rws$!?1%_i}>T|xBg%(--z#cHov)jNv5XS@`;m6un>Me?tr@aHR!X9^!yYC1Y&gu6a7q4V*TN5hpVXmT zwTIEY!taCXy`pb|cvqxHLqlYX z7IFt2cg3EgigsC1Kkx&Yr&^%pSD5suDlmKBjN z%=8^p6>YfOlRTt;KrZ@vOo++QsBYpap(?Srx?9qeiwj#=ys@veTeoGYjHq*TU%XlzvC*gP((KQ|AsCw)QE# z9&v{WUcZ0QM8q3Oo6NnF*u@-o%Bsqi-($##^#P zsJ1}nXMkg8J5yGN&mCP9LkSfE99j0P$YdZg@!5WPbDAbh%33cW3qL`!Us@^YnKs4k zq}Y!R0sY8^q9S$Qd6*&K7jcW)XscKZGfKuH8bu*}caLRV#!K1pW2vCY`jiLt|C`+x!f zzLs{mH%J9vn+^hNH;}PVb6jc!QaZWOU!tF)ti5X1B(-{tD-LVYxgT>86^@&Ri~~{P zCdRtj?xGD$JwbWdtJWBr2`61OQz3c{_}RM7nw z#wSVE6#4iof{{*bVEK>(?2t|+C)EMi>MV`LUmTBSj}XXPtXx&r`@j4%b#7QQ7yIeUS}@^} zXQ+DoHLw{_Q#E*XQO;}Zdb*3f=~rqAORYBz=z1#tWFs`FOkayu6$Af@|5We-`Qm@M z0729*Vyrk_>>4?#uh>z|nutK{aB+eA1SVyHbEGN`ZvN-Hw>~Jomto59`0`dwz^YZn zrV2YOZerm*sn0frvt$|0pEGxhx=@OAPo4DN4(}dnRPBN*AP>BZVbKEhRC5{@bSeV9 zu8mSQj!G{a5?fiNavrp3zfS$QJd{7m52n$j>>e#~c$RVN^_S0l4e&UQzKiaifzCvK zb3?f6i#2e)JNTjfH(3ovXFH_0nla=I%vX%ZXKe)7ff}1O)kUTX_*MbyHix+Q7{2)r(3Pg_cdz9Nt}pnWcu(%r)*@f;ATPY`UVq z!G!hytsH>4L6OFzzH$m|=&zOz1RU~k!Qlp!7=YSyn8>{F(E`cff1!L+i7a>+z&bD9 z2W7^5-95L=bY1W{ixRzzs8t7h*;~v5Swb23Sh!jf_PR!Blk5sJ-3xVYs7*a3PvWlNXbC)J9}0*2EJ6N@+JDFY9C4mnZOY-K`xa<7j&92Bh*RzikLTh!4l4NxXA$C(KrQij3U$*jxWR1J6GDYy?X=mhC;e+kO%~`zHmSLO?IL74>3?$O6T8s6P8C(hL(#AxcAc0BOe4*LIPpk|G9Tz0pDXb!W0_PuuU@NF+dhOL7H5o>fe*_htfQoMB$h z|6AR=$!84jqe;Me>DE_h1C!ze(b}H;@jL?4)=UYuBO9pc-5+f$I*LdvtB|&97k(fa znI>2oY3e_%kD6*q-1@O)vv1-;eXsC$^cPoi(R-C#Gu2_qkHzpyt`?G~KK>lm&@+K$ zHoI)C&pG`Fin4zpZq?d-h_y%1=#S#TO)OLT{L`Ho*nfNnm3Y2=Z#&%yxS{zn08M#8aM9-!8_(s)?mAzWj;^Q>4ZQ!e`T3Bv| zJRsaY!zC8XytO_QIJZ2(q}b#7GlwQU z6gFfB=nK6*tn&1g2?1dF%;6p%`QH<(a1Ewrb?08AxcqRLb6xJ^$Hu0DM!p*O_V&!T zHkU*&0SE60h@hW^Uh2+w#RCKm)ka@+t`1txJG9w)C%imsOkv#SKH0Qjs$feyKt&XI z*L(@~#_Xk^QmvoyodZVjHYff=0&)RYu`Nc<+Zhq91#;+;f#g!ns<|^rCXy#__IF}7 z!4^Jqo4m(gw+L`1<*;MjE1Sae3)7aFoy*E{$A=e#g6EJb+Pzn@=6wlwNVV9^_NRy| zcUZ;EvFckCZwz}gJ`BfEw_~Ydex&{uAm%cOQPlLL!ImmmF$SKvQ%v{sgX;v5QR>Id z9nczx26cQwXGaYl`J&$u;dCyu-1dG~7+c@eYj1~6%4I3knss%BL@bvi??$BfO>Cn< zl!+%F(bGK*%oJXhiHZ%m!(qbz-B9HvyJLn91N=GYl9t}WszuAQ>Y_bzofIzGb=jlCQqL{wdd4cF|;$Y3rTHN6V%8W(%{vXJ_F)lowExQ`0sH3DZ)={UtBQ{!TO{O zBLszFtjWGg1U`(XtZ2GN(9a=aV`6`p3h9T_C)GMkmHH}VzQIjpK=N+0N$kNVfFn_j zDJ}k_ss+IoGq@+=+urQ$=M@kk7~j|xq3cz#7ob)r?Mtwi3e;42p~XkzMajFXcr&}L z^!E0kY|g9XE6^gNN323yY@Iq<=aEaPd{~?eG=ZZ6*{u)3&e*rt8(BhN>1Zl#UgtcA z_lRTA1S_3$4gBlaC<2~MfJZ51&v3^Ywe2)L68jKlW(DZ01G9DbC04J0Jvi7^h>(A& zs<=jR^r<^t*ch`30dhBmdTN{V8XHgN6N2#V%aP-uF=(e@_mEOkqYToMi@sl}dnT(V zmzxye=^EJ4p-4J4Q=A(3dLJx8+$XSVf+{&9ycP(h7gS{lXa&ZVS!cW1(j4k8mlFY0t(LmO0X~rqSfMxi#!H;eYvax0~@9)T5)aB>&@5 zs|n%yL9AY_MepPC9YqIN4wmds*G2{x{a<)3IBy~bY$H0Hgr&cLS{{Nj&)|zbC%}Sv z@4s8ogjD<^6ONI?0GkY+6;$Rf!v|M6pE@mQ_CI{q#B1GX@9v33S?>rn8v4!$AgrEi z*LflWetZ|@)kp2njnP)ggO`_Sd_HTv^va!n=jGrgbJK{mTrns~icmhL?_gTef=p|0 zoL$wQjYyc&ye)s=6BpWEo2}QXazrx$I(S`Hk!A-JLVaKb+Vu|*bo4qN5A#+*hvj{2ESKZoHfZ8EFGLJ z1I2Oz{mv}DK+-m7lXRLn3M&2{FZGr{ED?oL8DO^{)nO4Zf1y@M0~Bc!uFds+L%-3? zxUkZW#jLb&we5Ad2<4Go3q%^}lj_s-B;1bHcDtbGLeep{9S3BdmcflgdUN=1ZzrD2 z{NAHm(89vGbuBLSA?8D>9?y8l$r6Rgx}yQ^8i?yCjVQHRH0x5gYs1eN6|_;xtV_~8 z=9IDL%dT34>wMNc(Zy-%bPSPK9Rzq5lXKAmH?XgPCcCe2UY8x>Rur~|0s9)uZPK`b z0r7u5gZfl%BH2En4?*8&pfSsgCqlD7O^8)yKxB#f3orQ?zKq{boCbyc7k@(Bh+vb6 z2r?5Q=#_rnG^06)&mIw(zDsdN?EE9eSC`A{Ixr6Y8kp4z#S<8ewmMBU#*alVn0+pt zzFTCZ$n{ly0i0C#Z;r)W`@H}zS&9CUR$ov86aNIfjQ6BY3t)he3hJq}d?5$?GrMpg zdjby|FHQd#wN);s4{G1?icm8A00E1*u4_rBYIJa!_SpIJPh9w{ax!`sCs5nj4Kfw~ zl$!i;{S3ZP@tIQfXxgwZx-$KH6$nK8wWF89Lk@G>e0k}fCN>wcKGtMh6dK8m;ONIi zoo(%*8J+$BO==kr(j&42ol+eiD_fB0md}hr^}x^_CAn*=;U zpzvK_vYc?*jMmBE{1%epPEt%t)>@x23tHzQC+}?I(f2t6z!FEQF1?zii}{V{@2U4% zQ{Hs?`;oT#l4)_2#xBuM>(RDkJ?W+O%Wt55oUuaS-Fj38F)wP~sG)o6n4@1Ut30)% z{8<((deDQ_V(h}d3rMDLv2G5#mg=Uooave{oo5?p3gkJ*IU}xIx~pg-r_qHeHWh#vlDE3Y8QyZ;Bi>RttFY> zfY|)MbJEU&wK;prPHgwrIdCkeLcIZUNzj{}tt?DJe`_l2I4k&L?J)70XSmAQxfHU+ z*c462$xG1M{2sg*EAs~%rRebWx@nE~jGS*=E0f4FTLm@GN3B~+LSed59@FL?YA_&7 zPyWl4WFiYYBrKL2JIG-QS{t#SKFS#oMl3NeCxUpu_{DUP92Sj*4*1`DO{`QI z3%`zHG!pu#b%2DYZ0_k#opagSVF7`SB=(v&XMUs~%)FR;&j?yn(L{mCF(f|reg$)x zjly}99KBRFTaHC&>f)@--#!P;sSfZCCib>%M;a6tQ?rj0v7P^Bprhl>oCi#i{cJ|Ocs+|FfS<& z>SuFFCE&Obq4!~Z&F?KQ7#^Ug_O033(2U%nMKMA#<6;XMj>$c{b0 z#LY5g_RqQ_R$c4;T|YtnnzYjg3?5^47@L0mgDsC*P@Rh5+{vWY{|Y1BCtTeGac{vA zV@GM1MOlxl4)2e>1OeaJBy=yr8)yPS9 zp*8$^ykhK7hBoX zM>+ym;9)M~+vHYQ%v4lLcTqc!i!0qt-2d1Y+r}J6V_eNBb%-g08H1n1%=jSO>FPkY zC2}EE0cm}ix}+1?X2iao#wF~PWy;Gjnx%TCNH+JOYN;*5Fui_b`*1c*s02FHB=L{pPlp)Oh9^t}q&fj9VFw9UmT;-UFBV)Y1Xt({1TB?s4Q@Li+W< zUQvieW^cEu3{qe-BuX@8Ya&*B?)MRCn}?X_+nT@v@0Dft7YYkJtyZ6ZA>d9lj!GR6 zNr(m8fJBv(U#s`BZ-RsO6Pc+|^&5WViGmTE09**(IS&rADOzH+ZYj4u+hGGe0+S23 z^6^Lee%IE;8r6O*mcbBYZCuU+A92Yo?{ELIiOq_Xf(6`}+4A)_27gBCJ3CR|EDh>= z!>RTs0GD&BxRt;Tqx|u!mSPQur;#~kIJQrwbZvXaKzKZXER@Z$+qkbR$CH0V%F~d|db1k6=Ts(Y$alEds90 zgnL9hjXq|qU(r#$Yl&wY!f0^=W{qF%EQQ~r{G+_SL4?gp8WUTs8Th>+{Z)$%Q+SCz zj@;g+3+RtCdK`_*R>9m)20#Duk}I}76rFkxj!-EoG)uaO7Vw6dFH=={Noeqy8gJ(vnGiY`2uU`(@g*=rM`XoxkMLa2+g#QV;P0N;`ct}Gh^la_JX zb5^@R0#qb$Z2Jy2=xsw~w9`x$KbJCyNvR!OoJ(mW&9eg@ zucQw$$>BzJ%!}fn+(-uJ?@RAM#lzAn;pDLlIL!236Et$kpk4M0Aq6%rQAwaj(KcCH zx!kk3P9%E7+<4YaivLtM`FHBx45WXBeXjm*pxgkCFSX^eNcLjwC-V=~!1gPAGKZKp ziFe&&>acW*W0vIp$lUw?-FV>J>$kff8h@LVhrEoElDuD?n`FyQycQU1wpI>g+kos4 zNL9hj-!46r{C&DHY!BiHw|<9^PCv@}O`baUNvbxpU-DiJr1e40iq+p3zn*E5D2fe$ zB<11l(SI?-cl;9+PGp?u7UT*v2F}w(&{R|{uVTjjctSqvhr!n9nBQ!49m!>PJ zZLu}&s^0b`*&VUUtiX0w>)b_L*|7`4pOic$MI*8h@32DPW-$;gsN$U6xF4$w6!B^T ze}J1*&xPYQ7DOLy;UPWZ@ns7F@TmW2GmO}XUgjrm2?7)0q>p^~`y{~2K%R+KtvK@+ zdi2LOc%)};0Ley9vOP&gl87Ein>uk?8`=R!4xiH!*Ta}3C7X9{)#v4b)qvR%J3Hf0 zzk{C{_QdH>Foac)iV~Guw zS%QhDt@_QeLb6}Q((F2WT_BikR@otI^L%S%w@5lh; z5oDM*16ZZe2ziQ~R8OuukP0&2b9@Sxh z0WCZk*RHlkJpFp(g6#iB+YyAC(JDJvE>b1inMr^3OSmz~p6#u#{eBd!Z2$ei3V>`r` zqqdCJ@-TQY?|lS`+0)EgGyb}2s2>b<=ia>)N1!z{>c~-Bg}(i*(bDkq87C}-e3MUP zl!`2~5o4uzKh)?cll_H)@;OtVQnk%UqHoZg-s7Pkit-#fu^_*=i;nGx zrJ|ngk85;!s*`lI7}NvIVr_67xtbtzlCcI(!hi+ND>3CZcfZ?;apmQ!3y)YmLN%TI zCr}!W-RBdS#zb@+42wyv@gaPT2VRR^HLT>t-~x?3hny}s1pMQ|g*~iyS~9R9EXb&7 z?~u;}jmqm{*z__@SfNt5{aMsGF&AX)GqM9nz$fD7h2!qzFC$=!Zw0<+pSj)C$br=d z9S!0uzu>PseeJ(}bBszlo;M_=fItw_>QkEuntE?cQhc*{E9n;E6)Y>4Jkjx#Deof1 z@K*faxk#Wr3X~s={WBa&Z5eg{V;3Ewi5G02xo85BP1xB6_nX+;k57a6Ywg7Fsm(Ya z6ka^V=aI~RZ!*+ohKrI{rQAlMLEFzUZPh0$4qn9hD98nIT>Ji(+%S@pgC%B_6&y-3 zRqlgPWUj5;7mGjJYc%!Vbew7EoAm=v7r(s+8pf=H1ov=4JhQ7 zmWZd9qS>kAWP;)Jt@QC%#TKxG$6#+GBSg?qQ{%SlQfMY8OA5soXCPyTpis4I6cAy7 z3u`X_nZ-Y8cSXh3dKHixHXZFm`Fu($1VAIBOEz{sK+Nu8Vy+R)dO0=!J7@Tyh6 zcb08b3*i43=Ayfqbts3kG-DGvm(>ol)ZRDY_zFFjMC{jh{5Sf)EwKOj*Jc5M08ZCP z4e&~E0)0boYILe55R^&{Nus78^8go2pnSWC!r|w$^S`M~dxWZJQuw9w0{0%`n10q_ zk9z?ciD+T54mwo-*u*lc*J;oV&n_>^NV{NF0?=>Pnl=hbc|vqZ#MwSI260D!_Ap9N zzbDS~F|P1YuL&1zYK^+hWwHl4bGhHOs*`qf2F;P;XM$a1l3Q859yML+(m|sH6I3nC z<{8T%FgQ~})~vBd7~B@L_evGMH)t#P+$d&MbQkspD`tM-ZG4XQ1(2SQ%uiIq!V;vR z-7#dRX2#(5-WenGt9A9~UnP9)#h|r!{yQLMKVs>)oG*+oM-N9WXpJ_@A{<*!Bz6Zc z2smzs77fnv{n#xBEZ|6=MV<}Fbp2D^0J|raEcLa$pJDxmrw>{;u-jKB-+^38zS_;? z$G0WiBq;-@lv!+if-_ZPM|LC5yD_MxBVtIq#lMMNV1k#>16@OAC*lUlj7NB z%Rk}Fmt`3!AvE}UAX@Dh8qSR=%-mphX27PZnUV*m`2ZoPpi)P{RZzaDQfi+_Hf5FR zS4;bR0guH#>(Il+waLm)nB+s@LL)!Ea{~{&T1P(}d2q|eHR@urC3P7T zEd<{uny3HFu}vg+`YLVedUN{D+j($d7x=JZ*0QMwtzqHnIvsN{$kg6KOJvagg}MlO zL&SmleVz=r%i(w~>7udz#B*n5Qh4H=tG39`52 zj=wb)K(v{S;wXPvDdhlW#0PlRyc*zEai}dpWPInPbA>;umA+>#Mk2ba)GQ`m^G_Fj zE#5pWgTUOeTybCAR+hwV+hG~MLS^}1h`jDt3QVi_3Gye0LARJA&7climt&2j`1Jyy zN|P;pji13%&?JN7%=}eq5FORiinW$Sc0sRUTt9WfTif_wJdc?F-=sNBnOGL&XWrB2nl`2j@Ghd z&c&P-uhsCE#1Tx^WHBR@9pSE@d znO*UgjOzU3zfaOy#wc+5W?FFn3%#z>!Y?mrB>x@gr81dIQtBR%2{0lYx&1EXtZ$y7 z708tKBTM_Xv%(=3?(W@i=g(oeBzudHeS3e}*luzcygJp5T@0pi_h`4(0>4jh<=8pL zK|T)`=E=sM44_OAeZz{c-VlQ{l*5Y-#s?X zc*|nu+XLqNEY&sOe5BXZbdNc-J6QNoq8Qd~N2|kjCh-VWJy4o=WleGP%oH){(dz-46wU0 z-9@?rL^3y%wkNXA>zpmF-(gwol>g}!1AnN6Gm^CFLIsN1!*guRfc=TL)xfhPtNL8^uUWtWZR5 zw=_ou(*bku*stLja?<9cd2nvjrKzqnYq=0i-39QhHIcWYfI)5$(&MSfzJ|LV<{0LK zv_3|+i5a#6NMS+m?O#HSBHHq=c}5ar?OEqkWZILl(~}ayasM0*VIG(CYT(w1$sh$wg~Vi7D`F ztDM?fWAb&hQ)o#Ax{1Q!j{EB-`Kkaor8C}}vBteAj2p(4hVHwKgv6<59Xs}dHzo(JsU!$Mv66<?&0-R|Kn^8~P>=m0TL*o@F70R);f-cv5z8`$pfBX2`23;mdm1Z@9cpA?z z;ycJ(cKewXvw!@a0T_F}lgeHhAjLkgHa>kK7$}ga+NN+K4ROi~L`FE5Hn=nX$FdBC z@!%Y73t%PoWx>i)Pq!%}8(D>>UAt@)eM7iH^v-=DG3>U9MJTzHW_Fjt9reVvXI`P0 z($~XLj*tq12-T7*4JfNJyeqoLKc$F5#T0ya=Mx>|@j}xBmJ=ckJa9;pb6Fsx>YL*D zK3Y};Bz)ar+>0^fI(N%@I7fka{0cTh3ApChT#yCC*Y`d>A)?c<>f8(w-y8VP7e^irN7An<$yN z_J5&((Kx6i{tefL1p^1LJK3A6`vcDH%=&hMV&g+CtkqCP*Whg&%EZ+%u?yZaqC=Cy zmP^Xu7pfK_|FyexY6LZ|ZSpd-3Qe0BsSuF?%up^oUsGRxh3qhvjgMc9h+Ky&S%z_z zRR?4b<0nvRHsl37R_U=!GjGg!5^h@`*5m8!z9Fk7(5zm}0@;92h>?}@{8zC1jyqqn z9m6kJwfT$SanuoF5@bEkLk#qirPHZ{HMg=xb90h~m{x5o*O09wV16fC6~@mar7E7G zz0AYYghfo6-vA+NoNK_F#9dfEaQ$3?@ax$Ruo?$=Pg!nrxke+K2pc$EMRrF0oxyqp z$FIx9FbUrj=u4%FNN*g55VN@YA?Y<@)}GhjI>|Np{;6EJvhRT$Qy5!{Vnq)7H~DEh zSw`)7kDtJ%*KLIDZFVj&6@6vbfKT6kfdP=|u7LMGn1tpA>^eZUspLFmb!Ok; zQF^-ZQ*ehY&VqSvJ9i?Uo?px8Fpk3SzkeA4KqLKMp<2k6DN|_54^OS3Z6WDHu<{;E z#byZz$ug*(r%%fgwbzV|$kPwQa%UBVg*>z%J><6%l$|f{J=)a+;|w>SBw6ODWc8rn z7Yp6*H%fA+o)zUT$m+c1;~X440*5cM{1Bd=aH22X=jQetBzcN*OZ7P*dzsvf*)_#@ zwNrk1Sf!7)n(l93tFMNn%iW4sFhqk{OAaE;89X=d>X#ywYBLex>35c?rZqMvLDEld z6uC_qkdeb2W@R>9d5<~%TAIHYk+9Uoyap^0S!+=EV*R;YY=Afvi0Di5sLd5lR=|`#FGUqDW-S z2_h{r`|xlk6?y9*j9v`&hu_ed<0kk?Eu z=4Fw@^|rXBpU{`eZ@vevz%?F+#40D9q6G+Ygct&etsEl6^+ zvc3ba=K7tf_xuGrXv3?>au>_&j!t>ovzxh3GN_KRxe3shKDEk2tRszt^&yc@(ShUU zdjx4-;k%@nS#&L~LoXvH+gg^mcrVk|>$*x@FSDu^O#7)rQzIZ2K5wbu7bl^fnJ{QD{7jZgH_$O&}7;y@N2Rtut8w z;&Z({<(&n!QB;Rp^0zu7UAUuG6swKr<`|z3H6mDh6;bP+{3w?ewfi+Ij(GicLxlJT zo^K$tH$H1FZNZEgu}Ls5fJ)3bg@L1>EY5~B66e?jmggy~`rt2HH$9EGsD*@&oVOXU zP1?xsYiD<^SOSJ4)e>Z3q@7BFwO`MW;{Me|>Xf7$lej&0B0O zu7#sYVr+rPO;vfS&cR9nUQn6J3$f`JyOUdL=vsT|NEpy&r9ElCLCw@pqh0Z-1QePC zdfo^6gSz<7P5*hM57;#%zF@VwC|+zvyEnhA5V{BUV@w&F4{neV&K*tQaq{hRdLR(d zf&<*n*cjJWdFG=J%Vo(inGE*6{=#39u7!W_@kPZifkTEzzaj5E_97dch>jnb_C&3p zCmQd+L>d%)zrkMOY(`98{0%Ca*ezhl_8wD)&U;@pmBeIa>geR_f$R_qBDKD;bh}nZ z7*na|JALIYc;oYq$;{dfh7v(LtEAkV8~;6*Bzw%ifNx!KP2s-FU0U!O2d<_sEzn(z zrA@x-%fbX>;Gd-Fw=3SXMc@Bh{%1cL8m7%)=>RK#^ffzOp-Rx(_U*X8nKe`vT+u~~ zWdL>e%Gv=fJX#yEMso)c=NK>h9isLxq&J91ZJjLoF+nsDA2f$|I>v?XAs)TKmagbR zWLBxIpqGbMVCEk9OV!K`4F{2Zs`=q3!chD9)61JDlYXiY${8{Y)p3<KDQv+MR{mGyX%3SeO+1?g8gKedB0_DK6L7jCt$oDluQqTyFC*-tXz`Gw zQf991l)0f6ztbUOi~_|Mj-s+y?n>)Op81k z6&m>9?Y+=z+bNQyiQv~vpr>+@O^UFjG$7u(XvqY91JiSHduV%u=#f?%OP)KM8=Dpb zhZ#?H!}HQ9uJWzN=N5Pc+ZLCP6)3?PE;ZJ!Dh{vsT11A1Qx{X1wos)*d#WokeZLKr zL&#}lY*ICc78WQE?BkHb@$Fy>6N&GzEXb(-JSNKoVEagx#dpL0590)bbfuTS(?Nidrj$t-X_%%`pJE3*P2imOG1CF4yBrWyPNVA(cPRN#&bmGdn&OyT4Wx(_V>{xl9~Tk2lj?I0AoBlh^Is(w zG~HKUkwURFwK#o2ZDw&JVosf1VM>T8sdrUwuq0e}Tq=*5w4-l+oWf|fxRO^szV=!B z{(a6!-1FD>&3h6GCR2)H6D(IEi` z!$6S^5sT}xXr?O8vZl|@->qPLU>@BNcl%}?b|(h2%1l+M6GC%2H2=`9&;JNaKhCU(x52nrTysRW`GFO z^)ph+e0t~n8OVTR&!uD*?AVM0@)%WxADTPlw7pir#>!!B^1@F|DWe!2wvGll-=D?xl7Z zmSme>8RizH=#Oe$!QB>SWA9;U)&Yt)V`K`}Z6kj*O}x~KD9i61RE-+0B$US;oJ#tj z`+bcC+Iyu!04MPtV+p$O){e=tgOyNi`IZzrpnRzo^!E*E;ZInK-_1r{8>2P-<^P2X z<41n4!-Vrj9(R#VF%(grTV0?DS>)LJ=qu&XSjW#lb`KInQ1*O^?UQWbgnbGKYtC;2 zRl>8YwU_s?P_$h~zv>x83j9V*;*!ngg0L0Vgs0UXXS-tD#(;5-e858&gqDn?)Aq?u13 zRi&mKnh-W*k;?lgk8!nGFs}6UY8mA0ndF=a?#pzoje682AklVKg5NeJ%WoSLlXkYdP1 zP!OPm(^fDQWPm#YLn+b6@NXBu0vVa6B8M0Rn0A=p(7vypzwLeOBA#)qwaeDX2y?gk z6q^+mSZb9R&iP+WyoYg7zx^cj3X~K?g+1Nk-k~Ty3S9O)X?e&EI+DmCkz8e>4550Q zFbB9|{I!Qf)^(3^RnYh}qdbkXOh?_lW9CX|RQ9_a9H9b+*5>5$G^2%;$cZvAXtq&s}r~1xTQs?jEdYWR$;7;5Flj zqpJ;Kt<_CvvNU(8KgYxpB?i-!snRZ1TO?&kFx#A4mU4wv)(XCl0tHe6-eG&3zRe5b zL#bKUfF5X>)nG|{Zj)I+k>l=YUadaD0qU76n#5UAyk9-XP*=`e8u&^O7nq4Q;dwMh&P2$2K(ps_JcR&mOI=S%Hj&nMYnwEUa48jm-+Gcm0Kv==n8KGuJ5Rc)K;eUm`(Ie!Y-v*`FDLfqmAhZDK zdsdVhA1^|oJ`iBzEz^C}QV#2PV7oTgJm)mNK}uJkDwj?N(b7ge-gbl0sMhorCkCN3 zzsYi_E4H9PCQW(pO`m{+$^RN=`0(P=(M=%?Z>_|egLanxv#vufbfcrqm1Z04p{y&~ zZ?e#R?{1j7hHGjlp=;rKLs;LZxc9{WS;UFLg*g+lUR~&#NEt*jeDEw3{h1SwXTLz2 z990r;t0}9@iJe6u9IQ2}vVh96%*Kri$5geGNKk9mThzhN6eOM6`B4=VdSLKR!qaolZq#rK&}et(eRcMApP)g(Ub1$DZ(AeixX9c=nnrDrjz;l`V zVokVB2KIa1w4wRc&D!Q4MCJ&@6!I@LEskgW+nF%_ZGh4&%Wz4=C_8jcrMQ$qqy`2l z+x3MiMY`44kE$4r@m&26Xo9}t_w)R_G-&aZ3}b%tL|F<(h6K59DTd#DnjuB_idlPG z%H$#<2{=|mnu%4qjbERCmDqTH! z8VkC3J)FrM7X80_G0gOmQ9^GM{>aOSPSnX^>~62m9Y*s1?c=G+lnf>dQ+QVg{6(B@M1 z3r70MbFC$xhF)OkII#OE2jt*CgpZy3RkSATGtR^VGGa%Iqo%0IyE}Q zc_MW7!YY##dMvMw$L+&>h6PC|7djt|8N}n6BrA=qiTA;hd>JVhWND!#h1kWZR9(V7 zObM+F6VPy#y{Vl^6y5;>sczM)m#`PXjZVZ`4TVJh!EF^V^>U-$tqUd$vc&i*o>Dn_ z$=c2T5k^sF`0egg(6oput7q2Yzz-J}KyWX_C95W3V4=x$LeB>~h z&QX5)>SPF^P(D*Ak7kKvG%=w_T7E$U%m${nPjgDU4g2tTESI!r@mq2X{y-U{KvBc5 z|FhK;&@I|X4!yM&DK)j6c{_%UNH)*X^e!bweg1vlQyTHaPkjiFE_>5Z|6|A``uvlG zLa)apUBRII(6D`er2NZq6ZI1e6+12U)pi!lEOkO!6vav-(xW}BOsdV<(*KSVX1_w- z`RS4m4d%fM;g?wj`Yngcg~T5`W)#sL<~=LIJ{SPScBd%ED8f5iQqYvD=^aBpCXkH$ z)F+;}pLqhq)x7JV10$j|$*!&i*2Ex&0F z*=em)1R!7)*m?qgh-HrX;87i#n#MZO;%baP9-g0O@mXZX2p;RenPF;tuThYIPt8Ol zw_g_g_WsgZ%161V-Bp}R=Fo&nz1+Fsd+S;Ul&Z%b6s772aAy-Ezj^j||AQb72ede~ zn%JG-A@@gUs+_$`!x_U^D9!uIn)GH_#T&_@pA*U;)vL?Y=-E7q2$%>=mm$#Vj`}3B zz3k%#zHu$-E}Y1YmWrIe_%}L@MEa5S9E=$c3$p{v4k!y1x;844^7h3?<;Kn5mybUujf65Y7Hl<9aRqMFqEeK2RnVIKY%QD-QIY&I~3TeOh zfcM{B4TaenVF`*P6|N3*IvH~{d-uX}#)wu>Iiz~Np(#JxFk;5XA&*jk4iyxOqp}%iyE(0#QwL_+S9FVxo@}~6T7$w|$RWH~b;;`DZ#C#mqc@p7 zQeOElxe*Qv;%NW}vD#GYJ1J#=&mR2>1dL%WX0|l)Jud^Uy}KXlQ2q2@EPABfHPj5o znULUKo(yOLy<=o(y|=|(ayR~4&f@PBdevx`4`abGBTPT{#t}s**u`LubLdEkx3kvj z-nYl8<%qV3yCS80Tw$Y;=Am!(0XelxxPEtP1kd8j*ZN!+mxR z@~ls%33RK&{rW|qT6k3N%zW;BL5if8hZ?C8t0=v_wX^Rnb{>@ndCY6He!fgsFhhRz0 zik)$X%)9^q_~LwS6u`YBnViAQNgPbZobsSwFJIifTtvtX@+mD)E9Br^;yQ%%&^iMu zo9ki4q4m#go<=BS_|HES#DC(_@-7BO##@uHJTRnn%~Z&L*Nst;f*w9b!mT-q*V-I$ z(qlJpRsx$%tCw9VUe{SCHv5)>j!1K8_%_Cc7V z;PdGbLaRuc3^t?j{hPPa9lK%(R;0GV{y<{pw^_2kP4W2lHbL(@f0%rDC`KZ?5(xGp zEEUZMheBXZ!BAk)(*Q+_krY44x|jxgu7ZgUlefs{OfPdPE(%Ta(yZ;}T=^~U#i3aK z9?)PA{Rg5mMIk(6I!yVsNvVI59$o~etWD!tbbifLM-g=0v}w}+4!%XB^)4AjHo$}+ zm*`twpMd7@inygRso*>gpzjGbE}SLhzw`^hjOZ`*a>i9xhh?a@DWi;%OHSD zfug564m$ObU>K`1j-Fa(4+MF9Tk@S(wFziR9X8XQ39V$O|1YR2gbQx^G(n6u@1xnX z=r+nM2K4W}Xnk<1!tov|`|k1!TqNU{=cwz5z$QTIKL31bOm>7q!G{kH&->UC$oBu~ z+xp(01E~pLt32FtWdM8UfsW#HXYi)!UK6K*-X{Hl)Ex|;-vNX_=PonsoWxPc zd)F9R-Wah{N{46NKxBeci)h=u(^ z+uLz-EdGkp`0-V`5Z)O!gIz$4ys)_4J(w#gyI>XRSyukOxPp`D2qzlF*k=qmgh*Hh z7P&gmDEBU}8115Hx@dbMXLLmtrLc9ppzfarTJY({A4<=^r9nb0n~6X~z%{+I2C-#! zKA-y45(LS^C+rqPi-;oNI`mO~QXHTJC!foh0m>)VVM6`Fkf#aQj@FHcw$hgv;q6JfS{Ucq%~U!Q6_c)usG zr3yE2E>;%GB-c+ExXeV$EvM6Mj^E2LHM(XJl`X{vs`;Q>XcJcR^Q*+Q624K<{@M{d zI%^l|OX&tJP!vj?lD<0vH(wD63nUQiqDjI`+jHq<-aR1b5}}#9HK_*!({DY$j$53h zx>pZ+q@~d=?LsdXH)te~(VZ#VH}OH5W<|DCP0GTajJr%dEw{=QrsCy+l2PU};jKZZ zDp@{B7fpg9%Cy}J5lJMHB`2h>^*^0SZbYmH(n`n2t z;s$a5r?&I}Lg1Y6tN0$o3d}_Rr{neCO4JIf|#&RU>Mx2J0FqW(LRa z%$>;vGz4J5)M?PdzUBf(X&0Z(CNCOdcc5N7?;OT}{ZT0Fb|Hmr$I#?Dj2U;V`Q(RL zUf=7JkCbN^sC&C58CgV_X6@+$u(axsuwUl=I#DUrvI?1bAPr1Pu4T((iAahGM2>^M zRMOJ>)gLZy!ek!2Zg%c%)ohja1Oz5IDcJ?XBp7G%&bFk-3gow*V!l~GgJ(;&Rj0lR zd5$|*D~5jSTvzQ1HDfHFK*0n*^CqteAu~Mf6s2;P)&T>+!0ZNWnZm`QeQMHOp?GK3aOPwSU_D+H z56_`Ub#Ag>j1@X<(AP3Of1dEQ65{r(Mqe*6tqBvw9uVL7*7^^>f5bc+QsIn;s*jeL zC_G?T5k)9ZddRy1H9WPRNx%AnlZX)Q z3CS6hBWOe?O^Q}BCX;oGf8cpGfZ^-{$t@JcfXA4A%@>?BGV@EO^5n)pti!Q}L?_OG zN^kApRq*D7ukkho4N{Cyla(`q0v_#1Sk0JC@R1@@DD~j!Czf=F`iP>GIb%Naq%X?vhLIpWRg^ruWiv*3IV*6{+#WpwfPv>Ropq-8b z6Az`=0n&nCZ!VSoB`Z^K(?$Y8ZLM5YWn9@?@Eb?sM% z_~i0+0iqAgz31LO>}qZX0A@O4_n6?qdwDm81jfAAiMoKkEel|v>*29QZn7ef1(w8U zwhB0VMFs9Fq~Y=S(6{<8_-(>w+>pm~f-@poOJr(O>Q%c*i)yq{+0>QZM6v8db~31Vf&UPiiU?_fVXZ)C6`)c=R3@W9>HncgZ7g8W~?ob)`{dy zVNN_8fnv3a9LK;?JtJT+7Vb
767e657;#YaUZl#L)(ecl? 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
a9H=U!VM;i#6}xN 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 diff --git a/Telegram/Resources/art/bg_old.png b/Telegram/Resources/art/bg_initial.png similarity index 100% rename from Telegram/Resources/art/bg_old.png rename to Telegram/Resources/art/bg_initial.png diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 8992a0b5a..0e8d43032 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -103,7 +103,7 @@ boxLabel: flatLabel(labelDefFlat) { defaultLeftOutlineButton: OutlineButton { outlineWidth: 3px; outlineFg: windowBg; - outlineFgOver: windowActiveFill; + outlineFgOver: windowActiveBg; textBg: windowBg; textBgOver: #f2f7fa; @@ -180,7 +180,7 @@ defaultCheckbox: Checkbox { checkBg: #ffffff; checkFg: #b3b3b3; checkFgOver: #b3b3b3; - checkFgActive: windowActiveFill; + checkFgActive: windowActiveBg; width: -44px; height: 22px; @@ -188,7 +188,7 @@ defaultCheckbox: Checkbox { textPosition: point(32px, 2px); diameter: 22px; thickness: 2px; - checkIcon: icon {{ "default_checkbox_check", #ffffff, point(4px, 7px) }}; + checkIcon: icon {{ "default_checkbox_check", windowActiveFg, point(4px, 7px) }}; font: normalFont; duration: 120; @@ -655,7 +655,7 @@ msgFileThumbLinkOutFgSelected: #31a298; msgFileNameTop: 16px; msgFileStatusTop: 37px; msgFileMinWidth: 294px; -msgFileInBg: windowActiveFill; +msgFileInBg: windowActiveBg; msgFileInBgOver: #4eade3; msgFileInBgSelected: #51a3d3; msgFileOutBg: #78c67f; @@ -671,7 +671,7 @@ msgWaveformBar: 2px; msgWaveformSkip: 1px; msgWaveformMin: 2px; msgWaveformMax: 20px; -msgWaveformInActive: windowActiveFill; +msgWaveformInActive: windowActiveBg; msgWaveformInActiveSelected: #51a3d3; msgWaveformInInactive: #d4dee6; msgWaveformInInactiveSelected: #9cc1e1; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index d7fc7245a..ac980b21e 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -23,19 +23,20 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org windowBg: #ffffff; // white: fallback for background windowTextFg: #000000; // black: fallback for text color windowSubTextFg: #999999; // gray: fallback for subtext color -windowActiveFill: #40ace3; // bright blue: fallback for blue filled active areas +windowActiveBg: #40ace3; // bright blue: fallback for blue filled active areas +windowActiveFg: #ffffff; // text on bright blue: fallback for text on active areas windowOverBg: #f0f0f0; // light gray: fallback for over background windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over color -windowActiveTextFg: #1485c2; // online blue: fallback for active color +windowActiveTextFg: #2687bf; // online blue: fallback for active color windowShadowFg: #000000; // black: fallback for shadow color imageBg: #000000; imageBgTransparent: #ffffff; // widgets -activeButtonBg: windowActiveFill; +activeButtonBg: windowActiveBg; activeButtonBgOver: #46b4eb; -activeButtonFg: #ffffff; +activeButtonFg: windowActiveFg; activeButtonFgOver: activeButtonFg; activeButtonSecondaryFg: #cceeff; activeButtonSecondaryFgOver: activeButtonSecondaryFg; @@ -45,7 +46,8 @@ lightButtonBgOver: #edf7ff; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; -menuIconFg: windowSubTextFg; +menuIconFg: #a8a8a8; +menuIconFgOver: #999999; // custom title bar for Windows titleBg: windowOverBg; @@ -95,7 +97,7 @@ settingsCloseFgOver: cancelIconFgOver; notificationsBoxMonitorFg: windowTextFg; -notificationSampleUserpicFg: windowActiveFill; +notificationSampleUserpicFg: windowActiveBg; notificationSampleCloseFg: #d7d7d7 | windowSubTextFg; notificationSampleTextFg: #d7d7d7 | windowSubTextFg; notificationSampleNameFg: #939393 | windowSubTextFg; @@ -106,6 +108,7 @@ introErrorFg: windowTextFg; // dialogs dialogsMenuIconFg: menuIconFg; +dialogsMenuIconFgOver: menuIconFgOver; dialogsBg: windowBg; dialogsNameFg: #373737; @@ -118,7 +121,7 @@ dialogsVerifiedIconBg: #4abcf1; dialogsVerifiedIconFg: #ffffff; dialogsSendingIconFg: #c1c1c1; dialogsSentIconFg: #5dc452; -dialogsUnreadBg: windowActiveFill; +dialogsUnreadBg: windowActiveBg; dialogsUnreadBgMuted: #bbbbbb; dialogsUnreadFg: #ffffff; @@ -137,22 +140,23 @@ dialogsUnreadBgOver: dialogsUnreadBg; dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; dialogsUnreadFgOver: dialogsUnreadFg; -dialogsBgActive: dialogsBgOver; -dialogsNameFgActive: dialogsNameFgOver; +dialogsBgActive: #419fd9; +dialogsNameFgActive: windowActiveFg; dialogsChatIconFgActive: dialogsNameFgActive; -dialogsDateFgActive: dialogsDateFgOver; -dialogsTextFgActive: dialogsTextFgOver; -dialogsTextFgServiceActive: dialogsTextFgServiceOver; -dialogsDraftFgActive: dialogsDraftFgOver; -dialogsVerifiedIconBgActive: dialogsVerifiedIconBgOver; -dialogsVerifiedIconFgActive: dialogsVerifiedIconFgOver; -dialogsSendingIconFgActive: dialogsSendingIconFgOver; -dialogsSentIconFgActive: dialogsSentIconFgOver; -dialogsUnreadBgActive: dialogsUnreadBgOver; -dialogsUnreadBgMutedActive: dialogsUnreadBgMutedOver; -dialogsUnreadFgActive: dialogsUnreadFgOver; +dialogsDateFgActive: windowActiveFg; +dialogsTextFgActive: windowActiveFg; +dialogsTextFgServiceActive: dialogsTextFgActive; +dialogsDraftFgActive: #c6e1f7; +dialogsVerifiedIconBgActive: dialogsTextFgActive; +dialogsVerifiedIconFgActive: dialogsBgActive; +dialogsSendingIconFgActive: #ffffff99; +dialogsSentIconFgActive: dialogsTextFgActive; +dialogsUnreadBgActive: dialogsTextFgActive; +dialogsUnreadBgMutedActive: #d3e2ee; +dialogsUnreadFgActive: dialogsBgActive; -dialogsForwardFg: #ffffff; +dialogsForwardBg: dialogsBgActive; +dialogsForwardFg: dialogsNameFgActive; // history topBarBg: windowBg; @@ -163,8 +167,8 @@ emojiPanHeaderFg: #999999 | windowSubTextFg; emojiPanHeaderBg: #fffffff2 | emojiPanBg; historyComposeAreaBg: windowBg; -historyComposeIconFg: #cccccc; -historyComposeIconFgOver: #bebebe; +historyComposeIconFg: menuIconFg; +historyComposeIconFgOver: menuIconFgOver; historyPinnedBg: historyComposeAreaBg; historyReplyBg: historyComposeAreaBg; historyReplyCancelIconFg: cancelIconFg; @@ -172,7 +176,6 @@ historyReplyCancelIconFgOver: cancelIconFgOver; historySendBg: historyComposeAreaBg; historySendBgOver: #f5f5f5 | historySendBg; -historyMenuItemBgOver: historySendBgOver; historyTextInFg: windowTextFg; historyTextOutFg: windowTextFg; diff --git a/Telegram/Resources/icons/dialogs_lock@2x.png b/Telegram/Resources/icons/dialogs_lock@2x.png index 74689cf49a818c4eeca7ed7f3b90799902e6f046..d8f8bfd749e327c6a9264a5be6e102a8083323e7 100644 GIT binary patch delta 418 zcmV;T0bTx>1H1!}B!7cRL_t(oh3%F-j>0eyMrXFLd$@#e5eb`AEhxsP}%-R7x4lNGT^7?>*zsWHOkipGU;Ia`_64O%{z! z7L83%R0sjqT7T&K9+>&hkq4JH?h1!y!HFMsuIHXG3CbdVvVsn2Mb z$zXE3-Gaej0M_d@Xf~T*I-P#15B-@jn);0Hb~{k7*Fme*0;|>P+xm994VKF#*zfnq zjec;u-9VvG0L9|3V{yOVzpW2-p)b_sMgxz>=jD?m`K!Ln7|nymCJ!2$r>8!=8)vf_ zD3{AoHojagpmx{m;o}|pJKo3W`Fut^lV3TVPM}h${ITwRjDEc#QVa6r#>$M*JZNn4 zps~q=#wHIMo2P$D!Ye23yZ#yPW3=DzgUMt9s?}fiOT$c)iE bXl#B0CR1df0Y0+H00000NkvXXu0mjftFpUg diff --git a/Telegram/Resources/icons/dialogs_unlock@2x.png b/Telegram/Resources/icons/dialogs_unlock@2x.png index 04b3ef9e6cf8f434ca744c1cc69466a53c6dba82..a6c7bbd2fc4d5a06b54a24c6f2bc24e17277227b 100644 GIT binary patch delta 424 zcmV;Z0ayO61HuE4B!7uXL_t(oh3%L-j)O1|hR0jjeR2uk0x5P*Mw2owvC`!rlpF%C zzzET~nk}*vELrRjJFn%F+|94~u;qtf06_2td_7wrlqjOR-42SPfV!^1IS0-;sOuVv zq5!2-j1~ZJS^7moN=ao|l8>@1NlLl6&(ByE%(9I7zV}KF^nZO%S(ZIkR7y!**9A)s zbX|AP%-$AYj8Rop6A5cA?e}{ULXZ%GwAN&;ozzuTMU1iZqFU=o^E}W0tk3g&Qm?g6 zFZz3ythGrpcZ)VnGis`o@@AA$qfFB@=|!D$qozU#Z$=0)$~fn47G;BNC`99O##qwa z!2h~JAHgabn}1a_HeONAIT&N0ZQIEa18_NZLdLZmB7!jnIOjpKvj^?*c%0nTu{j(L z_aFFe+rnnE3HsutAR=tH+u0hg=(S}_f$ygJoXzwqtfH}5MPsvy#%2|b&F6?BVseR) z9$wM&`5Z-Nj??KBR5$w$ErdQ!@M85&xAA%5E SKa}_Y0000QX*6u;uJTNn{(q{Mn+CX=4)rI?EidOv-h3=Hwoa^PX~CG?5mBrTn_a6 zeb8#PK&ey$yWI{i?yzrW^ZIUbLo-EQO0z4c!t#^}R+G<@@XKL3^ghrart/fonts/OpenSans-Semibold.ttf art/newmsg.wav art/bg.jpg - art/bg_old.png + art/bg_initial.png art/icon256.png art/iconbig256.png diff --git a/Telegram/SourceFiles/_other/packer.cpp b/Telegram/SourceFiles/_other/packer.cpp index 8203e2019..48e0a8b05 100644 --- a/Telegram/SourceFiles/_other/packer.cpp +++ b/Telegram/SourceFiles/_other/packer.cpp @@ -56,79 +56,83 @@ QString countBetaVersionSignature(quint64 version); typedef unsigned char uchar; typedef unsigned int uint32; typedef signed int int32; + namespace{ - inline uint32 sha1Shift(uint32 v, uint32 shift) { - return ((v << shift) | (v >> (32 - shift))); - } - void sha1PartHash(uint32 *sha, uint32 *temp) - { - uint32 a = sha[0], b = sha[1], c = sha[2], d = sha[3], e = sha[4], round = 0; - #define _shiftswap(f, v) { \ - uint32 t = sha1Shift(a, 5) + (f) + e + v + temp[round]; \ - e = d; \ - d = c; \ - c = sha1Shift(b, 30); \ - b = a; \ - a = t; \ - ++round; \ - } - #define _shiftshiftswap(f, v) { \ - temp[round] = sha1Shift((temp[round - 3] ^ temp[round - 8] ^ temp[round - 14] ^ temp[round - 16]), 1); \ - _shiftswap(f, v) \ - } - - while (round < 16) _shiftswap((b & c) | (~b & d), 0x5a827999) - while (round < 20) _shiftshiftswap((b & c) | (~b & d), 0x5a827999) - while (round < 40) _shiftshiftswap(b ^ c ^ d, 0x6ed9eba1) - while (round < 60) _shiftshiftswap((b & c) | (b & d) | (c & d), 0x8f1bbcdc) - while (round < 80) _shiftshiftswap(b ^ c ^ d, 0xca62c1d6) - - #undef _shiftshiftswap - #undef _shiftswap - - sha[0] += a; - sha[1] += b; - sha[2] += c; - sha[3] += d; - sha[4] += e; - } +inline uint32 sha1Shift(uint32 v, uint32 shift) { + return ((v << shift) | (v >> (32 - shift))); } +void sha1PartHash(uint32 *sha, uint32 *temp) { + uint32 a = sha[0], b = sha[1], c = sha[2], d = sha[3], e = sha[4], round = 0; + +#define _shiftswap(f, v) { \ + uint32 t = sha1Shift(a, 5) + (f) + e + v + temp[round]; \ + e = d; \ + d = c; \ + c = sha1Shift(b, 30); \ + b = a; \ + a = t; \ + ++round; \ + } + +#define _shiftshiftswap(f, v) { \ + temp[round] = sha1Shift((temp[round - 3] ^ temp[round - 8] ^ temp[round - 14] ^ temp[round - 16]), 1); \ + _shiftswap(f, v) \ + } + + while (round < 16) _shiftswap((b & c) | (~b & d), 0x5a827999) + while (round < 20) _shiftshiftswap((b & c) | (~b & d), 0x5a827999) + while (round < 40) _shiftshiftswap(b ^ c ^ d, 0x6ed9eba1) + while (round < 60) _shiftshiftswap((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + while (round < 80) _shiftshiftswap(b ^ c ^ d, 0xca62c1d6) + +#undef _shiftshiftswap +#undef _shiftswap + + sha[0] += a; + sha[1] += b; + sha[2] += c; + sha[3] += d; + sha[4] += e; +} + +} // namespace + int32 *hashSha1(const void *data, uint32 len, void *dest) { const uchar *buf = (const uchar *)data; - uint32 temp[80], block = 0, end; - uint32 sha[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}; - for (end = block + 64; block + 64 <= len; end = block + 64) { - for (uint32 i = 0; block < end; block += 4) { - temp[i++] = (uint32) buf[block + 3] - | (((uint32) buf[block + 2]) << 8) - | (((uint32) buf[block + 1]) << 16) - | (((uint32) buf[block]) << 24); - } - sha1PartHash(sha, temp); - } + uint32 temp[80], block = 0, end; + uint32 sha[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}; + for (end = block + 64; block + 64 <= len; end = block + 64) { + for (uint32 i = 0; block < end; block += 4) { + temp[i++] = (uint32) buf[block + 3] + | (((uint32) buf[block + 2]) << 8) + | (((uint32) buf[block + 1]) << 16) + | (((uint32) buf[block]) << 24); + } + sha1PartHash(sha, temp); + } - end = len - block; + end = len - block; memset(temp, 0, sizeof(uint32) * 16); - uint32 last = 0; - for (; last < end; ++last) { - temp[last >> 2] |= (uint32)buf[last + block] << ((3 - (last & 0x03)) << 3); - } - temp[last >> 2] |= 0x80 << ((3 - (last & 3)) << 3); - if (end >= 56) { - sha1PartHash(sha, temp); + uint32 last = 0; + for (; last < end; ++last) { + temp[last >> 2] |= (uint32)buf[last + block] << ((3 - (last & 0x03)) << 3); + } + temp[last >> 2] |= 0x80 << ((3 - (last & 3)) << 3); + if (end >= 56) { + sha1PartHash(sha, temp); memset(temp, 0, sizeof(uint32) * 16); - } - temp[15] = len << 3; - sha1PartHash(sha, temp); + } + temp[15] = len << 3; + sha1PartHash(sha, temp); uchar *sha1To = (uchar*)dest; - for (int32 i = 19; i >= 0; --i) { - sha1To[i] = (sha[i >> 2] >> (((3 - i) & 0x03) << 3)) & 0xFF; - } + for (int32 i = 19; i >= 0; --i) { + sha1To[i] = (sha[i >> 2] >> (((3 - i) & 0x03) << 3)) & 0xFF; + } return (int32*)sha1To; } diff --git a/Telegram/SourceFiles/_other/updater.cpp b/Telegram/SourceFiles/_other/updater.cpp index f70e65ca8..ef9f4a624 100644 --- a/Telegram/SourceFiles/_other/updater.cpp +++ b/Telegram/SourceFiles/_other/updater.cpp @@ -468,13 +468,13 @@ static const WCHAR *_exeName = L"Updater.exe"; LPTOP_LEVEL_EXCEPTION_FILTER _oldWndExceptionFilter = 0; typedef BOOL (FAR STDAPICALLTYPE *t_miniDumpWriteDump)( - _In_ HANDLE hProcess, - _In_ DWORD ProcessId, - _In_ HANDLE hFile, - _In_ MINIDUMP_TYPE DumpType, - _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam + _In_ HANDLE hProcess, + _In_ DWORD ProcessId, + _In_ HANDLE hFile, + _In_ MINIDUMP_TYPE DumpType, + _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam ); t_miniDumpWriteDump miniDumpWriteDump = 0; @@ -483,7 +483,7 @@ HANDLE _generateDumpFileAtPath(const WCHAR *path) { WCHAR szPath[maxFileLen]; wsprintf(szPath, L"%stdata\\", path); - if (!CreateDirectory(szPath, NULL)) { + if (!CreateDirectory(szPath, NULL)) { if (GetLastError() != ERROR_ALREADY_EXISTS) { return 0; } @@ -495,7 +495,7 @@ HANDLE _generateDumpFileAtPath(const WCHAR *path) { } } - WCHAR szFileName[maxFileLen]; + WCHAR szFileName[maxFileLen]; WCHAR szExeName[maxFileLen]; wcscpy_s(szExeName, _exeName); @@ -504,16 +504,16 @@ HANDLE _generateDumpFileAtPath(const WCHAR *path) { wsprintf(dotFrom, L""); } - SYSTEMTIME stLocalTime; + SYSTEMTIME stLocalTime; - GetLocalTime(&stLocalTime); + GetLocalTime(&stLocalTime); - wsprintf(szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp", - szPath, szExeName, updaterVersionStr, - stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, - stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond, - GetCurrentProcessId(), GetCurrentThreadId()); - return CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0); + wsprintf(szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp", + szPath, szExeName, updaterVersionStr, + stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, + stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond, + GetCurrentProcessId(), GetCurrentThreadId()); + return CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0); } void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) { @@ -553,16 +553,16 @@ void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) { } MINIDUMP_EXCEPTION_INFORMATION ExpParam = {0}; - ExpParam.ThreadId = GetCurrentThreadId(); - ExpParam.ExceptionPointers = pExceptionPointers; - ExpParam.ClientPointers = TRUE; + ExpParam.ThreadId = GetCurrentThreadId(); + ExpParam.ExceptionPointers = pExceptionPointers; + ExpParam.ClientPointers = TRUE; - miniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL); + miniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL); } LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers) { _generateDump(pExceptionPointers); - return _oldWndExceptionFilter ? (*_oldWndExceptionFilter)(pExceptionPointers) : EXCEPTION_CONTINUE_SEARCH; + return _oldWndExceptionFilter ? (*_oldWndExceptionFilter)(pExceptionPointers) : EXCEPTION_CONTINUE_SEARCH; } // see http://www.codeproject.com/Articles/154686/SetUnhandledExceptionFilter-and-the-C-C-Runtime-Li diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index a31418997..70e1e1b0d 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -73,15 +73,14 @@ BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent) void BackgroundBox::Inner::gotWallpapers(const MTPVector &result) { App::WallPapers wallpapers; - auto oldBackground = ImagePtr(qsl(":/gui/art/bg_old.png")); - wallpapers.push_back(App::WallPaper(Window::Theme::kOldBackground, oldBackground, oldBackground)); - const auto &v(result.c_vector().v); - for (int i = 0, l = v.size(); i < l; ++i) { - const auto &w(v.at(i)); + auto oldBackground = ImagePtr(qsl(":/gui/art/bg_initial.png")); + wallpapers.push_back(App::WallPaper(Window::Theme::kInitialBackground, oldBackground, oldBackground)); + auto &v = result.c_vector().v; + for_const (auto &w, v) { switch (w.type()) { case mtpc_wallPaper: { - const auto &d(w.c_wallPaper()); - const auto &sizes(d.vsizes.c_vector().v); + auto &d = w.c_wallPaper(); + auto &sizes = d.vsizes.c_vector().v; const MTPPhotoSize *thumb = 0, *full = 0; int32 thumbLevel = -1, fullLevel = -1; for (QVector::const_iterator j = sizes.cbegin(), e = sizes.cend(); j != e; ++j) { @@ -89,14 +88,14 @@ void BackgroundBox::Inner::gotWallpapers(const MTPVector &result) int32 w = 0, h = 0; switch (j->type()) { case mtpc_photoSize: { - const auto &s(j->c_photoSize().vtype.c_string().v); + auto &s = j->c_photoSize().vtype.c_string().v; if (s.size()) size = s[0]; w = j->c_photoSize().vw.v; h = j->c_photoSize().vh.v; } break; case mtpc_photoCachedSize: { - const auto &s(j->c_photoCachedSize().vtype.c_string().v); + auto &s = j->c_photoCachedSize().vtype.c_string().v; if (s.size()) size = s[0]; w = j->c_photoCachedSize().vw.v; h = j->c_photoCachedSize().vh.v; @@ -120,7 +119,7 @@ void BackgroundBox::Inner::gotWallpapers(const MTPVector &result) } break; case mtpc_wallPaperSolid: { - const auto &d(w.c_wallPaperSolid()); + auto &d = w.c_wallPaperSolid(); } break; } } diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index ff7d14d7b..293dc5ed8 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -166,13 +166,13 @@ contactsPhotoCheckbox: RoundImageCheckbox { imageRadius: 21px; imageSmallRadius: 18px; selectWidth: 2px; - selectFg: windowActiveFill; + selectFg: windowActiveBg; selectDuration: 150; checkBorder: windowBg; - checkBg: windowActiveFill; + checkBg: windowActiveBg; checkRadius: 10px; checkSmallRadius: 3px; - checkIcon: icon {{ "default_checkbox_check", windowBg, point(3px, 6px) }}; + checkIcon: icon {{ "default_checkbox_check", windowActiveFg, point(3px, 6px) }}; } contactsPhotoDisabledCheckFg: #bbbbbb; contactsNameCheckedFg: #2b88b8; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 3f056af98..704741cae 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -356,7 +356,7 @@ void ContactsBox::onFilterUpdate(const QString &filter) { void ContactsBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) { using AddItemWay = Ui::MultiSelect::AddItemWay; auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default; - _select->entity()->addItem(peer->id, peer->shortName(), st::windowActiveFill, PaintUserpicCallback(peer), addItemWay); + _select->entity()->addItem(peer->id, peer->shortName(), st::activeButtonBg, PaintUserpicCallback(peer), addItemWay); } void ContactsBox::onPeerSelectedChanged(PeerData *peer, bool checked) { diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 16a20cca7..9365d8b11 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -236,7 +236,7 @@ void ShareBox::onFilterUpdate(const QString &query) { void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) { using AddItemWay = Ui::MultiSelect::AddItemWay; auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default; - _select->addItem(peer->id, peer->shortName(), st::windowActiveFill, PaintUserpicCallback(peer), addItemWay); + _select->addItem(peer->id, peer->shortName(), st::activeButtonBg, PaintUserpicCallback(peer), addItemWay); } void ShareBox::onPeerSelectedChanged(PeerData *peer, bool checked) { diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 4d5ea7338..437c2c747 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -81,13 +81,16 @@ dialogsMenuToggle: IconButton { height: 32px; icon: icon {{ "dialogs_menu", dialogsMenuIconFg }}; + iconOver: icon {{ "dialogs_menu", dialogsMenuIconFgOver }}; iconPosition: point(6px, 6px); iconPositionDown: point(6px, 6px); } dialogsLock: IconButton(dialogsMenuToggle) { icon: icon {{ "dialogs_lock", dialogsMenuIconFg }}; + iconOver: icon {{ "dialogs_lock", dialogsMenuIconFgOver }}; } dialogsUnlockIcon: icon {{ "dialogs_unlock", dialogsMenuIconFg }}; +dialogsUnlockIconOver: icon {{ "dialogs_unlock", dialogsMenuIconFgOver }}; dialogsFilter: flatInput(inpDefGray) { font: font(fsize); @@ -114,10 +117,15 @@ dialogsMenu: DropdownMenu(defaultDropdownMenu) { } dialogsMenuPosition: point(-3px, -2px); dialogsMenuNewGroup: icon {{ "menu_new_group", menuIconFg }}; +dialogsMenuNewGroupOver: icon {{ "menu_new_group", menuIconFgOver }}; dialogsMenuNewChannel: icon {{ "menu_new_channel", menuIconFg }}; +dialogsMenuNewChannelOver: icon {{ "menu_new_channel", menuIconFgOver }}; dialogsMenuContacts: icon {{ "menu_contacts", menuIconFg }}; +dialogsMenuContactsOver: icon {{ "menu_contacts", menuIconFgOver }}; dialogsMenuSettings: icon {{ "menu_settings", menuIconFg }}; +dialogsMenuSettingsOver: icon {{ "menu_settings", menuIconFgOver }}; dialogsMenuHelp: icon {{ "menu_help", menuIconFg }}; +dialogsMenuHelpOver: icon {{ "menu_help", menuIconFgOver }}; dialogsChatTypeSkip: 22px; dialogsChatIcon: icon {{ "dialogs_chat", dialogsChatIconFg, point(1px, 4px) }}; @@ -191,4 +199,3 @@ dialogsForwardCancel: IconButton { iconPositionDown: point(12px, 11px); } dialogsForwardFont: semiboldFont; -dialogsForwardBg: windowActiveFill; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 93679c934..cf6a0a662 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1701,7 +1701,7 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) _lockUnlock->setVisible(Global::LocalPasscode()); subscribe(Global::RefLocalPasscodeChanged(), [this] { updateLockUnlockVisibility(); }); _lockUnlock->setClickedCallback([this] { - _lockUnlock->setIcon(&st::dialogsUnlockIcon); + _lockUnlock->setIcon(&st::dialogsUnlockIcon, &st::dialogsUnlockIconOver); App::wnd()->setupPasscode(); _lockUnlock->setIcon(nullptr); }); @@ -2016,19 +2016,19 @@ void DialogsWidget::showMainMenu() { _mainMenu.create(this, st::dialogsMenu); _mainMenu->addAction(lang(lng_create_group_title), [] { App::wnd()->onShowNewGroup(); - }, &st::dialogsMenuNewGroup); + }, &st::dialogsMenuNewGroup, &st::dialogsMenuNewGroupOver); _mainMenu->addAction(lang(lng_create_channel_title), [] { App::wnd()->onShowNewChannel(); - }, &st::dialogsMenuNewChannel); + }, &st::dialogsMenuNewChannel, &st::dialogsMenuNewChannelOver); _mainMenu->addAction(lang(lng_menu_contacts), [] { Ui::showLayer(new ContactsBox()); - }, &st::dialogsMenuContacts); + }, &st::dialogsMenuContacts, &st::dialogsMenuContactsOver); _mainMenu->addAction(lang(lng_menu_settings), [] { App::wnd()->showSettings(); - }, &st::dialogsMenuSettings); + }, &st::dialogsMenuSettings, &st::dialogsMenuSettingsOver); _mainMenu->addAction(lang(lng_settings_faq), [] { QDesktopServices::openUrl(telegramFaqLink()); - }, &st::dialogsMenuHelp); + }, &st::dialogsMenuHelp, &st::dialogsMenuHelpOver); } updateMainMenuGeometry(); _mainMenu->showAnimated(); diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 6f21d0d5a..d373a3282 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -157,32 +157,19 @@ historyComposeField: flatTextarea { historyComposeFieldMaxHeight: 224px; // historyMinHeight: 56px; -historyMediaTypeFile: icon {{ "media_type_file", historyComposeIconFg, point(2px, 2px) }}; -historyMediaTypeFileOver: icon {{ "media_type_file", historyComposeIconFgOver, point(2px, 2px) }}; -historyMediaTypePhoto: icon {{ "media_type_photo", historyComposeIconFg, point(2px, 2px) }}; -historyMediaTypePhotoOver: icon {{ "media_type_photo", historyComposeIconFgOver, point(2px, 2px) }}; -historyMediaTypeVideo: icon {{ "media_type_video", historyComposeIconFg, point(2px, 2px) }}; -historyMediaTypeVideoOver: icon {{ "media_type_video", historyComposeIconFgOver, point(2px, 2px) }}; -historyMediaTypeSong: icon {{ "media_type_song", historyComposeIconFg, point(0px, 0px) }}; -historyMediaTypeSongOver: icon {{ "media_type_song", historyComposeIconFgOver, point(0px, 0px) }}; -historyMediaTypeVoice: icon {{ "media_type_voice", historyComposeIconFg, point(2px, 2px) }}; -historyMediaTypeVoiceOver: icon {{ "media_type_voice", historyComposeIconFgOver, point(2px, 2px) }}; -historyMediaTypeLink: icon {{ "media_type_link", historyComposeIconFg, point(2px, 2px) }}; -historyMediaTypeLinkOver: icon {{ "media_type_link", historyComposeIconFgOver, point(2px, 2px) }}; - historyAttachDocument: IconButton { width: 46px; height: 46px; - icon: historyMediaTypeFile; - iconOver: historyMediaTypeFileOver; + icon: icon {{ "media_type_file", historyComposeIconFg, point(2px, 2px) }}; + iconOver: icon {{ "media_type_file", historyComposeIconFgOver, point(2px, 2px) }}; iconPosition: point(9px, 9px); iconPositionDown: point(9px, 10px); } historyAttachPhoto: IconButton(historyAttachDocument) { - icon: historyMediaTypePhoto; - iconOver: historyMediaTypePhotoOver; + icon: icon {{ "media_type_photo", historyComposeIconFg, point(2px, 2px) }}; + iconOver: icon {{ "media_type_photo", historyComposeIconFgOver, point(2px, 2px) }}; } historyAttachEmoji: IconButton(historyAttachDocument) { width: 33px; @@ -233,11 +220,23 @@ historyRecordFont: font(13px); historyRecordDurationFg: #000000; historyRecordTextTop: 14px; +historyMediaTypeFile: icon {{ "media_type_file", menuIconFg, point(2px, 2px) }}; +historyMediaTypeFileOver: icon {{ "media_type_file", menuIconFgOver, point(2px, 2px) }}; +historyMediaTypePhoto: icon {{ "media_type_photo", menuIconFg, point(2px, 2px) }}; +historyMediaTypePhotoOver: icon {{ "media_type_photo", menuIconFgOver, point(2px, 2px) }}; +historyMediaTypeVideo: icon {{ "media_type_video", menuIconFg, point(2px, 2px) }}; +historyMediaTypeVideoOver: icon {{ "media_type_video", menuIconFgOver, point(2px, 2px) }}; +historyMediaTypeSong: icon {{ "media_type_song", menuIconFg, point(0px, 0px) }}; +historyMediaTypeSongOver: icon {{ "media_type_song", menuIconFgOver, point(0px, 0px) }}; +historyMediaTypeVoice: icon {{ "media_type_voice", menuIconFg, point(2px, 2px) }}; +historyMediaTypeVoiceOver: icon {{ "media_type_voice", menuIconFgOver, point(2px, 2px) }}; +historyMediaTypeLink: icon {{ "media_type_link", menuIconFg, point(2px, 2px) }}; +historyMediaTypeLinkOver: icon {{ "media_type_link", menuIconFgOver, point(2px, 2px) }}; + historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { menu: Menu(defaultMenu) { skip: 5px; - itemBgOver: historyMenuItemBgOver; itemIconPosition: point(12px, 6px); itemPadding: margins(48px, 11px, 48px, 11px); } @@ -277,15 +276,15 @@ topBarSearch: IconButton { width: 44px; height: topBarHeight; - icon: icon {{ "title_search", #a8a8a8 }}; - iconOver: icon {{ "title_search", #a3a3a3 }}; + icon: icon {{ "title_search", menuIconFg }}; + iconOver: icon {{ "title_search", menuIconFgOver }}; iconPosition: point(13px, 18px); iconPositionDown: point(13px, 18px); } topBarMenuToggle: IconButton(topBarSearch) { - icon: icon {{ "title_menu_dots", #a8a8a8 }}; - iconOver: icon {{ "title_menu_dots", #a3a3a3 }}; + icon: icon {{ "title_menu_dots", menuIconFg }}; + iconOver: icon {{ "title_menu_dots", menuIconFgOver }}; iconPosition: point(18px, 17px); iconPositionDown: point(18px, 17px); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 8a1d11b03..08159e603 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -5004,8 +5004,8 @@ void HistoryWidget::loadMessages() { return firstLoadMessages(); } - bool loadMigrated = _migrated && (_history->isEmpty() || _history->loadedAtTop() || (!_migrated->isEmpty() && !_migrated->loadedAtBottom())); - History *from = loadMigrated ? _migrated : _history; + auto loadMigrated = _migrated && (_history->isEmpty() || _history->loadedAtTop() || (!_migrated->isEmpty() && !_migrated->loadedAtBottom())); + auto from = loadMigrated ? _migrated : _history; if (from->loadedAtTop()) { return; } diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index a4d188441..d56a65240 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -3630,8 +3630,7 @@ void writeBackground(int32 id, const QImage &img) { } quint32 size = sizeof(qint32) + sizeof(quint32) + (bmp.isEmpty() ? 0 : (sizeof(quint32) + bmp.size())); EncryptedDescriptor data(size); - data.stream << qint32(id); - if (!bmp.isEmpty()) data.stream << bmp; + data.stream << qint32(id) << bmp; FileWriteDescriptor file(_backgroundKey); file.writeEncrypted(data); @@ -3653,21 +3652,21 @@ bool readBackground() { QByteArray pngData; qint32 id; - bg.stream >> id; - if (id == Window::Theme::kOldBackground || id == Window::Theme::kDefaultBackground) { + bg.stream >> id >> pngData; + auto oldEmptyImage = (bg.stream.status() != QDataStream::Ok); + if (oldEmptyImage + || id == Window::Theme::kInitialBackground + || id == Window::Theme::kDefaultBackground) { _backgroundCanWrite = false; - if (bg.version < 8005) { + if (oldEmptyImage || bg.version < 8005) { Window::Theme::Background()->setImage(Window::Theme::kDefaultBackground); - if (id == Window::Theme::kOldBackground) { - Window::Theme::Background()->setTile(false); - } + Window::Theme::Background()->setTile(false); } else { Window::Theme::Background()->setImage(id); } _backgroundCanWrite = true; return true; } - bg.stream >> pngData; QImage image; QBuffer buf(&pngData); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 72780f900..3f9ff7e99 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1925,7 +1925,9 @@ void MainWidget::checkChatBackground() { if (_background->full->loaded()) { if (_background->full->isNull()) { Window::Theme::Background()->setImage(Window::Theme::kDefaultBackground); - } else if (_background->id == Window::Theme::kOldBackground || _background->id == Window::Theme::kDefaultBackground) { + } else if (false + || _background->id == Window::Theme::kInitialBackground + || _background->id == Window::Theme::kDefaultBackground) { Window::Theme::Background()->setImage(_background->id); } else { Window::Theme::Background()->setImage(_background->id, _background->full->pix().toImage()); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index da17f3560..e3dd2d5ee 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -159,8 +159,8 @@ void MainWindow::onStateChanged(Qt::WindowState state) { savePosition(state); } -void MainWindow::init() { - Platform::MainWindow::init(); +void MainWindow::initHook() { + Platform::MainWindow::initHook(); setWindowIcon(wndIcon); @@ -198,6 +198,7 @@ void MainWindow::firstShow() { trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()))->setEnabled(true); } psUpdateWorkmode(); + psFirstShow(); updateTrayMenu(); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 0fd6813a8..a40ca68b4 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -77,7 +77,6 @@ public: MainWindow(); ~MainWindow(); - void init(); void firstShow(); QWidget *filedialogParent(); @@ -168,6 +167,8 @@ protected: void closeEvent(QCloseEvent *e) override; void resizeEvent(QResizeEvent *e) override; + void initHook() override; + public slots: void updateIsActive(int timeout = 0); diff --git a/Telegram/SourceFiles/platform/win/window_title_win.cpp b/Telegram/SourceFiles/platform/win/window_title_win.cpp index a27c96f5d..32437e143 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.cpp +++ b/Telegram/SourceFiles/platform/win/window_title_win.cpp @@ -47,11 +47,12 @@ TitleWidget::TitleWidget(QWidget *parent) : Window::TitleWidget(parent) }); setAttribute(Qt::WA_OpaquePaintEvent); +} + +void TitleWidget::init() { + connect(window()->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onWindowStateChanged(Qt::WindowState))); + _maximized = (window()->windowState() & Qt::WindowMaximized); updateMaximizeRestoreButton(); - - onWindowStateChanged(); - - connect(parent->window()->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onWindowStateChanged(Qt::WindowState))); } void TitleWidget::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/platform/win/window_title_win.h b/Telegram/SourceFiles/platform/win/window_title_win.h index 49a4f11d2..cc7a0fa49 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.h +++ b/Telegram/SourceFiles/platform/win/window_title_win.h @@ -35,6 +35,8 @@ class TitleWidget : public Window::TitleWidget, private base::Subscriber { public: TitleWidget(QWidget *parent); + void init() override; + Window::HitTestResult hitTest(const QPoint &p) const override; public slots: diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index 5c7ffca41..efc6ff1ed 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -60,11 +60,11 @@ profilePrimaryButton: defaultActiveButton; profileSecondaryButton: defaultLightButton; profileAddMemberButton: RoundButton(profileSecondaryButton) { width: 62px; - icon: icon {{ "profile_add_member", windowActiveFill, point(20px, 10px) }}; + icon: icon {{ "profile_add_member", lightButtonFg, point(20px, 10px) }}; } profileDropAreaBg: profileBg; -profileDropAreaFg: windowActiveFill; +profileDropAreaFg: lightButtonFg; profileDropAreaPadding: margins(25px, 3px, 25px, 20px); profileDropAreaTitleFont: font(24px); profileDropAreaTitleTop: 30px; diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 7b0873f00..4bb43c4d2 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -2135,7 +2135,7 @@ void StickerPanInner::updateSelected() { 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) { + if (_previewShown && _selected >= 0 && _pressed != _selected) { _pressed = _selected; if (row >= 0 && col >= 0) { auto layout = _inlineRows.at(row).items.at(col); @@ -2260,7 +2260,7 @@ void StickerPanInner::updateSelected() { } } _selected = selIndex; - if (_pressed >= 0 && _selected >= 0 && _pressed != _selected) { + if (_previewShown && _selected >= 0 && _pressed != _selected) { _pressed = _selected; if (newSel >= 0 && xNewSel < 0) { Ui::showMediaPreview(sets.at(newSelTab).pack.at(newSel % MatrixRowShift)); diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 91ba059a5..752abc675 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -56,7 +56,7 @@ stickersFeaturedUnreadBg: msgFileInBg; stickersFeaturedUnreadSize: 5px; stickersFeaturedUnreadSkip: 5px; stickersFeaturedUnreadTop: 7px; -stickersFeaturedInstalled: icon {{ "mediaview_save_check", windowActiveFill }}; +stickersFeaturedInstalled: icon {{ "mediaview_save_check", lightButtonFg }}; stickersMaxHeight: 440px; stickersPadding: margins(19px, 17px, 19px, 17px); diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 2e0b1c931..a49e87450 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -28,10 +28,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Window { -MainWindow::MainWindow() : QMainWindow() +MainWindow::MainWindow() : QWidget() , _positionUpdatedTimer(this) , _body(this) { - setCentralWidget(_body); subscribe(Theme::Background(), [this](const Theme::BackgroundUpdate &data) { using Type = Theme::BackgroundUpdate::Type; if (data.type == Type::TestingTheme || data.type == Type::RevertingTheme || data.type == Type::ApplyingTheme) { @@ -40,20 +39,21 @@ MainWindow::MainWindow() : QMainWindow() } } }); + + _title = Platform::CreateTitleWidget(this); } void MainWindow::init() { _positionUpdatedTimer->setSingleShot(true); connect(_positionUpdatedTimer, SIGNAL(timeout()), this, SLOT(savePositionByTimer())); - _title = Platform::CreateTitleWidget(this); - auto p = palette(); p.setColor(QPalette::Window, st::windowBg->c); setPalette(p); - initSize(); + if (_title) _title->init(); + initSize(); initHook(); } diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 319b52163..f1c0e8fc4 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -26,7 +26,7 @@ namespace Window { class TitleWidget; -class MainWindow : public QMainWindow, protected base::Subscriber { +class MainWindow : public QWidget, protected base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 0253aa14d..06a2cdfe0 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -76,7 +76,7 @@ notifySendReply: IconButton { width: 36px; height: 36px; - icon: icon {{ "notification_send", windowActiveFill, point(3px, 9px) }}; + icon: icon {{ "notification_send", lightButtonFg, point(3px, 9px) }}; iconPosition: point(0px, 0px); iconPositionDown: point(0px, 1px); } diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp index 6a107cb8c..9865d38f1 100644 --- a/Telegram/SourceFiles/window/window_theme.cpp +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -440,18 +440,18 @@ void ChatBackground::setImage(int32 id, QImage &&image) { } setPreparedImage(std_::move(image)); } else { - if (_id == kDefaultBackground) { - image.load(qsl(":/gui/art/bg.jpg")); - } else if (_id == kOldBackground || image.isNull()) { - _id = kOldBackground; - image.load(qsl(":/gui/art/bg_old.png")); + if (_id == kInitialBackground) { + image.load(qsl(":/gui/art/bg_initial.png")); if (cRetina()) { image = image.scaledToWidth(image.width() * 2, Qt::SmoothTransformation); } else if (cScale() != dbisOne) { image = image.scaledToWidth(convertScale(image.width()), Qt::SmoothTransformation); } + } else if (_id == kDefaultBackground || image.isNull()) { + _id = kDefaultBackground; + image.load(qsl(":/gui/art/bg.jpg")); } - Local::writeBackground(_id, (_id == kDefaultBackground || _id == kOldBackground) ? QImage() : image); + Local::writeBackground(_id, (_id == kDefaultBackground || _id == kInitialBackground) ? QImage() : image); setPreparedImage(prepareBackgroundImage(std_::move(image))); } t_assert(!_image.isNull()); diff --git a/Telegram/SourceFiles/window/window_theme.h b/Telegram/SourceFiles/window/window_theme.h index 85c9d8b52..06b21e224 100644 --- a/Telegram/SourceFiles/window/window_theme.h +++ b/Telegram/SourceFiles/window/window_theme.h @@ -32,8 +32,8 @@ constexpr int32 kTestingDefaultBackground = -665; constexpr int32 kThemeBackground = -2; constexpr int32 kCustomBackground = -1; -constexpr int32 kOldBackground = 0; -constexpr int32 kDefaultBackground = 21; +constexpr int32 kInitialBackground = 0; +constexpr int32 kDefaultBackground = 105; struct Cached { QByteArray colors; diff --git a/Telegram/SourceFiles/window/window_title.h b/Telegram/SourceFiles/window/window_title.h index 15dad581d..578812eb3 100644 --- a/Telegram/SourceFiles/window/window_title.h +++ b/Telegram/SourceFiles/window/window_title.h @@ -40,6 +40,9 @@ enum class HitTestResult { class TitleWidget : public TWidget { public: using TWidget::TWidget; + + virtual void init() { + } virtual HitTestResult hitTest(const QPoint &p) const { return HitTestResult::None; } From 19f4e50e3399638c1f25c290f8a23858fece9f39 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Nov 2016 19:08:24 +0300 Subject: [PATCH 024/100] New compose area icons. --- Telegram/Resources/basic.style | 26 --- Telegram/Resources/colors.palette | 6 +- Telegram/Resources/icons/media_type_file.png | Bin 417 -> 203 bytes .../Resources/icons/media_type_file@2x.png | Bin 829 -> 337 bytes Telegram/Resources/icons/media_type_link.png | Bin 450 -> 0 bytes .../Resources/icons/media_type_link@2x.png | Bin 884 -> 0 bytes Telegram/Resources/icons/media_type_photo.png | Bin 451 -> 406 bytes .../Resources/icons/media_type_photo@2x.png | Bin 918 -> 841 bytes Telegram/Resources/icons/media_type_song.png | Bin 577 -> 0 bytes .../Resources/icons/media_type_song@2x.png | Bin 1050 -> 0 bytes Telegram/Resources/icons/media_type_video.png | Bin 302 -> 0 bytes .../Resources/icons/media_type_video@2x.png | Bin 605 -> 0 bytes Telegram/Resources/icons/media_type_voice.png | Bin 421 -> 0 bytes .../Resources/icons/media_type_voice@2x.png | Bin 779 -> 0 bytes .../Resources/icons/send_control_attach.png | Bin 0 -> 919 bytes .../icons/send_control_attach@2x.png | Bin 0 -> 1869 bytes .../icons/send_control_bot_command.png | Bin 400 -> 401 bytes .../icons/send_control_bot_command@2x.png | Bin 904 -> 706 bytes .../icons/send_control_bot_keyboard.png | Bin 308 -> 320 bytes .../icons/send_control_bot_keyboard@2x.png | Bin 643 -> 546 bytes .../icons/send_control_bot_keyboard_hide.png | Bin 160 -> 342 bytes .../send_control_bot_keyboard_hide@2x.png | Bin 263 -> 669 bytes .../Resources/icons/send_control_emoji.png | Bin 227 -> 438 bytes .../Resources/icons/send_control_emoji@2x.png | Bin 360 -> 747 bytes .../Resources/icons/send_control_record.png | Bin 408 -> 517 bytes .../icons/send_control_record@2x.png | Bin 680 -> 905 bytes .../Resources/icons/send_control_send.png | Bin 0 -> 598 bytes .../Resources/icons/send_control_send@2x.png | Bin 0 -> 1249 bytes Telegram/Resources/sample.tdesktop-theme | 6 +- Telegram/SourceFiles/dialogs/dialogs.style | 5 +- .../history/field_autocomplete.cpp | 2 +- Telegram/SourceFiles/history/history.style | 94 ++++++----- Telegram/SourceFiles/historywidget.cpp | 155 ++++++++---------- Telegram/SourceFiles/historywidget.h | 5 +- Telegram/SourceFiles/mainwidget.cpp | 15 +- .../settings/settings_scale_widget.cpp | 21 ++- .../settings/settings_scale_widget.h | 3 + .../ui/buttons/history_down_button.cpp | 2 +- Telegram/SourceFiles/ui/widgets/widgets.style | 2 +- 39 files changed, 168 insertions(+), 174 deletions(-) delete mode 100644 Telegram/Resources/icons/media_type_link.png delete mode 100644 Telegram/Resources/icons/media_type_link@2x.png delete mode 100644 Telegram/Resources/icons/media_type_song.png delete mode 100644 Telegram/Resources/icons/media_type_song@2x.png delete mode 100644 Telegram/Resources/icons/media_type_video.png delete mode 100644 Telegram/Resources/icons/media_type_video@2x.png delete mode 100644 Telegram/Resources/icons/media_type_voice.png delete mode 100644 Telegram/Resources/icons/media_type_voice@2x.png create mode 100644 Telegram/Resources/icons/send_control_attach.png create mode 100644 Telegram/Resources/icons/send_control_attach@2x.png create mode 100644 Telegram/Resources/icons/send_control_send.png create mode 100644 Telegram/Resources/icons/send_control_send@2x.png diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 0e8d43032..58331cba5 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -680,32 +680,6 @@ msgWaveformOutActiveSelected: #6badad; msgWaveformOutInactive: #b3e2b4; msgWaveformOutInactiveSelected: #91c3c3; -sendPadding: 9px; -btnSend: flatButton { - duration: 200; - cursor: cursor(pointer); - - color: btnYesColor; - overColor: btnYesHover; - - bgColor: historySendBg; - overBgColor: historySendBgOver; - - width: -32px; - height: 46px; - - textTop: 12px; - overTextTop: 12px; - downTextTop: 13px; - - font: font(16px); - overFont: font(16px); -} -btnUnblock: flatButton(btnSend) { - color: #d15948; - overColor: #d15948; -} - historyScroll: flatScroll(scrollDef) { barColor: #89a0b47a; bgColor: #89a0b44c; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index ac980b21e..c102bf3ef 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -169,13 +169,15 @@ emojiPanHeaderBg: #fffffff2 | emojiPanBg; historyComposeAreaBg: windowBg; historyComposeIconFg: menuIconFg; historyComposeIconFgOver: menuIconFgOver; +historySendIconFg: windowActiveBg; +historySendIconFgOver: windowActiveBg; historyPinnedBg: historyComposeAreaBg; historyReplyBg: historyComposeAreaBg; historyReplyCancelIconFg: cancelIconFg; historyReplyCancelIconFgOver: cancelIconFgOver; -historySendBg: historyComposeAreaBg; -historySendBgOver: #f5f5f5 | historySendBg; +historyComposeButtonBg: historyComposeAreaBg; +historyComposeButtonBgOver: #f5f5f5; historyTextInFg: windowTextFg; historyTextOutFg: windowTextFg; diff --git a/Telegram/Resources/icons/media_type_file.png b/Telegram/Resources/icons/media_type_file.png index dcc7996f18dc53f58420a1d783a6fa4e66cf5d75..37c310a39eafcf5739d0fc40abd996aebb7e91cf 100644 GIT binary patch delta 176 zcmV;h08js+1Iq!BBYyyrNkl&eT11BeLB3?#pns=5)jWoEoeN{J+?s_f67)PBK}5W)yD4geTI z=70fY9&jS}fF9}h-tJy&+m8O>|HDaMzyj_Awbtp&%vW{mOh eRBTwE{zX3jWX5fZU1uBs0000X01J_T zg^0*KB}0MkwY^zp`p_1>Klw@W0sw_CuAe7|LI{v^1`z>O1!jgABOu~*zutM?@3-0# z5oKn7ueIv3Ed815@RX8+LJ0mH5fA?m@xH*eZ60+87_y5o-hUHl;bs954MK>hBBHg{ z>jFI-5w(=!zNeI?HeM14Aq;T4)KZGpT2)nb+crNFW4tD?EQ^YWs;aK*dVpKhL^ODw zbN*YPg@Hs3(Vl=D>M7Ww)PQ`0)S`OT6PdXME7;y_fvRpU%Ri( za1gLf=CRfF8Grm!hNl^rpR>k#0H0s-tg+fEGW)C~Kfp(UzV>^#**6Q~l7NA1nEewz z3iOp=W8oqEC@||xW_FL;OTGx7XV7*cA`bAbk?slDNU%ydE6>LQMC7nq>tQrMfY1AY mh&&Us%tpf9=VdD2TEC@Ft#T6mn$`dS002ovPDHLkU;%>O5xKAc diff --git a/Telegram/Resources/icons/media_type_file@2x.png b/Telegram/Resources/icons/media_type_file@2x.png index 7df85a0b287bf745d68e7bf673d8fd103a47fa6c..c1e9fa77361b3924acc0b48672adcac4c86f56ae 100644 GIT binary patch delta 311 zcmV-70m%Nn2GIhLBYy!HNkl-ygZGU8dM2NhA1c>|qVgwI> z5Wxo^Lhu3z5d45h#5n-(5Yd2r-?IT;Awu(ikg3Nm^!<bS?|YJEzNzcFLv!9sb6J)+jw8v%{*$b= zsI`v&EAd3MZ7_?@x#Ww_^Ne*}A6*{RQ*qUP|0{I%1i;Em<*lxGh->=szTrL-QJRSf5u-Rf4rEDF=++(;N5+G3Vvvz^TFMXk_n)|`)r1VSXGskQlYK( zEdpGpRCw2ByiEr38_TksvY7|(o|1^K+ZzR*2$yyjqJO|sqBHdIq308j^cVTusW2|=R-MF*G1OXH1G@V+MPSI*2gfFh2@AH2_Bq>PRW*zfn2qrUG~!Ka)Il7QWAXTiHU_($O5DO6RJ7v5KWE=@xK zK&8}D_;~CRhdu^5!XFi*^2pHGnzL$fg z#Tn(Xr9mKq|Nbow4TB_=Wx5-tS58VCVxdEMIcla4i^u1eNd{;M4cue_DJh4 z##ArVs|C0h^r9#x9cG4)3jhEs4W^#Te*u14fcwd(ZCmfL?}HD$^<)(Q@LJPrtx-ye zLI^C&5~Y+c3wG_dfusV8q9A|c%?wWyFrt diff --git a/Telegram/Resources/icons/media_type_link.png b/Telegram/Resources/icons/media_type_link.png deleted file mode 100644 index 02b47d0e929ba9d5a1c580deb45ace071d8d7240..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)0h_?j&wTh=lu)-0ISssilRgZ0D#?Y2VK`6eA0~)q)aA5rBaD>U8h_wN1CQlu~_tY z4u=C(DivBR7U5=~sw%qOE;gGD!s>iJqgt){KaniU7z_qr=FpAZx?ZnnHk;1@*=!cu z?e?w4ZZR_&jfNK>%Q9@+{-8G#b{j&7+jZ|Hgz$Ko8Ld|9dC=UUVP=d*BbcV?8#Kc( z?q?jwLA_poo#bAx2WG~2JVrjB50&H_;B-2{vMm2yG?`3LE|>qbDh$KGe!qthBFIbb siH9ylQJ|`7pfB_;dRO23Wqu9d7yh^SlD{7Q)c^nh07*qoM6N<$f}^g=9{>OV diff --git a/Telegram/Resources/icons/media_type_link@2x.png b/Telegram/Resources/icons/media_type_link@2x.png deleted file mode 100644 index d10338d6efad695176ca3a6dcb121f6e5b3cc9a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 884 zcmV-)1B?8LP)7{=f8-B0jBK~Z5Pcnc${_L{YTNujT+FBO`Qpct|ZREtdFk`3VgV57X)CDM^xK+KbyB=<*40~{|LQPFgw7(9j0K92oVSzR_Hb@WzP51w;W*Ej8b$54DS63HFl0+RH9hx@m z^z<}291aQwgQTiz`8CMGKQz6)z2x!68a$#j<1*uf3Dxp8?{{9{wkHFG(gPb3lnW2{n-djBvpGox#PLZN{3^K&dMEn$3o9B#K8Hk<9o z#Y9oW{QP`H-(s4!~P=0N$zt@YX-s1w;Ekf^p~o0000< KMNUMnLSTX%4xb7D diff --git a/Telegram/Resources/icons/media_type_photo.png b/Telegram/Resources/icons/media_type_photo.png index 520c9b5d861ae86af97ebaebf144f0681a59f352..1bda90336ab7e0004964806f704b267a50b489b6 100644 GIT binary patch delta 379 zcmV->0fhd;1C|4jB!68=L_t(YiOrR7io!4u#ozRk%_(F+>>+G=0x8}=@CHJ}L;0t8 z3!zZ(6h`mh+YhbbCKGz^Um7;i>2zYA=Z}Cc%y07E*8r(0?0>dfMFc>@E?TeGNYfN? z93zfnSZlG}ZX3f0AXx5vKEIYK!X!yJ91bZW1VHCpFb`kP;F(sd6#;NC81Qg7)bmwU zQB?_mMNzZ|j7B3yQFLd1Q(2Z25n5~81A4t4lO%b%Mv|&ZW6Vpyw};FQx+_>gnx+&H zR#o+|w%_k5B6Q5Mto;^kHX8z71+ER8^^}b(=1i%V1t( z07(`_L2E6IF*L?7%d+5~e+B&BRdGI_J2Us*-+dDR^E~h5Mm-*n9FND3Tl6md{hRz# ZeE^y#dZVNT0;d1~002ovPDHLkV1kJ5tN;K2 delta 425 zcmV;a0apH&1H%K5B!7xYL_t(YiQScCvs;j>aov8$9)Pcy#i-@=^`DSsuk)_)!7lGd71N}`YO z?+|p^SXC9b+b!qwIio0|l#*GNb%Hnnd7g7L8W8}4AmD5^d-Qj?T+$fxKA@^920=jI z_jx!R9_!cZm8;c?0Jz)j-Un@Koms`!|)9*=)`-_;5*#&EG%5CD@TVVb7Q^PKzro}TAX2*Eth+alcWplKSe*XtiMCJaMo zI7tpjcKUEGmkWxbKwZ~};}}VjKnUSH*BS7xKFYG}Eh4-uOY}aj2l&;W{{PS?xkgx( TESfz600000NkvXXu0mjfGbYLO diff --git a/Telegram/Resources/icons/media_type_photo@2x.png b/Telegram/Resources/icons/media_type_photo@2x.png index e2a1fa4dfff7cd63b315ef1f94a4d8b843dc912d..87a72b39e08d058416ab2cf75e355c8f704db5c4 100644 GIT binary patch delta 818 zcmV-21I_%F2gwGIB!3V|L_t(&f$f-2ODbO+#=oPWt6o4Vqit&T%_a$Y1ran>2nx+E z5NhQsXyc|AQ4LB$q^+YD(57WAO2|#S5S_}QrspnxzZveiXT+JKIC`%S95@d1obU6I z^N*2;2sFcrgdJd1%>lyJ93X6s1)y9m6M#UiTrQ8JG!gx`jeiO^UDqL^pPwJ>@9&RP z`~N0x-rCy2_xHCD004Y_ePMHRGaP`8jSYN$ehPxsc zQDvGYuCK45D2hJ-MN!b{bi&Tfsnh8Uf?yvs8jY~C4pg(*2#VjO(VlF$g-?)mL#G- z7KNy4wHgwM#L#^^nM|TyuM5}l7;`iL+qQ9Va1i(aDwRsGZ99Abukg`m6xnPRx~@al zb*!zeAsUT(LWBxXtJT;&S&~pJ7SV3Exp!Y)UXah{xqkpbr_ZMXCSe7+;fYZ}c##byBeS3FzcNwo?7?TIs-ri>Byw|iDq*kkiNF?I=bGh8) z0c2TrJ=tv5x6hHPs;)ni$p`}oA7rnuuWo;3WhJm*Q54Uyg6iMBN-Qoey8Xw;$H4xF zhX=QxNPi?mV~7K&s_ORd?(V4FZu|B=KR?s`{k`W{A`)7}=jZ2)FQ3o*_G+5Oc&pW_ z@FErl==b|bCX|)=1I@c=ZdAw73BjMFB~YM0jBq#yzH{rhoAG_!tQx$fHiDgQjU> zp-_nJq34K~kjZ4w^E@u&{Zy@1QIe#=16*8O2shtVrBWFzz~0{8z|12e20_5`I7J({ zySvN%%tw*yy12Qy86O~@&!c5o12@R4TCIk;TrLbyEEX{cf~37gmhbzR&1OFVhT)t4 zSEgwq0H6c_=zse@jE#-KFzN2@4rXR%Kn4Ke<{uv);pXNB9v&V50AO}@78Vy5A)QVO zt1&Y2*+dAtt*57_xV*gl?Tk?@7KN=N?RNX~H^3{`b#ZldHS*NAy}ivnB?|+vZ5t;i zC;J*rr_-n?O5e{kP2*}wga8miaBglcWNcz$0u94J*MD{CILC3YTCHL#l?sh56bk73 zesTc&`}?t0YtQqrR4RpXPEJmkYI6g)u8XoPQzNBPDbix-`+jI8O{G%U>2zY(=LR@D zJftRMSw_ckBIh<5jnG{%3?p{^E&%h6?CR=@#_Q|rpsH%*+@+-@&@_#nUteFd+F;*O z-rnBQczh*f$+_SSYczSxG=NlUvtTq6E zaT+CrgxUm6(<1dZp67*H4^>r}Hd8`Y;-jOZFMs__v)TN1yk%Las%j|5G)<=3LI8pw z_|h84vW&W}qiLGhZnx34Z8Qu6%jNQyhOS&LGuIZ@jS@mIpU=mA6@oiEJIsxoPyocg z{f(~cq2B1bV{~*BE0qd&O`!l?*|v?kuJ_#^sH%zw2M3`xn@3#$=>vdk8tbQ*mltpx z2Y3JgG))t}@4mmkhw15Q7#P5y)dlz&e{j6Mz9#J>w%*_00pJ6m(P$)UhDFV0lg60M zW{2wCC?NzhnN0ZYbS{^}TCK*kKmA`Kgy8x4d8i$cx)j5zpD_y!vwi`R)-OQP`U84w V0SQqC{5}8x002ovPDHLkV1i`@t!4lK diff --git a/Telegram/Resources/icons/media_type_song.png b/Telegram/Resources/icons/media_type_song.png deleted file mode 100644 index da6d751ab71632b4c77fc09038ed505417e14d76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 577 zcmV-H0>1r;P)35QMwu_WS*9fYa$j zp-@0H8bv4+f+R_gKJH3nw35jry{qYTN;hAy-|wHgz!Mrbyh==FLSjYgQy=h*FbIG@jkeyjC6 z_8~wjmBMT`d&GJr%Oj>|`|&h@G4^i(@900kP?yW)Yya{z{VBWM4p9_wI2<5~;_m?j zK|rZgLN1qsEX!B*n}Fl-Xd2G*JO+aSJRZ+Y&tCwJ;~+^APNx$Bfq*HX(P-Qaw^%+~ zOpGzg=kwI>_YKFc%jLQn=Yc@LbX<44T>=2A)oOQ*MgUosO#$(EoRH0CO^sf!*YHy+ zm&-TL6pO`A-(s;a1<0~&2w;p+CX=C7t3~Venx@k!RjXABhr{2#$z*ajyi%zchW`W! zf6G%=#k7TLLr=w>(V=e*=}!3Vw@Ip=#mkIbCGIsO7b@Q0maSVYAub zcDupzJpR)Cb#rBb-Py+xr=_%JS;&6b^CQ-NeMiSzSw9334&6h$Nw38d5MPhrbZY18;Nfn+j? zv$HdZqKIfTidZa$TrQ{j2B4b2)zuYtcXzSBzmJQH3nY`tn(S7p2?T>dY;0_()vjt( z+eTkrUeszOiGSufP>H&L*3<>GrY@j0bpfrZ3usMUpjJFRJ(c}j^PiM|^Y(_%Y&Ju$ z*Q24K0oiO8cDudo_#Y#{vMk2N#xOZK2|*C5o~OA0MNycTn83oq0yvJ7JYRExm6a6) z0s-0cCEvuAUNFf)X?Q^9F7*8TtY}dH7#<$R^z^jiK3`r!EX#u9IJC61pja$|Wm%-t zX~m0D6os|5HN|~snl3Nk@pv#kK8~TGAyrRPO8b`{*2(4m{{EwY(P+f{{5<@AzvAX! zF*rD=N}s*Gy>|hg=W%j!^1mm8YEbDtr&Lx~S6^>pgTa7AB2km@0IB#lx*e;V>B(82H>LlgX$eV6|E$3%r%hW+O{WOC%bN zl1wH;9v&V@DwQG!2M1(kW`=Zjc7FWUODi6aD-OT4wIw@zY0)%ISe7MLtCcVeQ#o{L z&Cbp$7BCu(stCv`gTbIEd~a{BcHv(O#Ow7+4xY>9h#&}Bhkq3SKzu$Qspfvj=ksK7 zaglHwr*&`wfPDC?fljAGe}6wlMn>RpIAF0@001{PH;6`~*xA{ED2hm>QurzS1`6oC U<~Pwf_W%F@07*qoM6N<$f+No4z5oCK diff --git a/Telegram/Resources/icons/media_type_video.png b/Telegram/Resources/icons/media_type_video.png deleted file mode 100644 index 48a8321e1d0aa758513fb7613f6c2f572948bc32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 302 zcmV+}0nz@6P)Nkla)73B+@F0Bb?pcmokHV`Jd~#3rc}HSnicc39bAm?0zVF7wLw zlK0>zFEIcRPQ=HV=I?-qU^2##Wf?rrdwTOUO~~^cobvB01wlYf)BMnc5M=5eqGeg=x(=p!+e$mY zw(Yz6-|xZi50FwqDRo}Jx~_<#2yNTmUhvAIm1TM0rIfE}^7p4ANfO$&?W!4!F-p@E zj^jK%o_U_ZIY(7h2R;l#aLzFd0}Q;s%zqzn0iYY=%GmNK8vp4HM%7gw79xWwMsHR(Q@@s(MpV+M<(AkVbYgBup)W^B08+RFK8zTwl#!`#mw zSiURW*Zz1j^Lf^`R_^!Q?+$M6Wp?<`UAfryKzSnL7C3sy=nyD!CCjv9QAdErmr3^9 za;4i67u?NrzHD+nZuw}uP2TXj0c-Ugw!$n%(eE+>$@7(jx-N*l)s@t#L>GJ5^hqYm^=gZaG&5z&qJT>QZsr)s_5VNp)zv{kz+q#A%gT@{kYWXn4i_bfUbhJCZo9_Qvr~ORfBN>Zy^4YvZ!a7vFzB lIIu71QY-6)HW+%w|4)6LJKNKD{J>|N{7SsR$ diff --git a/Telegram/Resources/icons/media_type_voice.png b/Telegram/Resources/icons/media_type_voice.png deleted file mode 100644 index 66deec6dd67f4055196bcbbfdd554693ed143e99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 421 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 Hu0mjfMBA=E literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/send_control_bot_command.png b/Telegram/Resources/icons/send_control_bot_command.png index f18a7c01a1d22a5d1910c97eb4a4597b40129a82..34cc9d6eb02cb8f106965bb64e867f5a2fe0936c 100644 GIT binary patch literal 401 zcmV;C0dD?@P)zIyKiwnFCN5qezbe1$I~3g@n-T zmqxPe@qdhr>q~ vNwT}<%!p$)|f00000NkvXXu0mjfdmOY@ literal 400 zcmV;B0dM|^P)k$>azYR(b$Kz&4eIcd+mXg3TNth*uD-ErbvRn|!x$6Ly!~ z)sXCiY39#o9>dHVNkS6VTAXtfMM0A&iUQ{xVHi?bl0=%OjiBEmP16S&hGE?q$1%p3 z&pSSC#u$<$sSRr_a?SyeBuOLedyC^30OuUa^BjO62wI^*5CD+pIjYirW#MMC0pNH% z{y1J+ucCv&fZOc`z;rt82CdHQdcER#UVF6H>v1}r09dcrzs_rfR@bg9OUC1IH|TD+ z17N@3|2gkvbTk@KmL&jD6m^5HRx1F`=QI6&zdh=C9+%4nfaP-e^oG2IP9_ro?)N*x z;jkMt%Q67A+wD{E3)KA%gHvMiIVwRPHy#RA{=8)fMG ue*J{uqn=9tQ2DDJqbO?iM}1_7B;Enm&xvZ8QgXlm0000j4 z*yo#k`2u*JM|^LrW!@K{Ua#Zfa3H>>X_g1TbzR~aMbWRlvhwBn5_h{@a=lnAa`~QI zMxfnplb1J+V^mf3=_8OEz<4|+enAjC*w+EQ@&U_j{COxjX4?X!P(SSr~w-su;&{;vcDr33fZ=dRd{(Q~!{G}Bh@y!2xUO3s0Mj&y$Ki0m zTCG+dfb_oGz_CW0E5AR^k1*nPYYiNz-F@{{d>J$ApoC! zp;zkz@M1XV+dHN1YXZe~nxf4YZY1C{sbB=Yt(ph%9UGg~rST2{OQLop_IoAD3 zXGv#8fb1itR;%^0Bc@iXb^j3)K*gSP+9dpv(+#455P0?uWd;j3;+NC07*qoM6N<$f(pS=m;e9( literal 904 zcmV;319$w1P)iGB^nvf@jki!M0W&B4Ke(Zm=b zEDo3yBZff}qef#vIM2X?@${BFkA1QHlas*V-hY2R_uk%cO9&xg4oWE`lS#Gky$uCK2lm&<`H%Rnh*c>V@XEG;cTxm@-P>|ZLE%dot>+#QtXc~Di=H$byg zRRx~sS_}Br44OzJEE}fNX_%Ut0znYE%^SxtI5-H?)6U1_?(p!CyuH2IH}#^T zr>CdLv(;+B#KgppJQyywK_ig}G@DK2d3JX8V;|3leq&>Ua2&SX+1YV!>}ExS!5}<7 zJ|e%{+gs@C>+7wGuCA_<;o)JF+uz?OpP!%3&E2f%#l;2kG7JO4;jk?)&lc0%+#F+m zbaZ6f&oj`2g9FBVety2UKt)l6_xE??Q!14lO(U~hSxjqdYa|#9qMR#_r0;TCQI6x_ z=H>=@JU>4}C=_zFqK-iq78V%OY&Po%(i6~=lM~dx)oQ`yS!@-Zf-LB`u6s=6U*+hTTwv};Njr`^}WBpx3#ZZg=-Z}CX+-IMU>my+jD1Mx02n8 zo}ZtievL*0Mn*&@T5{H>tn}4p#Zb9v%d~GGcyB)LIJT< ztJTi=ECE@TnY#dJpX^DJ1c5-nGq^w?0FoqiJ;;@1xzo1YA(P2iHuO`OOy>JdwLkDY z57}(iZ?-ROZEabpIlybU5JkUJ*lsuxlBqZo6&1P5bO><;!-*)2tG~tKWXW!JSwfU1Pa4VgxBkeUkwyU^ zp2#UJoO2Kngb+Y-SA-BCB5=;FlF&KFqbbXh>31k1B+2vnOlxft{vFm@Hcdm4Y}@vl zAcR1Y51FSh#*ifEc_siV03<)_tdu&WuwVU7_bz$wR|WLmZ_;kL`+{_~y1eMWX`5eg z6t7D#48v+aj^ifnme+OBQM}vqPu^|%Bj0K;O%nhh%d&^fjWGa#7$bn?i~7C?08~{4 zt@Unmtu^Yp1^{$jcR6RC=ZBBXqA0Et4a-~>5ylvIo5mO^B6QAOCY(xHp2Y`BW&9uV SDPo=g0000#>h2u-rj z%X?3f6cHk&M3OYdyd^cpkR($|L}~l{LX5GF6JxwwD|CktUDu&-Y*kuo58S{@>9Q>K zd0p4bwZhBsf6CvFe~UE62$CR)7~|zy;h$frU;Fm8)+C?#`q=j!05A+g^O$GGaRdNt z+Xgu2Y7k7*M5R+vl}=RW&Fs|nszBA-L{#xcqk_RA>Hi~*$-2qA!T-fIHu5r!d3DRF5?DKQK~boE*W zqA0>V&*#?KNuK8zMNzkbCBAtVDP_9>&Uqs_%QB>tA|a&h(@h$L5Tul1mSqiHob#rE z)9GX-rD^(h(Or?IsijQ_(KK+mT&yHY>FmPGpp;tLlv34NP`Y?NpRn8Qpk%+_!|U~e zB|r$Vv>+l7@_PbQ1`yFI3WDH$3O(r3_qiwWS{P)2F$P-e)+>P48W>}v)cAq`(CD?$ z(-pMVeP<17tw*V;N0+`qo7l~5!>u1u(^tQ3;-#<|n43Wc9*;-w`u%<%rN);(w^7Cz z7Ddt84vV5d#@Hw|FjX6!gRPA&mG3mLUFJM%V5%2NqqEXyBP%k6fno+|_A^Vv!|9*+~t^2gQU zYDIwV5mOw;vyGVIIQAbg0jzw;2k-%7R)Cccy6cf72?`-lDTT(Ew+lALpi&Bj5SS!M k=U%C<5^H5Sr`14y02~##wkZfwO#lD@07*qoM6N<$g1g7_WB>pF literal 643 zcmV-}0(||6P)sI2#9<&NraOQn;6V z7v$!Ke{vEy^vJKY$F!#iAq08jc^(KM;5ZJ1VF+oOLQ}fkZg4yv!EqcQgaFU;49~-8 z$7;2LAP5=*`%Xa+fU2tHpd816Wm&BO=4DwHa2&^i{wBz$EX$%nHBBpQXqqO>W-|~3 zp={p#*oMO)%;$43O|y_e)3mHD&+~9Pov4St?}IGMjlq?fEX&~gKE)D6QI=r3t~0%8 z1M(d6Bs$!w}H@ey5y+!Ju_eK@cdW zG))2J=R;XA#u(x_X68>E$AB@eJ&xh33R?8C_m|ckmsjrr_foIdLq*CpW7Kq9|I1(b zMe0E7R%Ouh`CR(!a=FwV$Ea%7D~bY1k}%mAMG+{9QhVHgXZ@$uWqs4?KfZ{=X$;VBvYXXA=LeTzej~dGD(tJ8+J*O7#_ngip!3k z=fQHh{5a%du>jBWn528uXAuxWm|g&6l|4}uzd8W;vGF_)q9~RYatR^NwpoB}+l39= zlx^GZoysmaj)To+(>B{n+wHdau;FQ{sV`002ovPDHLkV1jxyExP~! diff --git a/Telegram/Resources/icons/send_control_bot_keyboard_hide.png b/Telegram/Resources/icons/send_control_bot_keyboard_hide.png index 6ada764b54ed16d0f0bd9044ad91600d2a1688b9..ceaaccf73cffb9a36e91007f709b16526385de0d 100644 GIT binary patch literal 342 zcmV-c0jd6pP)$;A0UDv(hpMjJT>$=8d ztu-j6hs2apXsu)Zx~`B?9ycI_psFf_5Mq%rX158Y6vmjC7eYW;mV1NzoJfFwzfBngIL_(on7#o3LHkC*2;oO6+E z97l}f7-Q!g@;rYhf^&s%&N0t3UTvOdaL%u&?7a-LEQ7T+l3Qz$W!Y63U(@>I@xU}q on5GG7nqGb*bn9}L{r}JI2_12bkP5+Y#Q*>R07*qoM6N<$f@N5nJ^%m! literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^;y}#D!3HFcd~W{?q|!ZI978O6mrnNPI$*%z@;vX} ze-*hydQB6ywfwT;R{T-f?54U>CDbKkzif5EG-c!5M)Bpcx%I4kbCM6R>hDng5NPhV z-0}VHh|Z4qOwi>!MI7+{_h=MYyiZojV*3FbpH{SxOv$h@k5_ z_WM28%xblQqA1_}ilSh(T5L-I zyB*5qa$5800}v53O@rroTyxv)7TfKX``q(9XquMAe6|2Ygi@(=*W|r$rBW%Y`RoCR zC~})zW3$;nRnujP)tvOhimo&CE%kY2*=qr3Tez5m)zz%dC?v0=91rPK< z*WqmZVm7@2E7PqJI?J~qa02{h3;f9WJfDR3WNOe5g!E;a9@qsUeomJLS7GEI*m6J- zNJL}cy?F3C2NqPt6BcLCuq>Xm9EJy8@T@ibQwWt(%I+iUkk7Q=tzOsy9cBv=3~2xW N002ovPDHLkV1k)_aB2Vm diff --git a/Telegram/Resources/icons/send_control_emoji.png b/Telegram/Resources/icons/send_control_emoji.png index 65c52d50c90461d2aa9f1601a5c790d4d3a593df..5698c8421b2595abfcc33c1edae0c4961cd4fcd5 100644 GIT binary patch literal 438 zcmV;n0ZIOeP)G67IwScjGxcvPYs@qcsLxg_;fmb8b8*&+`u$V>3N=X zUH4^I*L9`mdD1k^CqDPF$N!=LpePExUXM7A+3)v+VMv;$Plfq>o?5L&qtPHq5>~4f z069P^io$xmX1CiBMG=N!kR%EB`yDAI#bS|ixy0=TOmMwknM@`O zheJlA5y#^ZP1C4UDx_&j9LJo`XX^DjoU5n%E|Son zT>ll$mtu^nwJOH=CBFE+?*P`o70|k0iYJbX~``ZIn{bFbo{YIWx~QIp;SU#}P>)gg{COGXucPNGTCQ dKoX~hKLM~mfGHVAAJzZ>002ovPDHLkV1nRvT7m!o diff --git a/Telegram/Resources/icons/send_control_emoji@2x.png b/Telegram/Resources/icons/send_control_emoji@2x.png index 773350b0034ed0900fb2bf76bb3ddbc886f43b11..8a1a772b5980a692f2c2335d8bb6c1d68650ce62 100644 GIT binary patch literal 747 zcmVQDrYVi7wO>|({iT8-4n!KwNOQd|VVt;MB-n~oyR#nri69UarfKo`-` zR7(rd3^rmwa^CP>3pJ)m>wEM)@W8j+oZNf(?z#6901!ijILzYT0=@HDk?>6zkH=9el~691A%uzGYdA#bTcP z-^cso$;k;nBA3f~@^iVIi+_50+EYN!7A!9>yJD-WtDgMNzAY^+^(xWZ10y3NfntO5 zeFapjRaZ>YG*ABZ^>v^eAK}@bL?S`kwmbbaO_P!&dGaMmqG_5PzHQrFSXda`gaDYy zWT9k)mrqgLwDivy)#!M#T%NN=zhJ}BB6aH=d_!!vP*}>-KCK`R;$S8^Db`35Tm1`e1Cs;ZL>k}^78U& zd~$NK%Ylw!!R&CJq=ygadB}0RaG%NJByi_8APK|L?RJPOiW;Wd>o?qtAnO# z!nSR+S}io2P1I^N)a!NBYBk8RjBGZGXfz7LF!22R?0o|Ocyx3`l5`x0g+hUXAoz}w z*AWDPg+hUjt>VMO!!H*zzF{bFir3XwL?A dY4H6V{{+zv#FES*@W}uG002ovPDHLkV1g~1YM}rC literal 360 zcmV-u0hj)XP);C5!aOHZNO*XVd4Q!xYI%_ZssaPEEjYDR&gbia z=uk&8@s3BkJL3cZVF9d`5cvrGvk(J-G)=MF?EnB>*Wq%x{QBreeo++UoTCSvb5s-s z;VWOkiSmsx^c}{Ss8E#ey$=fB`>0TizdE1K!4Af8jP57OFUvBRSqKsBD8^3!&=RgC z`27Aq2`Qy;yWKDh1B4K7ALV%ttu?H*P)a@T$yz%Lmy|MUzm#%jzqR&J12s((r1f+< z(cy5QEXyd%GTQI=bUYpdUR~EOATB!A3FL_t(Ijm?z3s;fX0g_p;lB4NNLeNgZXd;wp;(lRgNi}(;? zmnL;!8cmhP%0durQYf)7vTkvS98WR_E@|$94gT$qarRoH0U$om*Sm-N{T@k@;Cj8{ zdc7h^672VTC4}G0^E}pd&7Z34nx5zJ=l3swG)-TcpGwp8cYlBof^FL>0d3n-2(bdJ z_P{hvSeB*ySIe?snx=XaNrF@l<=y+YQ}`GCM*vOJJ_BsGTg>zPD@fBc%=5ek-0yeX z?{{dLhH08GO_LJvcswvo6NC_W_I))?1Ix0o+wCxpBQBSV5>ONc#&N`Mw}WL_XqpCJ zMNvT4bp$~G0Dp+%7=7QrH1~avIF11TK@dRKbreMbL{UVNEX#7Ssqgzd9*=C>mTlYe zcs$bg{l)HOS&}59C?X8Q;Q4$eNoHBb&1SRs+_r7nw!PeMHXCMHMv^?A&om6)hT$qSLI{Ro$UM(Ul2ujFb=`Fu9mk=Ra%7b}DJ7+ptAt7^={U~&I2(q+ z!{NZw>BPS8m!`h&c{-hVI2@kKRv#$Oa$T3hFmM=#@5_BDLpAfR>lRmbU8nSh5}@lk hZnxWVt`5?UgmXO1CS)i)s0=(0g$HY z%4uO35{BWbmv*Kp0A1I;+u0bi{8pAFzVB~r>-#=sSuQ+d%sLN@F$}{1fOC$vZOO8X zEX!!y7UvuQ!!X<~uLDJEO;Hq21Ena6N71&;Ii(a)6fKKU6rq%Qn2DP}xy2kV_aSnN bzca0z6v!)SXw00000NkvXXu0mjfx>&IZ diff --git a/Telegram/Resources/icons/send_control_record@2x.png b/Telegram/Resources/icons/send_control_record@2x.png index 7a45e6df279e2dd1e7a9f9ed7ece34eb144743cc..3eba00f2e6fabf1a07195ab3e0c9eb9057445127 100644 GIT binary patch delta 883 zcmV-(1C0Er1&IfcBYy)>NklCT%6n->?P@(N$Co{xu;vjCK-HL1R2Ux+q zlbb_rClQ*Z4&6Jnzo28G2$d8U2?2kAh)uzwqy{u0a=zhv&FgKnM)9S*FFtS~_ug~P z^N^g}d(ZU&Ap9WwKS{op08tbnNfK05g<%*l31;_~uxvjXDrIN7%CS%7WZ6pzO@C*bbx&NKNG4-XHU6VUB; zR~FFebUX?0O;vmcqScp*$z+0^ot>q2e@p-X_CUz8&gTWQyMN=9#}5FdQVIQjAN_tGrBdmS3AnhpK&R7j@z7{Aa!xY!dfjzuSr=d9 z)a!L;&NNK|Kz;zQZQD^Z`@Ct)#FH@@0WU8vO#kTU z2!bFu{VAs(0A62Torz#Dh(sd6IN#jd%zdScFquqneSOW0B@zh)gF&Y+%d+!K$H&KH zS(cNbvMdwFaZCmmi$%}bUn~|GUykEQmStz&vMfraQojq6Kp;SkMuQ2G&*wK*oaXa+ z#;(z5P=6p0U@N0+Hp>`knnsaG#1%{wMUo_mR8=LzFvu_rQdN~CNg`1cUG|YkgfvZK zVrR2icWK7+Jk@G7#<1CJQYaK!rP5_uC={Y*v&qD))oR4^{QTN5nM_iz*JB)7t=8=K z^4wCX6t!9{CRVT4qhvDqeQ}z}WN17dGmg5hQ-7sWAwdw95(|Prl}d$lU1ws9$79N5 zGTw^Qv$HeY-riz=f1eru`1ru{^D`bFAJOf0q3b$;{}(So5O8{Wij$KQ93CDrexIM8 zI6ptf{r&w{D!AUjw58K&YPZ`9GoFZOG#W7***vt{ZAz!p>)j88!(pmct27)Aza^Q6 z;cRe7)oPW(;qbE0wmhMt(I}P6Wl|J{27|#u(!pRrilR`tT&8F=>NR0k%kEe#M!8(h zMV`y$C>D#Y{I__zpqQIDUyg2GE+gjQ#}B1j0Y5Ha?Ihe3{sRe%0OK=3)T{si002ov JPDHLkV1f&6tP%hK delta 657 zcmV;C0&e|@2dD**BYy&KNklyrrIjhI^c94;#XX*zUH@W|bIvXNU?KZ6-+b;EoEZQBTrzZB$0&-B zMHEG->-r+D&+*tnQ4}#v)1G4%X_}%aitiO5iXxU}IqSSbS$~#GM9d#LpU)3F?{Ge! z-y=ZNw8hRlXqq-FU<)%i9*=WJUbdZ7SXI^2H&9j8>6|f5UeF2{50U`n-g6DbP?vVAjLFR+ce!t(H53qc{9|CrP=Xq*=yWO59 zXKh(-0Dx`NG=J2>$HxaXzh19T3t(+o?xtyAo98*TxZCZh`RR0eS^#Uya_4yt+ayV- z#lzu1&9f{6-}e_g_kAC-ESuy`k_3PdLZ#AmT_eYFRO-z0{6lqNhcr!*=Xr{Q<2bDA znmR`a`CA#W8-`)@@^?WH(DS@m=bq=GAP7Bs!!UXe3V&T`X4`gebBF{0P*D_gT^Ea@ z_&Q${1-h<_ilPkK2!haiNZYpm36LZS+qR_+gkiX(#GV{s7*gDA+oB{%ba85$X74&3 z$9dVg<2XHD(=>;xPm0U=Rd7z9@>u%k!}=NL|;9+Aq4U_hODX+CAyN zWMhB1x__?GwrwPYpe)N@HnJ=uAp~vP9(3F}A9+dpPauloU^t#d6h%`loAIl(EbC?G zmSxR_Y&xE@EKgqT5V}y^hiPogHYn~v2mw`9VZYyls;bmp9LJC(3B+*>m&@f*N?0V| r9>`=ibDfYe{C^qIDqt1x)A$235i<#ck(@LD015yANkvXXu0mjfW1}@U diff --git a/Telegram/Resources/icons/send_control_send.png b/Telegram/Resources/icons/send_control_send.png new file mode 100644 index 0000000000000000000000000000000000000000..6414c8bfaa3d896cf5b3636b05df309a7d185b4d GIT binary patch literal 598 zcmV-c0;&CpP)x3tR?+0q^ea7!HRQ3*zzk$^w^PGcVZejo989PM@+ z-oHP1y-LQCr&1l zwP!(+&1RElXJ_2n+M1rh(b3UH!Hq_Pr>Cd%cs#R*e0h1fR`BEFBU7mqg%Dpq({j1I zQt-pW1Cz<*@>TKt{Jd0ftyZIHnyXcXVHjVQ-DEOhwOVB&kyx+nj*pLL27i8j(y}bZ zVzJHY`uzNSD%f!xRw@-nqtQ)*34mSzkR%C?<3I=j%d$``7V+}(vhasm7VhosF_X#g k@bGZs6aTyZ>!R=G7hy^Lj%k*TdjJ3c07*qoM6N<$g4Rao14CyXWOy9@W9#g>>NIy^ZYo^8B+lu z{@|#U29TS-OTcEc;q2M7O0gU`1i&+A&hW#B4_sMU;rRGC8yXszY&+!T_d#D@A5*ir zxyi}NNj5e%Dh-&MKv!26B}wA$Y;A3EVq$`qFJD#~bbo=`+FFLg;S7Ofe*OAIm&?WG z=4PdVvkU0;dcJw{W>0}+5{U$-rl#1@(V;YWx&Q!^qpuV25eyg+MfE4R0|^OhjVXf(=~FJH2~y`5^cS{VVO(a6=+)dLBn zW^Qh-ls%GO;rIJx3y{Aj`|H=QOCge8@$~7_f(WFFc)ecUxN(DmAQWGqx3{;T0;!pw zpXaSxw~8xp;lc&RVzHu0BoYbwd_FoH4(fEe!V6ffR(}5cxo85Z@p`>e9V^+H5x5xpN0suUH`zZr;3!Kp>D-uaOI$j3h~rBnj*5>sVi3&uF*X?Xm=nMk7onQ%3#1 z0#~kF!Leh}=@EF2ylR&*sw=KcHkynp{bD=I2-Uq&B1c%U>lmzS4$ z_wHSaqPXuenso@BJ9kc2cgu;-=fmjeC|_twzei4JQj=b`Sa&&X=y1Yc&0#GTbr!+;nmeuj*X46uCA^W-~@I($j+ZX53N>< ze6z8!f${NiOiWDRLvd7T`IMBs9{FfuZNP$*PTuzW%QEGsMH;^JaX zLnRW4aBy&tH8nLQ8!G#h-v@TPouN=@U+^D4e(>?*$2@uRq|(rt0v#P4jK||y!M}X@ z!hwMS9zT9uY1q9d@u^d%pwXmnPQQKohG);7VP<9q;cys-H3Yz>rY3&>{+%TG>C-1Z zeE5)+m6eL$;T+0;AL{Dr*xTF7s;Vlbp>i$-`&B}z{9XT*_!ILF98_7buwoB@00000 LNkvXXu0mjf=Tuf? literal 0 HcmV?d00001 diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index 128f748a8..87c9a5aee 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -137,12 +137,14 @@ emojiPanHeaderBg: #fffffff2; // emojiPanBg; historyComposeAreaBg: windowBg; historyComposeIconFg: menuIconFg; historyComposeIconFgOver: menuIconFgOver; +historySendIconFg: windowActiveBg; +historySendIconFgOver: windowActiveBg; historyPinnedBg: historyComposeAreaBg; historyReplyBg: historyComposeAreaBg; historyReplyCancelIconFg: cancelIconFg; historyReplyCancelIconFgOver: cancelIconFgOver; -historySendBg: historyComposeAreaBg; -historySendBgOver: #f5f5f5; // historySendBg; +historyComposeButtonBg: historyComposeAreaBg; +historyComposeButtonBgOver: #f5f5f5; historyTextInFg: windowTextFg; historyTextOutFg: windowTextFg; historyCaptionInFg: historyTextInFg; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 437c2c747..bc36e866d 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -104,13 +104,12 @@ dialogsFilter: flatInput(inpDefGray) { textMrg: margins(32px, 3px, 32px, 3px); } dialogsCancelSearch: IconButton(dialogsMenuToggle) { - icon: icon {{ "dialogs_cancel_search", #a6a6a6, point(0px, 1px) }}; + icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg, point(0px, 1px) }}; + iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver, point(0px, 1px) }}; } dialogsMenu: DropdownMenu(defaultDropdownMenu) { menu: Menu(defaultMenu) { - skip: 8px; - itemIconPosition: point(15px, 8px); itemPadding: margins(56px, 10px, 56px, 12px); } diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index b4db5ee2f..c0e21850f 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -541,7 +541,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#'); int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize; int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right(); - int32 htagleft = st::historyAttachPhoto.width + st::historyComposeField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; + int32 htagleft = st::historyAttach.width + st::historyComposeField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; if (!_srows->isEmpty()) { int32 rows = rowscount(_srows->size(), _stickersPerRow); diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index d373a3282..8cccec7ab 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -157,29 +157,61 @@ historyComposeField: flatTextarea { historyComposeFieldMaxHeight: 224px; // historyMinHeight: 56px; -historyAttachDocument: IconButton { +historySendPadding: 9px; +historySendRight: 2px; + +historyComposeButton: flatButton { + duration: 200; + cursor: cursor(pointer); + + color: btnYesColor; + overColor: btnYesHover; + + bgColor: historyComposeButtonBg; + overBgColor: historyComposeButtonBgOver; + + width: -32px; + height: 46px; + + textTop: 12px; + overTextTop: 12px; + downTextTop: 13px; + + font: font(16px); + overFont: font(16px); +} +historyUnblock: flatButton(historyComposeButton) { + color: #d15948; + overColor: #d15948; +} + +historySend: IconButton { width: 46px; height: 46px; - icon: icon {{ "media_type_file", historyComposeIconFg, point(2px, 2px) }}; - iconOver: icon {{ "media_type_file", historyComposeIconFgOver, point(2px, 2px) }}; + icon: icon {{ "send_control_send", historySendIconFg }}; + iconOver: icon {{ "send_control_send", historySendIconFgOver }}; - iconPosition: point(9px, 9px); - iconPositionDown: point(9px, 10px); + iconPosition: point(11px, 11px); + iconPositionDown: point(11px, 11px); } -historyAttachPhoto: IconButton(historyAttachDocument) { - icon: icon {{ "media_type_photo", historyComposeIconFg, point(2px, 2px) }}; - iconOver: icon {{ "media_type_photo", historyComposeIconFgOver, point(2px, 2px) }}; +historyAttach: IconButton(historySend) { + icon: icon {{ "send_control_attach", historyComposeIconFg }}; + iconOver: icon {{ "send_control_attach", historyComposeIconFgOver }}; } -historyAttachEmoji: IconButton(historyAttachDocument) { - width: 33px; +historyAttachFileIcon: icon {{ "media_type_file", historyComposeIconFg }}; +historyAttachFileIconOver: icon {{ "media_type_file", historyComposeIconFgOver }}; +historyAttachPhotoIcon: icon {{ "media_type_photo", historyComposeIconFg }}; +historyAttachPhotoIconOver: icon {{ "media_type_photo", historyComposeIconFgOver }}; + +historyAttachEmoji: IconButton(historyAttach) { icon: icon {{ "send_control_emoji", historyComposeIconFg }}; iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }}; - iconPosition: point(12px, 16px); - iconPositionDown: point(12px, 16px); + iconPosition: point(15px, 15px); + iconPositionDown: point(15px, 15px); } -historyEmojiCircle: size(19px, 19px); +historyEmojiCircle: size(20px, 20px); historyEmojiCirclePeriod: 1500; historyEmojiCircleDuration: 500; historyEmojiCircleTop: 13px; @@ -187,21 +219,18 @@ historyEmojiCircleLine: 2px; historyEmojiCircleFg: historyComposeIconFg; historyEmojiCircleFgOver: historyComposeIconFgOver; historyEmojiCirclePart: 3.5; -historyBotKeyboardShow: IconButton(historyAttachEmoji) { +historyBotKeyboardShow: IconButton(historySend) { icon: icon {{ "send_control_bot_keyboard", historyComposeIconFg }}; iconOver: icon {{ "send_control_bot_keyboard", historyComposeIconFgOver }}; - - iconPosition: point(6px, 12px); - iconPositionDown: point(6px, 12px); } -historyBotKeyboardHide: IconButton(historyAttachEmoji) { +historyBotKeyboardHide: IconButton(historySend) { icon: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFg }}; iconOver: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFgOver }}; - iconPosition: point(5px, 17px); - iconPositionDown: point(5px, 17px); + iconPosition: point(11px, 16px); + iconPositionDown: point(11px, 16px); } -historyBotCommandStart: IconButton(historyBotKeyboardShow) { +historyBotCommandStart: IconButton(historySend) { icon: icon {{ "send_control_bot_command", historyComposeIconFg }}; iconOver: icon {{ "send_control_bot_command", historyComposeIconFgOver }}; } @@ -220,27 +249,16 @@ historyRecordFont: font(13px); historyRecordDurationFg: #000000; historyRecordTextTop: 14px; +historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { + menu: Menu(defaultMenu) { + itemIconPosition: point(13px, 6px); + itemPadding: margins(54px, 11px, 54px, 11px); + } +} historyMediaTypeFile: icon {{ "media_type_file", menuIconFg, point(2px, 2px) }}; historyMediaTypeFileOver: icon {{ "media_type_file", menuIconFgOver, point(2px, 2px) }}; historyMediaTypePhoto: icon {{ "media_type_photo", menuIconFg, point(2px, 2px) }}; historyMediaTypePhotoOver: icon {{ "media_type_photo", menuIconFgOver, point(2px, 2px) }}; -historyMediaTypeVideo: icon {{ "media_type_video", menuIconFg, point(2px, 2px) }}; -historyMediaTypeVideoOver: icon {{ "media_type_video", menuIconFgOver, point(2px, 2px) }}; -historyMediaTypeSong: icon {{ "media_type_song", menuIconFg, point(0px, 0px) }}; -historyMediaTypeSongOver: icon {{ "media_type_song", menuIconFgOver, point(0px, 0px) }}; -historyMediaTypeVoice: icon {{ "media_type_voice", menuIconFg, point(2px, 2px) }}; -historyMediaTypeVoiceOver: icon {{ "media_type_voice", menuIconFgOver, point(2px, 2px) }}; -historyMediaTypeLink: icon {{ "media_type_link", menuIconFg, point(2px, 2px) }}; -historyMediaTypeLinkOver: icon {{ "media_type_link", menuIconFgOver, point(2px, 2px) }}; - -historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { - menu: Menu(defaultMenu) { - skip: 5px; - - itemIconPosition: point(12px, 6px); - itemPadding: margins(48px, 11px, 48px, 11px); - } -} historySilentToggle: IconButton(historyBotKeyboardShow) { icon: icon {{ "send_control_silent_off", historyComposeIconFg }}; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 08159e603..4b1ef2960 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1702,7 +1702,7 @@ void HistoryInner::toggleScrollDateShown() { _scrollDateShown = !_scrollDateShown; auto from = _scrollDateShown ? 0. : 1.; auto to = _scrollDateShown ? 1. : 0.; - _scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::historyAttachPhoto.duration); + _scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::historyAttach.duration); } void HistoryInner::repaintScrollDateCallback() { @@ -2303,7 +2303,7 @@ void HistoryInner::onParentGeometryChanged() { } MessageField::MessageField(HistoryWidget *history, const style::flatTextarea &st, const QString &ph, const QString &val) : FlatTextarea(history, st, ph, val), history(history) { - setMinHeight(st::btnSend.height - 2 * st::sendPadding); + setMinHeight(st::historySend.height - 2 * st::historySendPadding); setMaxHeight(st::historyComposeFieldMaxHeight); } @@ -2995,13 +2995,12 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _historyToEnd(this) , _fieldAutocomplete(this) , _reportSpamPanel(this) -, _send(this, lang(lng_send_button), st::btnSend) -, _unblock(this, lang(lng_unblock_button), st::btnUnblock) -, _botStart(this, lang(lng_bot_start), st::btnSend) -, _joinChannel(this, lang(lng_channel_join), st::btnSend) -, _muteUnmute(this, lang(lng_channel_mute), st::btnSend) -, _attachDocument(this, st::historyAttachDocument) -, _attachPhoto(this, st::historyAttachPhoto) +, _send(this, st::historySend) +, _unblock(this, lang(lng_unblock_button), st::historyUnblock) +, _botStart(this, lang(lng_bot_start), st::historyComposeButton) +, _joinChannel(this, lang(lng_channel_join), st::historyComposeButton) +, _muteUnmute(this, lang(lng_channel_mute), st::historyComposeButton) +, _attachToggle(this, st::historyAttach) , _attachEmoji(this, st::historyAttachEmoji) , _botKeyboardShow(this, st::historyBotKeyboardShow) , _botKeyboardHide(this, st::historyBotKeyboardHide) @@ -3035,8 +3034,18 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(_joinChannel, SIGNAL(clicked()), this, SLOT(onJoinChannel())); connect(_muteUnmute, SIGNAL(clicked()), this, SLOT(onMuteUnmute())); connect(_silent, SIGNAL(clicked()), this, SLOT(onBroadcastSilentChange())); - connect(_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect())); - connect(_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect())); + _attachToggle->setClickedCallback([this] { + if (cDefaultAttach() == dbidaPhoto) { + onPhotoSelect(); + } else { + onDocumentSelect(); + } + }); + if (cDefaultAttach() == dbidaPhoto) { + _attachToggle->setIcon(&st::historyAttachPhotoIcon, &st::historyAttachPhotoIconOver); + } else { + _attachToggle->setIcon(&st::historyAttachFileIcon, &st::historyAttachFileIconOver); + } connect(_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); connect(_field, SIGNAL(cancelled()), this, SLOT(onCancel())); connect(_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed())); @@ -3112,16 +3121,14 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _reportSpamPanel.move(0, 0); _reportSpamPanel.hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _attachEmoji->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); _silent->hide(); _botCommandStart->hide(); - _attachDocument->installEventFilter(_attachType); - _attachPhoto->installEventFilter(_attachType); + _attachToggle->installEventFilter(_attachType); _attachEmoji->installEventFilter(_emojiPan); connect(_botKeyboardShow, SIGNAL(clicked()), this, SLOT(onKbToggle())); @@ -4502,8 +4509,7 @@ void HistoryWidget::updateControlsVisibility() { _fieldAutocomplete->hide(); _field->hide(); _fieldBarCancel->hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _attachEmoji->hide(); _silent->hide(); _historyToEnd->hide(); @@ -4561,13 +4567,11 @@ void HistoryWidget::updateControlsVisibility() { _send->hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); _botStart->hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _silent->hide(); _kbScroll.hide(); _fieldBarCancel->hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _attachEmoji->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); @@ -4597,8 +4601,7 @@ void HistoryWidget::updateControlsVisibility() { _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _silent->hide(); _kbScroll.hide(); _fieldBarCancel->hide(); @@ -4626,8 +4629,7 @@ void HistoryWidget::updateControlsVisibility() { _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _silent->hide(); if (_kbShown) { _kbScroll.show(); @@ -4664,13 +4666,7 @@ void HistoryWidget::updateControlsVisibility() { } } } - if (cDefaultAttach() == dbidaPhoto) { - _attachDocument->hide(); - _attachPhoto->show(); - } else { - _attachDocument->show(); - _attachPhoto->hide(); - } + _attachToggle->show(); if (hasSilentToggle()) { _silent->show(); } else { @@ -4696,13 +4692,11 @@ void HistoryWidget::updateControlsVisibility() { _botStart->hide(); _joinChannel->hide(); _muteUnmute->hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _silent->hide(); _kbScroll.hide(); _fieldBarCancel->hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _attachEmoji->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); @@ -5456,8 +5450,7 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: _kbScroll.hide(); _reportSpamPanel.hide(); _historyToEnd->hide(); - _attachDocument->hide(); - _attachPhoto->hide(); + _attachToggle->hide(); _attachEmoji->hide(); _fieldAutocomplete->hide(); _silent->hide(); @@ -5545,7 +5538,7 @@ void HistoryWidget::animStop() { } void HistoryWidget::step_record(float64 ms, bool timer) { - float64 dt = ms / st::btnSend.duration; + float64 dt = ms / st::historyComposeButton.duration; if (dt >= 1 || !_send->isHidden() || isBotStart() || isBlocked()) { _a_record.stop(); a_recordDown.finish(); @@ -5571,15 +5564,13 @@ void HistoryWidget::step_recording(float64 ms, bool timer) { } else { a_recordingLevel.update(dt, anim::linear); } - if (timer) update(_attachDocument->geometry()); + if (timer) update(_attachToggle->geometry()); } void HistoryWidget::onPhotoSelect() { if (!_history) return; - _attachDocument->clearState(); - _attachDocument->hide(); - _attachPhoto->show(); + _attachToggle->setIcon(&st::historyAttachPhotoIcon, &st::historyAttachPhotoIconOver); _attachType->hideFast(); if (cDefaultAttach() != dbidaPhoto) { @@ -5605,9 +5596,7 @@ void HistoryWidget::onPhotoSelect() { void HistoryWidget::onDocumentSelect() { if (!_history) return; - _attachPhoto->clearState(); - _attachPhoto->hide(); - _attachDocument->show(); + _attachToggle->setIcon(&st::historyAttachFileIcon, &st::historyAttachFileIconOver); _attachType->hideFast(); if (cDefaultAttach() != dbidaDocument) { @@ -5663,7 +5652,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { QPoint pos(e ? e->pos() : mapFromGlobal(QCursor::pos())); bool inRecord = _send->geometry().contains(pos); bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); - bool inReplyEdit = QRect(st::historyReplySkip, _field->y() - st::sendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId()); + bool inReplyEdit = QRect(st::historyReplySkip, _field->y() - st::historySendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId()); bool inPinnedMsg = QRect(0, 0, width(), st::historyReplyHeight).contains(pos) && _pinnedBar; if (inRecord != _inRecord) { _inRecord = inRecord; @@ -5692,7 +5681,7 @@ void HistoryWidget::leaveToChildEvent(QEvent *e, QWidget *child) { // e -- from void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) { if (_replyForwardPressed) { _replyForwardPressed = false; - update(0, _field->y() - st::sendPadding - st::historyReplyHeight, width(), st::historyReplyHeight); + update(0, _field->y() - st::historySendPadding - st::historyReplyHeight, width(), st::historyReplyHeight); } if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; @@ -6417,47 +6406,47 @@ void HistoryWidget::updateOnlineDisplayTimer() { } void HistoryWidget::moveFieldControls() { - int w = width(), h = height(), right = w, bottom = h, keyboardHeight = 0; - int maxKeyboardHeight = st::historyComposeFieldMaxHeight - _field->height(); + auto keyboardHeight = 0; + auto bottom = height(); + auto maxKeyboardHeight = st::historyComposeFieldMaxHeight - _field->height(); _keyboard.resizeToWidth(width(), maxKeyboardHeight); if (_kbShown) { keyboardHeight = qMin(_keyboard.height(), maxKeyboardHeight); bottom -= keyboardHeight; - _kbScroll.setGeometry(0, bottom, w, keyboardHeight); + _kbScroll.setGeometry(0, bottom, width(), keyboardHeight); } -// _attachType ----------------------------------------------------------- _emojiPan --------- _fieldBarCancel +// _attachToggle --------------------------------------------------------- _emojiPan --------- _fieldBarCancel // (_attachDocument|_attachPhoto) _field (_silent|_cmdStart|_kbShow) (_kbHide|_attachEmoji) [_broadcast] _send // (_botStart|_unblock|_joinChannel|_muteUnmute) - int buttonsBottom = bottom - _attachDocument->height(); - _attachDocument->move(0, buttonsBottom); - _attachPhoto->move(0, buttonsBottom); - _field->move(_attachDocument->width(), bottom - _field->height() - st::sendPadding); - _send->move(right - _send->width(), buttonsBottom); + auto buttonsBottom = bottom - _attachToggle->height(); + auto left = 0; + _attachToggle->moveToLeft(left, buttonsBottom); left += _attachToggle->width(); + _field->moveToLeft(left, bottom - _field->height() - st::historySendPadding); + auto right = st::historySendRight; + _send->moveToRight(right, buttonsBottom); right += _send->width(); if (_inlineBotCancel) _inlineBotCancel->move(_send->pos()); - right -= _send->width(); - _attachEmoji->move(right - _attachEmoji->width(), buttonsBottom); - _botKeyboardHide->move(right - _botKeyboardHide->width(), buttonsBottom); - right -= _attachEmoji->width(); - _botKeyboardShow->move(right - _botKeyboardShow->width(), buttonsBottom); - _botCommandStart->move(right - _botCommandStart->width(), buttonsBottom); - _silent->move(right - _silent->width(), buttonsBottom); + _attachEmoji->moveToRight(right, buttonsBottom); + _botKeyboardHide->moveToRight(right, buttonsBottom); right += _botKeyboardHide->width(); + _botKeyboardShow->moveToRight(right, buttonsBottom); + _botCommandStart->moveToRight(right, buttonsBottom); + _silent->moveToRight(right, buttonsBottom); - right = w; - _fieldBarCancel->move(right - _fieldBarCancel->width(), _field->y() - st::sendPadding - _fieldBarCancel->height()); - _attachType->move(0, _attachDocument->y() - _attachType->height()); + _fieldBarCancel->moveToRight(0, _field->y() - st::historySendPadding - _fieldBarCancel->height()); + _attachType->moveToLeft(0, _attachToggle->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()); - _joinChannel->setGeometry(0, bottom - _joinChannel->height(), w, _joinChannel->height()); - _muteUnmute->setGeometry(0, bottom - _muteUnmute->height(), w, _muteUnmute->height()); + auto fullWidthButtonRect = QRect(0, bottom - _botStart->height(), width(), _botStart->height()); + _botStart->setGeometry(fullWidthButtonRect); + _unblock->setGeometry(fullWidthButtonRect); + _joinChannel->setGeometry(fullWidthButtonRect); + _muteUnmute->setGeometry(fullWidthButtonRect); } void HistoryWidget::updateFieldSize() { bool kbShowShown = _history && !_kbShown && _keyboard.hasMarkup(); - int fieldWidth = width() - _attachDocument->width(); + int fieldWidth = width() - _attachToggle->width(); fieldWidth -= _send->width(); fieldWidth -= _attachEmoji->width(); if (kbShowShown) fieldWidth -= _botKeyboardShow->width(); @@ -6525,14 +6514,14 @@ void HistoryWidget::onCheckFieldAutocomplete() { void HistoryWidget::updateFieldPlaceholder() { if (_editMsgId) { _field->setPlaceholder(lang(lng_edit_message_text)); - _send->setText(lang(lng_settings_save)); +// _send->setText(lang(lng_settings_save)); } else { if (_inlineBot && _inlineBot != LookingUpInlineBot) { _field->setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); } else { _field->setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_silent->checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_message_ph)); } - _send->setText(lang(lng_send_button)); +// _send->setText(lang(lng_send_button)); } } @@ -7097,7 +7086,7 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh newScrollHeight -= _unblock->height(); } else { if (_canSendMessages) { - newScrollHeight -= (_field->height() + 2 * st::sendPadding); + newScrollHeight -= (_field->height() + 2 * st::historySendPadding); } if (_editMsgId || replyToId() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) { newScrollHeight -= st::historyReplyHeight; @@ -7400,7 +7389,7 @@ void HistoryWidget::updateToEndVisibility() { } void HistoryWidget::mousePressEvent(QMouseEvent *e) { - _replyForwardPressed = QRect(0, _field->y() - st::sendPadding - st::historyReplyHeight, st::historyReplySkip, st::historyReplyHeight).contains(e->pos()); + _replyForwardPressed = QRect(0, _field->y() - st::historySendPadding - st::historyReplyHeight, st::historyReplySkip, st::historyReplyHeight).contains(e->pos()); if (_replyForwardPressed && !_fieldBarCancel->isHidden()) { updateField(); } else if (_inRecord && cHasAudioCapture()) { @@ -8480,7 +8469,7 @@ void HistoryWidget::updateField() { } void HistoryWidget::drawField(Painter &p, const QRect &rect) { - int32 backy = _field->y() - st::sendPadding, backh = _field->height() + 2 * st::sendPadding; + int32 backy = _field->y() - st::historySendPadding, backh = _field->height() + 2 * st::historySendPadding; Text *from = 0, *text = 0; bool serviceColor = false, hasForward = readyToForward(); ImagePtr preview; @@ -8644,20 +8633,20 @@ void HistoryWidget::drawRecording(Painter &p) { p.setRenderHint(QPainter::HighQualityAntialiasing); float64 delta = qMin(float64(a_recordingLevel.current()) / 0x4000, 1.); int32 d = 2 * qRound(st::historyRecordSignalMin + (delta * (st::historyRecordSignalMax - st::historyRecordSignalMin))); - p.drawEllipse(_attachPhoto->x() + (_attachEmoji->width() - d) / 2, _attachPhoto->y() + (_attachPhoto->height() - d) / 2, d, d); + p.drawEllipse(_attachToggle->x() + (_attachEmoji->width() - d) / 2, _attachToggle->y() + (_attachToggle->height() - d) / 2, d, d); p.setRenderHint(QPainter::HighQualityAntialiasing, false); QString duration = formatDurationText(_recordingSamples / AudioVoiceMsgFrequency); p.setFont(st::historyRecordFont); p.setPen(st::historyRecordDurationFg); - p.drawText(_attachPhoto->x() + _attachEmoji->width(), _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration); + p.drawText(_attachToggle->x() + _attachEmoji->width(), _attachToggle->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration); - int32 left = _attachPhoto->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send->width() - st::historyRecordVoice.width()) / 2); + int32 left = _attachToggle->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send->width() - st::historyRecordVoice.width()) / 2); int32 right = width() - _send->width(); p.setPen(anim::pen(st::historyRecordCancel, st::historyRecordCancelActive, a_recordCancelActive.current())); - p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, lang(lng_record_cancel)); + p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachToggle->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, lang(lng_record_cancel)); } void HistoryWidget::drawPinnedBar(Painter &p) { @@ -8766,12 +8755,12 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { } if (_scroll.isHidden()) { p.setClipRect(_scroll.geometry()); - HistoryLayout::paintEmpty(p, width(), height() - _field->height() - 2 * st::sendPadding); + HistoryLayout::paintEmpty(p, width(), height() - _field->height() - 2 * st::historySendPadding); } } else { style::font font(st::msgServiceFont); int32 w = font->width(lang(lng_willbe_history)) + st::msgPadding.left() + st::msgPadding.right(), h = font->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + 2; - QRect tr((width() - w) / 2, (height() - _field->height() - 2 * st::sendPadding - h) / 2, w, h); + QRect tr((width() - w) / 2, (height() - _field->height() - 2 * st::historySendPadding - h) / 2, w, h); HistoryLayout::ServiceMessagePainter::paintBubble(p, tr.x(), tr.y(), tr.width(), tr.height()); p.setPen(st::msgServiceColor->p); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 8af783d46..1af2d8003 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -1099,15 +1099,14 @@ private: ReportSpamPanel _reportSpamPanel; - ChildWidget _send; + ChildWidget _send; ChildWidget _unblock; ChildWidget _botStart; ChildWidget _joinChannel; ChildWidget _muteUnmute; mtpRequestId _unblockRequest = 0; mtpRequestId _reportSpamRequest = 0; - ChildWidget _attachDocument; - ChildWidget _attachPhoto; + ChildWidget _attachToggle; ChildWidget _attachEmoji; ChildWidget _botKeyboardShow; ChildWidget _botKeyboardHide; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 3f9ff7e99..80c4cc1d5 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "styles/style_dialogs.h" -#include "styles/style_history.h" #include "ui/buttons/peer_avatar_button.h" #include "ui/buttons/round_button.h" #include "ui/widgets/shadow.h" @@ -76,7 +75,7 @@ MainWidget::MainWidget(QWidget *parent) : TWidget(parent) , _topBar(this) , _playerPlaylist(this, Media::Player::Panel::Layout::OnlyPlaylist) , _playerPanel(this, Media::Player::Panel::Layout::Full) -, _mediaType(this, st::historyAttachDropdownMenu) +, _mediaType(this, st::defaultDropdownMenu) , _api(new ApiWrap(this)) { MTP::setGlobalDoneHandler(rpcDone(&MainWidget::updateReceived)); _ptsWaiter.setRequesting(true); @@ -1386,12 +1385,12 @@ void MainWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { for (int32 i = 0; i < OverviewCount; ++i) { if (mask & (1 << i)) { switch (i) { - case OverviewPhotos: _mediaType->addAction(lang(lng_media_type_photos), this, SLOT(onPhotosSelect()), &st::historyMediaTypePhoto, &st::historyMediaTypePhotoOver); break; - case OverviewVideos: _mediaType->addAction(lang(lng_media_type_videos), this, SLOT(onVideosSelect()), &st::historyMediaTypeVideo, &st::historyMediaTypeVideoOver); break; - case OverviewMusicFiles: _mediaType->addAction(lang(lng_media_type_songs), this, SLOT(onSongsSelect()), &st::historyMediaTypeSong, &st::historyMediaTypeSongOver); break; - case OverviewFiles: _mediaType->addAction(lang(lng_media_type_files), this, SLOT(onDocumentsSelect()), &st::historyMediaTypeFile, &st::historyMediaTypeFileOver); break; - case OverviewVoiceFiles: _mediaType->addAction(lang(lng_media_type_audios), this, SLOT(onAudiosSelect()), &st::historyMediaTypeVoice, &st::historyMediaTypeVoiceOver); break; - case OverviewLinks: _mediaType->addAction(lang(lng_media_type_links), this, SLOT(onLinksSelect()), &st::historyMediaTypeLink, &st::historyMediaTypeLinkOver); break; + case OverviewPhotos: _mediaType->addAction(lang(lng_media_type_photos), this, SLOT(onPhotosSelect())); break; + case OverviewVideos: _mediaType->addAction(lang(lng_media_type_videos), this, SLOT(onVideosSelect())); break; + case OverviewMusicFiles: _mediaType->addAction(lang(lng_media_type_songs), this, SLOT(onSongsSelect())); break; + case OverviewFiles: _mediaType->addAction(lang(lng_media_type_files), this, SLOT(onDocumentsSelect())); break; + case OverviewVoiceFiles: _mediaType->addAction(lang(lng_media_type_audios), this, SLOT(onAudiosSelect())); break; + case OverviewLinks: _mediaType->addAction(lang(lng_media_type_links), this, SLOT(onLinksSelect())); break; } } } diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp index 710bd4a39..a2971788e 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp @@ -81,10 +81,6 @@ void ScaleWidget::onAutoChosen() { } void ScaleWidget::setScale(DBIScale newScale) { - if (cConfigScale() == newScale) return; - - cSetConfigScale(newScale); - Local::writeSettings(); if (newScale == dbisAuto && !_auto->checked()) { _auto->setChecked(true); } else if (newScale != dbisAuto && _auto->checked()) { @@ -94,10 +90,17 @@ void ScaleWidget::setScale(DBIScale newScale) { if (_scale->activeSection() != newScale - 1) { _scale->setActiveSection(newScale - 1); } - if (cEvalScale(cConfigScale()) != cEvalScale(cRealScale())) { - auto box = new ConfirmBox(lang(lng_settings_need_restart), lang(lng_settings_restart_now), st::defaultBoxButton, lang(lng_settings_restart_later)); + + if (cEvalScale(newScale) != cEvalScale(cRealScale())) { + _newScale = newScale; + + auto box = new ConfirmBox(lang(lng_settings_need_restart), lang(lng_settings_restart_now), st::defaultBoxButton, lang(lng_cancel)); connect(box, SIGNAL(confirmed()), this, SLOT(onRestartNow())); + connect(box, SIGNAL(cancelled()), this, SLOT(onCancel())); Ui::showLayer(box); + } else { + cSetConfigScale(newScale); + Local::writeSettings(); } } @@ -116,7 +119,13 @@ void ScaleWidget::scaleChanged() { } void ScaleWidget::onRestartNow() { + cSetConfigScale(_newScale); + Local::writeSettings(); App::restart(); } +void ScaleWidget::onCancel() { + setScale(cRealScale()); +} + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.h b/Telegram/SourceFiles/settings/settings_scale_widget.h index 1f975a726..c75cacf6f 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.h +++ b/Telegram/SourceFiles/settings/settings_scale_widget.h @@ -38,6 +38,7 @@ public: private slots: void onAutoChosen(); void onRestartNow(); + void onCancel(); private: void scaleChanged(); @@ -47,6 +48,8 @@ private: ChildWidget _auto = { nullptr }; ChildWidget _scale = { nullptr }; + DBIScale _newScale = dbisAuto; + }; } // namespace Settings diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp index bb1fc740b..832f89040 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp @@ -143,7 +143,7 @@ EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : Button( void EmojiButton::paintEvent(QPaintEvent *e) { Painter p(this); - uint64 ms = getms(); + auto ms = getms(); p.fillRect(e->rect(), st::historyComposeAreaBg); diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 40604159d..2945416cd 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -206,7 +206,7 @@ discreteSliderDuration: 200; defaultMenuArrow: icon {{ "dropdown_submenu_arrow", #373737 }}; defaultMenu: Menu { - skip: 5px; + skip: 8px; itemBg: windowBg; itemBgOver: windowOverBg; From 475b35bf5585a81b9d0d01ea666b2793c2909fdf Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 8 Nov 2016 17:07:25 +0300 Subject: [PATCH 025/100] Custom title on macOS 10.10+ added. --- Telegram/SourceFiles/intro/introwidget.cpp | 8 +- Telegram/SourceFiles/mainwindow.cpp | 2 - .../platform/linux/main_window_linux.cpp | 4 + .../platform/linux/main_window_linux.h | 1 + .../platform/mac/main_window_mac.h | 19 +- .../platform/mac/main_window_mac.mm | 184 ++++++++++++++++-- .../platform/mac/window_title_mac.h | 46 +++++ .../platform/mac/window_title_mac.mm | 53 +++++ .../platform/platform_window_title.h | 8 +- .../platform/win/main_window_win.cpp | 2 + .../platform/win/window_title_win.cpp | 1 + Telegram/SourceFiles/pspecific_mac_p.h | 25 +-- Telegram/SourceFiles/pspecific_mac_p.mm | 120 +----------- Telegram/SourceFiles/window/main_window.cpp | 24 ++- Telegram/SourceFiles/window/main_window.h | 3 + Telegram/gyp/Telegram.gyp | 6 + 16 files changed, 336 insertions(+), 170 deletions(-) create mode 100644 Telegram/SourceFiles/platform/mac/window_title_mac.h create mode 100644 Telegram/SourceFiles/platform/mac/window_title_mac.mm diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index f007cf49f..aea428d67 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -77,7 +77,7 @@ void IntroWidget::onCheckUpdateStatus() { if (Sandbox::updatingState() == Application::UpdatingReady) { if (_update) return; _update.create(this, lang(lng_menu_update).toUpper(), st::defaultBoxButton); - _update->show(); + if (!_a_show.animating()) _update->show(); _update->setClickedCallback([] { checkReadyUpdate(); App::restart(); @@ -194,6 +194,8 @@ void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) { _a_show.stop(); step()->show(); + _settings->show(); + if (_update) _update->show(); if (step()->hasBack()) { _back->showFast(); } else { @@ -203,6 +205,8 @@ void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) { step()->hide(); _back->hideFast(); + _settings->hide(); + if (_update) _update->hide(); a_coordUnder = back ? anim::ivalue(-st::slideShift, 0) : anim::ivalue(0, -st::slideShift); a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0); @@ -228,6 +232,8 @@ void IntroWidget::step_show(float64 ms, bool timer) { if (step()->hasBack()) { _back->showFast(); } + _settings->show(); + if (_update) _update->show(); if (App::app()) App::app()->mtpUnpause(); } else { a_coordUnder.update(dt, Window::SlideAnimation::transition()); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index e3dd2d5ee..86984e543 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -162,8 +162,6 @@ void MainWindow::onStateChanged(Qt::WindowState state) { void MainWindow::initHook() { Platform::MainWindow::initHook(); - setWindowIcon(wndIcon); - Application::instance()->installEventFilter(this); connect(windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onStateChanged(Qt::WindowState))); connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWindowActiveChanged()), Qt::QueuedConnection); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 10e61acf9..208b4d3d5 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -191,6 +191,10 @@ MainWindow::MainWindow() _psUpdateIndicatorTimer.setSingleShot(true); } +void MainWindow::initHook() { + setWindowIcon(wndIcon); +} + bool MainWindow::psHasTrayIcon() const { return trayIcon || ((useAppIndicator || (useStatusIcon && trayIconChecked)) && (cWorkMode() != dbiwmWindowOnly)); } diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 315924d79..4ce43c036 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -60,6 +60,7 @@ public slots: void psUpdateIndicator(); protected: + void initHook() override; bool psHasTrayIcon() const; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index 15aec9e32..e2e634098 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -25,13 +25,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Platform { -class MacPrivate : public PsMacWindowPrivate { -public: - void activeSpaceChanged(); - void darkModeChanged(); - -}; - class MainWindow : public Window::MainWindow { Q_OBJECT @@ -64,8 +57,14 @@ public: void closeWithoutDestroy() override; + int getCustomTitleHeight() const { + return _customTitleHeight; + } + ~MainWindow(); + class Private; + public slots: void psShowTrayMenu(); @@ -82,6 +81,7 @@ private slots: protected: void stateChangedHook(Qt::WindowState state) override; + void initHook() override; QImage psTrayIcon(bool selected = false) const; bool psHasTrayIcon() const { @@ -106,7 +106,8 @@ protected: private: void createGlobalMenu(); - MacPrivate _private; + friend class Private; + std_::unique_ptr _private; mutable bool psIdle; mutable QTimer psIdleTimer; @@ -128,6 +129,8 @@ private: QAction *psNewChannel = nullptr; QAction *psShowTelegram = nullptr; + int _customTitleHeight = 0; + }; } // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index f165dc88a..3e0851c9d 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -28,18 +28,72 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "platform/platform_notifications_manager.h" #include "boxes/contactsbox.h" #include "boxes/aboutbox.h" - #include "lang.h" +#include "platform/mac/mac_utilities.h" #include -#include #include - +#include #include +#include + +@interface MainWindowObserver : NSObject { +} + +- (id) init:(MainWindow::Private*)window; +- (void) activeSpaceDidChange:(NSNotification *)aNotification; +- (void) darkModeChanged:(NSNotification *)aNotification; +- (void) screenIsLocked:(NSNotification *)aNotification; +- (void) screenIsUnlocked:(NSNotification *)aNotification; +- (void) windowWillEnterFullScreen:(NSNotification *)aNotification; +- (void) windowWillExitFullScreen:(NSNotification *)aNotification; + +@end namespace Platform { -void MacPrivate::activeSpaceChanged() { +class MainWindow::Private { +public: + Private(MainWindow *window); + + void setWindowBadge(const QString &str); + void startBounce(); + + void enableShadow(WId winId); + + bool filterNativeEvent(void *event); + + void willEnterFullScreen(); + void willExitFullScreen(); + + void initCustomTitle(NSWindow *window, NSView *view); + + ~Private(); + +private: + MainWindow *_public; + friend class MainWindow; + + MainWindowObserver *_observer; + +}; + +} // namespace Platform + +@implementation MainWindowObserver { + +MainWindow::Private *_private; + +} + +- (id) init:(MainWindow::Private*)window { + if (self = [super init]) { + _private = window; + } + return self; +} + +- (void) activeSpaceDidChange:(NSNotification *)aNotification { if (auto manager = Window::Notifications::Default::manager()) { manager->enumerateNotifications([](QWidget *widget) { objc_activateWnd(widget->winId()); @@ -47,14 +101,110 @@ void MacPrivate::activeSpaceChanged() { } } -void MacPrivate::darkModeChanged() { +- (void) darkModeChanged:(NSNotification *)aNotification { Notify::unreadCounterUpdated(); } +- (void) screenIsLocked:(NSNotification *)aNotification { + Global::SetScreenIsLocked(true); +} + +- (void) screenIsUnlocked:(NSNotification *)aNotification { + Global::SetScreenIsLocked(false); +} + +- (void) windowWillEnterFullScreen:(NSNotification *)aNotification { + _private->willEnterFullScreen(); +} + +- (void) windowWillExitFullScreen:(NSNotification *)aNotification { + _private->willExitFullScreen(); +} + +@end + +namespace Platform { + +MainWindow::Private::Private(MainWindow *window) +: _public(window) +, _observer([[MainWindowObserver alloc] init:this]) { + @autoreleasepool { + + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:_observer selector:@selector(activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:_observer selector:@selector(darkModeChanged:) name:Q2NSString(strNotificationAboutThemeChange()) object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:_observer selector:@selector(screenIsLocked:) name:Q2NSString(strNotificationAboutScreenLocked()) object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:_observer selector:@selector(screenIsUnlocked:) name:Q2NSString(strNotificationAboutScreenUnlocked()) object:nil]; + +#ifndef OS_MAC_STORE + // Register defaults for the whitelist of apps that want to use media keys + [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey, nil]]; +#endif // !OS_MAC_STORE + + } +} + +void MainWindow::Private::setWindowBadge(const QString &str) { + @autoreleasepool { + + [[NSApp dockTile] setBadgeLabel:Q2NSString(str)]; + + } +} + +void MainWindow::Private::startBounce() { + [NSApp requestUserAttention:NSInformationalRequest]; +} + +void MainWindow::Private::initCustomTitle(NSWindow *window, NSView *view) { + [window setStyleMask:[window styleMask] | NSFullSizeContentViewWindowMask]; + [window setTitlebarAppearsTransparent:YES]; + auto inner = [window contentLayoutRect]; + auto full = [view frame]; + _public->_customTitleHeight = qMax(qRound(full.size.height - inner.size.height), 0); + +#ifndef OS_MAC_OLD + [[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window]; + [[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window]; +#endif // !OS_MAC_OLD +} + +void MainWindow::Private::willEnterFullScreen() { + _public->setTitleVisibility(false); +} + +void MainWindow::Private::willExitFullScreen() { + _public->setTitleVisibility(true); +} + +void MainWindow::Private::enableShadow(WId winId) { +// [[(NSView*)winId window] setStyleMask:NSBorderlessWindowMask]; +// [[(NSView*)winId window] setHasShadow:YES]; +} + +bool MainWindow::Private::filterNativeEvent(void *event) { + NSEvent *e = static_cast(event); + if (e && [e type] == NSSystemDefined && [e subtype] == SPSystemDefinedEventMediaKeys) { +#ifndef OS_MAC_STORE + // If event tap is not installed, handle events that reach the app instead + if (![SPMediaKeyTap usesGlobalMediaKeyTap]) { + return objc_handleMediaKeyEvent(e); + } +#else // !OS_MAC_STORE + return objc_handleMediaKeyEvent(e); +#endif // else for !OS_MAC_STORE + } + return false; +} + +MainWindow::Private::~Private() { + [_observer release]; +} + MainWindow::MainWindow() : icon256(qsl(":/gui/art/icon256.png")) , iconbig256(qsl(":/gui/art/iconbig256.png")) -, wndIcon(QPixmap::fromImage(iconbig256, Qt::ColorOnly)) { +, wndIcon(QPixmap::fromImage(iconbig256, Qt::ColorOnly)) +, _private(std_::make_unique(this)) { QImage tray(qsl(":/gui/art/osxtray.png")); trayImg = tray.copy(0, cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4)); trayImgSel = tray.copy(tray.width() / (cRetina() ? 2 : 4), cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4)); @@ -81,6 +231,18 @@ void MainWindow::stateChangedHook(Qt::WindowState state) { } } +void MainWindow::initHook() { + _customTitleHeight = 0; + if (auto view = reinterpret_cast(winId())) { + if (auto window = [view window]) { + if ([window respondsToSelector:@selector(contentLayoutRect)] + && [window respondsToSelector:@selector(setTitlebarAppearsTransparent:)]) { + _private->initCustomTitle(window, view); + } + } + } +} + void MainWindow::onHideAfterFullScreen() { hide(); } @@ -124,7 +286,6 @@ void MainWindow::psUpdateWorkmode() { if (auto manager = Platform::Notifications::manager()) { manager->updateDelegate(); } - setWindowIcon(wndIcon); } void _placeCounter(QImage &img, int size, int count, const style::color &bg, const style::color &color) { @@ -166,10 +327,9 @@ void MainWindow::psUpdateCounter() { int32 counter = App::histories().unreadBadge(); setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); - setWindowIcon(wndIcon); QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0')); - _private.setWindowBadge(counter ? cnt : QString()); + _private->setWindowBadge(counter ? cnt : QString()); if (trayIcon) { bool muted = App::histories().unreadOnlyMuted(); @@ -195,7 +355,7 @@ void MainWindow::psFirstShow() { bool showShadows = true; show(); - _private.enableShadow(winId()); + _private->enableShadow(winId()); if (cWindowPos().maximized) { setWindowState(Qt::WindowMaximized); } @@ -353,11 +513,11 @@ void MainWindow::psMacUpdateMenu() { } void MainWindow::psFlash() { - _private.startBounce(); + return _private->startBounce(); } bool MainWindow::psFilterNativeEvent(void *event) { - return _private.filterNativeEvent(event); + return _private->filterNativeEvent(event); } bool MainWindow::eventFilter(QObject *obj, QEvent *evt) { diff --git a/Telegram/SourceFiles/platform/mac/window_title_mac.h b/Telegram/SourceFiles/platform/mac/window_title_mac.h new file mode 100644 index 000000000..6409888e3 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/window_title_mac.h @@ -0,0 +1,46 @@ +/* +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 "window/window_title.h" + +namespace Ui { +class PlainShadow; +} // namespace Ui + +namespace Platform { + +class TitleWidget : public Window::TitleWidget { +public: + TitleWidget(QWidget *parent, int height); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + ChildWidget _shadow; + +}; + +Window::TitleWidget *CreateTitleWidget(QWidget *parent); + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/window_title_mac.mm b/Telegram/SourceFiles/platform/mac/window_title_mac.mm new file mode 100644 index 000000000..4927d0cb1 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/window_title_mac.mm @@ -0,0 +1,53 @@ +/* +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 "platform/mac/window_title_mac.h" + +#include "ui/widgets/shadow.h" +#include "styles/style_window.h" +#include "platform/platform_main_window.h" + +namespace Platform { + +TitleWidget::TitleWidget(QWidget *parent, int height) : Window::TitleWidget(parent) +, _shadow(this, st::titleShadow) { + setAttribute(Qt::WA_OpaquePaintEvent); + resize(width(), height); +} + +void TitleWidget::paintEvent(QPaintEvent *e) { + Painter(this).fillRect(rect(), st::titleBg); +} + +void TitleWidget::resizeEvent(QResizeEvent *e) { + _shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth); +} + +Window::TitleWidget *CreateTitleWidget(QWidget *parent) { + if (auto window = qobject_cast(parent)) { + if (auto height = window->getCustomTitleHeight()) { + return new TitleWidget(parent, height); + } + } + return nullptr; +} + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/platform_window_title.h b/Telegram/SourceFiles/platform/platform_window_title.h index 6291c5d7e..1211acbbe 100644 --- a/Telegram/SourceFiles/platform/platform_window_title.h +++ b/Telegram/SourceFiles/platform/platform_window_title.h @@ -22,9 +22,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/window_title.h" -#if defined Q_OS_WIN +#ifdef Q_OS_MAC +#include "platform/mac/window_title_mac.h" +#elif defined Q_OS_WIN // Q_OS_MAC #include "platform/win/window_title_win.h" -#else // Q_OS_WIN +#elif defined Q_OS_WINRT || defined Q_OS_LINUX namespace Platform { inline Window::TitleWidget *CreateTitleWidget(QWidget *parent) { @@ -32,4 +34,4 @@ inline Window::TitleWidget *CreateTitleWidget(QWidget *parent) { } } // namespace Platform -#endif // else for Q_OS_WIN +#endif // Q_OS_MAC || Q_OS_WIN || Q_OS_WINRT || Q_OS_LINUX diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index bf2863d2a..7124be413 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -777,6 +777,8 @@ void MainWindow::initHook() { } psInitSysMenu(); + + setWindowIcon(wndIcon); } bool MainWindow::psHasNativeNotifications() { diff --git a/Telegram/SourceFiles/platform/win/window_title_win.cpp b/Telegram/SourceFiles/platform/win/window_title_win.cpp index 32437e143..582e8fb28 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.cpp +++ b/Telegram/SourceFiles/platform/win/window_title_win.cpp @@ -47,6 +47,7 @@ TitleWidget::TitleWidget(QWidget *parent) : Window::TitleWidget(parent) }); setAttribute(Qt::WA_OpaquePaintEvent); + resize(width(), st::titleHeight); } void TitleWidget::init() { diff --git a/Telegram/SourceFiles/pspecific_mac_p.h b/Telegram/SourceFiles/pspecific_mac_p.h index 5a613ceea..5e3219a90 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.h +++ b/Telegram/SourceFiles/pspecific_mac_p.h @@ -17,29 +17,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -class PsMacWindowData; - -class PsMacWindowPrivate { -public: - PsMacWindowPrivate(); - - void setWindowBadge(const QString &str); - void startBounce(); - - void enableShadow(WId winId); - - bool filterNativeEvent(void *event); - - virtual void activeSpaceChanged() { - } - virtual void darkModeChanged() { - } - - ~PsMacWindowPrivate(); - - PsMacWindowData *data; - -}; +// e is NSEvent* +bool objc_handleMediaKeyEvent(void *e); void objc_holdOnTop(WId winId); bool objc_darkMode(); diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index a99d5585a..b5965b08f 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -28,19 +28,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include -#include #include - +#include #include - #include using Platform::Q2NSString; using Platform::NSlang; using Platform::NS2QString; -bool handleMediaKeyEvent(NSEvent *e); - @interface qVisualize : NSObject { } @@ -152,68 +148,12 @@ ApplicationDelegate *_sharedDelegate = nil; - (void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)e { if (e && [e type] == NSSystemDefined && [e subtype] == SPSystemDefinedEventMediaKeys) { - handleMediaKeyEvent(e); + objc_handleMediaKeyEvent(e); } } @end -@interface ObserverHelper : NSObject { -} - -- (id) init:(PsMacWindowPrivate *)aWnd; -- (void) activeSpaceDidChange:(NSNotification *)aNotification; -- (void) darkModeChanged:(NSNotification *)aNotification; -- (void) screenIsLocked:(NSNotification *)aNotification; -- (void) screenIsUnlocked:(NSNotification *)aNotification; - -@end - -class PsMacWindowData { -public: - PsMacWindowData(PsMacWindowPrivate *wnd) : - wnd(wnd), - observerHelper([[ObserverHelper alloc] init:wnd]) { - } - - ~PsMacWindowData() { - [observerHelper release]; - } - - PsMacWindowPrivate *wnd; - ObserverHelper *observerHelper; - -}; - -@implementation ObserverHelper { - PsMacWindowPrivate *wnd; -} - -- (id) init:(PsMacWindowPrivate *)aWnd { - if (self = [super init]) { - wnd = aWnd; - } - return self; -} - -- (void) activeSpaceDidChange:(NSNotification *)aNotification { - wnd->activeSpaceChanged(); -} - -- (void) darkModeChanged:(NSNotification *)aNotification { - wnd->darkModeChanged(); -} - -- (void) screenIsLocked:(NSNotification *)aNotification { - Global::SetScreenIsLocked(true); -} - -- (void) screenIsUnlocked:(NSNotification *)aNotification { - Global::SetScreenIsLocked(false); -} - -@end - namespace Platform { void SetWatchingMediaKeys(bool watching) { @@ -224,34 +164,6 @@ void SetWatchingMediaKeys(bool watching) { } // namespace Platform -PsMacWindowPrivate::PsMacWindowPrivate() : data(new PsMacWindowData(this)) { - @autoreleasepool { - - [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:data->observerHelper selector:@selector(activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil]; - [[NSDistributedNotificationCenter defaultCenter] addObserver:data->observerHelper selector:@selector(darkModeChanged:) name:Q2NSString(strNotificationAboutThemeChange()) object:nil]; - [[NSDistributedNotificationCenter defaultCenter] addObserver:data->observerHelper selector:@selector(screenIsLocked:) name:Q2NSString(strNotificationAboutScreenLocked()) object:nil]; - [[NSDistributedNotificationCenter defaultCenter] addObserver:data->observerHelper selector:@selector(screenIsUnlocked:) name:Q2NSString(strNotificationAboutScreenUnlocked()) object:nil]; - -#ifndef OS_MAC_STORE - // Register defaults for the whitelist of apps that want to use media keys - [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:[SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey, nil]]; -#endif // OS_MAC_STORE - - } -} - -void PsMacWindowPrivate::setWindowBadge(const QString &str) { - @autoreleasepool { - - [[NSApp dockTile] setBadgeLabel:Q2NSString(str)]; - - } -} - -void PsMacWindowPrivate::startBounce() { - [NSApp requestUserAttention:NSInformationalRequest]; -} - void objc_holdOnTop(WId winId) { NSWindow *wnd = [reinterpret_cast(winId) window]; [wnd setHidesOnDeactivate:NO]; @@ -289,7 +201,9 @@ void objc_activateWnd(WId winId) { [wnd orderFront:wnd]; } -bool handleMediaKeyEvent(NSEvent *e) { +bool objc_handleMediaKeyEvent(void *ev) { + auto e = reinterpret_cast(ev); + int keyCode = (([e data1] & 0xFFFF0000) >> 16); int keyFlags = ([e data1] & 0x0000FFFF); int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA; @@ -330,26 +244,6 @@ bool handleMediaKeyEvent(NSEvent *e) { return false; } -void PsMacWindowPrivate::enableShadow(WId winId) { -// [[(NSView*)winId window] setStyleMask:NSBorderlessWindowMask]; -// [[(NSView*)winId window] setHasShadow:YES]; -} - -bool PsMacWindowPrivate::filterNativeEvent(void *event) { - NSEvent *e = static_cast(event); - if (e && [e type] == NSSystemDefined && [e subtype] == SPSystemDefinedEventMediaKeys) { -#ifndef OS_MAC_STORE - // If event tap is not installed, handle events that reach the app instead - if (![SPMediaKeyTap usesGlobalMediaKeyTap]) { - return handleMediaKeyEvent(e); - } -#else // !OS_MAC_STORE - return handleMediaKeyEvent(e); -#endif // else for !OS_MAC_STORE - } - return false; -} - void objc_debugShowAlert(const QString &str) { @autoreleasepool { @@ -366,10 +260,6 @@ void objc_outputDebugString(const QString &str) { } } -PsMacWindowPrivate::~PsMacWindowPrivate() { - delete data; -} - bool objc_idleSupported() { int64 idleTime = 0; return objc_idleTime(idleTime); diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index a49e87450..8a0fb16cb 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -39,11 +39,11 @@ MainWindow::MainWindow() : QWidget() } } }); - - _title = Platform::CreateTitleWidget(this); } void MainWindow::init() { + initHook(); + _positionUpdatedTimer->setSingleShot(true); connect(_positionUpdatedTimer, SIGNAL(timeout()), this, SLOT(savePositionByTimer())); @@ -51,10 +51,11 @@ void MainWindow::init() { p.setColor(QPalette::Window, st::windowBg->c); setPalette(p); - if (_title) _title->init(); + if ((_title = Platform::CreateTitleWidget(this))) { + _title->init(); + } initSize(); - initHook(); } HitTestResult MainWindow::hitTest(const QPoint &p) const { @@ -104,6 +105,13 @@ void MainWindow::positionUpdated() { _positionUpdatedTimer->start(SaveWindowPositionTimeout); } +void MainWindow::setTitleVisibility(bool visible) { + if (_title && (_title->isHidden() == visible)) { + _title->setVisible(visible); + updateControlsGeometry(); + } +} + int32 MainWindow::screenNameChecksum(const QString &name) const { auto bytes = name.toUtf8(); return hashCrc32(bytes.constData(), bytes.size()); @@ -114,9 +122,13 @@ void MainWindow::setPositionInited() { } void MainWindow::resizeEvent(QResizeEvent *e) { + updateControlsGeometry(); +} + +void MainWindow::updateControlsGeometry() { auto bodyTop = 0; - if (_title) { - _title->setGeometry(0, bodyTop, width(), st::titleHeight); + if (_title && !_title->isHidden()) { + _title->setGeometry(0, bodyTop, width(), _title->height()); bodyTop += _title->height(); } _body->setGeometry(0, bodyTop, width(), height() - bodyTop); diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index f1c0e8fc4..594bf2dd2 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -40,6 +40,8 @@ public: } void positionUpdated(); + void setTitleVisibility(bool visible); + virtual void closeWithoutDestroy(); virtual ~MainWindow(); @@ -70,6 +72,7 @@ private slots: } private: + void updateControlsGeometry(); void initSize(); ChildObject _positionUpdatedTimer; diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 6faf9b7d5..141f4a54c 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -369,6 +369,8 @@ '<(src_loc)/platform/mac/main_window_mac.h', '<(src_loc)/platform/mac/notifications_manager_mac.mm', '<(src_loc)/platform/mac/notifications_manager_mac.h', + '<(src_loc)/platform/mac/window_title_mac.mm', + '<(src_loc)/platform/mac/window_title_mac.h', '<(src_loc)/platform/win/main_window_win.cpp', '<(src_loc)/platform/win/main_window_win.h', '<(src_loc)/platform/win/notifications_manager_win.cpp', @@ -605,6 +607,8 @@ '<(src_loc)/platform/mac/main_window_mac.h', '<(src_loc)/platform/mac/notifications_manager_mac.mm', '<(src_loc)/platform/mac/notifications_manager_mac.h', + '<(src_loc)/platform/mac/window_title_mac.mm', + '<(src_loc)/platform/mac/window_title_mac.h', '<(sp_media_key_tap_loc)/SPMediaKeyTap.m', '<(sp_media_key_tap_loc)/SPMediaKeyTap.h', '<(sp_media_key_tap_loc)/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m', @@ -628,6 +632,8 @@ '<(src_loc)/platform/win/main_window_win.h', '<(src_loc)/platform/win/notifications_manager_win.cpp', '<(src_loc)/platform/win/notifications_manager_win.h', + '<(src_loc)/platform/win/window_title_win.cpp', + '<(src_loc)/platform/win/window_title_win.h', '<(src_loc)/platform/win/windows_app_user_model_id.cpp', '<(src_loc)/platform/win/windows_app_user_model_id.h', '<(src_loc)/platform/win/windows_dlls.cpp', From 494254496efe6ed68c2570e1739f5adc6d6e3b17 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 8 Nov 2016 17:43:10 +0300 Subject: [PATCH 026/100] 10019003: Some compose area icons improved. Box headers redesigned. --- Telegram/Resources/basic.style | 9 ++- Telegram/Resources/colors.palette | 14 ++-- Telegram/Resources/icons/box_title_shadow.png | Bin 85 -> 85 bytes .../Resources/icons/box_title_shadow@2x.png | Bin 105 -> 107 bytes Telegram/Resources/icons/dropdown_shadow.png | Bin 128 -> 168 bytes .../Resources/icons/dropdown_shadow@2x.png | Bin 146 -> 240 bytes .../Resources/icons/send_control_save.png | Bin 0 -> 358 bytes .../Resources/icons/send_control_save@2x.png | Bin 0 -> 687 bytes .../icons/send_control_silent_off.png | Bin 518 -> 378 bytes .../icons/send_control_silent_off@2x.png | Bin 1029 -> 660 bytes .../icons/send_control_silent_on.png | Bin 685 -> 572 bytes .../icons/send_control_silent_on@2x.png | Bin 1487 -> 1051 bytes Telegram/Resources/sample.tdesktop-theme | 12 +-- Telegram/Resources/winrc/Telegram.rc | 8 +- Telegram/Resources/winrc/Updater.rc | 8 +- Telegram/SourceFiles/boxes/aboutbox.cpp | 12 ++- Telegram/SourceFiles/boxes/abstractbox.cpp | 72 ++++++++++-------- Telegram/SourceFiles/boxes/abstractbox.h | 33 ++++---- Telegram/SourceFiles/boxes/addcontactbox.cpp | 34 ++++----- Telegram/SourceFiles/boxes/autolockbox.cpp | 11 ++- Telegram/SourceFiles/boxes/backgroundbox.cpp | 4 +- Telegram/SourceFiles/boxes/boxes.style | 32 ++++---- Telegram/SourceFiles/boxes/confirmbox.cpp | 8 +- .../SourceFiles/boxes/confirmphonebox.cpp | 6 +- Telegram/SourceFiles/boxes/connectionbox.cpp | 17 ++--- Telegram/SourceFiles/boxes/contactsbox.cpp | 6 +- .../SourceFiles/boxes/downloadpathbox.cpp | 4 +- Telegram/SourceFiles/boxes/emojibox.cpp | 10 +-- Telegram/SourceFiles/boxes/languagebox.cpp | 4 +- .../SourceFiles/boxes/localstoragebox.cpp | 8 +- Telegram/SourceFiles/boxes/passcodebox.cpp | 16 ++-- Telegram/SourceFiles/boxes/report_box.cpp | 4 +- Telegram/SourceFiles/boxes/sessionsbox.cpp | 4 +- Telegram/SourceFiles/boxes/sharebox.cpp | 4 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 8 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 4 +- Telegram/SourceFiles/boxes/usernamebox.cpp | 6 +- Telegram/SourceFiles/core/version.h | 2 +- Telegram/SourceFiles/history/history.style | 13 ++-- Telegram/SourceFiles/historywidget.cpp | 14 ++-- Telegram/SourceFiles/historywidget.h | 7 +- Telegram/SourceFiles/mainwindow.cpp | 1 + .../media/player/media_player.style | 2 +- Telegram/SourceFiles/settings/settings.style | 11 +-- .../settings/settings_fixed_bar.cpp | 2 +- .../SourceFiles/settings/settings_fixed_bar.h | 4 +- .../SourceFiles/settings/settings_widget.cpp | 9 +-- .../SourceFiles/settings/settings_widget.h | 4 +- .../SourceFiles/ui/buttons/icon_button.cpp | 3 + Telegram/SourceFiles/ui/countryinput.cpp | 6 +- .../SourceFiles/ui/widgets/multi_select.h | 4 +- Telegram/SourceFiles/ui/widgets/widgets.style | 7 +- Telegram/build/version | 2 +- 53 files changed, 223 insertions(+), 226 deletions(-) create mode 100644 Telegram/Resources/icons/send_control_save.png create mode 100644 Telegram/Resources/icons/send_control_save@2x.png diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 58331cba5..7957db994 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -1061,9 +1061,12 @@ infoButton: PeerAvatarButton { profileTopBarBackIconFg: #0290d7; profileTopBarBackIcon: icon {{ "title_back", profileTopBarBackIconFg }}; -historyReplyCancelIcon: icon {{ "box_button_close-invert", historyReplyBg }}; -boxSearchCancelIcon: icon {{ "box_button_close-invert", boxSearchBg }}; -settingsFixedBarCloseIcon: icon {{ "box_button_close-invert", settingsFixedBarBg }}; +historyReplyCancelIcon: icon {{ "box_button_close", historyReplyCancelFg }}; +historyReplyCancelIconOver: icon {{ "box_button_close", historyReplyCancelFgOver }}; +boxSearchCancelIcon: icon {{ "box_button_close", boxSearchCancelIconFg }}; +boxSearchCancelIconOver: icon {{ "box_button_close", boxSearchCancelIconFgOver }}; +boxBlockTitleCloseIcon: icon {{ "box_button_close", boxBlockTitleCloseFg }}; +boxBlockTitleCloseIconOver: icon {{ "box_button_close", boxBlockTitleCloseFgOver }}; notifyFadeRight: icon {{ "fade_horizontal", notificationBg }}; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index c102bf3ef..41f036e6b 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -81,6 +81,12 @@ boxSearchBg: boxBg; boxSearchCancelIconFg: cancelIconFg; boxSearchCancelIconFgOver: cancelIconFgOver; +boxBlockTitleBg: boxBg; +boxBlockTitleFg: boxTitleFg; +boxBlockTitleAdditionalFg: #808080; +boxBlockTitleCloseFg: cancelIconFg; +boxBlockTitleCloseFgOver: cancelIconFgOver; + membersAboutLimitFg: windowSubTextFg; contactsBg: windowBg; @@ -91,10 +97,6 @@ photoCropFadeBg: #0000007f; photoCropPointFg: #ffffff7f; // settings -settingsFixedBarBg: boxBg; -settingsCloseFg: cancelIconFg; -settingsCloseFgOver: cancelIconFgOver; - notificationsBoxMonitorFg: windowTextFg; notificationSampleUserpicFg: windowActiveBg; @@ -173,8 +175,8 @@ historySendIconFg: windowActiveBg; historySendIconFgOver: windowActiveBg; historyPinnedBg: historyComposeAreaBg; historyReplyBg: historyComposeAreaBg; -historyReplyCancelIconFg: cancelIconFg; -historyReplyCancelIconFgOver: cancelIconFgOver; +historyReplyCancelFg: cancelIconFg; +historyReplyCancelFgOver: cancelIconFgOver; historyComposeButtonBg: historyComposeAreaBg; historyComposeButtonBgOver: #f5f5f5; diff --git a/Telegram/Resources/icons/box_title_shadow.png b/Telegram/Resources/icons/box_title_shadow.png index bba09b240d712d5e50cb65048156956e074db473..90ae5c049ebd7a4c9aa0a8707d07c3de693b0654 100644 GIT binary patch delta 46 zcmWFyouH!`5ES(5{Fi@!tJ&Dh%(ngK0fPUG&JJv@k9WGoF#v(5tDnm{r-UW|xc3nh delta 46 zcmWFyouH#BAS(Lz{DGrKzs_%*IrFDIGc)tydIk+WmSlrF-QO61z|+;wWt~$(69B~P B5#|5@ diff --git a/Telegram/Resources/icons/box_title_shadow@2x.png b/Telegram/Resources/icons/box_title_shadow@2x.png index 7d97eb59e7aaaa5ac8b018389343a449f9fcb6ae..bf3dee4cda57d90fab0a60282ba8cd78a2236207 100644 GIT binary patch delta 76 zcmd1Io}l7xW`-W`5$wnDZnbgzi*XP#P_%j-|p$({abX7 gapFwo#>Owd8MeCe>%Dty%f|o&p00i_>zopr0NipQCIA2c delta 74 zcmd1KoS@=r;OXKR!Z9=U)J9$gLkf4<$Jm~hDc29oe;=%*nr15Rnv^^ z`M>3-V{ff*DZ6=%>yKiE*fib)jpAA1$!Xck?mJ8}F!pFtnIY+M@8HZ!QO+(0?kZ;L z-gofq;P`ScF!aQkse2mV^{qN2k*vMBZ$?2{UG7=Vi7cKTzj*8RJs145T=>FwR_&WT S?=*l`GkCiCxvXun_z~gjqxx}WQ z-|Z9hF6ntOx9$>R3p{YtpGkq!^Txe>6Ixa&ac+~};2T@@z8+<3( a?q^hJV(FWHr7;I+B!j1`pUXO@geCw`L@IRv diff --git a/Telegram/Resources/icons/dropdown_shadow@2x.png b/Telegram/Resources/icons/dropdown_shadow@2x.png index 1931c7b11f2dd9ca5696bf63775026d9f503f2e8..c44d45270a6b7b788d369388af7aa7149bab2f69 100644 GIT binary patch literal 240 zcmV^-_)k!%{J6$3$#_BpXt5xM1H@$G}EdJa_82RJ)m3i=lew7s}4 z!3fz3Kma@vNAwTO5?Gf~1i-)}yiDlZ5RxOM?JdFV&H#*LB&C!xO*$PpV%D|R6pbm< zkv&FINJ{TrdhgO&8}M}GcyAsx?7FUac3Q~NS$ptpA=`{&x7PMRKWgD5IW7C(T-h({XUb5;>%Cj3%>VtG~BHEDAvKT<(k4v uNd->hW|bZKqSr5NHY%R+|G%@O1>-L+mf8u^9oGOYVeoYIb6Mw<&;$TcP%&fx diff --git a/Telegram/Resources/icons/send_control_save.png b/Telegram/Resources/icons/send_control_save.png new file mode 100644 index 0000000000000000000000000000000000000000..199ab2b0c0f84399f1a7c9728b21680f71bcb55f GIT binary patch literal 358 zcmV-s0h#`ZP)18992;)RW^oi6BhaW+lbb(*e_*rt2WS+Ei481f8pjI7%|M}Gu}C5i+-&a+ zcYeEGotMkEo$Z|Oeb2MA^L~smhQ4dHT4B50LKH=?zL3pkgGWVC)DPr&9zpK+JC@7k z|3HJm06`ECvMi(i`Fw`1>z}@W!8A=&RYjMs*DH#mpmSN4n9XK?1@gU7RTZ3b{I1jK zgz0qJCE4eQecvZYp68fMCSB+3wduMp(lo_rH0nI8Uk=KYxtuMPF#rGn07*qoM6N<$ EfyU#HSO|%OHYh?M4lYd&4i3?rTS!AwG&zW~HQgcHLKnf0AQBw3 z%?7omB1%L98!qG!<^4CvTfXH{%bOiIaKHW!zx#2{Jw!zKtG=W%Rc~p#YsuH+|6F-X2)(|z#R!6cQmMqmAGcyrpetsV1a+!(AWHQibG^6VDHmp`FZf|c92n2qFF&d4yyu4&$lF8)fe-<0G zxVVUVy$%47Wf|dc7#zoqjWL-_xW2w-V&d^Qc%C1xo*)RQR4QMJq*AF-?;1pem6a9T z-Q6)UhlhvXx;e^bv&>@EY87U)nHjd(Y-l!{%zQK&{T_x0w6?Z}hldAd(RRBHuh;t} z=yW>KZnv4a{r&x)4i3Wd@-k|*8ngJz%L}%*x8Fl>>2wC3?{>Qg27{AeK0rjhc}Nt+p{QX`adUHXQq2FLe|>j%7u{}m zY|QiXGyHzP5}3gtB7)!V$J5i(NKB{G!TS2TQW!Rfh~RKI&}cN67)g@g@pzQP3l002ovPDHLkV1oQyMrQy3 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/send_control_silent_off.png b/Telegram/Resources/icons/send_control_silent_off.png index fd0a88d5ff223b8c99e4f318ca6542b63a8af011..8e25352d736fc04a8e85e19f547e25a874769b48 100644 GIT binary patch literal 378 zcmV-=0fqjFP)aDUkpRqbI2^no&RO$}D2mXwEyi)g^?F4VMP7M-=ZTi; Y0rVMdBpSFzD*ylh07*qoM6N<$f|Nt<8 literal 518 zcmV+h0{Q)kP)~#3^?Hyb3CH6R0MO}l9N&HFXEKw?004%=q2r@&w+jHs=kxxd?RFag+&TdO3PE8s)A zu0z*#Fbo4C0z*VtEEbf_W@$Q|(r7dyj^ke2{w?Zs#dFH7zV}T@elVUNrGir z_)A*^{I zwOXy7IxBUliviWU(;89hfmK~G`OBLrf!ZW}G67UH5JY9GBp5J4eniwHV* zaS5(X^4FTt8~DLlgv|NQZ)Tj1${>UQeX)LpUBIV`0KyhE0G{WOs;cO6xgguNk)kMw z=Xok#KqC}IfoJu4JsMtg0M6$#24I>daUbSEL0SN>(=rT0)aN%sNG6%EP$-~wyN&Ag z`pfGK!=U5whz^GXF;5~(DM=D677J|KxM?<9{~izo0s8&E=fa<*C<<^K7Y;xw zm4fwp{dVyWEtgA3CX>Me2!a5s)e0--e!qjJY0zvoK@>$`Sr%fk7^Kr_Xti1}nM_E#9+bX* z1*ibnwv7PbWmuL))Qbv$VHmF0bX_OzN9@n&5xHCrOw+`U!_(;m*=&}yA9;WW#CSXg u%d)_-EEtVOG>WJ9kuNAkr%>4XdVoKp0W-LW36?Pc0000|J6vodyMbgQknxrX65GsUdV+n3VkZ4z%5Q4Tr+pM+QqK%ut7STG|v`BK3LJ%bc zEs7EbLo9rj*AH}IVSyBi$Ky4x%Ixec@o#Q! z)( zb#+B+d#96=6B1jkR%#zLs3b|X5q+Z9*Vn(rf6>1fsqZux47Cw`qH@rNh6ZXMHK^Ha zCUK!qpe|6U`}=zmcXoDC`=~+N+uKQ;PN%60RO;^Tj>Li>Q2VGs`}+Dwe0Fw5U7%98 zx3?s2X=$PMQG=FmQ$C-kE>NlC<6{y#oz9xPRDG=cq~!5o z6BC+O)a7!Ke4;4ov-KvENpX;{*=(9Y0RS&9E=bOvo*wO69|#11Wmy0Kh(@EDp7?UD zuC9`JYHEtg^W{vk*({m4*Xz|~H30xSIyw{wX^JD?=NCsuM@gb=Hme(?JfhJk$sZ1f z)qxs~M$G5)BvBv`&>gh9yIZlUZEbC}gDx#Ckp%nu`?^EQBNB;_I=j2OwSoG4KE+IX zdwc5!%CfAY*38Td9jL?Mz?YX7Vw{_ss~e;|Mn*;yutP&bUxPL`H)AfBBZi%wow`HH zqx=`HBuVIUxjqJUI-NfTDUY?aH3h8S? zt?G6a3I*zSKgV&<+S*$6K7+vkrBX@p`yT+{9cV2wnM@!ELe)tz3CtEPY1ZT8;~0y@ zP!vT>r_-27Brq5ZBEvAck}@P)L}-GT`csw2|FVd6w8%6%Bh^i0y@Csj;PnInJ0000< KMNUMnLSTXjRsXaA literal 685 zcmV;e0#f~nP)dXv)*Vk97tE&qpg~MS0Ha0fOLnSdZGz7rc*BABm z^+j9sJdfk!=xEVZibkV&p2x?>2h-Ej0L;zJ6^7Q;)nR5_TwD|d=KJO4W$>YRo<}qq{RzFk zzQ)X`t*xyHn%_Ra)zwv?hb8Is^i=Kb?K(I((BR;p+S=OW`@V{q<2Z6%SCf;IIzKGShbO-)T|Zf@4|^RvR?uzcTFV`HOS*OlwK>gedu-QAtu-rm&O+N#^zTg}eS>c>ju zdngnll}Z8N`#u0qPfs|GQ}BEw63N9%zl?Z1&dbXSW=1xfE%gumTTHI~|7pJg6tt^= Tk_K-Y00000NkvXXu0mjfPZ3b2 diff --git a/Telegram/Resources/icons/send_control_silent_on@2x.png b/Telegram/Resources/icons/send_control_silent_on@2x.png index 58a7460a484eecf25f6a5f51172c8bf9b3c3ad09..810b0947009cfc2096dafdffdd312cc9f05bee93 100644 GIT binary patch literal 1051 zcmV+$1mydPP)?ue0go#y`~;DxC{1Zul+phclO@rEI1qyfqrl_)VhFj;~zk+ zLofh|L_&7Eos!8UB@zj;*=!_{NCfeM5W{A(;d5B6Rw2BE0eF3VWdP#wc*T9V97UxA zC@jlyTt$5viD9C~_s{+OfF>2Q60 z%^aJr;_~tm)6>({xdS^pJBUOg&}y{>=O-p6kV>VBF8=>vv)LdPi-iMNU0wapER{-O zVPTr?xBoZkJ!sqirCX-bjptrXd>2x~J?BnAD8yg!1^OZ^^?(gq$a&m&j#YHF- z3N$x2LnIPmaBvVyOG`LCJ^d0wB9T;n2j=JJ!Sg(0Xti3=*x1PAH#If=)pT-l62V}Q z(X`v`!rX!J@o|JgA;$3d_!v^Dw4|+i^w5uvZI4|jKWjN$F=ExNnA zONRKo6IWMP!T=Bv+S}XV^Z6Kvr>7^3jEs~7F*P;C=#7q!3Ijky*xue|41wQm!ra_k zNf2jeXL(v%TU){bTwGif1OR}yw>PY;tb7~5WHROHcsw5A0G^+p^UOZKW^+%F@1?uCZ&^eSIC7OeQZklgYqrHW$oSC=_{L<(dN!5exj!{{pjN994u_d_E|&{!ZEZ}xNF;*O>8u|BBErDH00Mykv-alZreGuZ zosVEu0Eh@OnG9~Xn_2((_=w@*;cwMY6M)?Da5x-{Ml2SCUazlzfZXx0SS-k9vw1q{ zbQ;Uc%fbR|Zf=$hkQ*kGiD?A0*(@v;i!gVfQ1Rse5E1lxJz}vKqv>!sif#mp0{E(U zA;ax!0GlSu%8-|t6jYisoY3U#2l;Ca4i9gRkV^Ye3bc6R>V;SIhc!>((I}jbeCs7@<%IFJ8PTv@0+l-Me=$ zCnhFnR6cz8z~<)Wy!!e}S62FbK3=$RA*0ikD_7FOsH)0%JkG_%MJ_EZrM2<<{lx+G z`Ft{HI#{pQtLu1vex6-jU97FGrNv^Q*=(lSY-U+m8J$iiJ3Bi$IXS8G@%em(g9d{^ z8Bhqpd-v|iwq3h+Q3xU1A3S(qxTB7a4xR7e!-or>`ktO1-3UB=`jh~8?bdExx%^TS5c8raUK~WR{fXn5=)vH(Y0u@3)Q52BGnl)>R2MPemWD<3Cbr={J zkg&II-Gblm&l&Xl_wN7zMNzPJt?`2~r?dXlsZ+Rn_pbc*csv*!9LyyImY0`hxxBpG zuwTxXlFOGb@4hdyCMHrEGElEU89V z%gZ2%ii!%nd-o3Y_4TX9nVFf9<%)_5!=Qk{^w8hmPm-*usgZSCwrt_l)RgYTX>D!I z8pr8$O54%V(clH=O9RwtKR--$0Iv?^5lupzn(Umjc?z+)xoy4 zwaNB7ckamg)H`q9pi@&*GGI$fi(ybrQ*TQo+0)ZQ0Nl21n;g+ZBEe0YHsuehZ6cC< z`SNAXkeYUPck5uaOLOW4y}P?RKhX8-*K>Y;UIsdO@?_qi0C@iVd6_v#a%5zL)z#IE z#bUCLojZ5t4w^a&Iqb}=+Su4AZ}>t827^J4jEqS5p`oGNKwDZ`bW^o||Nf$Y0$^oj zB^MVL<@!`rRrVu{2DxLH7>(`40sj10iVmKVu0j8FlnwqkMTCG;j&dy4w z;o;$;L#Aq9Utd~SRaLof-@f#q4u?Z7Y#{_ECMNz-NKHFCJ6C{e;&!{Goubi>j*i0R zasdEHA`*$<)vH&itgMvJhg1#xhx{f0-oAZ139r|S6`;9@!{I<# zSsA{3`I7OCP7-Fb8B0q``1tXo;n;q^A0Cefmo8nx{rmTS#mZNYA3wh863`?|BF~s> z-EMd4Yof69^7!%NRo8SR5-CP1D?Aqt4i097j7FohSSrect()); - st::boxBlueTitleShadow.fill(p, QRect(r.left(), 0, r.width(), height())); -} AbstractBox::AbstractBox(int w) : LayerWidget(App::wnd()->bodyWidget()) { setAttribute(Qt::WA_OpaquePaintEvent); - resize(w, 0); + resize((w > 0) ? w : st::boxWideWidth, 0); } void AbstractBox::prepare() { + raiseShadow(); showAll(); } @@ -53,12 +47,11 @@ void AbstractBox::keyPressEvent(QKeyEvent *e) { } void AbstractBox::resizeEvent(QResizeEvent *e) { - if (_blueClose) { - _blueClose->moveToRight(0, 0); + if (_blockClose) { + _blockClose->moveToRight(0, 0); } - if (_blueShadow) { - _blueShadow->moveToLeft(0, st::boxTitleHeight); - _blueShadow->resize(width(), st::boxBlueTitleShadow.height()); + if (_blockShadow) { + _blockShadow->setGeometry(0, st::boxBlockTitleHeight, width(), st::boxBlockTitleShadow.height()); } LayerWidget::resizeEvent(e); } @@ -75,21 +68,27 @@ bool AbstractBox::paint(QPainter &p) { return false; } -void AbstractBox::paintTitle(Painter &p, const QString &title, const QString &additional) { - p.setFont(st::boxTitleFont); - if (_blueTitle) { - p.fillRect(0, 0, width(), st::boxTitleHeight, st::boxBlueTitleBg); - p.setPen(st::boxBlueTitleFg); +int AbstractBox::titleHeight() const { + return _blockTitle ? st::boxBlockTitleHeight : st::boxTitleHeight; +} - int32 titleWidth = st::boxTitleFont->width(title); - p.drawTextLeft(st::boxBlueTitlePosition.x(), st::boxBlueTitlePosition.y(), width(), title, titleWidth); +void AbstractBox::paintTitle(Painter &p, const QString &title, const QString &additional) { + if (_blockTitle) { + p.fillRect(0, 0, width(), titleHeight(), st::boxBlockTitleBg); + + p.setFont(st::boxBlockTitleFont); + p.setPen(st::boxBlockTitleFg); + + auto titleWidth = st::boxBlockTitleFont->width(title); + p.drawTextLeft(st::boxBlockTitlePosition.x(), st::boxBlockTitlePosition.y(), width(), title, titleWidth); if (!additional.isEmpty()) { - p.setFont(st::boxTextFont); - p.setPen(st::boxBlueTitleAdditionalFg); - p.drawTextLeft(st::boxBlueTitlePosition.x() + titleWidth + st::boxBlueTitleAdditionalSkip, st::boxBlueTitlePosition.y(), width(), additional); + p.setFont(st::boxBlockTitleAdditionalFont); + p.setPen(st::boxBlockTitleAdditionalFg); + p.drawTextLeft(st::boxBlockTitlePosition.x() + titleWidth + st::boxBlockTitleAdditionalSkip, st::boxBlockTitlePosition.y(), width(), additional); } } else { + p.setFont(st::boxTitleFont); p.setPen(st::boxTitleFg); p.drawTextLeft(st::boxTitlePosition.x(), st::boxTitlePosition.y(), width(), title); } @@ -135,24 +134,27 @@ void AbstractBox::onClose() { emit closed(this); } -void AbstractBox::setBlueTitle(bool blue) { - _blueTitle = blue; - _blueShadow.create(this); - _blueClose.create(this, st::boxBlueClose); - _blueClose->setClickedCallback([this] { onClose(); }); +void AbstractBox::setBlockTitle(bool block) { + _blockTitle = block; + _blockShadow.create(this, st::boxBlockTitleShadow); + _blockClose.create(this, st::boxBlockTitleClose); + _blockClose->setClickedCallback([this] { onClose(); }); } void AbstractBox::raiseShadow() { - if (_blueShadow) { - _blueShadow->raise(); + if (_blockShadow) { + _blockShadow->raise(); } } +ScrollableBoxShadow::ScrollableBoxShadow(QWidget *parent) : Ui::PlainShadow(parent, st::boxScrollShadowBg) { +} + ScrollableBox::ScrollableBox(const style::flatScroll &scroll, int32 w) : AbstractBox(w) , _scroll(this, scroll) -, _topSkip(st::boxTitleHeight) +, _topSkip(st::boxBlockTitleHeight) , _bottomSkip(st::boxScrollSkip) { - setBlueTitle(true); + setBlockTitle(true); } void ScrollableBox::resizeEvent(QResizeEvent *e) { @@ -161,6 +163,8 @@ void ScrollableBox::resizeEvent(QResizeEvent *e) { } void ScrollableBox::init(TWidget *inner, int bottomSkip, int topSkip) { + if (bottomSkip < 0) bottomSkip = st::boxScrollSkip; + if (topSkip < 0) topSkip = st::boxBlockTitleHeight; _bottomSkip = bottomSkip; _topSkip = topSkip; _scroll->setOwnedWidget(inner); @@ -168,6 +172,8 @@ void ScrollableBox::init(TWidget *inner, int bottomSkip, int topSkip) { } void ScrollableBox::setScrollSkips(int bottomSkip, int topSkip) { + if (bottomSkip < 0) bottomSkip = st::boxScrollSkip; + if (topSkip < 0) topSkip = st::boxBlockTitleHeight; if (_topSkip != topSkip || _bottomSkip != bottomSkip) { _topSkip = topSkip; _bottomSkip = bottomSkip; diff --git a/Telegram/SourceFiles/boxes/abstractbox.h b/Telegram/SourceFiles/boxes/abstractbox.h index e140267c9..46733444f 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.h +++ b/Telegram/SourceFiles/boxes/abstractbox.h @@ -24,27 +24,21 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/shadow.h" namespace Ui { -class MaskButton; +class IconButton; +class GradientShadow; } // namespace Ui -class BlueTitleShadow : public TWidget { -public: - BlueTitleShadow(QWidget *parent) : TWidget(parent) { - } - void paintEvent(QPaintEvent *e); -}; - class AbstractBox : public LayerWidget, protected base::Subscriber { Q_OBJECT public: - AbstractBox(int w = st::boxWideWidth); + AbstractBox(int w = 0); void parentResized() override; void showDone() override { showAll(); } - void setBlueTitle(bool blue); + void setBlockTitle(bool block); void raiseShadow(); public slots: @@ -57,6 +51,7 @@ protected: void prepare(); bool paint(QPainter &p); + int titleHeight() const; void paintTitle(Painter &p, const QString &title, const QString &additional = QString()); void setMaxHeight(int32 maxHeight); void resizeMaxHeight(int32 newWidth, int32 maxHeight); @@ -72,25 +67,25 @@ private: bool _closed = false; - bool _blueTitle = false; - ChildWidget _blueClose = { nullptr }; - ChildWidget _blueShadow = { nullptr }; + bool _blockTitle = false; + ChildWidget _blockClose = { nullptr }; + ChildWidget _blockShadow = { nullptr }; }; class ScrollableBoxShadow : public Ui::PlainShadow { public: - ScrollableBoxShadow(QWidget *parent) : Ui::PlainShadow(parent, st::boxScrollShadowBg) { - } + ScrollableBoxShadow(QWidget *parent); + }; class ScrollableBox : public AbstractBox { public: - ScrollableBox(const style::flatScroll &scroll, int w = st::boxWideWidth); + ScrollableBox(const style::flatScroll &scroll, int w = 0); protected: - void init(TWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); - void setScrollSkips(int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); + void init(TWidget *inner, int bottomSkip = -1, int topSkip = -1); + void setScrollSkips(int bottomSkip = -1, int topSkip = -1); void resizeEvent(QResizeEvent *e) override; @@ -108,7 +103,7 @@ private: class ItemListBox : public ScrollableBox { public: - ItemListBox(const style::flatScroll &scroll, int32 w = st::boxWideWidth); + ItemListBox(const style::flatScroll &scroll, int32 w = 0); }; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 17bf2fea9..fedcb3376 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -19,21 +19,21 @@ 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/addcontactbox.h" +#include "styles/style_boxes.h" +#include "styles/style_dialogs.h" +#include "lang.h" #include "application.h" -#include "addcontactbox.h" -#include "contactsbox.h" -#include "confirmbox.h" -#include "photocropbox.h" +#include "boxes/contactsbox.h" +#include "boxes/confirmbox.h" +#include "boxes/photocropbox.h" #include "ui/filedialog.h" #include "ui/buttons/checkbox.h" #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" #include "observer_peer.h" -#include "styles/style_boxes.h" -#include "styles/style_dialogs.h" AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : AbstractBox(st::boxWidth) , _first(this, st::defaultInputField, lang(lng_signup_firstname), fname) @@ -73,7 +73,7 @@ void AddContactBox::initBox() { bool readyToAdd = !_phone->getLastText().isEmpty() && (!_first->getLastText().isEmpty() || !_last->getLastText().isEmpty()); _boxTitle = lang(readyToAdd ? lng_confirm_contact_data : lng_enter_contact_data); } - setMaxHeight(st::boxTitleHeight + st::contactPadding.top() + _first->height() + st::contactSkip + _last->height() + st::contactPhoneSkip + _phone->height() + st::contactPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::contactPadding.top() + _first->height() + st::contactSkip + _last->height() + st::contactPhoneSkip + _phone->height() + st::contactPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom()); _retry->hide(); connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); @@ -115,8 +115,8 @@ void AddContactBox::paintEvent(QPaintEvent *e) { } else { p.setPen(st::boxTextFg); p.setFont(st::boxTextFont); - int32 h = height() - st::boxTitleHeight - st::contactPadding.top() - st::contactPadding.bottom() - st::boxPadding.bottom() - st::boxButtonPadding.top() - _retry->height() - st::boxButtonPadding.bottom(); - p.drawText(QRect(st::boxPadding.left(), st::boxTitleHeight + st::contactPadding.top(), width() - st::boxPadding.left() - st::boxPadding.right(), h), lng_contact_not_joined(lt_name, _sentName), style::al_topleft); + int32 h = height() - titleHeight() - st::contactPadding.top() - st::contactPadding.bottom() - st::boxPadding.bottom() - st::boxButtonPadding.top() - _retry->height() - st::boxButtonPadding.bottom(); + p.drawText(QRect(st::boxPadding.left(), titleHeight() + st::contactPadding.top(), width() - st::boxPadding.left() - st::boxPadding.right(), h), lng_contact_not_joined(lt_name, _sentName), style::al_topleft); } } @@ -125,11 +125,11 @@ void AddContactBox::resizeEvent(QResizeEvent *e) { _last->resize(_first->width(), _last->height()); _phone->resize(_first->width(), _last->height()); if (_invertOrder) { - _last->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), st::boxTitleHeight + st::contactPadding.top()); + _last->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), titleHeight() + st::contactPadding.top()); _first->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _last->y() + _last->height() + st::contactSkip); _phone->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _first->y() + _first->height() + st::contactPhoneSkip); } else { - _first->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), st::boxTitleHeight + st::contactPadding.top()); + _first->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), titleHeight() + st::contactPadding.top()); _last->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _first->y() + _first->height() + st::contactSkip); _phone->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _last->y() + _last->height() + st::contactPhoneSkip); } @@ -919,7 +919,7 @@ _invertOrder(!peer->isChat() && langFirstNameGoesSecond()) { _first->setMaxLength(MaxGroupChannelTitle); _last->setMaxLength(MaxGroupChannelTitle); - int32 h = st::boxTitleHeight + st::contactPadding.top() + _first->height(); + int32 h = titleHeight() + st::contactPadding.top() + _first->height(); if (_peer->isUser()) { _boxTitle = lang(_peer == App::self() ? lng_edit_self_title : lng_edit_contact_title); h += st::contactSkip + _last->height(); @@ -989,10 +989,10 @@ void EditNameTitleBox::resizeEvent(QResizeEvent *e) { _first->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _first->height()); _last->resize(_first->size()); if (_invertOrder) { - _last->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::contactPadding.top()); + _last->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), titleHeight() + st::contactPadding.top()); _first->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _last->y() + _last->height() + st::contactSkip); } else { - _first->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::contactPadding.top()); + _first->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), titleHeight() + st::contactPadding.top()); _last->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _first->y() + _first->height() + st::contactSkip); } @@ -1161,7 +1161,7 @@ void EditChannelBox::onDescriptionResized() { } void EditChannelBox::updateMaxHeight() { - int32 h = st::boxTitleHeight + st::newGroupInfoPadding.top() + _title->height(); + int32 h = titleHeight() + st::newGroupInfoPadding.top() + _title->height(); h += st::newGroupDescriptionPadding.top() + _description->height() + st::newGroupDescriptionPadding.bottom(); if (!_channel->isMegagroup()) { h += st::newGroupPublicLinkPadding.top() + _sign->height() + st::newGroupPublicLinkPadding.bottom(); @@ -1175,7 +1175,7 @@ void EditChannelBox::updateMaxHeight() { void EditChannelBox::resizeEvent(QResizeEvent *e) { _title->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _title->height()); - _title->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::newGroupInfoPadding.top() + st::newGroupNamePosition.y()); + _title->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), titleHeight() + st::newGroupInfoPadding.top() + st::newGroupNamePosition.y()); _description->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _title->y() + _title->height() + st::newGroupDescriptionPadding.top()); diff --git a/Telegram/SourceFiles/boxes/autolockbox.cpp b/Telegram/SourceFiles/boxes/autolockbox.cpp index b0fa448ce..e6310cda0 100644 --- a/Telegram/SourceFiles/boxes/autolockbox.cpp +++ b/Telegram/SourceFiles/boxes/autolockbox.cpp @@ -19,12 +19,11 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +#include "boxes/autolockbox.h" + #include "lang.h" - #include "localstorage.h" - -#include "autolockbox.h" -#include "confirmbox.h" +#include "boxes/confirmbox.h" #include "mainwidget.h" #include "mainwindow.h" #include "ui/buttons/checkbox.h" @@ -36,9 +35,9 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { int32 opts[] = { 60, 300, 3600, 18000 }, cnt = sizeof(opts) / sizeof(opts[0]); - resizeMaxHeight(st::langsWidth, st::boxTitleHeight + cnt * (st::boxOptionListPadding.top() + st::langsButton.height) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _close.height() + st::boxButtonPadding.bottom()); + resizeMaxHeight(st::langsWidth, titleHeight() + cnt * (st::boxOptionListPadding.top() + st::langsButton.height) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _close.height() + st::boxButtonPadding.bottom()); - int32 y = st::boxTitleHeight + st::boxOptionListPadding.top(); + int32 y = titleHeight() + st::boxOptionListPadding.top(); _options.reserve(cnt); for (int32 i = 0; i < cnt; ++i) { int32 v = opts[i]; diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index 70e1e1b0d..30a0851b2 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -19,9 +19,9 @@ 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/backgroundbox.h" -#include "backgroundbox.h" +#include "lang.h" #include "mainwidget.h" #include "mainwindow.h" #include "window/window_theme.h" diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 293dc5ed8..de9b5f400 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -23,22 +23,19 @@ using "basic.style"; using "ui/widgets/widgets.style"; using "intro/intro.style"; -boxBlueTitleBg: #6393b5; -boxBlueTitleFg: #ffffff; -boxBlueTitleAdditionalFg: #dae9f5; -boxBlueTitleAdditionalSkip: 12px; -boxBlueTitlePosition: point(23px, 18px); -boxBlueTitleShadow: icon {{ "box_title_shadow", windowShadowFg }}; +boxBlockTitleHeight: 48px; +boxBlockTitlePosition: point(18px, 14px); +boxBlockTitleFont: font(boxFontSize semibold); +boxBlockTitleAdditionalSkip: 6px; +boxBlockTitleAdditionalFont: normalFont; +boxBlockTitleShadow: icon {{ "box_title_shadow", windowShadowFg }}; -boxBlueClose: MaskButton(defaultMaskButton) { - width: boxTitleHeight; - height: boxTitleHeight; +boxBlockTitleClose: IconButton(defaultIconButton) { + width: boxBlockTitleHeight; + height: boxBlockTitleHeight; - bg: boxBlueTitleBg; - iconBg: #c8e1f0; - iconBgOver: #ffffff; - - icon: icon {{ "box_button_close-invert", boxBlueTitleBg }}; + icon: boxBlockTitleCloseIcon; + iconOver: boxBlockTitleCloseIconOver; } confirmInviteTitle: flatLabel(labelDefFlat) { @@ -147,15 +144,12 @@ contactsMultiSelect: MultiSelect { fieldIcon: boxFieldSearchIcon; fieldIconSkip: 36px; - fieldCancel: MaskButton(defaultMaskButton) { + fieldCancel: IconButton { width: 41px; height: 48px; - bg: boxSearchBg; icon: boxSearchCancelIcon; - - iconBg: boxSearchCancelIconFg; - iconBgOver: boxSearchCancelIconFgOver; + iconOver: boxSearchCancelIconOver; iconPosition: point(8px, 18px); iconPositionDown: point(8px, 18px); diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index d907c745d..951fc3d15 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "boxes/confirmbox.h" +#include "styles/style_boxes.h" #include "lang.h" #include "mainwidget.h" #include "mainwindow.h" @@ -28,7 +29,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "ui/buttons/checkbox.h" #include "core/click_handler_types.h" -#include "styles/style_boxes.h" #include "localstorage.h" TextParseOptions _confirmBoxTextOptions = { @@ -316,7 +316,7 @@ ConvertToSupergroupBox::ConvertToSupergroupBox(ChatData *chat) : AbstractBox(st: _note.setText(st::boxTextFont, lng_profile_convert_warning(lt_bold_start, textcmdStartSemibold(), lt_bold_end, textcmdStopSemibold()), _confirmBoxTextOptions); _textWidth = st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right(); _textHeight = _text.countHeight(_textWidth); - setMaxHeight(st::boxTitleHeight + _textHeight + st::boxPadding.bottom() + _note.countHeight(_textWidth) + st::boxButtonPadding.top() + _convert->height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + _textHeight + st::boxPadding.bottom() + _note.countHeight(_textWidth) + st::boxButtonPadding.top() + _convert->height() + st::boxButtonPadding.bottom()); textstyleRestore(); connect(_convert, SIGNAL(clicked()), this, SLOT(onConvert())); @@ -382,8 +382,8 @@ void ConvertToSupergroupBox::paintEvent(QPaintEvent *e) { // draw box title / text p.setPen(st::boxTextFg); textstyleSet(&st::boxTextStyle); - _text.drawLeft(p, st::boxPadding.left(), st::boxTitleHeight, _textWidth, width()); - _note.drawLeft(p, st::boxPadding.left(), st::boxTitleHeight + _textHeight + st::boxPadding.bottom(), _textWidth, width()); + _text.drawLeft(p, st::boxPadding.left(), titleHeight(), _textWidth, width()); + _note.drawLeft(p, st::boxPadding.left(), titleHeight() + _textHeight + st::boxPadding.bottom(), _textWidth, width()); textstyleRestore(); } diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.cpp b/Telegram/SourceFiles/boxes/confirmphonebox.cpp index 7dc63ab2d..23ca5c067 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.cpp +++ b/Telegram/SourceFiles/boxes/confirmphonebox.cpp @@ -93,7 +93,7 @@ void ConfirmPhoneBox::setCallStatus(const CallStatus &status) { } void ConfirmPhoneBox::launch() { - setBlueTitle(true); + setBlockTitle(true); _about = new FlatLabel(this, st::confirmPhoneAboutLabel); TextWithEntities aboutText; @@ -110,7 +110,7 @@ void ConfirmPhoneBox::launch() { _send = new BoxButton(this, lang(lng_confirm_phone_send), st::defaultBoxButton); _cancel = new BoxButton(this, lang(lng_cancel), st::cancelBoxButton); - setMaxHeight(st::boxTitleHeight + st::usernamePadding.top() + _code->height() + st::usernameSkip + _about->height() + st::usernameSkip + _send->height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::usernamePadding.top() + _code->height() + st::usernameSkip + _about->height() + st::usernameSkip + _send->height() + st::boxButtonPadding.bottom()); connect(_send, SIGNAL(clicked()), this, SLOT(onSendCode())); connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); @@ -289,7 +289,7 @@ QString ConfirmPhoneBox::getCallText() const { void ConfirmPhoneBox::resizeEvent(QResizeEvent *e) { _code->resize(width() - st::usernamePadding.left() - st::usernamePadding.right(), _code->height()); - _code->moveToLeft(st::usernamePadding.left(), st::boxTitleHeight + st::usernamePadding.top()); + _code->moveToLeft(st::usernamePadding.left(), titleHeight() + st::usernamePadding.top()); _about->moveToLeft(st::usernamePadding.left(), _code->y() + _code->height() + st::usernameSkip); diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index f4f65460e..5fc825fbd 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -19,11 +19,10 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +#include "boxes/connectionbox.h" + #include "lang.h" - #include "localstorage.h" - -#include "connectionbox.h" #include "mainwidget.h" #include "mainwindow.h" #include "ui/buttons/checkbox.h" @@ -62,7 +61,7 @@ void ConnectionBox::showAll() { _tcpProxyRadio->show(); _tryIPv6->show(); - int32 h = st::boxTitleHeight + st::boxOptionListPadding.top() + _autoRadio->height() + st::boxOptionListPadding.top() + _httpProxyRadio->height() + st::boxOptionListPadding.top() + _tcpProxyRadio->height() + st::boxOptionListPadding.top() + st::connectionIPv6Skip + _tryIPv6->height() + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); + int32 h = titleHeight() + st::boxOptionListPadding.top() + _autoRadio->height() + st::boxOptionListPadding.top() + _httpProxyRadio->height() + st::boxOptionListPadding.top() + _tcpProxyRadio->height() + st::boxOptionListPadding.top() + st::connectionIPv6Skip + _tryIPv6->height() + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); if (_httpProxyRadio->checked() || _tcpProxyRadio->checked()) { h += 2 * st::boxOptionListPadding.top() + 2 * _hostInput->height(); _hostInput->show(); @@ -97,7 +96,7 @@ void ConnectionBox::paintEvent(QPaintEvent *e) { } void ConnectionBox::resizeEvent(QResizeEvent *e) { - _autoRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxTitleHeight + st::boxOptionListPadding.top()); + _autoRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), titleHeight() + st::boxOptionListPadding.top()); _httpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _autoRadio->y() + _autoRadio->height() + st::boxOptionListPadding.top()); int32 inputy = 0; @@ -221,7 +220,7 @@ AutoDownloadBox::AutoDownloadBox() : AbstractBox(st::boxWidth) , _gifPrivate(this, lang(lng_media_auto_private_chats), !(cAutoDownloadGif() & dbiadNoPrivate), st::defaultBoxCheckbox) , _gifGroups(this, lang(lng_media_auto_groups), !(cAutoDownloadGif() & dbiadNoGroups), st::defaultBoxCheckbox) , _gifPlay(this, lang(lng_media_auto_play), cAutoPlayGif(), st::defaultBoxCheckbox) -, _sectionHeight(st::boxTitleHeight + 2 * (st::defaultBoxCheckbox.height + st::setLittleSkip)) +, _sectionHeight(titleHeight() + 2 * (st::defaultBoxCheckbox.height + st::setLittleSkip)) , _save(this, lang(lng_connection_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { @@ -258,13 +257,13 @@ void AutoDownloadBox::paintEvent(QPaintEvent *e) { } void AutoDownloadBox::resizeEvent(QResizeEvent *e) { - _photoPrivate->moveToLeft(st::boxTitlePosition.x(), st::boxTitleHeight + st::setLittleSkip); + _photoPrivate->moveToLeft(st::boxTitlePosition.x(), titleHeight() + st::setLittleSkip); _photoGroups->moveToLeft(st::boxTitlePosition.x(), _photoPrivate->y() + _photoPrivate->height() + st::setLittleSkip); - _audioPrivate->moveToLeft(st::boxTitlePosition.x(), _sectionHeight + st::boxTitleHeight + st::setLittleSkip); + _audioPrivate->moveToLeft(st::boxTitlePosition.x(), _sectionHeight + titleHeight() + st::setLittleSkip); _audioGroups->moveToLeft(st::boxTitlePosition.x(), _audioPrivate->y() + _audioPrivate->height() + st::setLittleSkip); - _gifPrivate->moveToLeft(st::boxTitlePosition.x(), 2 * _sectionHeight + st::boxTitleHeight + st::setLittleSkip); + _gifPrivate->moveToLeft(st::boxTitlePosition.x(), 2 * _sectionHeight + titleHeight() + st::setLittleSkip); _gifGroups->moveToLeft(st::boxTitlePosition.x(), _gifPrivate->y() + _gifPrivate->height() + st::setLittleSkip); _gifPlay->moveToLeft(st::boxTitlePosition.x(), _gifGroups->y() + _gifGroups->height() + st::setLittleSkip); diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 704741cae..20c59e3aa 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -22,9 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/contactsbox.h" #include "dialogs/dialogs_indexed_list.h" +#include "styles/style_boxes.h" #include "styles/style_dialogs.h" #include "styles/style_history.h" -#include "styles/style_boxes.h" #include "lang.h" #include "boxes/addcontactbox.h" #include "mainwidget.h" @@ -307,7 +307,7 @@ void ContactsBox::paintEvent(QPaintEvent *e) { } int ContactsBox::getTopScrollSkip() const { - auto result = st::boxTitleHeight; + auto result = titleHeight(); if (!_select->isHidden()) { result += _select->height(); } @@ -332,7 +332,7 @@ void ContactsBox::resizeEvent(QResizeEvent *e) { ItemListBox::resizeEvent(e); _select->resizeToWidth(width()); - _select->moveToLeft(0, st::boxTitleHeight); + _select->moveToLeft(0, titleHeight()); updateScrollSkips(); diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.cpp b/Telegram/SourceFiles/boxes/downloadpathbox.cpp index b680a07b5..704c916de 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.cpp +++ b/Telegram/SourceFiles/boxes/downloadpathbox.cpp @@ -65,7 +65,7 @@ void DownloadPathBox::showAll() { _save->show(); _cancel->show(); - int32 h = st::boxTitleHeight + st::boxOptionListPadding.top() + _default->height() + st::boxOptionListPadding.top() + _temp->height() + st::boxOptionListPadding.top() + _dir->height(); + int32 h = titleHeight() + st::boxOptionListPadding.top() + _default->height() + st::boxOptionListPadding.top() + _temp->height() + st::boxOptionListPadding.top() + _dir->height(); if (_dir->checked()) h += st::downloadPathSkip + _pathLink->height(); h += st::boxOptionListPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); @@ -80,7 +80,7 @@ void DownloadPathBox::paintEvent(QPaintEvent *e) { } void DownloadPathBox::resizeEvent(QResizeEvent *e) { - _default->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxTitleHeight + st::boxOptionListPadding.top()); + _default->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), titleHeight() + st::boxOptionListPadding.top()); _temp->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _default->y() + _default->height() + st::boxOptionListPadding.top()); _dir->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _temp->y() + _temp->height() + st::boxOptionListPadding.top()); int32 inputx = st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultRadiobutton.textPosition.x(); diff --git a/Telegram/SourceFiles/boxes/emojibox.cpp b/Telegram/SourceFiles/boxes/emojibox.cpp index 2300aa5ba..977638d03 100644 --- a/Telegram/SourceFiles/boxes/emojibox.cpp +++ b/Telegram/SourceFiles/boxes/emojibox.cpp @@ -19,9 +19,9 @@ 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/emojibox.h" -#include "emojibox.h" +#include "lang.h" #include "mainwidget.h" #include "mainwindow.h" @@ -72,13 +72,13 @@ namespace { } EmojiBox::EmojiBox() : _esize(EmojiSizes[EIndex + 1]) { - setBlueTitle(true); + setBlockTitle(true); fillBlocks(); _blockHeight = st::emojiReplaceInnerHeight; - resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + 2 * st::emojiReplacePadding, st::boxTitleHeight + st::emojiReplacePadding + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + st::emojiReplacePadding); + resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + 2 * st::emojiReplacePadding, titleHeight() + st::emojiReplacePadding + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + st::emojiReplacePadding); prepare(); } @@ -129,7 +129,7 @@ void EmojiBox::paintEvent(QPaintEvent *e) { p.setFont(st::emojiTextFont); p.setPen(st::boxTextFg); - int32 top = st::boxTitleHeight + st::emojiReplacePadding + (st::emojiReplaceHeight - _blockHeight) / 2; + int32 top = titleHeight() + st::emojiReplacePadding + (st::emojiReplaceHeight - _blockHeight) / 2; for (Blocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) { int32 rowSize = i->size(), left = (width() - rowSize * st::emojiReplaceWidth) / 2; for (BlockRow::const_iterator j = i->cbegin(), en = i->cend(); j != en; ++j) { diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp index 44674bf0b..4795030f9 100644 --- a/Telegram/SourceFiles/boxes/languagebox.cpp +++ b/Telegram/SourceFiles/boxes/languagebox.cpp @@ -34,7 +34,7 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { bool haveTestLang = (cLang() == languageTest); - int32 y = st::boxTitleHeight + st::boxOptionListPadding.top(); + int32 y = titleHeight() + st::boxOptionListPadding.top(); _langs.reserve(languageCount + (haveTestLang ? 1 : 0)); if (haveTestLang) { _langs.push_back(new Ui::Radiobutton(this, qsl("lang"), languageTest, qsl("Custom Lang"), (cLang() == languageTest), st::langsButton)); @@ -56,7 +56,7 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { connect(_langs.back(), SIGNAL(changed()), this, SLOT(onChange())); } - resizeMaxHeight(st::langsWidth, st::boxTitleHeight + (languageCount + (haveTestLang ? 1 : 0)) * (st::boxOptionListPadding.top() + st::langsButton.height) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _close.height() + st::boxButtonPadding.bottom()); + resizeMaxHeight(st::langsWidth, titleHeight() + (languageCount + (haveTestLang ? 1 : 0)) * (st::boxOptionListPadding.top() + st::langsButton.height) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _close.height() + st::boxButtonPadding.bottom()); connect(&_close, SIGNAL(clicked()), this, SLOT(onClose())); diff --git a/Telegram/SourceFiles/boxes/localstoragebox.cpp b/Telegram/SourceFiles/boxes/localstoragebox.cpp index 23057c10a..2c459fa57 100644 --- a/Telegram/SourceFiles/boxes/localstoragebox.cpp +++ b/Telegram/SourceFiles/boxes/localstoragebox.cpp @@ -21,10 +21,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "boxes/localstoragebox.h" +#include "styles/style_boxes.h" #include "localstorage.h" #include "ui/flatbutton.h" #include "lang.h" -#include "styles/style_boxes.h" #include "mainwindow.h" LocalStorageBox::LocalStorageBox() : AbstractBox() @@ -50,8 +50,8 @@ void LocalStorageBox::updateControls() { rowsHeight = st::linkFont->height + st::localStorageBoxSkip; } _clear->setVisible(_imagesCount > 0 || _audiosCount > 0); - setMaxHeight(st::boxTitleHeight + st::localStorageBoxSkip + rowsHeight + _clear->height() + st::boxButtonPadding.top() + _close->height() + st::boxButtonPadding.bottom()); - _clear->moveToLeft(st::boxPadding.left(), st::boxTitleHeight + st::localStorageBoxSkip + rowsHeight); + setMaxHeight(titleHeight() + st::localStorageBoxSkip + rowsHeight + _clear->height() + st::boxButtonPadding.top() + _close->height() + st::boxButtonPadding.bottom()); + _clear->moveToLeft(st::boxPadding.left(), titleHeight() + st::localStorageBoxSkip + rowsHeight); _close->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close->height()); update(); } @@ -83,7 +83,7 @@ void LocalStorageBox::paintEvent(QPaintEvent *e) { p.setFont(st::boxTextFont); p.setPen(st::windowTextFg); checkLocalStoredCounts(); - int top = st::boxTitleHeight + st::localStorageBoxSkip; + int top = titleHeight() + st::localStorageBoxSkip; if (_imagesCount > 0) { auto text = lng_settings_images_cached(lt_count, _imagesCount, lt_size, formatSizeText(Local::storageImagesSize() + Local::storageStickersSize() + Local::storageWebFilesSize())); p.drawTextLeft(st::boxPadding.left(), top, width(), text); diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp index 6340da7fc..ab956eb0f 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.cpp +++ b/Telegram/SourceFiles/boxes/passcodebox.cpp @@ -75,7 +75,7 @@ PasscodeBox::PasscodeBox(const QByteArray &newSalt, const QByteArray &curSalt, b } void PasscodeBox::init() { - setBlueTitle(true); + setBlockTitle(true); textstyleSet(&st::usernameTextStyle); _about.setRichText(st::normalFont, lang(_cloudPwd ? lng_cloud_password_about : lng_passcode_about)); @@ -84,17 +84,17 @@ void PasscodeBox::init() { if (_turningOff) { _oldPasscode.show(); _boxTitle = lang(_cloudPwd ? lng_cloud_password_remove : lng_passcode_remove); - setMaxHeight(st::boxTitleHeight + st::passcodePadding.top() + _oldPasscode.height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0) + _aboutHeight + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::passcodePadding.top() + _oldPasscode.height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0) + _aboutHeight + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); } else { bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode(); if (has) { _oldPasscode.show(); _boxTitle = lang(_cloudPwd ? lng_cloud_password_change : lng_passcode_change); - setMaxHeight(st::boxTitleHeight + st::passcodePadding.top() + _oldPasscode.height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0) + _newPasscode.height() + st::contactSkip + _reenterPasscode.height() + st::passcodeSkip + (_cloudPwd ? _passwordHint.height() + st::contactSkip : 0) + _aboutHeight + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::passcodePadding.top() + _oldPasscode.height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0) + _newPasscode.height() + st::contactSkip + _reenterPasscode.height() + st::passcodeSkip + (_cloudPwd ? _passwordHint.height() + st::contactSkip : 0) + _aboutHeight + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); } else { _oldPasscode.hide(); _boxTitle = lang(_cloudPwd ? lng_cloud_password_create : lng_passcode_create); - setMaxHeight(st::boxTitleHeight + st::passcodePadding.top() + _newPasscode.height() + st::contactSkip + _reenterPasscode.height() + st::passcodeSkip + (_cloudPwd ? _passwordHint.height() + st::contactSkip : 0) + _aboutHeight + (_cloudPwd ? st::contactSkip + _recoverEmail.height() + st::passcodeSkip : st::passcodePadding.bottom()) + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::passcodePadding.top() + _newPasscode.height() + st::contactSkip + _reenterPasscode.height() + st::passcodeSkip + (_cloudPwd ? _passwordHint.height() + st::contactSkip : 0) + _aboutHeight + (_cloudPwd ? st::contactSkip + _recoverEmail.height() + st::passcodeSkip : st::passcodePadding.bottom()) + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); } } @@ -233,7 +233,7 @@ void PasscodeBox::resizeEvent(QResizeEvent *e) { bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode(); int32 w = st::boxWidth - st::boxPadding.left() - st::boxPadding.right(); _oldPasscode.resize(w, _oldPasscode.height()); - _oldPasscode.moveToLeft(st::boxPadding.left(), st::boxTitleHeight + st::passcodePadding.top()); + _oldPasscode.moveToLeft(st::boxPadding.left(), titleHeight() + st::passcodePadding.top()); _newPasscode.resize(w, _newPasscode.height()); _newPasscode.moveToLeft(st::boxPadding.left(), _oldPasscode.y() + ((_turningOff || has) ? (_oldPasscode.height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0)) : 0)); _reenterPasscode.resize(w, _reenterPasscode.height()); @@ -496,9 +496,9 @@ RecoverBox::RecoverBox(const QString &pattern) : AbstractBox(st::boxWidth) , _saveButton(this, lang(lng_passcode_submit), st::defaultBoxButton) , _cancelButton(this, lang(lng_cancel), st::cancelBoxButton) , _recoverCode(this, st::defaultInputField, lang(lng_signin_code)) { - setBlueTitle(true); + setBlockTitle(true); - setMaxHeight(st::boxTitleHeight + st::passcodePadding.top() + st::passcodeSkip + _recoverCode.height() + st::passcodeSkip + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::passcodePadding.top() + st::passcodeSkip + _recoverCode.height() + st::passcodeSkip + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSubmit())); connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose())); @@ -535,7 +535,7 @@ void RecoverBox::paintEvent(QPaintEvent *e) { void RecoverBox::resizeEvent(QResizeEvent *e) { _recoverCode.resize(st::boxWidth - st::boxPadding.left() - st::boxPadding.right(), _recoverCode.height()); - _recoverCode.moveToLeft(st::boxPadding.left(), st::boxTitleHeight + st::passcodePadding.top() + st::passcodeSkip); + _recoverCode.moveToLeft(st::boxPadding.left(), titleHeight() + st::passcodePadding.top() + st::passcodeSkip); _saveButton.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _saveButton.height()); _cancelButton.moveToRight(st::boxButtonPadding.right() + _saveButton.width() + st::boxButtonPadding.left(), _saveButton.y()); diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index 9d09e825f..8da5e09ac 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -57,7 +57,7 @@ void ReportBox::paintEvent(QPaintEvent *e) { } void ReportBox::resizeEvent(QResizeEvent *e) { - _reasonSpam->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxTitleHeight + st::boxOptionListPadding.top()); + _reasonSpam->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), titleHeight() + st::boxOptionListPadding.top()); _reasonViolence->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonSpam->y() + _reasonSpam->height() + st::boxOptionListPadding.top()); _reasonPornography->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonViolence->y() + _reasonViolence->height() + st::boxOptionListPadding.top()); _reasonOther->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonPornography->y() + _reasonPornography->height() + st::boxOptionListPadding.top()); @@ -143,7 +143,7 @@ bool ReportBox::reportFail(const RPCError &error) { } void ReportBox::updateMaxHeight() { - int32 h = st::boxTitleHeight + 4 * (st::boxOptionListPadding.top() + _reasonSpam->height()) + st::boxButtonPadding.top() + _report->height() + st::boxButtonPadding.bottom(); + int32 h = titleHeight() + 4 * (st::boxOptionListPadding.top() + _reasonSpam->height()) + st::boxButtonPadding.top() + _report->height() + st::boxButtonPadding.bottom(); if (_reasonOtherText) { h += st::newGroupDescriptionPadding.top() + _reasonOtherText->height() + st::newGroupDescriptionPadding.bottom(); } diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index 747a45dc5..3898aecdd 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -45,7 +45,7 @@ SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll) connect(App::wnd(), SIGNAL(newAuthorization()), this, SLOT(onNewAuthorization())); connect(&_shortPollTimer, SIGNAL(timeout()), this, SLOT(onShortPollAuthorizations())); - init(_inner, st::boxButtonPadding.bottom() + _done.height() + st::boxButtonPadding.top(), st::boxTitleHeight); + init(_inner, st::boxButtonPadding.bottom() + _done.height() + st::boxButtonPadding.top(), titleHeight()); _inner->resize(width(), st::noContactsHeight); prepare(); @@ -76,7 +76,7 @@ void SessionsBox::paintEvent(QPaintEvent *e) { if (paint(p)) return; paintTitle(p, lang(lng_sessions_other_header)); - p.translate(0, st::boxTitleHeight); + p.translate(0, titleHeight()); if (_loading) { p.setFont(st::noContactsFont->f); diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 9365d8b11..6969a24b0 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -83,7 +83,7 @@ ShareBox::ShareBox(CopyCallback &©Callback, SubmitCallback &&submitCallback, } int ShareBox::getTopScrollSkip() const { - auto result = st::boxTitleHeight; + auto result = titleHeight(); if (!_select->isHidden()) { result += _select->height(); } @@ -186,7 +186,7 @@ void ShareBox::resizeEvent(QResizeEvent *e) { ItemListBox::resizeEvent(e); _select->resizeToWidth(width()); - _select->moveToLeft(0, st::boxTitleHeight); + _select->moveToLeft(0, titleHeight()); updateScrollSkips(); diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 7bf031fd0..c0093b7ce 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -188,7 +188,7 @@ void StickersBox::setup() { _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); _topShadow.create(this, st::contactsAboutShadow); } - ItemListBox::init(_inner, bottomSkip, st::boxTitleHeight + _aboutHeight); + ItemListBox::init(_inner, bottomSkip, titleHeight() + _aboutHeight); setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight))); connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); @@ -244,7 +244,7 @@ int32 StickersBox::countHeight() const { if (_section == Section::Installed) { bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); } - return st::boxTitleHeight + _aboutHeight + _inner->height() + bottomSkip; + return titleHeight() + _aboutHeight + _inner->height() + bottomSkip; } void StickersBox::disenableDone(const MTPmessages_StickerSetInstallResult &result, mtpRequestId req) { @@ -306,7 +306,7 @@ void StickersBox::paintEvent(QPaintEvent *e) { return lang(lng_stickers_archived); })(); paintTitle(p, title); - p.translate(0, st::boxTitleHeight); + p.translate(0, titleHeight()); if (_aboutHeight > 0) { p.fillRect(0, 0, width(), _aboutHeight, st::contactsAboutBg); @@ -343,7 +343,7 @@ void StickersBox::resizeEvent(QResizeEvent *e) { _inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0); updateVisibleTopBottom(); if (_topShadow) { - _topShadow->setGeometry(0, st::boxTitleHeight + _aboutHeight, width(), st::lineWidth); + _topShadow->setGeometry(0, titleHeight() + _aboutHeight, width(), st::lineWidth); } if (_save) { _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index cc71ef5a8..831962c75 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -200,9 +200,9 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) { } } if (d.vset.type() == mtpc_stickerSet) { - auto &s(d.vset.c_stickerSet()); + auto &s = d.vset.c_stickerSet(); _setTitle = stickerSetTitle(s); - _title = st::boxTitleFont->elided(_setTitle, width() - st::boxTitlePosition.x() - st::boxTitleHeight); + _title = st::boxBlockTitleFont->elided(_setTitle, width() - st::boxBlockTitlePosition.x() - st::boxBlockTitleHeight); _setShortName = qs(s.vshort_name); _setId = s.vid.v; _setAccess = s.vaccess_hash.v; diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp index dea3b4056..aa4365cf6 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.cpp +++ b/Telegram/SourceFiles/boxes/usernamebox.cpp @@ -33,13 +33,13 @@ _username(this, st::defaultInputField, qsl("@username"), App::self()->username, _link(this, QString(), st::defaultBoxLinkButton), _saveRequestId(0), _checkRequestId(0), _about(st::boxWidth - st::usernamePadding.left()) { - setBlueTitle(true); + setBlockTitle(true); _goodText = App::self()->username.isEmpty() ? QString() : lang(lng_username_available); textstyleSet(&st::usernameTextStyle); _about.setRichText(st::boxTextFont, lang(lng_username_about)); - resizeMaxHeight(st::boxWidth, st::boxTitleHeight + st::usernamePadding.top() + _username.height() + st::usernameSkip + _about.countHeight(st::boxWidth - st::usernamePadding.left()) + 3 * st::usernameTextStyle.lineHeight + st::usernamePadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + resizeMaxHeight(st::boxWidth, titleHeight() + st::usernamePadding.top() + _username.height() + st::usernameSkip + _about.countHeight(st::boxWidth - st::usernamePadding.left()) + 3 * st::usernameTextStyle.lineHeight + st::usernamePadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); textstyleRestore(); connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); @@ -106,7 +106,7 @@ void UsernameBox::paintEvent(QPaintEvent *e) { void UsernameBox::resizeEvent(QResizeEvent *e) { _username.resize(width() - st::usernamePadding.left() - st::usernamePadding.right(), _username.height()); - _username.moveToLeft(st::usernamePadding.left(), st::boxTitleHeight + st::usernamePadding.top()); + _username.moveToLeft(st::usernamePadding.left(), titleHeight() + st::usernamePadding.top()); textstyleSet(&st::usernameTextStyle); int32 availw = st::boxWidth - st::usernamePadding.left(), h = _about.countHeight(availw); diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 2c472b8fb..0afb5405b 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019002ULL) +#define BETA_VERSION_MACRO (10019003ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 8cccec7ab..e5c4a6a06 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -195,6 +195,9 @@ historySend: IconButton { iconPosition: point(11px, 11px); iconPositionDown: point(11px, 11px); } +historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(3px, 7px) }}; +historyEditSaveIconOver: icon {{ "send_control_save", historySendIconFgOver, point(3px, 7px) }}; + historyAttach: IconButton(historySend) { icon: icon {{ "send_control_attach", historyComposeIconFg }}; iconOver: icon {{ "send_control_attach", historyComposeIconFgOver }}; @@ -276,17 +279,17 @@ historyReplyIconPosition: point(13px, 13px); historyReplyIcon: icon {{ "history_action_reply", #6fa1d2 }}; historyForwardIcon: icon {{ "history_action_forward", #6fa1d2 }}; historyEditIcon: icon {{ "history_action_edit", #6fa1d2 }}; -historyReplyCancel: MaskButton(defaultMaskButton) { +historyReplyCancel: IconButton { width: 49px; height: 49px; - bg: historyReplyBg; icon: historyReplyCancelIcon; + iconOver: historyReplyCancelIconOver; - iconBg: historyReplyCancelIconFg; - iconBgOver: historyReplyCancelIconFgOver; + iconPosition: point(-1px, -1px); + iconPositionDown: point(-1px, -1px); } -historyInlineBotCancel: MaskButton(historyReplyCancel) { +historyInlineBotCancel: IconButton(historyReplyCancel) { height: 46px; } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 4b1ef2960..bdb6ecee0 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2996,10 +2996,10 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _fieldAutocomplete(this) , _reportSpamPanel(this) , _send(this, st::historySend) -, _unblock(this, lang(lng_unblock_button), st::historyUnblock) -, _botStart(this, lang(lng_bot_start), st::historyComposeButton) -, _joinChannel(this, lang(lng_channel_join), st::historyComposeButton) -, _muteUnmute(this, lang(lng_channel_mute), st::historyComposeButton) +, _unblock(this, lang(lng_unblock_button).toUpper(), st::historyUnblock) +, _botStart(this, lang(lng_bot_start).toUpper(), st::historyComposeButton) +, _joinChannel(this, lang(lng_channel_join).toUpper(), st::historyComposeButton) +, _muteUnmute(this, lang(lng_channel_mute).toUpper(), st::historyComposeButton) , _attachToggle(this, st::historyAttach) , _attachEmoji(this, st::historyAttachEmoji) , _botKeyboardShow(this, st::historyBotKeyboardShow) @@ -6473,7 +6473,7 @@ void HistoryWidget::clearInlineBot() { void HistoryWidget::inlineBotChanged() { bool isInlineBot = _inlineBot && (_inlineBot != LookingUpInlineBot); if (isInlineBot && !_inlineBotCancel) { - _inlineBotCancel = std_::make_unique(this, st::historyInlineBotCancel); + _inlineBotCancel = std_::make_unique(this, st::historyInlineBotCancel); connect(_inlineBotCancel.get(), SIGNAL(clicked()), this, SLOT(onInlineBotCancel())); _inlineBotCancel->setGeometry(_send->geometry()); _attachEmoji->raise(); @@ -6514,14 +6514,14 @@ void HistoryWidget::onCheckFieldAutocomplete() { void HistoryWidget::updateFieldPlaceholder() { if (_editMsgId) { _field->setPlaceholder(lang(lng_edit_message_text)); -// _send->setText(lang(lng_settings_save)); + _send->setIcon(&st::historyEditSaveIcon, &st::historyEditSaveIconOver); } else { if (_inlineBot && _inlineBot != LookingUpInlineBot) { _field->setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); } else { _field->setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_silent->checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_message_ph)); } -// _send->setText(lang(lng_send_button)); + _send->setIcon(nullptr); } } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 1af2d8003..37df22358 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -42,7 +42,6 @@ class DropdownMenu; class PlainShadow; class PopupMenu; class IconButton; -class MaskButton; class HistoryDownButton; class EmojiButton; } // namespace Ui @@ -897,7 +896,7 @@ private: Text _replyEditMsgText; mutable SingleTimer _updateEditTimeLeftDisplay; - ChildWidget _fieldBarCancel; + ChildWidget _fieldBarCancel; void updateReplyEditTexts(bool force = false); struct PinnedBar { @@ -907,7 +906,7 @@ private: MsgId msgId = 0; HistoryItem *msg = nullptr; Text text; - ChildWidget cancel; + ChildWidget cancel; ChildWidget shadow; }; std_::unique_ptr _pinnedBar; @@ -1087,7 +1086,7 @@ private: UserData *_inlineBot = nullptr; QString _inlineBotUsername; mtpRequestId _inlineBotResolveRequestId = 0; - std_::unique_ptr _inlineBotCancel; + std_::unique_ptr _inlineBotCancel; void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result); bool inlineBotResolveFail(QString name, const RPCError &error); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 86984e543..35e199cbf 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -559,6 +559,7 @@ void MainWindow::showConnecting(const QString &text, const QString &reconnect) { _connecting->set(text, reconnect); } else { _connecting.create(bodyWidget(), text, reconnect); + _connecting->show(); updateControlsGeometry(); fixOrder(); } diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 41804f51e..1211d3c34 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -215,7 +215,7 @@ mediaPlayerScroll: flatScroll(solidScroll) { } mediaPlayerListHeightMax: 280px; mediaPlayerListMarginBottom: 10px; -mediaPlayerScrollShadow: icon {{ "playlist_shadow", #000000 }}; +mediaPlayerScrollShadow: icon {{ "playlist_shadow", windowShadowFg }}; mediaPlayerListMarginTop: 8px; mediaPlayerListIconFg: #ffffff; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 32ad78e0f..071e2bb8e 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -31,21 +31,12 @@ settingsMargin: 48px; settingsFixedBarHeight: 52px; settingsFixedBarFont: font(14px semibold); -settingsFixedBarFg: windowTextFg; settingsFixedBarTextLeft: 20px; settingsFixedBarTextTop: 16px; -settingsFixedBarClose: MaskButton(defaultMaskButton) { +settingsFixedBarClose: IconButton(boxBlockTitleClose) { width: settingsFixedBarHeight; height: settingsFixedBarHeight; - - bg: settingsFixedBarBg; - icon: settingsFixedBarCloseIcon; - - iconBg: settingsCloseFg; - iconBgOver: settingsCloseFgOver; } -settingsFixedBarShadowBg1: #00000021; -settingsFixedBarShadowBg2: #0000000b; settingsMarginTop: 34px; settingsPhotoSize: 112px; diff --git a/Telegram/SourceFiles/settings/settings_fixed_bar.cpp b/Telegram/SourceFiles/settings/settings_fixed_bar.cpp index 82c1cdad8..5954df433 100644 --- a/Telegram/SourceFiles/settings/settings_fixed_bar.cpp +++ b/Telegram/SourceFiles/settings/settings_fixed_bar.cpp @@ -48,7 +48,7 @@ void FixedBar::resizeEvent(QResizeEvent *e) { void FixedBar::paintEvent(QPaintEvent *e) { Painter p(this); - p.fillRect(e->rect(), st::settingsFixedBarBg); + p.fillRect(e->rect(), st::boxBlockTitleBg); p.setFont(st::settingsFixedBarFont); p.setPen(st::windowTextFg); diff --git a/Telegram/SourceFiles/settings/settings_fixed_bar.h b/Telegram/SourceFiles/settings/settings_fixed_bar.h index 720da2b91..7ebc87e3d 100644 --- a/Telegram/SourceFiles/settings/settings_fixed_bar.h +++ b/Telegram/SourceFiles/settings/settings_fixed_bar.h @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once namespace Ui { -class MaskButton; +class IconButton; } // namespace Ui namespace Settings { @@ -37,7 +37,7 @@ protected: int resizeGetHeight(int newWidth) override; private: - ChildWidget _close; + ChildWidget _close; }; diff --git a/Telegram/SourceFiles/settings/settings_widget.cpp b/Telegram/SourceFiles/settings/settings_widget.cpp index c3ae485e8..0c377d720 100644 --- a/Telegram/SourceFiles/settings/settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_widget.cpp @@ -128,14 +128,12 @@ Widget::Widget(QWidget *parent) : LayerWidget(parent) , _scroll(this, st::setScroll) , _inner(this) , _fixedBar(this) -, _fixedBarShadow1(this, st::settingsFixedBarShadowBg1) -, _fixedBarShadow2(this, st::settingsFixedBarShadowBg2) { +, _fixedBarShadow(this, st::boxBlockTitleShadow) { _scroll->setOwnedWidget(_inner); setAttribute(Qt::WA_OpaquePaintEvent); _fixedBar->move(0, 0); - _fixedBarShadow1->move(0, _fixedBar->y() + st::settingsFixedBarHeight); - _fixedBarShadow2->move(0, _fixedBarShadow1->y() + st::lineWidth); + _fixedBarShadow->move(0, _fixedBar->y() + st::settingsFixedBarHeight); _scroll->move(0, st::settingsFixedBarHeight); connect(_inner, SIGNAL(heightUpdated()), this, SLOT(onInnerHeightUpdated())); @@ -211,8 +209,7 @@ void Widget::resizeEvent(QResizeEvent *e) { } _fixedBar->resizeToWidth(width()); - _fixedBarShadow1->resize(width(), st::lineWidth); - _fixedBarShadow2->resize(width(), st::lineWidth); + _fixedBarShadow->resize(width(), st::boxBlockTitleShadow.height()); QSize scrollSize(width(), height() - _fixedBar->height()); if (_scroll->size() != scrollSize) { diff --git a/Telegram/SourceFiles/settings/settings_widget.h b/Telegram/SourceFiles/settings/settings_widget.h index 06dde46d8..b32afaea4 100644 --- a/Telegram/SourceFiles/settings/settings_widget.h +++ b/Telegram/SourceFiles/settings/settings_widget.h @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "layerwidget.h" namespace Ui { -class PlainShadow; +class GradientShadow; } // namespace Ui namespace Settings { @@ -54,7 +54,7 @@ private: ChildWidget _scroll; ChildWidget _inner; ChildWidget _fixedBar; - ChildWidget _fixedBarShadow1, _fixedBarShadow2; + ChildWidget _fixedBarShadow; int _contentLeft = 0; diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.cpp b/Telegram/SourceFiles/ui/buttons/icon_button.cpp index e5794fe68..f1003d0a1 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/icon_button.cpp @@ -60,6 +60,9 @@ void IconButton::paintEvent(QPaintEvent *e) { if (position.x() < 0) { position.setX((width() - icon->width()) / 2); } + if (position.y() < 0) { + position.setY((height() - icon->height()) / 2); + } icon->paint(p, position, width()); if (over > 0. && over < 1.) { auto iconOver = overIcon(); diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index 71ecb4bda..e43b4eade 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -201,7 +201,7 @@ CountrySelectBox::CountrySelectBox() : ItemListBox(st::countriesScroll, st::boxW , _topShadow(this) { _select->resizeToWidth(st::boxWidth); - ItemListBox::init(_inner, st::boxScrollSkip, st::boxTitleHeight + _select->height()); + ItemListBox::init(_inner, st::boxScrollSkip, titleHeight() + _select->height()); _select->setQueryChangedCallback([this](const QString &query) { onFilterUpdate(query); }); _select->setSubmittedCallback([this](bool) { onSubmit(); }); @@ -240,10 +240,10 @@ void CountrySelectBox::resizeEvent(QResizeEvent *e) { ItemListBox::resizeEvent(e); _select->resizeToWidth(width()); - _select->moveToLeft(0, st::boxTitleHeight); + _select->moveToLeft(0, titleHeight()); _inner->resizeToWidth(width()); - _topShadow.setGeometry(0, st::boxTitleHeight + _select->height(), width(), st::lineWidth); + _topShadow.setGeometry(0, titleHeight() + _select->height(), width(), st::lineWidth); } void CountrySelectBox::showAll() { diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.h b/Telegram/SourceFiles/ui/widgets/multi_select.h index 423ea1797..9ac568ee6 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.h +++ b/Telegram/SourceFiles/ui/widgets/multi_select.h @@ -26,7 +26,7 @@ class InputField; namespace Ui { -class MaskButton; +class IconButton; class MultiSelect : public TWidget { public: @@ -154,7 +154,7 @@ private: int _fieldTop = 0; int _fieldWidth = 0; ChildWidget _field; - ChildWidget _cancel; + ChildWidget _cancel; int _newHeight = 0; IntAnimation _height; diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 2945416cd..7a4fbebdf 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -119,7 +119,7 @@ MultiSelect { fieldMinWidth: pixels; fieldIcon: icon; fieldIconSkip: pixels; - fieldCancel: MaskButton; + fieldCancel: IconButton; fieldCancelSkip: pixels; } @@ -181,6 +181,11 @@ defaultLabelSimple: LabelSimple { textFg: windowTextFg; } +defaultIconButton: IconButton { + iconPosition: point(-1px, -1px); + iconPositionDown: point(-1px, -1px); +} + defaultMaskButton: MaskButton { iconPosition: point(-1px, -1px); iconPositionDown: point(-1px, -1px); diff --git a/Telegram/build/version b/Telegram/build/version index 8301c82a1..577f8e9c5 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019002 +BetaVersion 10019003 From 8ff3779c9af84160c3f60cea38933b51538cb8c3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 9 Nov 2016 11:34:38 +0300 Subject: [PATCH 027/100] Custom title bar for macOS 10.10+ improved, colors in palette now. --- Telegram/Resources/colors.palette | 4 +- Telegram/Resources/sample.tdesktop-theme | 2 + Telegram/SourceFiles/mainwindow.cpp | 7 ---- Telegram/SourceFiles/mainwindow.h | 1 - .../platform/linux/main_window_linux.h | 3 +- .../platform/mac/main_window_mac.h | 10 +++-- .../platform/mac/main_window_mac.mm | 23 +++++++--- .../platform/mac/window_title_mac.h | 8 +++- .../platform/mac/window_title_mac.mm | 42 +++++++++++++++++-- .../platform/win/main_window_win.cpp | 10 +++-- .../platform/win/main_window_win.h | 5 ++- Telegram/SourceFiles/window/main_window.cpp | 20 ++++++++- Telegram/SourceFiles/window/main_window.h | 15 ++++++- 13 files changed, 116 insertions(+), 34 deletions(-) diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 41f036e6b..668888f2e 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -49,7 +49,7 @@ lightButtonFgOver: lightButtonFg; menuIconFg: #a8a8a8; menuIconFgOver: #999999; -// custom title bar for Windows +// custom title bar for Windows and macOS titleBg: windowOverBg; titleShadow: #00000003; titleButtonFg: #ababab; @@ -57,6 +57,8 @@ titleButtonBgOver: #e5e5e5; titleButtonFgOver: #9a9a9a; titleButtonCloseBgOver: #e81123; titleButtonCloseFgOver: #ffffff; +titleFgActive: #3e3c3e; +titleFg: #acacac; // tray icon trayCounterBg: #f23c34; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index 7db210cc7..da0b103bf 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -51,6 +51,8 @@ titleButtonBgOver: #e5e5e5; titleButtonFgOver: #9a9a9a; titleButtonCloseBgOver: #e81123; titleButtonCloseFgOver: #ffffff; +titleFgActive: #3e3c3e; +titleFg: #acacac; trayCounterBg: #f23c34; trayCounterBgMute: #888888; trayCounterFg: #ffffff; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 35e199cbf..e7e665031 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -103,7 +103,6 @@ MainWindow::MainWindow() { Notify::unreadCounterUpdated(); } }); - subscribe(Global::RefUnreadCounterUpdate(), [this] { updateUnreadCounter(); }); resize(st::windowDefaultWidth, st::windowDefaultHeight); @@ -388,12 +387,6 @@ void MainWindow::setupMain(const MTPUser *self) { updateConnectingStatus(); } -void MainWindow::updateUnreadCounter() { - if (!Global::started() || App::quitting()) return; - - psUpdateCounter(); -} - void MainWindow::showSettings() { if (_passcode) return; diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index a40ca68b4..c6d032f8a 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -216,7 +216,6 @@ private slots: void onWindowActiveChanged(); private: - void updateUnreadCounter(); void showConnecting(const QString &text, const QString &reconnect = QString()); void hideConnecting(); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 4ce43c036..824005b5c 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -43,8 +43,6 @@ public: void psRefreshTaskbarIcon() { } - void psUpdateCounter(); - bool psHasNativeNotifications(); virtual QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) = 0; @@ -61,6 +59,7 @@ public slots: protected: void initHook() override; + void unreadCounterChangedHook() override; bool psHasTrayIcon() const; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index e2e634098..da0490c5c 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -45,10 +45,6 @@ public: bool psFilterNativeEvent(void *event); - bool eventFilter(QObject *obj, QEvent *evt) override; - - void psUpdateCounter(); - bool psHasNativeNotifications() { return !(QSysInfo::macVersion() < QSysInfo::MV_10_8); } @@ -80,8 +76,12 @@ private slots: void onHideAfterFullScreen(); protected: + bool eventFilter(QObject *obj, QEvent *evt) override; + void stateChangedHook(Qt::WindowState state) override; void initHook() override; + void titleVisibilityChangedHook() override; + void unreadCounterChangedHook() override; QImage psTrayIcon(bool selected = false) const; bool psHasTrayIcon() const { @@ -105,6 +105,8 @@ protected: private: void createGlobalMenu(); + void updateTitleCounter(); + void updateIconCounters(); friend class Private; std_::unique_ptr _private; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 3e0851c9d..1a8a56a23 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -169,11 +169,11 @@ void MainWindow::Private::initCustomTitle(NSWindow *window, NSView *view) { } void MainWindow::Private::willEnterFullScreen() { - _public->setTitleVisibility(false); + _public->setTitleVisible(false); } void MainWindow::Private::willExitFullScreen() { - _public->setTitleVisibility(true); + _public->setTitleVisible(true); } void MainWindow::Private::enableShadow(WId winId) { @@ -243,6 +243,10 @@ void MainWindow::initHook() { } } +void MainWindow::titleVisibilityChangedHook() { + updateTitleCounter(); +} + void MainWindow::onHideAfterFullScreen() { hide(); } @@ -269,7 +273,7 @@ void MainWindow::psSetupTrayIcon() { connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)), Qt::UniqueConnection); App::wnd()->updateTrayMenu(); } - psUpdateCounter(); + updateIconCounters(); trayIcon->show(); } @@ -323,10 +327,17 @@ void _placeCounter(QImage &img, int size, int count, const style::color &bg, con p.drawText(size - w - d - skip, size - f->height + f->ascent - skip, cnt); } -void MainWindow::psUpdateCounter() { - int32 counter = App::histories().unreadBadge(); +void MainWindow::updateTitleCounter() { + setWindowTitle(titleVisible() ? QString() : titleText()); +} - setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); +void MainWindow::unreadCounterChangedHook() { + updateTitleCounter(); + updateIconCounters(); +} + +void MainWindow::updateIconCounters() { + auto counter = App::histories().unreadBadge(); QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0')); _private->setWindowBadge(counter ? cnt : QString()); diff --git a/Telegram/SourceFiles/platform/mac/window_title_mac.h b/Telegram/SourceFiles/platform/mac/window_title_mac.h index 6409888e3..3ccdfe3b7 100644 --- a/Telegram/SourceFiles/platform/mac/window_title_mac.h +++ b/Telegram/SourceFiles/platform/mac/window_title_mac.h @@ -28,16 +28,20 @@ class PlainShadow; namespace Platform { -class TitleWidget : public Window::TitleWidget { +class MainWindow; + +class TitleWidget : public Window::TitleWidget, private base::Subscriber { public: - TitleWidget(QWidget *parent, int height); + TitleWidget(MainWindow *parent, int height); protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; + void mouseDoubleClickEvent(QMouseEvent *e) override; private: ChildWidget _shadow; + QFont _font; }; diff --git a/Telegram/SourceFiles/platform/mac/window_title_mac.mm b/Telegram/SourceFiles/platform/mac/window_title_mac.mm index 4927d0cb1..8d6c84311 100644 --- a/Telegram/SourceFiles/platform/mac/window_title_mac.mm +++ b/Telegram/SourceFiles/platform/mac/window_title_mac.mm @@ -27,26 +27,62 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Platform { -TitleWidget::TitleWidget(QWidget *parent, int height) : Window::TitleWidget(parent) +TitleWidget::TitleWidget(MainWindow *parent, int height) : Window::TitleWidget(parent) , _shadow(this, st::titleShadow) { setAttribute(Qt::WA_OpaquePaintEvent); resize(width(), height); + +#ifndef OS_MAC_OLD + QStringList families = { qsl(".SF NS Text"), qsl("Helvetica Neue") }; + for (auto family : families) { + _font.setFamily(family); + if (QFontInfo(_font).family() == _font.family()) { + break; + } + } +#endif // OS_MAC_OLD + + if (QFontInfo(_font).family() == _font.family()) { + _font.setPixelSize((height * 15) / 24); + } else { + _font = st::normalFont; + } + + subscribe(Global::RefUnreadCounterUpdate(), [this] { update(); }); } void TitleWidget::paintEvent(QPaintEvent *e) { - Painter(this).fillRect(rect(), st::titleBg); + Painter p(this); + + p.fillRect(rect(), st::titleBg); + + p.setPen(isActiveWindow() ? st::titleFgActive : st::titleFg); + p.setFont(_font); + + p.drawText(rect(), static_cast(parentWidget())->titleText(), style::al_center); } void TitleWidget::resizeEvent(QResizeEvent *e) { _shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth); } +void TitleWidget::mouseDoubleClickEvent(QMouseEvent *e) { + auto window = parentWidget(); + if (window->windowState() == Qt::WindowMaximized) { + window->setWindowState(Qt::WindowNoState); + } else { + window->setWindowState(Qt::WindowMaximized); + } +} + Window::TitleWidget *CreateTitleWidget(QWidget *parent) { +#ifndef OS_MAC_OLD if (auto window = qobject_cast(parent)) { if (auto height = window->getCustomTitleHeight()) { - return new TitleWidget(parent, height); + return new TitleWidget(window, height); } } +#endif // !OS_MAC_OLD return nullptr; } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 7124be413..bd128bf19 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -683,7 +683,7 @@ void MainWindow::psSetupTrayIcon() { connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray())); App::wnd()->updateTrayMenu(); } - psUpdateCounter(); + updateIconCounters(); trayIcon->show(); } @@ -723,7 +723,12 @@ void MainWindow::psUpdateWorkmode() { } } -void MainWindow::psUpdateCounter() { +void MainWindow::unreadCounterChangedHook() { + setWindowTitle(titleText()); + updateIconCounters(); +} + +void MainWindow::updateIconCounters() { auto counter = App::histories().unreadBadge(); auto muted = App::histories().unreadOnlyMuted(); @@ -746,7 +751,6 @@ void MainWindow::psUpdateCounter() { trayIcon->setIcon(forTrayIcon); } - setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); psDestroyIcons(); ps_iconSmall = createHIconFromQIcon(iconSmall, iconSizeSmall.width(), iconSizeSmall.height()); ps_iconBig = createHIconFromQIcon(iconBig, iconSizeBig.width(), iconSizeBig.height()); diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 36ae6ef2a..4a00efd6e 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -50,8 +50,6 @@ public: void psRefreshTaskbarIcon(); - void psUpdateCounter(); - bool psHasNativeNotifications(); virtual QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) = 0; @@ -93,6 +91,7 @@ public slots: protected: void initHook() override; int32 screenNameChecksum(const QString &name) const override; + void unreadCounterChangedHook() override; bool psHasTrayIcon() const { return trayIcon; @@ -110,6 +109,8 @@ protected: QTimer psUpdatedPositionTimer; private: + void updateIconCounters(); + void psDestroyIcons(); static UINT _taskbarCreatedMsgId; diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 8a0fb16cb..3dec77fd9 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -30,7 +30,8 @@ namespace Window { MainWindow::MainWindow() : QWidget() , _positionUpdatedTimer(this) -, _body(this) { +, _body(this) +, _titleText(qsl("Telegram")) { subscribe(Theme::Background(), [this](const Theme::BackgroundUpdate &data) { using Type = Theme::BackgroundUpdate::Type; if (data.type == Type::TestingTheme || data.type == Type::RevertingTheme || data.type == Type::ApplyingTheme) { @@ -39,6 +40,7 @@ MainWindow::MainWindow() : QWidget() } } }); + subscribe(Global::RefUnreadCounterUpdate(), [this] { updateUnreadCounter(); }); } void MainWindow::init() { @@ -105,11 +107,16 @@ void MainWindow::positionUpdated() { _positionUpdatedTimer->start(SaveWindowPositionTimeout); } -void MainWindow::setTitleVisibility(bool visible) { +bool MainWindow::titleVisible() const { + return _title && !_title->isHidden(); +} + +void MainWindow::setTitleVisible(bool visible) { if (_title && (_title->isHidden() == visible)) { _title->setVisible(visible); updateControlsGeometry(); } + titleVisibilityChangedHook(); } int32 MainWindow::screenNameChecksum(const QString &name) const { @@ -134,6 +141,15 @@ void MainWindow::updateControlsGeometry() { _body->setGeometry(0, bodyTop, width(), height() - bodyTop); } +void MainWindow::updateUnreadCounter() { + if (!Global::started() || App::quitting()) return; + + auto counter = App::histories().unreadBadge(); + _titleText = (counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram"); + + unreadCounterChangedHook(); +} + void MainWindow::savePosition(Qt::WindowState state) { if (state == Qt::WindowActive) state = windowHandle()->windowState(); if (state == Qt::WindowMinimized || !positionInited()) return; diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 594bf2dd2..1e3f11f9b 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -40,7 +40,11 @@ public: } void positionUpdated(); - void setTitleVisibility(bool visible); + bool titleVisible() const; + void setTitleVisible(bool visible); + QString titleText() const { + return _titleText; + } virtual void closeWithoutDestroy(); @@ -61,6 +65,12 @@ protected: virtual void stateChangedHook(Qt::WindowState state) { } + virtual void titleVisibilityChangedHook() { + } + + virtual void unreadCounterChangedHook() { + } + // This one is overriden in Windows for historical reasons. virtual int32 screenNameChecksum(const QString &name) const; @@ -73,6 +83,7 @@ private slots: private: void updateControlsGeometry(); + void updateUnreadCounter(); void initSize(); ChildObject _positionUpdatedTimer; @@ -81,6 +92,8 @@ private: ChildWidget _title = { nullptr }; ChildWidget _body; + QString _titleText; + }; } // namespace Window From 45ce0ecb1fc4bbe8c9fce86c5542465ab1f740d3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 9 Nov 2016 11:35:41 +0300 Subject: [PATCH 028/100] Dialogs filter input field without search icon. --- Telegram/Resources/basic.style | 2 +- Telegram/SourceFiles/dialogs/dialogs.style | 3 +-- Telegram/SourceFiles/overview/overview.style | 13 +++++++++++++ Telegram/SourceFiles/overviewwidget.cpp | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 7957db994..e06e944ed 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -279,7 +279,7 @@ shadowToggleDuration: 200; slideDuration: 240; slideShift: 100px; slideFadeOutBg: #0000003c; -slideShadow: icon {{ "slide_shadow", #000000 }}; +slideShadow: icon {{ "slide_shadow", windowShadowFg }}; btnYesColor: #0080c0; btnYesHover: #0073ad; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index bc36e866d..a50ba0084 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -97,11 +97,10 @@ dialogsFilter: flatInput(inpDefGray) { bgColor: #f2f2f2; phColor: #949494; phFocusColor: #a4a4a4; - icon: fieldSearchIcon; width: 240px; height: 32px; - textMrg: margins(32px, 3px, 32px, 3px); + textMrg: margins(12px, 3px, 12px, 3px); } dialogsCancelSearch: IconButton(dialogsMenuToggle) { icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg, point(0px, 1px) }}; diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index 3c5ef8638..a9fb2ac68 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -20,6 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "basic.style"; using "history/history.style"; +using "ui/widgets/widgets.style"; using "media/view/mediaview.style"; OverviewFileLayout { @@ -116,3 +117,15 @@ overviewLinksChecked: icon { { "overview_links_check_bg", overviewCheckedBg }, { "overview_links_check", #ffffff, point(4px, 5px) }, }; + +overviewFilter: flatInput(inpDefGray) { + font: font(fsize); + bgColor: #f2f2f2; + phColor: #949494; + phFocusColor: #a4a4a4; + icon: fieldSearchIcon; + + width: 240px; + height: 32px; + textMrg: margins(32px, 3px, 32px, 3px); +} diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 7172ff36b..d5b19764c 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -54,7 +54,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD , _history(App::history(_peer->id)) , _channel(peerToChannel(_peer->id)) , _rowWidth(st::msgMinWidth) -, _search(this, st::dialogsFilter, lang(lng_dlg_filter)) +, _search(this, st::overviewFilter, lang(lng_dlg_filter)) , _cancelSearch(this, st::dialogsCancelSearch) , _itemsToBeLoaded(LinksOverviewPerPage * 2) , _width(st::windowMinWidth) { From 3366e05b77f6e79304c88b1a9b9d087e71f848a8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 9 Nov 2016 16:40:51 +0300 Subject: [PATCH 029/100] Rich dropdown animations. --- Telegram/Resources/colors.palette | 3 + .../Resources/icons/round_shadow_bottom.png | Bin 0 -> 86 bytes .../icons/round_shadow_bottom@2x.png | Bin 0 -> 107 bytes .../icons/round_shadow_bottom_left.png | Bin 0 -> 179 bytes .../icons/round_shadow_bottom_left@2x.png | Bin 0 -> 262 bytes .../Resources/icons/round_shadow_left.png | Bin 0 -> 78 bytes .../Resources/icons/round_shadow_left@2x.png | Bin 0 -> 93 bytes Telegram/Resources/icons/round_shadow_top.png | Bin 0 -> 75 bytes .../Resources/icons/round_shadow_top@2x.png | Bin 0 -> 90 bytes .../Resources/icons/round_shadow_top_left.png | Bin 0 -> 141 bytes .../icons/round_shadow_top_left@2x.png | Bin 0 -> 195 bytes Telegram/Resources/sample.tdesktop-theme | 3 + Telegram/SourceFiles/app.cpp | 1 + Telegram/SourceFiles/core/lambda_wrap.h | 18 +- Telegram/SourceFiles/dialogswidget.cpp | 2 +- Telegram/SourceFiles/historywidget.cpp | 1 + Telegram/SourceFiles/layout.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 1 + .../media/player/media_player.style | 2 +- .../media/player/media_player_panel.cpp | 2 +- .../player/media_player_volume_controller.cpp | 4 +- .../SourceFiles/media/view/mediaview.style | 16 +- Telegram/SourceFiles/mediaview.cpp | 2 +- Telegram/SourceFiles/ui/animation.h | 36 +- .../ui/effects/panel_animation.cpp | 492 ++++++++++++++++++ .../SourceFiles/ui/effects/panel_animation.h | 134 +++++ Telegram/SourceFiles/ui/effects/rect_shadow.h | 2 + Telegram/SourceFiles/ui/flatinput.cpp | 15 +- Telegram/SourceFiles/ui/style/style_core.cpp | 15 +- Telegram/SourceFiles/ui/twidget.cpp | 2 +- .../SourceFiles/ui/widgets/inner_dropdown.cpp | 143 +++-- .../SourceFiles/ui/widgets/inner_dropdown.h | 28 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 242 +++++++-- Telegram/SourceFiles/ui/widgets/popup_menu.h | 30 +- Telegram/SourceFiles/ui/widgets/shadow.cpp | 55 ++ Telegram/SourceFiles/ui/widgets/shadow.h | 50 ++ Telegram/SourceFiles/ui/widgets/widgets.style | 90 +++- .../SourceFiles/window/top_bar_widget.cpp | 2 +- Telegram/gyp/Telegram.gyp | 2 + 39 files changed, 1234 insertions(+), 160 deletions(-) create mode 100644 Telegram/Resources/icons/round_shadow_bottom.png create mode 100644 Telegram/Resources/icons/round_shadow_bottom@2x.png create mode 100644 Telegram/Resources/icons/round_shadow_bottom_left.png create mode 100644 Telegram/Resources/icons/round_shadow_bottom_left@2x.png create mode 100644 Telegram/Resources/icons/round_shadow_left.png create mode 100644 Telegram/Resources/icons/round_shadow_left@2x.png create mode 100644 Telegram/Resources/icons/round_shadow_top.png create mode 100644 Telegram/Resources/icons/round_shadow_top@2x.png create mode 100644 Telegram/Resources/icons/round_shadow_top_left.png create mode 100644 Telegram/Resources/icons/round_shadow_top_left@2x.png create mode 100644 Telegram/SourceFiles/ui/effects/panel_animation.cpp create mode 100644 Telegram/SourceFiles/ui/effects/panel_animation.h diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 668888f2e..c7431da64 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -29,6 +29,7 @@ windowOverBg: #f0f0f0; // light gray: fallback for over background windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over color windowActiveTextFg: #2687bf; // online blue: fallback for active color windowShadowFg: #000000; // black: fallback for shadow color +windowShadowFgFallback: #cdcdcd; // gray: fallback for shadow without imageBg: #000000; imageBgTransparent: #ffffff; @@ -46,6 +47,8 @@ lightButtonBgOver: #edf7ff; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; +menuBg: windowBg; +menuBgOver: windowOverBg; menuIconFg: #a8a8a8; menuIconFgOver: #999999; diff --git a/Telegram/Resources/icons/round_shadow_bottom.png b/Telegram/Resources/icons/round_shadow_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..28ff21173b715c8a366cc9d9993b49649d35a77a GIT binary patch literal 86 zcmeAS@N?(olHy`uVBq!ia0vp^j6lr7!3HG#Z-iO_DOpb!#}JO0$p!`nKhA%7bd;NI knST5}1A`44HvC~{2;InGy!(}P1W+M^r>mdKI;Vst06-=ey#N3J literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/round_shadow_bottom@2x.png b/Telegram/Resources/icons/round_shadow_bottom@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a64ca5c8e364c18a98e98d764e312fda9f8e57e7 GIT binary patch literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-R!3HER>*_fHDI-r8#}JO0s>gQnGAQt{9CTfz z^gVv6^8Tg=YO|8l_#f=DW-wQpawcSz*UP-rv?+I4A26$EiK)$5@5~O=&EVW(NV5)#+g$ zHyr=_Uw-e(H@7TRxSX7%eq9IpARTRHE`y?W`K zVj1?=yNr%i+&E;G%=4{w`S)|%a``h@SuW@ b&KU*Km`n*u6{1-oD!M> q?fy2ugd&x-Dmjx|W-ip7-p_7R!I;zk_0xBtdInEdKbLh*2~7a)F&hp5 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/round_shadow_top.png b/Telegram/Resources/icons/round_shadow_top.png new file mode 100644 index 0000000000000000000000000000000000000000..39edc4a4ebe14f516bca0a1b64bdba296d74fe57 GIT binary patch literal 75 zcmeAS@N?(olHy`uVBq!ia0vp^j6lr9!3HE}Hf~b~QbL|Cjv*W~lM@mXf1X#-&^W=s Yz|PL-=FVdQ&MBb@00%!1LI3~& literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/round_shadow_top@2x.png b/Telegram/Resources/icons/round_shadow_top@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..af421f2a05bde2ce515e850112f9fc42d65ddd73 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-S!3HFkynkm2q!c|}978x}svg_O%b>u(tl+cz o+yCOy37wOlI8AQY_V*^6oXQi%py!?vEzopr0L2>_&Hw-a literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/round_shadow_top_left.png b/Telegram/Resources/icons/round_shadow_top_left.png new file mode 100644 index 0000000000000000000000000000000000000000..555aeb74d8e707a2e0bfd40a0f426cc6b4de958c GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^96-#@!3HEvBYEP0REVdGV+hC0+&)J>WbP0l+XkKQtwIl literal 0 HcmV?d00001 diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index da0b103bf..d8e443105 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -30,6 +30,7 @@ windowOverBg: #f0f0f0; windowSubTextFgOver: #7c99b2; windowActiveTextFg: #2687bf; windowShadowFg: #000000; +windowShadowFgFallback: #cdcdcd; imageBg: #000000; imageBgTransparent: #ffffff; activeButtonBg: windowActiveBg; @@ -42,6 +43,8 @@ lightButtonBg: windowBg; lightButtonBgOver: #edf7ff; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; +menuBg: windowBg; +menuBgOver: windowOverBg; menuIconFg: #a8a8a8; menuIconFgOver: #999999; titleBg: windowOverBg; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index f5cce4723..c62caffd5 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2235,6 +2235,7 @@ namespace { ::cornersMaskSmall[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied)); ::cornersMaskSmall[i]->setDevicePixelRatio(cRetinaFactor()); } + prepareCorners(MenuCorners, st::buttonRadius, st::menuBg); prepareCorners(BotKbOverCorners, st::dateRadius, st::msgBotKbOverBg); prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg); prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceSelectBg); diff --git a/Telegram/SourceFiles/core/lambda_wrap.h b/Telegram/SourceFiles/core/lambda_wrap.h index 4e4abe2ae..a29afbaaa 100644 --- a/Telegram/SourceFiles/core/lambda_wrap.h +++ b/Telegram/SourceFiles/core/lambda_wrap.h @@ -147,8 +147,9 @@ struct lambda_wrap_helper_move_impl : static void construct_move_lambda_method(void *lambda, void *source) { static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment."); auto space = sizeof(JustLambda); - auto aligned = std_::align(alignof(JustLambda), space, lambda, space); - t_assert(aligned == lambda); + // We want to be able to pass lambda by value in 32bit Windows version. + //auto aligned = std_::align(alignof(JustLambda), space, lambda, space); + //t_assert(aligned == lambda); auto source_lambda = static_cast(source); new (lambda) JustLambda(static_cast(*source_lambda)); } @@ -222,8 +223,9 @@ struct lambda_wrap_helper_copy_impl : static void construct_copy_lambda_method(void *lambda, const void *source) { static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment."); auto space = sizeof(JustLambda); - auto aligned = std_::align(alignof(JustLambda), space, lambda, space); - t_assert(aligned == lambda); + // We want to be able to pass lambda by value in 32bit Windows version. + //auto aligned = std_::align(alignof(JustLambda), space, lambda, space); + //t_assert(aligned == lambda); auto source_lambda = static_cast(source); new (lambda) JustLambda(static_cast(*source_lambda)); } @@ -319,11 +321,9 @@ protected: lambda_unique(const BaseHelper *helper, const Private &) : helper_(helper) { } - static_assert(BaseHelper::kStorageSize % sizeof(void*) == 0, "Bad pointer size."); - union { - void *(storage_[BaseHelper::kStorageSize / sizeof(void*)]); - typename BaseHelper::alignment alignment_; - }; + using alignment = typename BaseHelper::alignment; + static_assert(BaseHelper::kStorageSize % sizeof(alignment) == 0, "Bad storage size."); + alignment storage_[BaseHelper::kStorageSize / sizeof(alignment)]; const BaseHelper *helper_; }; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index cf6a0a662..780e1355e 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -2031,7 +2031,7 @@ void DialogsWidget::showMainMenu() { }, &st::dialogsMenuHelp, &st::dialogsMenuHelpOver); } updateMainMenuGeometry(); - _mainMenu->showAnimated(); + _mainMenu->showAnimated(Ui::PanelAnimation::Origin::TopLeft); } void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index bdb6ecee0..7d1b2e133 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3128,6 +3128,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _silent->hide(); _botCommandStart->hide(); + _attachType->setOrigin(Ui::PanelAnimation::Origin::BottomLeft); _attachToggle->installEventFilter(_attachType); _attachEmoji->installEventFilter(_emojiPan); diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index db82d0759..eb8d59911 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -36,6 +36,7 @@ enum RoundCorners { SmallMaskCorners = 0x00, // for images LargeMaskCorners, + MenuCorners, BotKbOverCorners, StickerCorners, StickerSelectedCorners, diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 80c4cc1d5..720c031ed 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -160,6 +160,7 @@ MainWidget::MainWidget(QWidget *parent) : TWidget(parent) MTP::setGlobalFailHandler(rpcFail(&MainWidget::updateFail)); _mediaType->hide(); + _mediaType->setOrigin(Ui::PanelAnimation::Origin::TopRight); _topBar->mediaTypeButton()->installEventFilter(_mediaType); show(); diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 1211d3c34..0adbbfed7 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -34,7 +34,7 @@ MediaPlayerButton { cancelStroke: pixels; } -mediaPlayerActiveFg: #54b5ed; +mediaPlayerActiveFg: windowActiveBg; mediaPlayerInactiveFg: #dfebf2; mediaPlayerButton: MediaPlayerButton { diff --git a/Telegram/SourceFiles/media/player/media_player_panel.cpp b/Telegram/SourceFiles/media/player/media_player_panel.cpp index 2ec37694d..05f3862d1 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.cpp +++ b/Telegram/SourceFiles/media/player/media_player_panel.cpp @@ -172,7 +172,7 @@ void Panel::paintEvent(QPaintEvent *e) { if (_layout != Layout::Full) { shadowedSides |= (rtl() ? Side::Left : Side::Right) | Side::Top; } - _shadow.paint(p, shadowedRect, st::defaultInnerDropdown.shadowShift, shadowedSides); + _shadow.paint(p, shadowedRect, st::defaultDropdownShadowShift, shadowedSides); p.fillRect(shadowedRect, st::windowBg); } diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp index 249da8bfd..237127fa0 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp @@ -81,7 +81,7 @@ void VolumeController::applyVolumeChange(float64 volume) { } VolumeWidget::VolumeWidget(QWidget *parent) : TWidget(parent) -, _shadow(st::defaultInnerDropdown.shadow) +, _shadow(st::defaultDropdownShadow) , _controller(this) { hide(); _controller->setIsVertical(true); @@ -145,7 +145,7 @@ void VolumeWidget::paintEvent(QPaintEvent *e) { auto shadowedRect = rect().marginsRemoved(getMargin()); using ShadowSide = Ui::RectShadow::Side; auto shadowedSides = ShadowSide::Left | ShadowSide::Right | ShadowSide::Bottom; - _shadow.paint(p, shadowedRect, st::defaultInnerDropdown.shadowShift, shadowedSides); + _shadow.paint(p, shadowedRect, st::defaultDropdownShadowShift, shadowedSides); p.fillRect(shadowedRect.x(), 0, shadowedRect.width(), shadowedRect.y() + shadowedRect.height(), st::windowBg); } diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 4390bd489..0d0b3ea6a 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -137,10 +137,24 @@ mediaviewMenu: Menu(defaultMenu) { separatorFg: mediaviewMenuFg; } +mediaviewMenuShadow: Shadow(defaultEmptyShadow) { + fallback: mediaviewMenuBg; +} +mediaviewPanelAnimation: PanelAnimation(defaultPanelAnimation) { + fadeBg: mediaviewMenuBg; + shadow: mediaviewMenuShadow; +} mediaviewPopupMenu: PopupMenu(defaultPopupMenu) { - shadow: icon {}; + shadow: mediaviewMenuShadow; menu: mediaviewMenu; + animation: mediaviewPanelAnimation; } mediaviewDropdownMenu: DropdownMenu(defaultDropdownMenu) { menu: mediaviewMenu; + wrap: InnerDropdown(defaultInnerDropdown) { + bg: mediaviewMenuBg; + animation: mediaviewPanelAnimation; + scrollPadding: margins(0px, 8px, 0px, 8px); + shadow: mediaviewMenuShadow; + } } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 8b19be1c5..c28f02360 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -2542,7 +2542,7 @@ void MediaView::onDropdown() { _dropdown->addAction(action.text, this, action.member); } _dropdown->moveToRight(0, height() - _dropdown->height()); - _dropdown->showAnimated(); + _dropdown->showAnimated(Ui::PanelAnimation::Origin::BottomRight); _dropdown->setFocus(); } diff --git a/Telegram/SourceFiles/ui/animation.h b/Telegram/SourceFiles/ui/animation.h index f58e4b3b1..8b5ff5460 100644 --- a/Telegram/SourceFiles/ui/animation.h +++ b/Telegram/SourceFiles/ui/animation.h @@ -213,6 +213,25 @@ void startManager(); void stopManager(); void registerClipManager(Media::Clip::Manager *manager); +inline uint64 shifted(uint32 components) { + auto wide = static_cast(components); + return (wide & 0x00000000000000FFULL) + | ((wide & 0x000000000000FF00ULL) << 8) + | ((wide & 0x0000000000FF0000ULL) << 16) + | ((wide & 0x00000000FF000000ULL) << 24); +} + +inline uint32 unshifted(uint64 components) { + return static_cast((components & 0x000000000000FF00ULL) >> 8) + | static_cast((components & 0x00000000FF000000ULL) >> 16) + | static_cast((components & 0x0000FF0000000000ULL) >> 24) + | static_cast((components & 0xFF00000000000000ULL) >> 32); +} + +inline uint64 reshifted(uint64 components) { + return (components >> 8) & 0x00FF00FF00FF00FFULL; +} + inline int interpolate(int a, int b, float64 b_ratio) { return qRound(a + float64(b - a) * b_ratio); } @@ -220,11 +239,20 @@ inline int interpolate(int a, int b, float64 b_ratio) { inline QColor color(QColor a, QColor b, float64 b_ratio) { auto bOpacity = snap(interpolate(0, 255, b_ratio), 0, 255) + 1; auto aOpacity = (256 - bOpacity); + auto bBits = static_cast(b.alpha() & 0xFF) + | (static_cast(b.red() & 0xFF) << 16) + | (static_cast(b.green() & 0xFF) << 32) + | (static_cast(b.blue() & 0xFF) << 48); + auto aBits = static_cast(a.alpha() & 0xFF) + | (static_cast(a.red() & 0xFF) << 16) + | (static_cast(a.green() & 0xFF) << 32) + | (static_cast(a.blue() & 0xFF) << 48); + auto resultBits = (aBits * aOpacity + bBits * bOpacity) >> 8; return { - (a.red() * aOpacity + b.red() * bOpacity) >> 8, - (a.green() * aOpacity + b.green() * bOpacity) >> 8, - (a.blue() * aOpacity + b.blue() * bOpacity) >> 8, - (a.alpha() * aOpacity + b.alpha() * bOpacity) >> 8 + static_cast((resultBits >> 16) & 0xFF), + static_cast((resultBits >> 32) & 0xFF), + static_cast((resultBits >> 48) & 0xFF), + static_cast(resultBits & 0xFF), }; } diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.cpp b/Telegram/SourceFiles/ui/effects/panel_animation.cpp new file mode 100644 index 000000000..ef783f5ff --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/panel_animation.cpp @@ -0,0 +1,492 @@ +/* +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 "ui/effects/panel_animation.h" + +namespace Ui { + +void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) { + t_assert(!started()); + _finalImage = std_::move(finalImage).convertToFormat(QImage::Format_ARGB32_Premultiplied); + + t_assert(!_finalImage.isNull()); + _finalWidth = _finalImage.width(); + _finalHeight = _finalImage.height(); + + setStartWidth(); + setStartHeight(); + setStartAlpha(); + setStartFadeTop(); + createFadeMask(); + setWidthDuration(); + setHeightDuration(); + setAlphaDuration(); + setShadow(); + + auto checkCorner = [this, inner](Corner &corner) { + if (!corner.valid()) return; + if ((_startWidth >= 0 && _startWidth < _finalWidth) + || (_startHeight >= 0 && _startHeight < _finalHeight)) { + t_assert(corner.width <= inner.width()); + t_assert(corner.height <= inner.height()); + } + }; + checkCorner(_topLeft); + checkCorner(_topRight); + checkCorner(_bottomLeft); + checkCorner(_bottomRight); + _finalInts = reinterpret_cast(_finalImage.constBits()); + _finalIntsPerLine = (_finalImage.bytesPerLine() >> 2); + _finalIntsPerLineAdded = _finalIntsPerLine - _finalWidth; + t_assert(_finalImage.depth() == static_cast(sizeof(uint32) << 3)); + t_assert(_finalImage.bytesPerLine() == (_finalIntsPerLine << 2)); + t_assert(_finalIntsPerLineAdded >= 0); + + _finalInnerLeft = inner.x(); + _finalInnerTop = inner.y(); + _finalInnerWidth = inner.width(); + _finalInnerHeight = inner.height(); + _finalInnerRight = _finalInnerLeft + _finalInnerWidth; + _finalInnerBottom = _finalInnerTop + _finalInnerHeight; + t_assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner)); +} + +void PanelAnimation::setShadow() { + if (_skipShadow) return; + + _shadow.extend = _st.shadow.extend; + _shadow.left = cloneImage(_st.shadow.left); + if (_shadow.valid()) { + _shadow.topLeft = cloneImage(_st.shadow.topLeft); + _shadow.top = cloneImage(_st.shadow.top); + _shadow.topRight = cloneImage(_st.shadow.topRight); + _shadow.right = cloneImage(_st.shadow.right); + _shadow.bottomRight = cloneImage(_st.shadow.bottomRight); + _shadow.bottom = cloneImage(_st.shadow.bottom); + _shadow.bottomLeft = cloneImage(_st.shadow.bottomLeft); + t_assert(!_shadow.topLeft.isNull() + && !_shadow.top.isNull() + && !_shadow.topRight.isNull() + && !_shadow.right.isNull() + && !_shadow.bottomRight.isNull() + && !_shadow.bottom.isNull() + && !_shadow.bottomLeft.isNull()); + } else { + _shadow.topLeft = + _shadow.top = + _shadow.topRight = + _shadow.right = + _shadow.bottomRight = + _shadow.bottom = + _shadow.bottomLeft = QImage(); + } +} + +void PanelAnimation::setStartWidth() { + _startWidth = qRound(_st.startWidth * _finalImage.width()); + if (_startWidth >= 0) t_assert(_startWidth <= _finalWidth); +} + +void PanelAnimation::setStartHeight() { + _startHeight = qRound(_st.startHeight * _finalImage.height()); + if (_startHeight >= 0) t_assert(_startHeight <= _finalHeight); +} + +void PanelAnimation::setStartAlpha() { + _startAlpha = qRound(_st.startOpacity * 255); + t_assert(_startAlpha >= 0 && _startAlpha < 256); +} + +void PanelAnimation::setStartFadeTop() { + _startFadeTop = qRound(_st.startFadeTop * _finalImage.height()); +} + +void PanelAnimation::createFadeMask() { + auto resultHeight = qRound(_finalImage.height() * _st.fadeHeight); + auto finalAlpha = qRound(_st.fadeOpacity * 255); + t_assert(finalAlpha >= 0 && finalAlpha < 256); + auto result = QImage(1, resultHeight, QImage::Format_ARGB32_Premultiplied); + auto ints = reinterpret_cast(result.bits()); + auto intsPerLine = (result.bytesPerLine() >> 2); + auto up = (_origin == PanelAnimation::Origin::BottomLeft || _origin == PanelAnimation::Origin::BottomRight); + auto from = up ? resultHeight : 0, to = resultHeight - from, delta = up ? -1 : 1; + for (auto y = from; y != to; y += delta) { + auto alpha = static_cast(finalAlpha * y) / resultHeight; + *ints = (0xFFU << 24) | (alpha << 16) | (alpha << 8) | alpha; + ints += intsPerLine; + } + _fadeMask = style::colorizeImage(result, _st.fadeBg); + _fadeHeight = _fadeMask.height(); + _fadeInts = reinterpret_cast(_fadeMask.constBits()); + _fadeIntsPerLine = (_fadeMask.bytesPerLine() >> 2); + t_assert(_fadeMask.bytesPerLine() == (_fadeIntsPerLine << 2)); +} + +void PanelAnimation::setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight) { + setCornerMask(_topLeft, std_::move(topLeft)); + setCornerMask(_topRight, std_::move(topRight)); + setCornerMask(_bottomLeft, std_::move(bottomLeft)); + setCornerMask(_bottomRight, std_::move(bottomRight)); +} + +void PanelAnimation::setSkipShadow(bool skipShadow) { + t_assert(!started()); + _skipShadow = skipShadow; +} + +void PanelAnimation::setCornerMask(Corner &corner, QImage &&image) { + t_assert(!started()); + corner.image = std_::move(image); + if (corner.valid()) { + corner.width = corner.image.width(); + corner.height = corner.image.height(); + corner.bytes = corner.image.constBits(); + corner.bytesPerPixel = (corner.image.depth() >> 3); + corner.bytesPerLineAdded = corner.image.bytesPerLine() - corner.width * corner.bytesPerPixel; + t_assert(corner.image.depth() == (corner.bytesPerPixel << 3)); + t_assert(corner.bytesPerLineAdded >= 0); + if (_startWidth >= 0) t_assert(corner.width <= _startWidth); + if (_startHeight >= 0) t_assert(corner.height <= _startHeight); + if (!_finalImage.isNull()) { + t_assert(corner.width <= _finalInnerWidth); + t_assert(corner.height <= _finalInnerHeight); + } + } else { + corner.width = corner.height = 0; + } +} + +QImage PanelAnimation::cloneImage(const style::icon &source) { + if (source.empty()) return QImage(); + + auto result = QImage(source.width(), source.height(), QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::transparent); + { + Painter p(&result); + source.paint(p, 0, 0, source.width()); + } + return std_::move(result); +} + +void PanelAnimation::setWidthDuration() { + _widthDuration = _st.widthDuration; + t_assert(_widthDuration >= 0.); + t_assert(_widthDuration <= 1.); +} + +void PanelAnimation::setHeightDuration() { + t_assert(!started()); + _heightDuration = _st.heightDuration; + t_assert(_heightDuration >= 0.); + t_assert(_heightDuration <= 1.); +} + +void PanelAnimation::setAlphaDuration() { + t_assert(!started()); + _alphaDuration = _st.opacityDuration; + t_assert(_alphaDuration >= 0.); + t_assert(_alphaDuration <= 1.); +} + +void PanelAnimation::start() { + t_assert(!started()); + t_assert(!_finalImage.isNull()); + _frame = QImage(_finalWidth, _finalHeight, QImage::Format_ARGB32_Premultiplied); + _frame.setDevicePixelRatio(_finalImage.devicePixelRatio()); + _frameIntsPerLine = (_frame.bytesPerLine() >> 2); + _frameInts = reinterpret_cast(_frame.bits()); + _frameIntsPerLineAdded = _frameIntsPerLine - _finalWidth; + t_assert(_frame.depth() == static_cast(sizeof(uint32) << 3)); + t_assert(_frame.bytesPerLine() == (_frameIntsPerLine << 2)); + t_assert(_frameIntsPerLineAdded >= 0); +} + +const QImage &PanelAnimation::getFrame(float64 dt, float64 opacity) { + t_assert(started()); + t_assert(dt >= 0.); + + auto &transition = anim::easeOutCirc; + constexpr auto finalAlpha = 256; + auto alpha = (dt >= _alphaDuration) ? finalAlpha : anim::interpolate(_startAlpha + 1, finalAlpha, transition(1., dt / _alphaDuration)); + _frameAlpha = anim::interpolate(0, alpha, opacity); + + auto frameWidth = (_startWidth < 0 || dt >= _widthDuration) ? _finalInnerWidth : anim::interpolate(_startWidth, _finalInnerWidth, transition(1., dt / _widthDuration)); + auto frameHeight = (_startHeight < 0 || dt >= _heightDuration) ? _finalInnerHeight : anim::interpolate(_startHeight, _finalInnerHeight, transition(1., dt / _heightDuration)); + auto frameLeft = (_origin == Origin::TopLeft || _origin == Origin::BottomLeft) ? _finalInnerLeft : (_finalInnerRight - frameWidth); + auto frameTop = (_origin == Origin::TopLeft || _origin == Origin::TopRight) ? _finalInnerTop : (_finalInnerBottom - frameHeight); + auto frameRight = frameLeft + frameWidth; + auto frameBottom = frameTop + frameHeight; + + auto fadeTop = (_fadeHeight > 0) ? snap(anim::interpolate(_startFadeTop, _finalInnerHeight, transition(1., dt)), 0, frameHeight) : frameHeight; + auto fadeBottom = (fadeTop < frameHeight) ? qMin(fadeTop + _fadeHeight, frameHeight) : frameHeight; + auto fadeSkipLines = 0; + if (_origin == Origin::BottomLeft || _origin == Origin::BottomRight) { + fadeTop = frameHeight - fadeTop; + fadeBottom = frameHeight - fadeBottom; + qSwap(fadeTop, fadeBottom); + fadeSkipLines = fadeTop + _fadeHeight - fadeBottom; + } + fadeTop += frameTop; + fadeBottom += frameTop; + + auto finalInts = _finalInts + frameLeft + frameTop * _finalIntsPerLine; + auto frameInts = _frameInts + frameLeft + frameTop * _frameIntsPerLine; + auto finalIntsPerLineAdd = (_finalWidth - frameWidth) + _finalIntsPerLineAdded; + auto frameIntsPerLineAdd = (_finalWidth - frameWidth) + _frameIntsPerLineAdded; + + // Draw frameWidth x fadeTop with fade first color. + auto fadeInts = _fadeInts + fadeSkipLines * _fadeIntsPerLine; + auto fadeWithMasterAlpha = [this](uint32 fade) { + auto fadeAlphaAddition = (256 - (fade >> 24)); + auto fadePattern = anim::shifted(fade); + return [this, fadeAlphaAddition, fadePattern](uint32 source) { + auto sourceAlpha = (source >> 24) + 1; + auto sourcePattern = anim::shifted(source); + auto mixedPattern = anim::reshifted(fadePattern * sourceAlpha + sourcePattern * fadeAlphaAddition); + return anim::unshifted(mixedPattern * _frameAlpha); + }; + }; + if (frameTop != fadeTop) { + // Take the fade components from the first line of the fade mask. + auto withMasterAlpha = fadeWithMasterAlpha(_fadeInts ? *_fadeInts : 0); + for (auto y = frameTop; y != fadeTop; ++y) { + for (auto x = frameLeft; x != frameRight; ++x) { + *frameInts++ = withMasterAlpha(*finalInts++); + } + finalInts += finalIntsPerLineAdd; + frameInts += frameIntsPerLineAdd; + } + } + + // Draw frameWidth x (fadeBottom - fadeTop) with fade gradient. + for (auto y = fadeTop; y != fadeBottom; ++y) { + auto withMasterAlpha = fadeWithMasterAlpha(*fadeInts); + for (auto x = frameLeft; x != frameRight; ++x) { + *frameInts++ = withMasterAlpha(*finalInts++); + } + finalInts += finalIntsPerLineAdd; + frameInts += frameIntsPerLineAdd; + fadeInts += _fadeIntsPerLine; + } + + // Draw frameWidth x (frameBottom - fadeBottom) with fade final color. + if (fadeBottom != frameBottom) { + // Take the fade components from the last line of the fade mask. + auto withMasterAlpha = fadeWithMasterAlpha(*(fadeInts - _fadeIntsPerLine)); + for (auto y = fadeBottom; y != frameBottom; ++y) { + for (auto x = frameLeft; x != frameRight; ++x) { + *frameInts++ = withMasterAlpha(*finalInts++); + } + finalInts += finalIntsPerLineAdd; + frameInts += frameIntsPerLineAdd; + } + } + + // Draw corners + auto innerLeft = qMax(_finalInnerLeft, frameLeft); + auto innerTop = qMax(_finalInnerTop, frameTop); + auto innerRight = qMin(_finalInnerRight, frameRight); + auto innerBottom = qMin(_finalInnerBottom, frameBottom); + if (innerLeft != _finalInnerLeft || innerTop != _finalInnerTop) { + paintCorner(_topLeft, innerLeft, innerTop); + } + if (innerRight != _finalInnerRight || innerTop != _finalInnerTop) { + paintCorner(_topRight, innerRight - _topRight.width, innerTop); + } + if (innerLeft != _finalInnerLeft || innerBottom != _finalInnerBottom) { + paintCorner(_bottomLeft, innerLeft, innerBottom - _bottomLeft.height); + } + if (innerRight != _finalInnerRight || innerBottom != _finalInnerBottom) { + paintCorner(_bottomRight, innerRight - _bottomRight.width, innerBottom - _bottomRight.height); + } + + // Fill the rest with transparent + if (frameTop) { + memset(_frameInts, 0, _frameIntsPerLine * frameTop * sizeof(uint32)); + } + auto widthLeft = (_finalWidth - frameRight); + if (frameLeft || widthLeft) { + auto frameInts = _frameInts + frameTop * _frameIntsPerLine; + for (auto y = frameTop; y != frameBottom; ++y) { + memset(frameInts, 0, frameLeft * sizeof(uint32)); + memset(frameInts + frameLeft + frameWidth, 0, widthLeft * sizeof(uint32)); + frameInts += _frameIntsPerLine; + } + } + if (auto heightLeft = (_finalHeight - frameBottom)) { + memset(_frameInts + frameBottom * _frameIntsPerLine, 0, _frameIntsPerLine * heightLeft * sizeof(uint32)); + } + + // Draw shadow + if (_shadow.valid()) { + paintShadow(innerLeft, innerTop, innerRight, innerBottom); + } + + // Debug + //frameInts = _frameInts; + //auto pattern = anim::shifted((static_cast(0xFF) << 24) | (static_cast(0xFF) << 16) | (static_cast(0xFF) << 8) | static_cast(0xFF)); + //for (auto y = 0; y != _finalHeight; ++y) { + // for (auto x = 0; x != _finalWidth; ++x) { + // auto source = *frameInts; + // auto sourceAlpha = (source >> 24); + // *frameInts = anim::unshifted(anim::shifted(source) * 256 + pattern * (256 - sourceAlpha)); + // ++frameInts; + // } + // frameInts += _frameIntsPerLineAdded; + //} + + return _frame; +} + +void PanelAnimation::paintCorner(Corner &corner, int left, int top) { + auto mask = corner.bytes; + auto bytesPerPixel = corner.bytesPerPixel; + auto bytesPerLineAdded = corner.bytesPerLineAdded; + auto frameInts = _frameInts + top * _frameIntsPerLine + left; + auto frameIntsPerLineAdd = _frameIntsPerLine - corner.width; + for (auto y = 0; y != corner.height; ++y) { + for (auto x = 0; x != corner.width; ++x) { + auto alpha = static_cast(*mask) + 1; + *frameInts = anim::unshifted(anim::shifted(*frameInts) * alpha); + ++frameInts; + mask += bytesPerPixel; + } + frameInts += frameIntsPerLineAdd; + mask += bytesPerLineAdded; + } +} + +void PanelAnimation::paintShadow(int left, int top, int right, int bottom) { + left -= _shadow.extend.left(); + top -= _shadow.extend.top(); + right += _shadow.extend.right(); + bottom += _shadow.extend.bottom(); + paintShadowCorner(left, top, _shadow.topLeft); + paintShadowCorner(right - _shadow.topRight.width(), top, _shadow.topRight); + paintShadowCorner(right - _shadow.bottomRight.width(), bottom - _shadow.bottomRight.height(), _shadow.bottomRight); + paintShadowCorner(left, bottom - _shadow.bottomLeft.height(), _shadow.bottomLeft); + paintShadowVertical(left, top + _shadow.topLeft.height(), bottom - _shadow.bottomLeft.height(), _shadow.left); + paintShadowVertical(right - _shadow.right.width(), top + _shadow.topRight.height(), bottom - _shadow.bottomRight.height(), _shadow.right); + paintShadowHorizontal(left + _shadow.topLeft.width(), right - _shadow.topRight.width(), top, _shadow.top); + paintShadowHorizontal(left + _shadow.bottomLeft.width(), right - _shadow.bottomRight.width(), bottom - _shadow.bottom.height(), _shadow.bottom); +} + +void PanelAnimation::paintShadowCorner(int left, int top, const QImage &image) { + auto imageWidth = image.width(); + auto imageHeight = image.height(); + auto imageInts = reinterpret_cast(image.constBits()); + auto imageIntsPerLine = (image.bytesPerLine() >> 2); + auto imageIntsPerLineAdded = imageIntsPerLine - imageWidth; + if (left < 0) { + auto shift = -base::take(left); + imageWidth -= shift; + imageInts += shift; + } + if (top < 0) { + auto shift = -base::take(top); + imageHeight -= shift; + imageInts += shift * imageIntsPerLine; + } + if (left + imageWidth > _finalWidth) { + imageWidth = _finalWidth - left; + } + if (top + imageHeight > _finalHeight) { + imageHeight = _finalHeight - top; + } + if (imageWidth < 0 || imageHeight < 0) return; + + auto frameInts = _frameInts + top * _frameIntsPerLine + left; + auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth; + for (auto y = 0; y != imageHeight; ++y) { + for (auto x = 0; x != imageWidth; ++x) { + auto source = *frameInts; + auto shadowAlpha = _frameAlpha - (source >> 24); + *frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha); + ++frameInts; + ++imageInts; + } + frameInts += frameIntsPerLineAdd; + imageInts += imageIntsPerLineAdded; + } +} + +void PanelAnimation::paintShadowVertical(int left, int top, int bottom, const QImage &image) { + auto imageWidth = image.width(); + auto imageInts = reinterpret_cast(image.constBits()); + if (left < 0) { + auto shift = -base::take(left); + imageWidth -= shift; + imageInts += shift; + } + if (top < 0) top = 0; + if (bottom > _finalHeight) bottom = _finalHeight; + if (left + imageWidth > _finalWidth) { + imageWidth = _finalWidth - left; + } + if (imageWidth < 0 || bottom <= top) return; + + auto frameInts = _frameInts + top * _frameIntsPerLine + left; + auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth; + for (auto y = top; y != bottom; ++y) { + for (auto x = 0; x != imageWidth; ++x) { + auto source = *frameInts; + auto shadowAlpha = _frameAlpha - (source >> 24); + *frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha); + ++frameInts; + ++imageInts; + } + frameInts += frameIntsPerLineAdd; + imageInts -= imageWidth; + } +} + +void PanelAnimation::paintShadowHorizontal(int left, int right, int top, const QImage &image) { + auto imageHeight = image.height(); + auto imageInts = reinterpret_cast(image.constBits()); + auto imageIntsPerLine = (image.bytesPerLine() >> 2); + if (top < 0) { + auto shift = -base::take(top); + imageHeight -= shift; + imageInts += shift * imageIntsPerLine; + } + if (left < 0) left = 0; + if (right > _finalWidth) right = _finalWidth; + if (top + imageHeight > _finalHeight) { + imageHeight = _finalHeight - top; + } + if (imageHeight < 0 || right <= left) return; + + auto frameInts = _frameInts + top * _frameIntsPerLine + left; + auto frameIntsPerLineAdd = _frameIntsPerLine - (right - left); + for (auto y = 0; y != imageHeight; ++y) { + auto imagePattern = anim::shifted(*imageInts); + for (auto x = left; x != right; ++x) { + auto source = *frameInts; + auto shadowAlpha = _frameAlpha - (source >> 24); + *frameInts = anim::unshifted(anim::shifted(source) * 256 + imagePattern * shadowAlpha); + ++frameInts; + } + frameInts += frameIntsPerLineAdd; + imageInts += imageIntsPerLine; + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.h b/Telegram/SourceFiles/ui/effects/panel_animation.h new file mode 100644 index 000000000..988315483 --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/panel_animation.h @@ -0,0 +1,134 @@ +/* +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 "styles/style_widgets.h" + +namespace Ui { + +class PanelAnimation { +public: + enum class Origin { + TopLeft, + TopRight, + BottomLeft, + BottomRight, + }; + PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) { + } + + void setFinalImage(QImage &&finalImage, QRect inner); + void setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight); + void setSkipShadow(bool skipShadow); + + void start(); + const QImage &getFrame(float64 dt, float64 opacity); + +private: + void setStartWidth(); + void setStartHeight(); + void setStartAlpha(); + void setStartFadeTop(); + void createFadeMask(); + void setWidthDuration(); + void setHeightDuration(); + void setAlphaDuration(); + void setShadow(); + + bool started() const { + return !_frame.isNull(); + } + + struct Corner { + QImage image; + int width = 0; + int height = 0; + const uchar *bytes = nullptr; + int bytesPerPixel = 0; + int bytesPerLineAdded = 0; + + bool valid() const { + return !image.isNull(); + } + }; + void setCornerMask(Corner &corner, QImage &&image); + void paintCorner(Corner &corner, int left, int top); + + struct Shadow { + style::margins extend; + QImage left, topLeft, top, topRight, right, bottomRight, bottom, bottomLeft; + + bool valid() const { + return !left.isNull(); + } + }; + QImage cloneImage(const style::icon &source); + void paintShadow(int left, int top, int right, int bottom); + void paintShadowCorner(int left, int top, const QImage &image); + void paintShadowVertical(int left, int top, int bottom, const QImage &image); + void paintShadowHorizontal(int left, int right, int top, const QImage &image); + + const style::PanelAnimation &_st; + Origin _origin = Origin::TopLeft; + + QImage _finalImage; + const uint32 *_finalInts = nullptr; + int _finalIntsPerLine = 0; + int _finalIntsPerLineAdded = 0; + int _finalWidth = 0; + int _finalHeight = 0; + int _finalInnerLeft = 0; + int _finalInnerTop = 0; + int _finalInnerRight = 0; + int _finalInnerBottom = 0; + int _finalInnerWidth = 0; + int _finalInnerHeight = 0; + + Shadow _shadow; + bool _skipShadow = false; + int _startWidth = -1; + int _startHeight = -1; + int _startAlpha = 0; + + int _startFadeTop = 0; + QImage _fadeMask; + int _fadeHeight = 0; + const uint32 *_fadeInts = nullptr; + int _fadeIntsPerLine = 0; + + Corner _topLeft; + Corner _topRight; + Corner _bottomLeft; + Corner _bottomRight; + + float64 _widthDuration = 1.; + float64 _heightDuration = 1.; + float64 _alphaDuration = 1.; + + QImage _frame; + uint32 *_frameInts = nullptr; + int _frameAlpha = 0; // recounted each getFrame() + int _frameIntsPerLine = 0; + int _frameIntsPerLineAdded = 0; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/rect_shadow.h b/Telegram/SourceFiles/ui/effects/rect_shadow.h index 6822046b9..af1e28b13 100644 --- a/Telegram/SourceFiles/ui/effects/rect_shadow.h +++ b/Telegram/SourceFiles/ui/effects/rect_shadow.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include "styles/style_widgets.h" + namespace Ui { class RectShadow { diff --git a/Telegram/SourceFiles/ui/flatinput.cpp b/Telegram/SourceFiles/ui/flatinput.cpp index 10b872d7b..ec04a0da6 100644 --- a/Telegram/SourceFiles/ui/flatinput.cpp +++ b/Telegram/SourceFiles/ui/flatinput.cpp @@ -240,11 +240,16 @@ void FlatInput::updatePlaceholderText() { void FlatInput::contextMenuEvent(QContextMenuEvent *e) { if (auto menu = createStandardContextMenu()) { - menu->addSeparator(); - auto action = menu->addAction(QString("test")); - action->setMenu(new QMenu(this)); - action->menu()->addAction(QString("test123")); - action->menu()->addAction(QString("test456")); + //menu->addSeparator(); + //auto action = menu->addAction(QString("test")); + //action->setMenu(new QMenu(this)); + //action->menu()->addAction(QString("test123")); + //action->menu()->addAction(QString("test456")); + //action->menu()->addAction(QString("test678")); + //auto second = action->menu()->addAction(QString("test90")); + //second->setMenu(new QMenu(this)); + //second->menu()->addAction(QString("testing111")); + //second->menu()->addAction(QString("testing222")); (new Ui::PopupMenu(menu))->popup(e->globalPos()); } } diff --git a/Telegram/SourceFiles/ui/style/style_core.cpp b/Telegram/SourceFiles/ui/style/style_core.cpp index 2e45ed8bd..98ff07b11 100644 --- a/Telegram/SourceFiles/ui/style/style_core.cpp +++ b/Telegram/SourceFiles/ui/style/style_core.cpp @@ -90,10 +90,10 @@ void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect auto green = (c.green() * initialAlpha) >> 8; auto blue = (c.blue() * initialAlpha) >> 8; auto alpha = (255 * initialAlpha) >> 8; - auto pattern = static_cast(alpha) - | (static_cast(red) << 16) - | (static_cast(green) << 32) - | (static_cast(blue) << 48); + auto pattern = static_cast(blue) + | (static_cast(green) << 16) + | (static_cast(red) << 32) + | (static_cast(alpha) << 48); auto resultBytesPerPixel = (src.depth() >> 3); auto resultIntsPerPixel = 1; @@ -113,12 +113,7 @@ void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect for (int y = 0; y != height; ++y) { for (int x = 0; x != width; ++x) { auto maskOpacity = static_cast(*maskBytes) + 1; - auto masked = (pattern * maskOpacity) >> 8; - auto alpha = static_cast(masked & 0xFF); - auto red = static_cast((masked >> 16) & 0xFF); - auto green = static_cast((masked >> 32) & 0xFF); - auto blue = static_cast((masked >> 48) & 0xFF); - *resultInts = blue | (green << 8) | (red << 16) | (alpha << 24); + *resultInts = anim::unshifted(pattern * maskOpacity); maskBytes += maskBytesPerPixel; resultInts += resultIntsPerPixel; } diff --git a/Telegram/SourceFiles/ui/twidget.cpp b/Telegram/SourceFiles/ui/twidget.cpp index 90c13f3dd..4eec33cda 100644 --- a/Telegram/SourceFiles/ui/twidget.cpp +++ b/Telegram/SourceFiles/ui/twidget.cpp @@ -72,7 +72,7 @@ QPixmap myGrab(TWidget *target, QRect rect) { result.fill(Qt::transparent); target->grabStart(); - target->render(&result, QPoint(), QRegion(rect), QWidget::DrawChildren | QWidget::IgnoreMask); + target->render(&result, QPoint(0, 0), rect, QWidget::DrawChildren | QWidget::IgnoreMask); target->grabFinish(); return result; diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index 0805d1a17..11b614bc3 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -23,13 +23,21 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "ui/scrollarea.h" +#include "ui/widgets/shadow.h" #include "profile/profile_members_widget.h" +#include "ui/effects/panel_animation.h" + +namespace { + +constexpr float64 kFadeHeight = 1. / 3; +constexpr int kFadeAlphaMax = 160; + +} // namespace namespace Ui { InnerDropdown::InnerDropdown(QWidget *parent, const style::InnerDropdown &st) : TWidget(parent) , _st(st) -, _shadow(_st.shadow) , _scroll(this, _st.scroll) { _hideTimer.setSingleShot(true); connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideAnimated())); @@ -99,35 +107,39 @@ void InnerDropdown::onScroll() { void InnerDropdown::paintEvent(QPaintEvent *e) { Painter p(this); - if (!_cache.isNull()) { - bool animating = _a_appearance.animating(getms()); - if (animating) { - p.setOpacity(_a_appearance.current(_hiding ? 0. : 1.)); - } else if (_hiding || isHidden()) { - hideFinished(); - return; + auto ms = getms(); + if (_a_show.animating(ms)) { + if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) { + p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity)); } + return; + } else if (_a_opacity.animating(ms)) { + p.setOpacity(_a_opacity.current(0.)); p.drawPixmap(0, 0, _cache); - if (!animating) { - showChildren(); - _cache = QPixmap(); - } + return; + } else if (_hiding || isHidden()) { + hideFinished(); + return; + } else if (_showAnimation) { + p.drawImage(0, 0, _showAnimation->getFrame(1., 1.)); + _showAnimation.reset(); + showChildren(); return; } - // draw shadow - auto shadowedRect = rect().marginsRemoved(_st.padding); - _shadow.paint(p, shadowedRect, _st.shadowShift); - p.fillRect(shadowedRect, st::windowBg); + auto inner = rect().marginsRemoved(_st.padding); + Shadow::paint(p, inner, width(), _st.shadow); + App::roundRect(p, inner, _st.bg, ImageRoundRadius::Small); } void InnerDropdown::enterEvent(QEvent *e) { - showAnimated(); + showAnimated(_origin); return TWidget::enterEvent(e); } void InnerDropdown::leaveEvent(QEvent *e) { - if (_a_appearance.animating(getms())) { + auto ms = getms(); + if (_a_show.animating(ms) || _a_opacity.animating(ms)) { hideAnimated(); } else { _hideTimer.start(300); @@ -136,18 +148,24 @@ void InnerDropdown::leaveEvent(QEvent *e) { } void InnerDropdown::otherEnter() { - showAnimated(); + showAnimated(_origin); } void InnerDropdown::otherLeave() { - if (_a_appearance.animating(getms())) { + auto ms = getms(); + if (_a_show.animating(ms) || _a_opacity.animating(ms)) { hideAnimated(); } else { _hideTimer.start(0); } } -void InnerDropdown::showAnimated() { +void InnerDropdown::setOrigin(PanelAnimation::Origin origin) { + _origin = origin; +} + +void InnerDropdown::showAnimated(PanelAnimation::Origin origin) { + setOrigin(origin); _hideTimer.stop(); showStarted(); } @@ -160,8 +178,7 @@ void InnerDropdown::hideAnimated(HideOption option) { if (_hiding) return; _hideTimer.stop(); - _hiding = true; - startAnimation(); + startOpacityAnimation(true); } void InnerDropdown::hideFast() { @@ -169,22 +186,12 @@ void InnerDropdown::hideFast() { _hideTimer.stop(); _hiding = false; - _a_appearance.finish(); + _a_opacity.finish(); hideFinished(); } -void InnerDropdown::startAnimation() { - auto from = _hiding ? 1. : 0.; - auto to = _hiding ? 0. : 1.; - if (!_a_appearance.animating()) { - showChildren(); - _cache = myGrab(this); - } - hideChildren(); - _a_appearance.start([this] { repaintCallback(); }, from, to, _st.duration); -} - void InnerDropdown::hideFinished() { + _a_show.finish(); _cache = QPixmap(); _ignoreShowEvents = false; if (!isHidden()) { @@ -195,25 +202,83 @@ void InnerDropdown::hideFinished() { } } +void InnerDropdown::prepareCache() { + if (_a_opacity.animating()) return; + + auto showAnimation = base::take(_a_show); + auto showAnimationData = base::take(_showAnimation); + showChildren(); + _cache = myGrab(this); + _showAnimation = base::take(showAnimationData); + _a_show = base::take(showAnimation); +} + +void InnerDropdown::startOpacityAnimation(bool hiding) { + _hiding = false; + prepareCache(); + _hiding = hiding; + hideChildren(); + _a_opacity.start([this] { opacityAnimationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., _st.duration); +} + void InnerDropdown::showStarted() { if (_ignoreShowEvents) return; if (isHidden()) { show(); + startShowAnimation(); + return; } else if (!_hiding) { return; } - _hiding = false; - startAnimation(); + startOpacityAnimation(false); } -void InnerDropdown::repaintCallback() { +void InnerDropdown::startShowAnimation() { + if (!_a_show.animating()) { + auto opacityAnimation = base::take(_a_opacity); + showChildren(); + auto cache = grabForPanelAnimation(); + _a_opacity = base::take(opacityAnimation); + + _showAnimation = std_::make_unique(_st.animation, _origin); + _showAnimation->setFinalImage(std_::move(cache), rect().marginsRemoved(_st.padding)); + auto corners = App::cornersMask(ImageRoundRadius::Small); + _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + _showAnimation->start(); + } + hideChildren(); + _a_show.start([this] { showAnimationCallback(); }, 0., 1., _st.showDuration); +} + +QImage InnerDropdown::grabForPanelAnimation() { + myEnsureResized(this); + auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + { + Painter p(&result); + App::roundRect(p, rect().marginsRemoved(_st.padding), _st.bg, ImageRoundRadius::Small); + for (auto child : children()) { + if (auto widget = qobject_cast(child)) { + widget->render(&p, widget->pos(), widget->rect(), QWidget::DrawChildren | QWidget::IgnoreMask); + } + } + } + return std_::move(result); +} + +void InnerDropdown::opacityAnimationCallback() { update(); - if (!_a_appearance.animating() && _hiding) { + if (_hiding && !_a_opacity.animating()) { _hiding = false; hideFinished(); } } +void InnerDropdown::showAnimationCallback() { + update(); +} + bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) { if (e->type() == QEvent::Enter) { otherEnter(); diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h index b955f43f5..7f7dfe988 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h @@ -20,8 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/effects/rect_shadow.h" #include "styles/style_widgets.h" +#include "ui/effects/panel_animation.h" class ScrollArea; @@ -36,7 +36,7 @@ public: void setOwnedWidget(TWidget *widget); bool overlaps(const QRect &globalRect) { - if (isHidden() || _a_appearance.animating()) return false; + if (isHidden() || _a_show.animating() || _a_opacity.animating()) return false; return rect().marginsRemoved(_st.padding).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); } @@ -46,7 +46,6 @@ public: void otherEnter(); void otherLeave(); - void showFast(); void hideFast(); void setHiddenCallback(base::lambda_unique callback) { @@ -54,10 +53,11 @@ public: } bool isHiding() const { - return _hiding && _a_appearance.animating(); + return _hiding && _a_opacity.animating(); } - void showAnimated(); + void setOrigin(PanelAnimation::Origin origin); + void showAnimated(PanelAnimation::Origin origin); enum class HideOption { Default, IgnoreShow, @@ -84,28 +84,34 @@ private slots: } private: + QImage grabForPanelAnimation(); + void startShowAnimation(); + void startOpacityAnimation(bool hiding); + void prepareCache(); + class Container; - void repaintCallback(); + void showAnimationCallback(); + void opacityAnimationCallback(); void hideFinished(); void showStarted(); - void startAnimation(); - void updateHeight(); const style::InnerDropdown &_st; - bool _hiding = false; + PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; + std_::unique_ptr _showAnimation; + FloatAnimation _a_show; + bool _hiding = false; QPixmap _cache; - FloatAnimation _a_appearance; + FloatAnimation _a_opacity; QTimer _hideTimer; bool _ignoreShowEvents = false; base::lambda_unique _hiddenCallback; - RectShadow _shadow; ChildWidget _scroll; int _maxHeight = 0; diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index 6dc106def..6f9985e9a 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -18,6 +18,7 @@ #include "stdafx.h" #include "ui/widgets/popup_menu.h" +#include "ui/widgets/shadow.h" #include "pspecific.h" #include "application.h" #include "lang.h" @@ -26,19 +27,13 @@ namespace Ui { PopupMenu::PopupMenu(const style::PopupMenu &st) : TWidget(nullptr) , _st(st) -, _menu(this, _st.menu) -, _shadow(_st.shadow) -, a_opacity(1) -, _a_hide(animation(this, &PopupMenu::step_hide)) { +, _menu(this, _st.menu) { init(); } PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(nullptr) , _st(st) -, _menu(this, menu, _st.menu) -, _shadow(_st.shadow) -, a_opacity(1) -, _a_hide(animation(this, &PopupMenu::step_hide)) { +, _menu(this, menu, _st.menu) { init(); for (auto action : actions()) { @@ -50,8 +45,6 @@ PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(nullptr) } void PopupMenu::init() { - _padding = _shadow.getDimensions(_st.shadowShift); - _menu->setResizedCallback([this] { handleMenuResize(); }); _menu->setActivatedCallback([this](QAction *action, int actionTop, TriggeredSource source) { handleActivated(action, actionTop, source); @@ -63,8 +56,7 @@ void PopupMenu::init() { _menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); }); _menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); }); - _menu->moveToLeft(_padding.left(), _padding.top()); - handleMenuResize(); + handleCompositingUpdate(); setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::Popup | Qt::NoDropShadowWindowHint); setMouseTracking(true); @@ -75,9 +67,17 @@ void PopupMenu::init() { setAttribute(Qt::WA_TranslucentBackground, true); } +void PopupMenu::handleCompositingUpdate() { + _padding = _compositing ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); + _menu->moveToLeft(_padding.left() + _st.scrollPadding.left(), _padding.top() + _st.scrollPadding.top()); + handleMenuResize(); +} + void PopupMenu::handleMenuResize() { - resize(_padding.left() + _menu->width() + _padding.right(), _padding.top() + _menu->height() + _padding.bottom()); - _inner = QRect(_padding.left(), _padding.top(), width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom()); + auto newWidth = _padding.left() + _st.scrollPadding.left() + _menu->width() + _st.scrollPadding.right() + _padding.right(); + auto newHeight = _padding.top() + _st.scrollPadding.top() + _menu->height() + _st.scrollPadding.bottom() + _padding.bottom(); + resize(newWidth, newHeight); + _inner = rect().marginsRemoved(_padding); } QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) { @@ -106,21 +106,36 @@ PopupMenu::Actions &PopupMenu::actions() { void PopupMenu::paintEvent(QPaintEvent *e) { Painter p(this); - auto clip = e->rect(); - p.setClipRect(clip); - auto compositionMode = p.compositionMode(); - p.setCompositionMode(QPainter::CompositionMode_Source); - if (_a_hide.animating()) { - p.setOpacity(a_opacity.current()); + auto ms = getms(); + if (_a_show.animating(ms)) { + if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) { + p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity)); + } + } else if (_a_opacity.animating(ms)) { + p.setOpacity(_a_opacity.current(0.)); p.drawPixmap(0, 0, _cache); - return; + } else if (_hiding || isHidden()) { + hideFinished(); + } else if (_showAnimation) { + p.drawImage(0, 0, _showAnimation->getFrame(1., 1.)); + _showAnimation.reset(); + showChildren(); + } else { + paintBg(p); } +} - // This is the minimal alpha value that allowed mouse tracking in OS X. - p.fillRect(clip, QColor(255, 255, 255, 13)); - p.setCompositionMode(compositionMode); - - _shadow.paint(p, _inner, _st.shadowShift); +void PopupMenu::paintBg(Painter &p) { + if (_compositing) { + Shadow::paint(p, _inner, width(), _st.shadow); + App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small); + } else { + p.fillRect(0, 0, width() - _padding.right(), _padding.top(), _st.shadow.fallback); + p.fillRect(width() - _padding.right(), 0, _padding.right(), height() - _padding.bottom(), _st.shadow.fallback); + p.fillRect(_padding.left(), height() - _padding.bottom(), width() - _padding.left(), _padding.bottom(), _st.shadow.fallback); + p.fillRect(0, _padding.top(), _padding.left(), height() - _padding.top(), _st.shadow.fallback); + p.fillRect(_inner, _st.menu.itemBg); + } } void PopupMenu::handleActivated(QAction *action, int actionTop, TriggeredSource source) { @@ -225,21 +240,13 @@ void PopupMenu::hideEvent(QHideEvent *e) { void PopupMenu::hideMenu(bool fast) { if (isHidden()) return; - if (_parent && !_a_hide.animating()) { + if (_parent && !_a_opacity.animating()) { _parent->childHiding(this); } if (fast) { - if (_a_hide.animating()) { - _a_hide.stop(); - } - a_opacity = anim::fvalue(0, 0); - hideFinish(); + hideFast(); } else { - if (!_a_hide.animating()) { - _cache = myGrab(this); - a_opacity.start(0); - _a_hide.start(); - } + hideAnimated(); if (_parent) { _parent->hideMenu(); } @@ -255,20 +262,134 @@ void PopupMenu::childHiding(PopupMenu *child) { } } -void PopupMenu::hideFinish() { - hide(); +void PopupMenu::setOrigin(PanelAnimation::Origin origin) { + _origin = origin; } -void PopupMenu::step_hide(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_hide.stop(); - a_opacity.finish(); - hideFinish(); - } else { - a_opacity.update(dt, anim::linear); +void PopupMenu::showAnimated(PanelAnimation::Origin origin) { + setOrigin(origin); + showStarted(); +} + +void PopupMenu::hideAnimated() { + if (isHidden()) return; + if (_hiding) return; + + startOpacityAnimation(true); +} + +void PopupMenu::hideFast() { + if (isHidden()) return; + + _hiding = false; + _a_opacity.finish(); + hideFinished(); +} + +void PopupMenu::hideFinished() { + _a_show.finish(); + _cache = QPixmap(); + if (!isHidden()) { + hide(); } - if (timer) update(); +} + +void PopupMenu::prepareCache() { + if (_a_opacity.animating()) return; + + auto showAnimation = base::take(_a_show); + auto showAnimationData = base::take(_showAnimation); + showChildren(); + _cache = myGrab(this); + _showAnimation = base::take(showAnimationData); + _a_show = base::take(showAnimation); +} + +void PopupMenu::startOpacityAnimation(bool hiding) { + _hiding = false; + if (!_compositing) { + _a_opacity.finish(); + if (hiding) { + hideFinished(); + } else { + update(); + } + return; + } + prepareCache(); + _hiding = hiding; + hideChildren(); + _a_opacity.start([this] { opacityAnimationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., _st.duration); +} + +void PopupMenu::showStarted() { + if (isHidden()) { + show(); + startShowAnimation(); + return; + } else if (!_hiding) { + return; + } + startOpacityAnimation(false); +} + +void PopupMenu::startShowAnimation() { + if (!_compositing) { + _a_show.finish(); + update(); + return; + } + if (!_a_show.animating()) { + auto opacityAnimation = base::take(_a_opacity); + showChildren(); + auto cache = grabForPanelAnimation(); + _a_opacity = base::take(opacityAnimation); + + _showAnimation = std_::make_unique(_st.animation, _origin); + _showAnimation->setFinalImage(std_::move(cache), _inner); + if (_compositing) { + auto corners = App::cornersMask(ImageRoundRadius::Small); + _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + } else { + _showAnimation->setSkipShadow(true); + } + _showAnimation->start(); + } + hideChildren(); + _a_show.start([this] { showAnimationCallback(); }, 0., 1., _st.showDuration); +} + +void PopupMenu::opacityAnimationCallback() { + update(); + if (_hiding && !_a_opacity.animating()) { + _hiding = false; + hideFinished(); + } +} + +void PopupMenu::showAnimationCallback() { + update(); +} + +QImage PopupMenu::grabForPanelAnimation() { + myEnsureResized(this); + auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + { + Painter p(&result); + if (_compositing) { + App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small); + } else { + p.fillRect(_inner, _st.menu.itemBg); + } + for (auto child : children()) { + if (auto widget = qobject_cast(child)) { + widget->render(&p, widget->pos(), widget->rect(), QWidget::DrawChildren | QWidget::IgnoreMask); + } + } + } + return std_::move(result); } void PopupMenu::deleteOnHide(bool del) { @@ -282,8 +403,15 @@ void PopupMenu::popup(const QPoint &p) { void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source) { _parent = parent; - QPoint w = p - QPoint(0, _padding.top()); - QRect r = Sandbox::screenGeometry(p); + auto origin = PanelAnimation::Origin::TopLeft; + auto w = p - QPoint(0, _padding.top()); + auto r = Sandbox::screenGeometry(p); +#ifdef Q_OS_LINUX + _compositing = QX11Info::isCompositingManagerRunning(QApplication::desktop()->screenNumber(p)); +#else // Q_OS_LINUX + _compositing = true; +#endif // Q_OS_LINUX + handleCompositingUpdate(); if (rtl()) { if (w.x() - width() < r.x() - _padding.left()) { if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) { @@ -299,8 +427,9 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou if (_parent && w.x() - _parent->width() + _padding.left() + _padding.right() - width() + _padding.right() >= r.x() - _padding.left()) { w.setX(w.x() + _padding.left() + _padding.right() - _parent->width() - width() + _padding.left() + _padding.right()); } else { - w.setX(r.x() + r.width() - width() + _padding.right()); + w.setX(p.x() - width() + _padding.right()); } + origin = PanelAnimation::Origin::TopRight; } } if (w.y() + height() - _padding.bottom() > r.y() + r.height()) { @@ -308,13 +437,18 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou w.setY(r.y() + r.height() - height() + _padding.bottom()); } else { w.setY(p.y() - height() + _padding.bottom()); + origin = (origin == PanelAnimation::Origin::TopRight) ? PanelAnimation::Origin::BottomRight : PanelAnimation::Origin::BottomLeft; } } + if (w.x() < r.x()) { + w.setX(r.x()); + } if (w.y() < r.y()) { w.setY(r.y()); } move(w); + setOrigin(origin); _menu->setShowSource(source); psUpdateOverlayed(this); @@ -323,11 +457,7 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou windowHandle()->requestActivate(); activateWindow(); - if (_a_hide.animating()) { - _a_hide.stop(); - _cache = QPixmap(); - } - a_opacity = anim::fvalue(1, 1); + startShowAnimation(); } PopupMenu::~PopupMenu() { diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.h b/Telegram/SourceFiles/ui/widgets/popup_menu.h index 6863a311e..b53255376 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.h +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.h @@ -18,8 +18,8 @@ #pragma once #include "styles/style_widgets.h" -#include "ui/effects/rect_shadow.h" #include "ui/widgets/menu.h" +#include "ui/effects/panel_animation.h" namespace Ui { @@ -58,14 +58,28 @@ protected: } private: + void paintBg(Painter &p); + void hideFast(); + void setOrigin(PanelAnimation::Origin origin); + void showAnimated(PanelAnimation::Origin origin); + void hideAnimated(); + + QImage grabForPanelAnimation(); + void startShowAnimation(); + void startOpacityAnimation(bool hiding); + void prepareCache(); void childHiding(PopupMenu *child); - void step_hide(float64 ms, bool timer); + void showAnimationCallback(); + void opacityAnimationCallback(); void init(); - void hideFinish(); + + void hideFinished(); + void showStarted(); using TriggeredSource = Ui::Menu::TriggeredSource; + void handleCompositingUpdate(); void handleMenuResize(); void handleActivated(QAction *action, int actionTop, TriggeredSource source); void handleTriggered(QAction *action, int actionTop, TriggeredSource source); @@ -97,12 +111,16 @@ private: QRect _inner; style::margins _padding; - Ui::RectShadow _shadow; SubmenuPointer _activeSubmenu; + PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft; + std_::unique_ptr _showAnimation; + FloatAnimation _a_show; + + bool _compositing = true; + bool _hiding = false; QPixmap _cache; - anim::fvalue a_opacity; - Animation _a_hide; + FloatAnimation _a_opacity; bool _deleteOnHide = true; bool _triggering = false; diff --git a/Telegram/SourceFiles/ui/widgets/shadow.cpp b/Telegram/SourceFiles/ui/widgets/shadow.cpp index 38c49e1fe..13fd54eb3 100644 --- a/Telegram/SourceFiles/ui/widgets/shadow.cpp +++ b/Telegram/SourceFiles/ui/widgets/shadow.cpp @@ -53,4 +53,59 @@ void ToggleableShadow::paintEvent(QPaintEvent *e) { p.fillRect(e->rect(), _color); } +void Shadow::paint(Painter &p, const QRect &box, int outerWidth, const style::Shadow &st, Sides sides) { + auto left = (sides & Side::Left); + auto top = (sides & Side::Top); + auto right = (sides & Side::Right); + auto bottom = (sides & Side::Bottom); + if (left) { + auto from = box.y(); + auto to = from + box.height(); + if (top && !st.topLeft.empty()) { + st.topLeft.paint(p, box.x() - st.extend.left(), box.y() - st.extend.top(), outerWidth); + from += st.topLeft.height() - st.extend.top(); + } + if (bottom && !st.bottomLeft.empty()) { + st.bottomLeft.paint(p, box.x() - st.extend.left(), box.y() + box.height() + st.extend.bottom() - st.bottomLeft.height(), outerWidth); + to -= st.bottomLeft.height() - st.extend.bottom(); + } + if (to > from && !st.left.empty()) { + st.left.fill(p, rtlrect(box.x() - st.extend.left(), from, st.left.width(), to - from, outerWidth)); + } + } + if (right) { + auto from = box.y(); + auto to = from + box.height(); + if (top && !st.topRight.empty()) { + st.topRight.paint(p, box.x() + box.width() + st.extend.right() - st.topRight.width(), box.y() - st.extend.top(), outerWidth); + from += st.topRight.height() - st.extend.top(); + } + if (bottom && !st.bottomRight.empty()) { + st.bottomRight.paint(p, box.x() + box.width() + st.extend.right() - st.bottomRight.width(), box.y() + box.height() + st.extend.bottom() - st.bottomRight.height(), outerWidth); + to -= st.bottomRight.height() - st.extend.bottom(); + } + if (to > from && !st.right.empty()) { + st.right.fill(p, rtlrect(box.x() + box.width() + st.extend.right() - st.right.width(), from, st.right.width(), to - from, outerWidth)); + } + } + if (top && !st.top.empty()) { + auto from = box.x(); + auto to = from + box.width(); + if (left && !st.topLeft.empty()) from += st.topLeft.width() - st.extend.left(); + if (right && !st.topRight.empty()) to -= st.topRight.width() - st.extend.right(); + if (to > from) { + st.top.fill(p, rtlrect(from, box.y() - st.extend.top(), to - from, st.top.height(), outerWidth)); + } + } + if (bottom && !st.bottom.empty()) { + auto from = box.x(); + auto to = from + box.width(); + if (left && !st.bottomLeft.empty()) from += st.bottomLeft.width() - st.extend.left(); + if (right && !st.bottomRight.empty()) to -= st.bottomRight.width() - st.extend.right(); + if (to > from) { + st.bottom.fill(p, rtlrect(from, box.y() + box.height() + st.extend.bottom() - st.bottom.height(), to - from, st.bottom.height(), outerWidth)); + } + } +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/shadow.h b/Telegram/SourceFiles/ui/widgets/shadow.h index 798a45dd5..bf1ef00b6 100644 --- a/Telegram/SourceFiles/ui/widgets/shadow.h +++ b/Telegram/SourceFiles/ui/widgets/shadow.h @@ -1,5 +1,24 @@ +/* +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. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ #pragma once +#include "styles/style_widgets.h" + namespace Ui { class PlainShadow : public TWidget { @@ -63,4 +82,35 @@ private: }; +class Shadow : public TWidget { +public: + enum class Side { + Left = 0x01, + Top = 0x02, + Right = 0x04, + Bottom = 0x08, + }; + Q_DECLARE_FLAGS(Sides, Side); + Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(Sides); + + Shadow(QWidget *parent, const style::Shadow &st, Sides sides = Side::Left | Side::Top | Side::Right | Side::Bottom) : TWidget(parent) + , _st(st) + , _sides(sides) { + } + + static void paint(Painter &p, const QRect &box, int outerWidth, const style::Shadow &st, Sides sides = Side::Left | Side::Top | Side::Right | Side::Bottom); + +protected: + void paintEvent(QPaintEvent *e) override { + Painter p(this); + paint(p, rect().marginsRemoved(_st.extend), width(), _st, _sides); + } + +private: + const style::Shadow &_st; + Sides _sides; + +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(Shadow::Sides); + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 7a4fbebdf..db60dba29 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -40,6 +40,19 @@ IconButton { duration: int; } +Shadow { + left: icon; + topLeft: icon; + top: icon; + topRight: icon; + right: icon; + bottomRight: icon; + bottom: icon; + bottomLeft: icon; + extend: margins; + fallback: color; +} + MaskButton { width: pixels; height: pixels; @@ -148,23 +161,41 @@ Menu { widthMax: pixels; } +PanelAnimation { + startWidth: double; + widthDuration: double; + startHeight: double; + heightDuration: double; + startOpacity: double; + opacityDuration: double; + startFadeTop: double; + fadeHeight: double; + fadeOpacity: double; + fadeBg: color; + shadow: Shadow; +} + PopupMenu { - shadow: icon; - shadowShift: pixels; + shadow: Shadow; + scrollPadding: margins; + animation: PanelAnimation; menu: Menu; duration: int; + showDuration: int; } InnerDropdown { padding: margins; - shadow: icon; - shadowShift: pixels; + shadow: Shadow; + animation: PanelAnimation; duration: int; + showDuration: int; width: pixels; + bg: color; scroll: flatScroll; scrollMargin: margins; scrollPadding: margins; @@ -209,9 +240,39 @@ discreteSliderLabelFont: normalFont; discreteSliderLabelFg: #1485c2; discreteSliderDuration: 200; +defaultRoundShadow: Shadow { + left: icon {{ "round_shadow_left", windowShadowFg }}; + topLeft: icon {{ "round_shadow_top_left", windowShadowFg }}; + top: icon {{ "round_shadow_top", windowShadowFg }}; + topRight: icon {{ "round_shadow_top_left-flip_horizontal", windowShadowFg }}; + right: icon {{ "round_shadow_left-flip_horizontal", windowShadowFg }}; + bottomRight: icon {{ "round_shadow_bottom_left-flip_horizontal", windowShadowFg }}; + bottom: icon {{ "round_shadow_bottom", windowShadowFg }}; + bottomLeft: icon {{ "round_shadow_bottom_left", windowShadowFg }}; + extend: margins(3px, 2px, 3px, 4px); + fallback: windowShadowFgFallback; +} +defaultEmptyShadow: Shadow { + fallback: windowBg; +} + +defaultPanelAnimation: PanelAnimation { + startWidth: 0.5; + widthDuration: 0.6; + startHeight: 0.3; + heightDuration: 0.9; + startOpacity: 0.2; + opacityDuration: 0.3; + startFadeTop: 0.; + fadeHeight: 0.2; + fadeOpacity: 1.0; + fadeBg: menuBg; + shadow: defaultRoundShadow; +} + defaultMenuArrow: icon {{ "dropdown_submenu_arrow", #373737 }}; defaultMenu: Menu { - skip: 8px; + skip: 0px; itemBg: windowBg; itemBgOver: windowOverBg; @@ -235,23 +296,30 @@ defaultMenu: Menu { widthMax: 300px; } defaultPopupMenu: PopupMenu { - shadow: defaultDropdownShadow; - shadowShift: defaultDropdownShadowShift; + shadow: defaultRoundShadow; + animation: defaultPanelAnimation; + + scrollPadding: margins(0px, 8px, 0px, 8px); menu: defaultMenu; - duration: 120; + duration: 150; + showDuration: 200; } defaultInnerDropdown: InnerDropdown { padding: margins(10px, 10px, 10px, 10px); - shadow: defaultDropdownShadow; - shadowShift: defaultDropdownShadowShift; + shadow: defaultRoundShadow; + animation: defaultPanelAnimation; duration: 150; + showDuration: 200; + bg: menuBg; scroll: solidScroll; } defaultDropdownMenu: DropdownMenu { - wrap: defaultInnerDropdown; + wrap: InnerDropdown(defaultInnerDropdown) { + scrollPadding: margins(0px, 8px, 0px, 8px); + } menu: defaultMenu; } diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index f58b25a74..4bcedc72d 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -105,7 +105,7 @@ void TopBarWidget::showMenu() { _menu.destroyDelayed(); }); _menu->moveToRight(0, 0); - _menu->showAnimated(); + _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); } } } diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 141f4a54c..93504aea6 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -461,6 +461,8 @@ '<(src_loc)/ui/buttons/peer_avatar_button.h', '<(src_loc)/ui/buttons/round_button.cpp', '<(src_loc)/ui/buttons/round_button.h', + '<(src_loc)/ui/effects/panel_animation.cpp', + '<(src_loc)/ui/effects/panel_animation.h', '<(src_loc)/ui/effects/radial_animation.cpp', '<(src_loc)/ui/effects/radial_animation.h', '<(src_loc)/ui/effects/rect_shadow.cpp', From 1310b156dc73fd45e539297bf73761cf1f215778 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 10 Nov 2016 21:10:31 +0300 Subject: [PATCH 030/100] Closed beta 10019004: new add contact button, various improvements. --- Telegram/Resources/basic.style | 30 +--- Telegram/Resources/colors.palette | 5 +- Telegram/Resources/icons/contacts_add.png | Bin 316 -> 107 bytes Telegram/Resources/icons/contacts_add@2x.png | Bin 590 -> 126 bytes .../Resources/icons/history_down_circle.png | Bin 628 -> 651 bytes .../icons/history_down_circle@2x.png | Bin 1561 -> 1621 bytes .../Resources/icons/history_down_shadow.png | Bin 1458 -> 1508 bytes .../icons/history_down_shadow@2x.png | Bin 3645 -> 3794 bytes Telegram/Resources/sample.tdesktop-theme | 3 + Telegram/Resources/winrc/Telegram.rc | 8 +- Telegram/Resources/winrc/Updater.rc | 8 +- Telegram/SourceFiles/boxes/boxes.style | 38 ++++- Telegram/SourceFiles/boxes/contactsbox.cpp | 131 ++++++++---------- Telegram/SourceFiles/boxes/contactsbox.h | 7 +- Telegram/SourceFiles/boxes/members_box.cpp | 68 ++++----- Telegram/SourceFiles/boxes/members_box.h | 8 +- Telegram/SourceFiles/core/version.h | 2 +- Telegram/SourceFiles/history/history.style | 11 +- Telegram/SourceFiles/historywidget.cpp | 4 +- .../settings/settings_scale_widget.cpp | 3 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 6 +- Telegram/SourceFiles/stickers/stickers.style | 2 +- .../ui/buttons/history_down_button.cpp | 2 +- .../SourceFiles/ui/widgets/inner_dropdown.cpp | 22 +-- .../SourceFiles/ui/widgets/multi_select.cpp | 2 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 10 +- Telegram/SourceFiles/ui/widgets/widgets.style | 16 +++ Telegram/build/version | 2 +- 28 files changed, 187 insertions(+), 201 deletions(-) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index e06e944ed..1a358f229 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -173,6 +173,7 @@ defaultInputField: InputField { height: 32px; } +defaultCheckboxIcon: icon {{ "default_checkbox_check", windowActiveFg, point(4px, 7px) }}; defaultCheckbox: Checkbox { textFg: windowTextFg; textBg: windowBg; @@ -188,7 +189,7 @@ defaultCheckbox: Checkbox { textPosition: point(32px, 2px); diameter: 22px; thickness: 2px; - checkIcon: icon {{ "default_checkbox_check", windowActiveFg, point(4px, 7px) }}; + checkIcon: defaultCheckboxIcon; font: normalFont; duration: 120; @@ -752,33 +753,6 @@ layerSlideDuration: 200; layerHideDuration: 200; layerPadding: margins(10px, 10px, 10px, 10px); -contactPadding: margins(49px, 22px, 0px, 6px); -contactSkip: 13px; -contactPhoneSkip: 30px; - -contactsPhotoSize: 42px; -contactsPadding: margins(16px, 7px, 16px, 7px); -contactsNameTop: 2px; -contactsNameFont: semiboldFont; -contactsStatusTop: 23px; -contactsStatusFont: font(fsize); -contactsStatusFg: #999999; -contactsStatusFgOver: #7c99b2; -contactsStatusFgOnline: #3b8dcc; -contactsCheckPosition: point(8px, 16px); -contactsAboutBg: #f7f7f7; -contactsAboutShadow: #0000001F; -contactsAdminCheckbox: Checkbox(defaultBoxCheckbox) { - font: semiboldFont; - textBg: #f7f7f7; - textPosition: point(34px, 1px); -} -contactsAboutHeight: 42px; -contactsAboutTop: 9px; -contactsScroll: flatScroll(boxScroll) { - deltab: 0px; -} - simpleCloseIcon: icon {{ "simple_close", #c7c7c7 }}; simpleCloseIconOver: icon {{ "simple_close", #a3a3a3 }}; dialogsForwardCancelIcon: icon {{ "simple_close", dialogsForwardFg }}; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index c7431da64..80a2b9a87 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -97,6 +97,9 @@ membersAboutLimitFg: windowSubTextFg; contactsBg: windowBg; contactsBgOver: windowOverBg; contactsNameFg: boxTextFg; +contactsStatusFg: windowSubTextFg; +contactsStatusFgOver: contactsStatusFg; +contactsStatusFgOnline: #3b8dcc; photoCropFadeBg: #0000007f; photoCropPointFg: #ffffff7f; @@ -170,7 +173,7 @@ topBarBg: windowBg; emojiPanBg: windowBg; emojiPanCategories: #f7f7f7 | windowBg; -emojiPanHeaderFg: #999999 | windowSubTextFg; +emojiPanHeaderFg: windowSubTextFg; emojiPanHeaderBg: #fffffff2 | emojiPanBg; historyComposeAreaBg: windowBg; diff --git a/Telegram/Resources/icons/contacts_add.png b/Telegram/Resources/icons/contacts_add.png index 98f770cc60fa3ad5e2d14176edf905c8f6cda015..3f3efebd0b11cedb540598fa10d43954c6d84ffa 100644 GIT binary patch literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`MxHK?Ar`&KDG3Qb&Nnds`OiNw zDJA8|(w0Wf6yCs+%9jEJXVYwb&kW1sti z0Jd$rT+}U?Nyin O00006nLBhKm02{ z7U`Z;@?rDkJtdjCnp%_AnBG1s(YvbD0H_)W8aB`0db)BSBZHW{AXwmstN~-JH1Mv^kfDlB%Qpq%mR(7IMh()+t z?7HiU$xOzVxR3V^1VXqoIk~wrcK`q|&ZX-*Qc96AhQ9Bk@B7FYLrN(ch7oHn$xAGd z5Q3Ca41xfo==FL?2zjqCAp~vPjugFTGMOYP%@3lK3d`PS`Ypn`u5-&Ej3jD01034z zHe@o~u%~o79jS3VBEQIWU8(FMA~ME=GxtbXobvl76yNtjRaL1>T(4Ikgb2m{a{P;; zC{N?${`DK-3a{5|>B+a-t@PM1xWdEXP*pdEC2wU z&u7^0_psS)V7XkvVzB_nabUOGB_jQFH5-P3rfDK$OnOB##?UlPp@aM*5wZV1Xf~TT zo6TO>c%Cz#&#~2N#o9t6g*8n>$8kPf^pWXwiltKNHQ{=_j;GTpaoHQ5=V7DKkQ3H4 z4L#3GP4>p+a*@k)zu!+?_Qqf^h%Kxf4hPKTa{Rp{0glHb#C^Ad_|U{tChW`*#;5Ca c-@-}r6AJc4d6~cMp#T5?07*qoM6N<$f)-m8cmMzZ diff --git a/Telegram/Resources/icons/history_down_circle.png b/Telegram/Resources/icons/history_down_circle.png index f94e8dd7b61b5cdee657cf703e1a7a19af1d6152..001abe2ddcb59c14de938795cf8d940e96cf5e9e 100644 GIT binary patch literal 651 zcmV;60(AX}P)2}Jbo6<>W9!tX9ab1vlMaY9*2N0luGd2p&s$x zUfaZ%9&;b~4}paImO#!q8X$;Jzb<*$FbvA&GL=e&Y&Of|<0F=35s$~&?RKnID?HC* zKA-da{LFATWHcJ__V#uWT7(_jwneMe60Yltu(_@)TCJ9_Z9DXP_Y?f)QmG`m-EL^) zsk+^+D3waL0XLh?t3m&+*=$}9oJys{WHPxKRyLVTL@JfK3|uG_gy(rT$I3j<6NN(I zJaDmC6u$5O7*^){z9<%p;lRmcQmohOAIHizn~g{&lfl56ris~XrVJ~a&1OQ=wC}-< zMnkprP@~cK8l1^wR2x5)$z)E0`~ALZ->3Th{=dQbd|tKfWchsl0M^-8uh+TPj%^ws zVzC&m>tdRwI@K2~mrE?mV!Pd<1Jr7@yTt;SrpeRO6Tk@}GG=F~H-`{Ds+p1kB>vTG7x7)*Y zq<-Kbs!Js9bP<}(X8)sQK2h9AA!=Jyaw4@EC?`_et#Tr@p?&-zI*xNQtm8PRKCtt^ za;|+S)9dwacPMi_*Esenm&>8w3kXfB&&e5WeqYyBiEYg%Y5iTUDv5?+qrS~vTd8{ zy8c?|a5#J!@b8Ah;r~M=NusOO>TSrh)oMj0NqP>dX&Mc~@a@R7Fbt`tY5Ab7R*NP{ z@?*%fBuQwi)p`sn2m+0w=*N+1aU4@Y5dID2IF5Rr#|)X~c^>6B?ml!d7_c_}X)qYv zhRU+c+IE#J%h%A^Y{uGk)oeEV4pmi^wdroEs!{-y06@Rr$ETLsxKgP^gCIbyR%1-M z-G09Z&+{PNZueu608p#d(ChU;Ow(j-$;>oOkY=;V+L2ka*#r?qk+mZ;Q4~RVo@edI zjOTffYPHJRky*7`1xb>Gwd0{k6h*8JnMF|q5{4mbM`pX-4rIICvUX&)-EKi#*JbU< z%ynH5$8lIYGIJcKKtCm?(+T78n6>9_lgR|<^BF)vew=|KZAI{*n>$5O3cOuLkq#hG zlp~#V3UNr5P9csR(<#J3?$-~@vaGiuEz7#~;pB&=^Xkin>2&(~%Z9J>itC$pyPa!I z0F+C$c`KL8=yWO-EIfp_p#Y*uv{)NpUrKo2uXTBp-YL O0000 zO-~bB7{~u}r;Ao&=u6aU4c1Udj9@Bd=|){Bega=8U%+k%Y}v47MZW;Du+fl^C^glE z%AyizB0~(ug&IIpZDJ5ir)@&wJTAOFw=WcEJ2PiIcYeu9rZYo%o?lN+XFBIa0zj}a zjDL6sAUY1-0aOwU265@qC0w|00o~o*2nK^Ve*8FmJ|A2z7mgh}hTYv=sH%#cogL)! zdHnqO6B`>F`0(Ka-o1MVMNzP^vB5hPh6IjH#Oc$gaqZeQT)A=uSFc{h*|TSLdEd>= zO}u*b3NK&2#ETa%@b&9gUEa4<$jsvLcqkT&Q97NbU%!5l(P)}R>2#W6u^4$g9#eDu zx0v&f`uh54dU~3Q#iHR-8&ND4X?l8^`uh6B5gds`Xnua4G)?0swiZp(D49%BBog_1 z1$TCKQX-MCl-OD{O`}92L7knQe~(~?!$G5?qf{sqc>6mvg+hTwM@Pxwa9CS#C={aQ z2&fD>~_0pVPS!{1H)NZSRl9CZLVOC$3u#u@Xp;d6h&$0F0i>j z=;-L6rKKg_Ih)SX(h_xabQmjGmSuYU_%ZKXxiLRK-^v%?;ZGLvYN1)CrlyP$9F0bK z=Vlp(hllkQ+|$!Ts;ctN%QC2{NVZWRSCXv zuUQT5>guxP>VV(xM^{%Dd(8x2ym*mIKq6AUbf8yJDpB0!H7uZk7evegQ(%NPn#Af z|Kz}G@b2y|w_rqu3C`#9JmU}wRaM!xE!Y4`r4rk=1slMRA3xYO6TG#x#U&UKsqE>) z1b_bgnM*Jtvbnj*wwd6MA3t&lMno!IGQn$WYg~d6kxG|L@Y}aY$blVCve_&a7Z*(rOcWLu7wcxYs_UEf z@Zm$#0uqf!j~>4Za z@aKz9#H$NSOwe3eqS2@jHzTU5QZyRX#fh$hfj@VAfAb{ynM|gcJ2dkJ1Amrz7#kbo zy5dW-6pKX~8ymC!G7pu-UZ})i!R0bAFhI#gM)*(e*HQw zU%sqc*V$YyhjcoP=g*%bl}aI-&Fb>Lt-w`qE$7aiV@pnkLLmf$L7X^o0zRJ)M~@!G zkt0U{0L5Yvg+c*aTU+@4{X5pz*YWAoC#iY00000NkvXXu0mjf?7|ch literal 1561 zcmV+!2Il#RP) zT~E_s6vzKI~&2xKxYy-_a|KY=fdFW_zn-0}&yVqXBgaM6&EC?nGg zm5WJWiIk8TFT?Uta>h0}qU7?{+h-PPJN!N8Yk%!TBo#OF0g+ig$5!%twL9tk@sYD(|*L8}; zV${*m(Y!+2+S+Jzbd++r9JjBE$>nl1Iyy>iZEejZRF-90T3X`vQ!`6TOVriX)f_^H zhK4Ac&2l@bn`|~qLqkJ+LM2I}$;nA>4~7$sMoE&S#tL;f95gdC!=1Zn%*@P?!{KO* zP?yU^^YioEIh)S>{5-i_E^~#t-ELA8g*(@Vp(si%XIYiKvAw;W78e(}b8I+^i;L9W z-fpZ=Ns{RK^XJ^Tu`xS4Tg`#$|8KJ5(Y#tFCnt>&8V-lKb7>lehllGE+TGnvnx=8* z(KKk9M%~@rwF#9ZiBhQ)cMeTQDwX>^{-(Y-+$`w2Uhc~O z!3FVnoIBQ*VQy~j-+gE>7{v1OG6X>|ah^=KQ6bkVB{n**r0kCfmy?_6{EwcukPA48bcu@MeB=p|B zdn^H2fxCC_mR<`0$nW>#>({S#|5+nMgbNogU~6mZ&mKBBIB3hP13?gQ=gu7ff4}m# zZ{KDO$O;r43IOQaw{P(Ie7q60II61R+_`fQE?v4rYin!V@mK;`mLc}_^>N2x8T$JA zAole1aK~X8dU|>w%CgKIhh>mu8KP0++iif(&Q6GD&z|Lu!!r1MK8Rkgmpcy2;PH4M zIvft}I4pzH>4ZQ;M{d&D)Q-5fx5piWr4X~(EO!i+LDMu_sF4&31zV_*{P^($;`a77 zcN~^MRaJ;zzI@@1!!m4cZbGy#-5JN)+8V@kI?Wx2Wk{#f5LZ`Mx#O@5tE;Q_EfAw9 z-U1N;;N81--0`$LMTa5)ynOkRH5e;UbZ8HBFn}Wi9Wt2=78Vw`<7!zJ78XhscuHfN zo<4ob7Er76?Af!@YkN3jos=EUxU;i^>FH^FJV8;80~Tdq{Y75D`M55EMlz zEjutId}Uh5OdGf^EKdl9b#bS8>{{4~V z_SD20sCsIms;U?tA2&U)R%LvA{8*Vq?CITAQ1kR|*aZlVP*(whVjCVF=FX#OsJlc) zu|=a%?p)Z3Mx&L;tW2mRN%Z2y3+|lQh{xl`mKg%##g<-!o5z@Ia!RxWCtN~J0}18SU5;7{50 zv9U4MCn{7+KA)$tv9abWyS~5K4+b!Pg9LyE1_qk42Grc#91RQ%n3A)pLW?aJ4AP@V zk662cLZLv9A3vsGFlcJ74G~&w9*?K=_@72}2m2rY&3~F zCvoD$2>?JopGPj2!}j(zzJLFY_4Rdp{`?u~bQ+4HV10d^ITPSF2tYu;G6!Js00000 LNkvXXu0mjf0iFvJ diff --git a/Telegram/Resources/icons/history_down_shadow.png b/Telegram/Resources/icons/history_down_shadow.png index 8317b07d1e90900db040d357f80cd17b0a67a78c..17e6289c237f027ecf3f1c7195671dfbcedc0ccd 100644 GIT binary patch literal 1508 zcmVf#t6oy}JT)=W@O(B&nRR+7eBnyN}C3YauMY~G&iPWER|4Jg2WtS~PDD)!|3o4gN zAxpbPw761A++YVVmgX*UsnTEwP)JbJM`Dv1+vmyg*q%A#0YGqz_~)hrm^FWe7M)us zM9unye!m}g@7@K=vIqnMplKQuMS&0k$8peZw_%zl48y?b=_xv$&P=aA6Ct0^2ae+q zjYc7gA|jCpc%H}o`}e^x47^@12qCa-8)s){Xf~T@G#bz}4XUc*%ao128D=_`en0Q@^_aCtl)JbCg2FJ8RB)2C12_xneU#ksJw zv;>ahKv5Lx^*ZYH`pEtx-$Q~R;MuchNTpI>7-pt{P6ER)NTpIx6b1Eq9h#;M?K|{a z&hPglnM@*+$$;nixdb{1JkKMO$sn0bj=WQb3Q3ZLbUKao_4T;~I1TIT>qw{5kR)kn zA6Frt&xcqnhD0K9qqo{+7=}S2kw7dKgU{!4?c*xMaUA0DI7CsLYk<=riX!6iI5>`T zZFdzCMGU-5{b+`xM_(-qmU#CG))h*4G0AS zfnQUAMI{^#Ll6W60)c_H0U?%U!Snn=Jq#|xFbsH}cO4S0843o2@Or)165KR+y0h!8T>)OS%B5b7^tEIPJr!*QH} zzYhp?IvrS+<=S~sa2yAgWx1|53<$MaEf|Ke&{?u=8z(0xXti1cZ39AvVW81yz_P4s z32quJ%R-~kfMFN|ZLS%r)oM6CKE9UVrlG$;IaY?+?KTue896JwX*5lPqA25yAplTS z71e6hHKjL=VHl`Zt58+dwcS-n*LBFUJoKQOL{(MDvJ73nM0^ob9Q!yTrP)fHhbkAGg1gR zf9Bh^jRy}NKoA5hEiKJ3)T`e#Gef|0srfDJ&2!QALxzCv_%fh>N@36D8 zgEw#9pxJDWb=+7&!1)(M|C5NKD6Fik%&c;E8C}Z zG)+UjUWaL#@caE>S$1aGvMdXqKYzxnSFiB$SeEDJw={D7uuI669l zEX(-x>C?@X^)J%jcp-`+BuRoG2;eyG*T#$f!g^n%*=$1Bb<}D#D2f7ARcE&GVkSZt zp=la{K;XBnG5r^Euh&Dn-TpNzZnau}lnpm#E_Bn}y4_f}PUt`A&2|U_Ci77M0000< KMNUMnLSTZ4HoQXs literal 1458 zcmV;j1x@;iP)hipkiq zwFIRmlhx4#JhZQT`m}vM&&LCR;2(s#iIe#Ke%!r#7c9#n z5D0*#X;2geLI`ZzMyJz(VHnVL9VaIz=ytn*ea$}>_xXI_I1bTh6rw015{ZE4dECE$ z9}L65>-B;V0?V>+dU}dhtA%E>2~}00Cjy{=muq;c~*Vjodmm}Z4 zeIvI;v)LrMT#l@-uM?JKCpw)Umv@H4VY0coN%HwTF%09@qz4#=LGt-L+1%VDp-|}0 z#Dl>g+1lD7dwYAtvaDN|9$;A(+1uMATU%Qs7z|!doTh2Av9Uo4g~C*&hvf@}0@>Ku zAT&*n6<-AK-|&R@cszLWzZ88h-8 z7X$&%o;^c4od&}&QxP2p!!Ssv(~xBujYb2is=E3YhJX0|exy<;@aqv8k zY&MHjDm8NFUE;A=44F&@Yiny$6`aJ{+8Q#M3}UgEtKAXz`Fx1S<47ixGo2zAVHgI< zWD@as96q1VX?Mgqjzc1mfGCPH5uAi5iby09;5g1{TNv(KU0nssvNMsJ1k18mSy^!f zbpbeAi^O6vL?V%y$^DLKG>TX(2AZY;oQnqn0cUACuY|*42!en>AOPT8oMl?t zd!wIZjd3Tt|Dr za9X$koTXCaiYe2)umJo@X6GHtvS8abfOGL~w+quWoyK{@wr!ZE>Expe0MKr?q3ilw zN8Pe493LN}-EIRo7uR(i&1Ms(Y0gw`5~gXQ*=$1BbpS&Vuh;81Iy#!E+$07YNv?=@ zIvvQeJd$P16RN60mSy)G2LMG;P^;CPTQN`Qx{g|{21QYvHb-33G)R)<>YNu8MS&zq z&@|0ybHsbS9;($UDwWDyJ*1{-qEe}#TCKWvc3nBAEXydBN;o(;n7ZI)92^{=R4PH1 zWmmgPyxZ-fTrQ(nEV^#Xtk5(K#bOcVa(Sc}FtQnWE}1 zPESvf&*zcL<;Ko{5pm$`ooQJX9z1veK@hOGxHtvXU-bKZynp{5J3Bl0@Zkgc{r5^)ht(@-cBu)V#F z-Q8UrA0JN~KT#Ywi@d6;Xfzry3SoBTP#OKeS@#@tpynOi*`}_Ma4CA-| zyK?c$3@gqv5((M$lh8b5bj6@;`g+gE$20ou}Y`L)4 z>%lZl{P^($s;c7f@DP$D;nSy2w_h$CW>BCNMG>)B41yql<2a{4JJ_Hc2)9}-Xqtw4 zy$)HHp(x5<6=?rhd>ERh5eNiaCGo+BtKaXV)9E-_R=eH4c67(_KVwH-6lu3ez5oCK M07*qoM6N<$g2!UDBme*a diff --git a/Telegram/Resources/icons/history_down_shadow@2x.png b/Telegram/Resources/icons/history_down_shadow@2x.png index 8127513da33abb25a80b54e34bb19f59d227c7f3..c7a2d5f2d2d77c4c3db293c8922d1ca59bfa0e31 100644 GIT binary patch literal 3794 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 literal 3645 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 n&30w%1r3!|A^&s~oY5qgC#4y diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index d8e443105..533981196 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -81,6 +81,9 @@ membersAboutLimitFg: windowSubTextFg; contactsBg: windowBg; contactsBgOver: windowOverBg; contactsNameFg: boxTextFg; +contactsStatusFg: windowSubTextFg; +contactsStatusFgOver: contactsStatusFg; +contactsStatusFgOnline: #3b8dcc; photoCropFadeBg: #0000007f; photoCropPointFg: #ffffff7f; notificationsBoxMonitorFg: windowTextFg; diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 3c0a5ec7e..5b1c34e86 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,3 - PRODUCTVERSION 0,10,19,3 + FILEVERSION 0,10,19,4 + PRODUCTVERSION 0,10,19,4 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.19.3" + VALUE "FileVersion", "0.10.19.4" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.3" + VALUE "ProductVersion", "0.10.19.4" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index e936c8742..a51fcdfcc 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,3 - PRODUCTVERSION 0,10,19,3 + FILEVERSION 0,10,19,4 + PRODUCTVERSION 0,10,19,4 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.19.3" + VALUE "FileVersion", "0.10.19.4" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.3" + VALUE "ProductVersion", "0.10.19.4" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index de9b5f400..b4a82f04d 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -89,10 +89,40 @@ contactUserIcon: icon {{ "add_contact_user", #999999 }}; contactPhoneIcon: icon {{ "add_contact_phone", #999999 }}; contactIconTop: 10px; -contactsNewItemHeight: 53px; -contactsNewItemIcon: icon {{ "contacts_add", #749fc2, point(29px, 19px) }}; -contactsNewItemTop: 18px; -contactsNewItemFg: #4b82af; +contactsAdd: IconButton { + width: 52px; + height: 52px; + + icon: contactsAddIcon; + iconOver: contactsAddIconOver; +} +contactsAddPosition: point(14px, 8px); + +contactPadding: margins(49px, 22px, 0px, 6px); +contactSkip: 13px; +contactPhoneSkip: 30px; + +contactsPhotoSize: 42px; +contactsPadding: margins(16px, 7px, 16px, 7px); +contactsNameTop: 2px; +contactsNameFont: semiboldFont; +contactsStatusTop: 23px; +contactsStatusFont: font(fsize); +contactsCheckPosition: point(8px, 16px); +contactsAllAdminsTop: 18px; +contactsAboutBg: #f7f7f7; +contactsAboutShadow: #0000001F; +contactsAdminCheckbox: Checkbox(defaultBoxCheckbox) { + font: semiboldFont; + textBg: #f7f7f7; + textPosition: point(34px, 1px); +} +contactsAboutSkip: 53px; +contactsAboutHeight: 42px; +contactsAboutTop: 9px; +contactsScroll: flatScroll(boxScroll) { + deltab: 0px; +} contactsMultiSelect: MultiSelect { bg: boxSearchBg; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 20c59e3aa..517d9e8f7 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -31,6 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "application.h" #include "ui/buttons/checkbox.h" +#include "ui/buttons/icon_button.h" #include "ui/filedialog.h" #include "ui/widgets/multi_select.h" #include "ui/effects/widget_slide_wrap.h" @@ -108,7 +109,13 @@ void ContactsBox::init() { auto bottomSkip = inviting ? (st::boxButtonPadding.top() + _next->height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip; ItemListBox::init(_inner, bottomSkip, topSkip); - connect(_inner, SIGNAL(addRequested()), App::wnd(), SLOT(onShowAddContact())); + if (_inner->creating() == CreatingGroupNone && !_inner->chat() && !_inner->channel() && !_inner->bot()) { + _add.create(this, st::contactsAdd); + _add->setClickedCallback([] { + App::wnd()->onShowAddContact(); + }); + } + _inner->setPeerSelectedChangedCallback([this](PeerData *peer, bool checked) { onPeerSelectedChanged(peer, checked); }); @@ -336,6 +343,10 @@ void ContactsBox::resizeEvent(QResizeEvent *e) { updateScrollSkips(); + if (_add) { + _add->moveToRight(st::contactsAddPosition.x(), height() - st::contactsAddPosition.y() - _add->height()); + } + _inner->resize(width(), _inner->height()); _next->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next->height()); _cancel->moveToRight(st::boxButtonPadding.right() + _next->width() + st::boxButtonPadding.left(), _next->y()); @@ -558,7 +569,6 @@ ContactsBox::Inner::ContactData::ContactData(PeerData *peer, base::lambda_wrapcontactsList()) @@ -597,7 +607,7 @@ ContactsBox::Inner::Inner(QWidget *parent, ChatData *chat, MembersFilter members , _addContactLnk(this, lang(lng_add_contact_button)) { initList(); if (membersFilter == MembersFilter::Admins) { - _newItemHeight = st::contactsNewItemHeight + qMax(_aboutAllAdmins.countHeight(_aboutWidth), _aboutAdmins.countHeight(_aboutWidth)) + st::contactsAboutHeight; + _aboutHeight = st::contactsAboutSkip + qMax(_aboutAllAdmins.countHeight(_aboutWidth), _aboutAdmins.countHeight(_aboutWidth)) + st::contactsAboutHeight; if (_contacts->isEmpty()) { App::api()->requestFullPeer(_chat); } @@ -836,7 +846,7 @@ void ContactsBox::Inner::peerUpdated(PeerData *peer) { for_const (auto row, _contacts->all()) { if (row->attached == i.value()) { row->attached = nullptr; - update(0, _newItemHeight + _rowHeight * row->pos(), width(), _rowHeight); + update(0, _aboutHeight + _rowHeight * row->pos(), width(), _rowHeight); } } if (!_filter.isEmpty()) { @@ -863,9 +873,9 @@ void ContactsBox::Inner::loadProfilePhotos(int32 yFrom) { if (_filter.isEmpty()) { if (!_contacts->isEmpty()) { - auto i = _contacts->cfind(yFrom - _newItemHeight, _rowHeight); + auto i = _contacts->cfind(yFrom - _aboutHeight, _rowHeight); for (auto end = _contacts->cend(); i != end; ++i) { - if ((_newItemHeight + (*i)->pos() * _rowHeight) >= yTo) { + if ((_aboutHeight + (*i)->pos() * _rowHeight) >= yTo) { break; } (*i)->history()->peer->loadUserpic(); @@ -1046,24 +1056,17 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { int32 yFrom = r.y(), yTo = r.y() + r.height(); if (_filter.isEmpty()) { if (!_contacts->isEmpty() || !_byUsername.isEmpty()) { - if (_newItemHeight) { - if (_chat) { - p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); - p.fillRect(0, _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, width(), st::lineWidth, st::shadowColor); - p.setPen(st::boxTextFg); - p.drawTextLeft(st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(lng_chat_all_members_admins)); - int aboutw = width() - st::contactsPadding.left() - st::contactsPadding.right(); - (_allAdmins->checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsNewItemHeight + st::contactsAboutTop, aboutw); - } else { - p.fillRect(0, 0, width(), st::contactsNewItemHeight, _newItemSel ? st::contactsBgOver : st::contactsBg); - p.setFont(st::contactsNameFont); - st::contactsNewItemIcon.paint(p, 0, 0, width()); - p.setPen(st::contactsNewItemFg); - p.drawTextLeft(st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(lng_add_contact_button)); - } - yFrom -= _newItemHeight; - yTo -= _newItemHeight; - p.translate(0, _newItemHeight); + if (_aboutHeight) { + p.fillRect(0, 0, width(), _aboutHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); + p.fillRect(0, _aboutHeight - st::contactsPadding.bottom() - st::lineWidth, width(), st::lineWidth, st::shadowColor); + p.setPen(st::boxTextFg); + p.drawTextLeft(st::contactsPadding.left(), st::contactsAllAdminsTop, width(), lang(lng_chat_all_members_admins)); + int aboutw = width() - st::contactsPadding.left() - st::contactsPadding.right(); + (_allAdmins->checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsAboutSkip + st::contactsAboutTop, aboutw); + + yFrom -= _aboutHeight; + yTo -= _aboutHeight; + p.translate(0, _aboutHeight); } if (!_contacts->isEmpty()) { auto i = _contacts->cfind(yFrom, _rowHeight); @@ -1103,13 +1106,13 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { text = lang((cDialogsReceived() && !_searching) ? (sharingBotGame() ? lng_bot_no_chats : lng_bot_no_groups) : lng_contacts_loading); } else if (_chat && _membersFilter == MembersFilter::Admins) { text = lang(lng_contacts_loading); - p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); - p.fillRect(0, _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, width(), st::lineWidth, st::shadowColor); + p.fillRect(0, 0, width(), _aboutHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); + p.fillRect(0, _aboutHeight - st::contactsPadding.bottom() - st::lineWidth, width(), st::lineWidth, st::shadowColor); p.setPen(st::boxTextFg); - p.drawTextLeft(st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(lng_chat_all_members_admins)); + p.drawTextLeft(st::contactsPadding.left(), st::contactsAllAdminsTop, width(), lang(lng_chat_all_members_admins)); int aboutw = width() - st::contactsPadding.left() - st::contactsPadding.right(); - (_allAdmins->checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsNewItemHeight + st::contactsAboutTop, aboutw); - p.translate(0, _newItemHeight); + (_allAdmins->checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsAboutSkip + st::contactsAboutTop, aboutw); + p.translate(0, _aboutHeight); } else if (cContactsReceived() && !_searching) { text = lang(lng_no_contacts); skip = st::noContactsFont->height; @@ -1171,9 +1174,9 @@ void ContactsBox::Inner::enterEvent(QEvent *e) { int ContactsBox::Inner::getSelectedRowTop() const { if (_filter.isEmpty()) { if (_sel) { - return _newItemHeight + (_sel->pos() * _rowHeight); + return _aboutHeight + (_sel->pos() * _rowHeight); } else if (_byUsernameSel >= 0) { - return _newItemHeight + (_contacts->size() * _rowHeight) + st::searchedBarHeight + (_byUsernameSel * _rowHeight); + return _aboutHeight + (_contacts->size() * _rowHeight) + st::searchedBarHeight + (_byUsernameSel * _rowHeight); } } else { if (_filteredSel >= 0) { @@ -1186,13 +1189,9 @@ int ContactsBox::Inner::getSelectedRowTop() const { } void ContactsBox::Inner::updateSelectedRow() { - if (_filter.isEmpty() && _newItemSel) { - update(0, 0, width(), st::contactsNewItemHeight); - } else { - auto rowTop = getSelectedRowTop(); - if (rowTop >= 0) { - updateRowWithTop(rowTop); - } + auto rowTop = getSelectedRowTop(); + if (rowTop >= 0) { + updateRowWithTop(rowTop); } } @@ -1204,12 +1203,12 @@ int ContactsBox::Inner::getRowTopWithPeer(PeerData *peer) const { if (_filter.isEmpty()) { for (auto i = _contacts->cbegin(), end = _contacts->cend(); i != end; ++i) { if ((*i)->history()->peer == peer) { - return _newItemHeight + ((*i)->pos() * _rowHeight); + return _aboutHeight + ((*i)->pos() * _rowHeight); } } for (auto i = 0, count = _byUsername.size(); i != count; ++i) { if (_byUsername[i] == peer) { - return _newItemHeight + (_contacts->size() * _rowHeight) + st::searchedBarHeight + (i * _rowHeight); + return _aboutHeight + (_contacts->size() * _rowHeight) + st::searchedBarHeight + (i * _rowHeight); } } } else { @@ -1237,10 +1236,9 @@ void ContactsBox::Inner::updateRowWithPeer(PeerData *peer) { void ContactsBox::Inner::leaveEvent(QEvent *e) { _mouseSel = false; setMouseTracking(false); - if (_newItemSel || _sel || _filteredSel >= 0 || _byUsernameSel >= 0) { + if (_sel || _filteredSel >= 0 || _byUsernameSel >= 0) { updateSelectedRow(); _sel = 0; - _newItemSel = false; _filteredSel = _byUsernameSel = -1; } } @@ -1316,10 +1314,6 @@ void ContactsBox::Inner::chooseParticipant() { } else { PeerData *peer = 0; if (_filter.isEmpty()) { - if (_newItemSel) { - emit addRequested(); - return; - } if (_byUsernameSel >= 0 && _byUsernameSel < _byUsername.size()) { peer = _byUsername[_byUsernameSel]; } else if (_sel) { @@ -1431,19 +1425,14 @@ void ContactsBox::Inner::updateSelection() { QPoint p(mapFromGlobal(_lastMousePos)); bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); if (_filter.isEmpty()) { - bool newItemSel = false; - if (_newItemHeight) { - if (in && (p.y() >= 0) && (p.y() < _newItemHeight) && !(_chat && _membersFilter == MembersFilter::Admins)) { - newItemSel = true; - } - p.setY(p.y() - _newItemHeight); + if (_aboutHeight) { + p.setY(p.y() - _aboutHeight); } - Dialogs::Row *newSel = (in && !newItemSel && (p.y() >= 0) && (p.y() < _contacts->size() * _rowHeight)) ? _contacts->rowAtY(p.y(), _rowHeight) : nullptr; - int32 byUsernameSel = (in && !newItemSel && p.y() >= _contacts->size() * _rowHeight + st::searchedBarHeight) ? ((p.y() - _contacts->size() * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1; + Dialogs::Row *newSel = (in && (p.y() >= 0) && (p.y() < _contacts->size() * _rowHeight)) ? _contacts->rowAtY(p.y(), _rowHeight) : nullptr; + int32 byUsernameSel = (in && p.y() >= _contacts->size() * _rowHeight + st::searchedBarHeight) ? ((p.y() - _contacts->size() * _rowHeight - st::searchedBarHeight) / _rowHeight) : -1; if (byUsernameSel >= _byUsername.size()) byUsernameSel = -1; - if (newSel != _sel || byUsernameSel != _byUsernameSel || newItemSel != _newItemSel) { + if (newSel != _sel || byUsernameSel != _byUsernameSel) { updateSelectedRow(); - _newItemSel = newItemSel; _sel = newSel; _byUsernameSel = byUsernameSel; updateSelectedRow(); @@ -1678,10 +1667,10 @@ void ContactsBox::Inner::refresh() { } if (!_contacts->isEmpty() || !_byUsername.isEmpty()) { if (!_addContactLnk->isHidden()) _addContactLnk->hide(); - resize(width(), _newItemHeight + (_contacts->size() * _rowHeight) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * _rowHeight))); + resize(width(), _aboutHeight + (_contacts->size() * _rowHeight) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * _rowHeight))); } else if (_chat && _membersFilter == MembersFilter::Admins) { if (!_addContactLnk->isHidden()) _addContactLnk->hide(); - resize(width(), _newItemHeight + st::noContactsHeight); + resize(width(), _aboutHeight + st::noContactsHeight); } else { if (cContactsReceived() && !bot()) { if (_addContactLnk->isHidden()) _addContactLnk->show(); @@ -1740,7 +1729,7 @@ ContactsBox::Inner::~Inner() { void ContactsBox::Inner::resizeEvent(QResizeEvent *e) { _addContactLnk->move((width() - _addContactLnk->width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); - _allAdmins->moveToLeft(st::contactsPadding.left(), st::contactsNewItemTop); + _allAdmins->moveToLeft(st::contactsPadding.left(), st::contactsAllAdminsTop); } void ContactsBox::Inner::selectSkip(int32 dir) { @@ -1752,20 +1741,16 @@ void ContactsBox::Inner::selectSkip(int32 dir) { for (auto i = _contacts->cbegin(); *i != _sel; ++i) { ++cur; } - if (_newItemHeight) ++cur; } else if (_byUsernameSel >= 0) { cur = (_contacts->size() + _byUsernameSel); - if (_newItemHeight) ++cur; - } else if (!_newItemSel) { + } else { cur = -1; } cur += dir; if (cur <= 0) { - _newItemSel = (_chat && _membersFilter == MembersFilter::Admins) ? false : (_newItemHeight ? true : false); - _sel = (!_newItemHeight && !_contacts->isEmpty()) ? *_contacts->cbegin() : nullptr; - _byUsernameSel = (!_newItemHeight && _contacts->isEmpty() && !_byUsername.isEmpty()) ? 0 : -1; - } else if (cur >= _contacts->size() + (_newItemHeight ? 1 : 0)) { - _newItemSel = false; + _sel = (!_contacts->isEmpty()) ? *_contacts->cbegin() : nullptr; + _byUsernameSel = (_contacts->isEmpty() && !_byUsername.isEmpty()) ? 0 : -1; + } else if (cur >= _contacts->size()) { if (_byUsername.isEmpty()) { _sel = _contacts->isEmpty() ? nullptr : *(_contacts->cend() - 1); _byUsernameSel = -1; @@ -1775,8 +1760,6 @@ void ContactsBox::Inner::selectSkip(int32 dir) { if (_byUsernameSel >= _byUsername.size()) _byUsernameSel = _byUsername.size() - 1; } } else { - _newItemSel = false; - if (_newItemHeight) --cur; for (auto i = _contacts->cbegin(); ; ++i) { _sel = *i; if (!cur) { @@ -1809,7 +1792,7 @@ void ContactsBox::Inner::selectSkip(int32 dir) { } if (_byUsernameSel < 0) { if (!_contacts->isEmpty()) { - if (!_newItemSel && !_sel) _sel = *(_contacts->cend() - 1); + if (!_sel) _sel = *(_contacts->cend() - 1); if (_sel) { for (auto i = _contacts->cfind(_sel), b = _contacts->cbegin(); i != b && contactData(*i)->disabledChecked; --i) { _sel = *i; @@ -1821,12 +1804,10 @@ void ContactsBox::Inner::selectSkip(int32 dir) { } } } - if (_newItemSel) { - emit mustScrollTo(0, _newItemHeight); - } else if (_sel) { - emit mustScrollTo(_newItemHeight + _sel->pos() * _rowHeight, _newItemHeight + (_sel->pos() + 1) * _rowHeight); + if (_sel) { + emit mustScrollTo(_aboutHeight + _sel->pos() * _rowHeight, _aboutHeight + (_sel->pos() + 1) * _rowHeight); } else if (_byUsernameSel >= 0) { - emit mustScrollTo(_newItemHeight + (_contacts->size() + _byUsernameSel) * _rowHeight + st::searchedBarHeight, _newItemHeight + (_contacts->size() + _byUsernameSel + 1) * _rowHeight + st::searchedBarHeight); + emit mustScrollTo(_aboutHeight + (_contacts->size() + _byUsernameSel) * _rowHeight + st::searchedBarHeight, _aboutHeight + (_contacts->size() + _byUsernameSel + 1) * _rowHeight + st::searchedBarHeight); } } else { int cur = (_filteredSel >= 0) ? _filteredSel : ((_byUsernameSel >= 0) ? (_filtered.size() + _byUsernameSel) : -1); diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index d0020347b..93807e328 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -91,6 +91,7 @@ private: class Inner; ChildWidget _inner; ChildWidget> _select; + ChildWidget _add = { nullptr }; ChildWidget _next; ChildWidget _cancel; @@ -187,7 +188,6 @@ signals: void mustScrollTo(int ymin, int ymax); void searchByUsername(); void adminAdded(); - void addRequested(); private slots: void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow); @@ -252,9 +252,8 @@ private: base::lambda_unique _peerSelectedChangedCallback; - int32 _rowHeight; - int _newItemHeight = 0; - bool _newItemSel = false; + int _rowHeight; + int _aboutHeight = 0; ChatData *_chat = nullptr; ChannelData *_channel = nullptr; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 00c654e3c..5dbedd23c 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -28,13 +28,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "boxes/contactsbox.h" #include "boxes/confirmbox.h" +#include "ui/buttons/icon_button.h" #include "observer_peer.h" MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll) , _inner(this, channel, filter) { ItemListBox::init(_inner); - connect(_inner, SIGNAL(addRequested()), this, SLOT(onAdd())); + if (channel->amCreator() && (channel->membersCount() < (channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!channel->isMegagroup() && !channel->isPublic()) || filter == MembersFilter::Admins)) { + _add.create(this, st::contactsAdd); + _add->setClickedCallback([this] { onAdd(); }); + } connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); connect(_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int))); @@ -69,6 +73,10 @@ void MembersBox::paintEvent(QPaintEvent *e) { void MembersBox::resizeEvent(QResizeEvent *e) { ItemListBox::resizeEvent(e); _inner->resize(width(), _inner->height()); + + if (_add) { + _add->moveToRight(st::contactsAddPosition.x(), height() - st::contactsAddPosition.y() - _add->height()); + } } void MembersBox::onScroll() { @@ -99,8 +107,6 @@ void MembersBox::onAdminAdded() { MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter filter) : TWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _newItemHeight((channel->amCreator() && (channel->membersCount() < (channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!channel->isMegagroup() && !channel->isPublic()) || filter == MembersFilter::Admins)) ? st::contactsNewItemHeight : 0) -, _newItemSel(false) , _channel(channel) , _filter(filter) , _kickText(lang(lng_profile_kick)) @@ -148,17 +154,6 @@ void MembersBox::Inner::paintEvent(QPaintEvent *e) { p.setPen(st::noContactsColor); p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); } else { - if (_newItemHeight) { - p.fillRect(0, 0, width(), _newItemHeight, _newItemSel ? st::contactsBgOver : st::contactsBg); - st::contactsNewItemIcon.paint(p, 0, 0, width()); - p.setFont(st::contactsNameFont); - p.setPen(st::contactsNewItemFg); - p.drawTextLeft(st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(_filter == MembersFilter::Admins ? lng_channel_add_admins : lng_channel_add_members)); - - yFrom -= _newItemHeight; - yTo -= _newItemHeight; - p.translate(0, _newItemHeight); - } int32 from = floorclamp(yFrom, _rowHeight, 0, _rows.size()); int32 to = ceilclamp(yTo, _rowHeight, 0, _rows.size()); p.translate(0, from * _rowHeight); @@ -271,19 +266,16 @@ void MembersBox::Inner::selectSkip(int32 dir) { _mouseSel = false; int cur = -1; - if (_newItemHeight && _newItemSel) { - cur = 0; - } else if (_sel >= 0) { - cur = _sel + (_newItemHeight ? 1 : 0); + if (_sel >= 0) { + cur = _sel; } cur += dir; if (cur <= 0) { - _newItemSel = _newItemHeight ? true : false; - _sel = (_newItemSel || _rows.isEmpty()) ? -1 : 0; - } else if (cur >= _rows.size() + (_newItemHeight ? 1 : 0)) { + _sel = _rows.isEmpty() ? -1 : 0; + } else if (cur >= _rows.size()) { _sel = -1; } else { - _sel = cur - (_newItemHeight ? 1 : 0); + _sel = cur; } if (dir > 0) { if (_sel < 0 || _sel >= _rows.size()) { @@ -291,13 +283,11 @@ void MembersBox::Inner::selectSkip(int32 dir) { } } else { if (!_rows.isEmpty()) { - if (_sel < 0 && !_newItemSel) _sel = _rows.size() - 1; + if (_sel < 0) _sel = _rows.size() - 1; } } - if (_newItemSel) { - emit mustScrollTo(0, _newItemHeight); - } else if (_sel >= 0) { - emit mustScrollTo(_newItemHeight + _sel * _rowHeight, _newItemHeight + (_sel + 1) * _rowHeight); + if (_sel >= 0) { + emit mustScrollTo(_sel * _rowHeight, (_sel + 1) * _rowHeight); } update(); @@ -318,10 +308,10 @@ void MembersBox::Inner::loadProfilePhotos(int32 yFrom) { if (yFrom < 0) yFrom = 0; if (!_rows.isEmpty()) { - int32 from = (yFrom - _newItemHeight) / _rowHeight; + int32 from = yFrom / _rowHeight; if (from < 0) from = 0; if (from < _rows.size()) { - int32 to = ((yTo - _newItemHeight) / _rowHeight) + 1; + int32 to = (yTo / _rowHeight) + 1; if (to > _rows.size()) to = _rows.size(); for (; from < to; ++from) { @@ -332,10 +322,6 @@ void MembersBox::Inner::loadProfilePhotos(int32 yFrom) { } void MembersBox::Inner::chooseParticipant() { - if (_newItemSel) { - emit addRequested(); - return; - } if (_sel < 0 || _sel >= _rows.size()) return; if (PeerData *peer = _rows[_sel]) { Ui::hideLayer(); @@ -353,7 +339,7 @@ void MembersBox::Inner::refresh() { if (_filter != MembersFilter::Recent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) { _aboutHeight = 0; } - resize(width(), st::membersPadding.top() + _newItemHeight + _rows.size() * _rowHeight + st::membersPadding.bottom() + _aboutHeight); + resize(width(), st::membersPadding.top() + _rows.size() * _rowHeight + st::membersPadding.bottom() + _aboutHeight); } update(); } @@ -378,7 +364,6 @@ MembersAlreadyIn MembersBox::Inner::already() const { void MembersBox::Inner::clearSel() { updateSelectedRow(); - _newItemSel = false; _sel = _kickSel = _kickDown = -1; _lastMousePos = QCursor::pos(); updateSel(); @@ -425,15 +410,13 @@ void MembersBox::Inner::updateSel() { QPoint p(mapFromGlobal(_lastMousePos)); p.setY(p.y() - st::membersPadding.top()); bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); - bool newItemSel = (in && p.y() >= 0 && p.y() < _newItemHeight); - int32 newSel = (in && !newItemSel && p.y() >= _newItemHeight && p.y() < _newItemHeight + _rows.size() * _rowHeight) ? ((p.y() - _newItemHeight) / _rowHeight) : -1; + int32 newSel = (in && p.y() >= 0 && p.y() < _rows.size() * _rowHeight) ? (p.y() / _rowHeight) : -1; int32 newKickSel = newSel; - if (newSel >= 0 && (!data(newSel)->canKick || !QRect(width() - _kickWidth - st::contactsPadding.right() - st::contactsCheckPosition.x(), _newItemHeight + newSel * _rowHeight + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) { + if (newSel >= 0 && (!data(newSel)->canKick || !QRect(width() - _kickWidth - st::contactsPadding.right() - st::contactsCheckPosition.x(), newSel * _rowHeight + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) { newKickSel = -1; } - if (newSel != _sel || newKickSel != _kickSel || newItemSel != _newItemSel) { + if (newSel != _sel || newKickSel != _kickSel) { updateSelectedRow(); - _newItemSel = newItemSel; _sel = newSel; _kickSel = newKickSel; updateSelectedRow(); @@ -446,11 +429,8 @@ void MembersBox::Inner::peerUpdated(PeerData *peer) { } void MembersBox::Inner::updateSelectedRow() { - if (_newItemSel) { - update(0, st::membersPadding.top(), width(), _newItemHeight); - } if (_sel >= 0) { - update(0, st::membersPadding.top() + _newItemHeight + _sel * _rowHeight, width(), _rowHeight); + update(0, st::membersPadding.top() + _sel * _rowHeight, width(), _rowHeight); } } diff --git a/Telegram/SourceFiles/boxes/members_box.h b/Telegram/SourceFiles/boxes/members_box.h index a9639610f..b2bdffbdd 100644 --- a/Telegram/SourceFiles/boxes/members_box.h +++ b/Telegram/SourceFiles/boxes/members_box.h @@ -42,7 +42,6 @@ public: public slots: void onScroll(); - void onAdd(); void onAdminAdded(); protected: @@ -51,8 +50,11 @@ protected: void resizeEvent(QResizeEvent *e) override; private: + void onAdd(); + class Inner; ChildWidget _inner; + ChildWidget _add = { nullptr }; ContactsBox *_addBox = nullptr; @@ -89,7 +91,6 @@ public: signals: void mustScrollTo(int ymin, int ymax); - void addRequested(); void loaded(); public slots: @@ -132,8 +133,7 @@ private: void clear(); - int32 _rowHeight, _newItemHeight; - bool _newItemSel; + int _rowHeight; ChannelData *_channel; MembersFilter _filter; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 0afb5405b..9b9bba03d 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019003ULL) +#define BETA_VERSION_MACRO (10019004ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index e5c4a6a06..961d89d65 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -24,19 +24,16 @@ using "ui/widgets/widgets.style"; historyPaddingBottom: 10px; -historyToDown: icon { - { "history_down_shadow", #00000040 }, - { "history_down_circle", #ffffff, point(2px, 1px) }, -}; historyToDownPosition: point(12px, 10px); historyToDownArrow: icon { - { "history_down_arrow", #b9b9b9, point(14px, 19px) }, + { "history_down_arrow", #b9b9b9, point(17px, 23px) }, }; historyToDownPaddingTop: 10px; historyToDownBadgeFont: semiboldFont; historyToDownBadgeSize: 22px; historyToDownShownAfter: 480px; +historyToDownDuration: 150; historyEmptyDog: icon {{ "history_empty_dog", #ffffff }}; historyEmptySize: 128px; @@ -177,8 +174,8 @@ historyComposeButton: flatButton { overTextTop: 12px; downTextTop: 13px; - font: font(16px); - overFont: font(16px); + font: semiboldFont; + overFont: semiboldFont; } historyUnblock: flatButton(historyComposeButton) { color: #d15948; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 7d1b2e133..efa04d70d 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -4231,7 +4231,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _canSendMessages = canSendMessages(_peer); if (_peer && _peer->isChannel()) { _peer->asChannel()->updateFull(); - _joinChannel->setText(lang(_peer->isMegagroup() ? lng_group_invite_join : lng_channel_join)); + _joinChannel->setText(lang(_peer->isMegagroup() ? lng_group_invite_join : lng_channel_join).toUpper()); } _unblockRequest = _reportSpamRequest = 0; @@ -4371,7 +4371,7 @@ void HistoryWidget::updateFieldSubmitSettings() { void HistoryWidget::updateNotifySettings() { if (!_peer || !_peer->isChannel()) return; - _muteUnmute->setText(lang(_history->mute() ? lng_channel_unmute : lng_channel_mute)); + _muteUnmute->setText(lang(_history->mute() ? lng_channel_unmute : lng_channel_mute).toUpper()); if (_peer->notify != UnknownNotifySettings) { _silent->setChecked(_peer->notify != EmptyNotifySettings && (_peer->notify->flags & MTPDpeerNotifySettings::Flag::f_silent)); if (_silent->isHidden() && hasSilentToggle()) { diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp index a2971788e..63a9674a7 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp @@ -86,14 +86,13 @@ void ScaleWidget::setScale(DBIScale newScale) { } else if (newScale != dbisAuto && _auto->checked()) { _auto->setChecked(false); } + _newScale = newScale; if (newScale == dbisAuto) newScale = cScreenScale(); if (_scale->activeSection() != newScale - 1) { _scale->setActiveSection(newScale - 1); } if (cEvalScale(newScale) != cEvalScale(cRealScale())) { - _newScale = newScale; - auto box = new ConfirmBox(lang(lng_settings_need_restart), lang(lng_settings_restart_now), st::defaultBoxButton, lang(lng_cancel)); connect(box, SIGNAL(confirmed()), this, SLOT(onRestartNow())); connect(box, SIGNAL(cancelled()), this, SLOT(onCancel())); diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 4bb43c4d2..f52735d6b 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -1191,14 +1191,14 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { ClickHandlerPtr activated = ClickHandler::unpressed(); - _lastMousePos = e->globalPos(); - updateSelected(); - if (_previewShown) { _previewShown = false; return; } + _lastMousePos = e->globalPos(); + updateSelected(); + if (showingInlineItems()) { if (_selected < 0 || _selected != pressed || !activated) { return; diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 752abc675..d2616bb60 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -51,7 +51,7 @@ stickersFeaturedFont: contactsNameFont; stickersFeaturedPosition: point(16px, 6px); stickersFeaturedBadgeFont: semiboldFont; stickersFeaturedBadgeSize: 21px; -stickersFeaturedPen: contactsNewItemFg; +stickersFeaturedPen: lightButtonFg; stickersFeaturedUnreadBg: msgFileInBg; stickersFeaturedUnreadSize: 5px; stickersFeaturedUnreadSkip: 5px; diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp index 832f89040..9eab4635d 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp @@ -114,7 +114,7 @@ void HistoryDownButton::hideAnimated() { void HistoryDownButton::toggleAnimated() { _shown = !_shown; float64 from = _shown ? 0. : 1., to = _shown ? 1. : 0.; - _a_show.start([this] { update(); }, from, to, st::historyAttachEmoji.duration); + _a_show.start([this] { update(); }, from, to, st::historyToDownDuration); } void HistoryDownButton::finishAnimation() { diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index 11b614bc3..467d7ce48 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -112,24 +112,20 @@ void InnerDropdown::paintEvent(QPaintEvent *e) { if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) { p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity)); } - return; } else if (_a_opacity.animating(ms)) { p.setOpacity(_a_opacity.current(0.)); p.drawPixmap(0, 0, _cache); - return; } else if (_hiding || isHidden()) { hideFinished(); - return; } else if (_showAnimation) { p.drawImage(0, 0, _showAnimation->getFrame(1., 1.)); _showAnimation.reset(); showChildren(); - return; + } else { + auto inner = rect().marginsRemoved(_st.padding); + Shadow::paint(p, inner, width(), _st.shadow); + App::roundRect(p, inner, _st.bg, ImageRoundRadius::Small); } - - auto inner = rect().marginsRemoved(_st.padding); - Shadow::paint(p, inner, width(), _st.shadow); - App::roundRect(p, inner, _st.bg, ImageRoundRadius::Small); } void InnerDropdown::enterEvent(QEvent *e) { @@ -269,9 +265,13 @@ QImage InnerDropdown::grabForPanelAnimation() { void InnerDropdown::opacityAnimationCallback() { update(); - if (_hiding && !_a_opacity.animating()) { - _hiding = false; - hideFinished(); + if (!_a_opacity.animating()) { + if (_hiding) { + _hiding = false; + hideFinished(); + } else { + showChildren(); + } } } diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index d4c7c0364..f803a8c60 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -349,7 +349,7 @@ void MultiSelect::Inner::Item::setVisibleAnimated(bool visible) { prepareCache(); auto from = visible ? 0. : 1.; auto to = visible ? 1. : 0.; - auto transition = visible ? anim::bumpy(1.125) : anim::linear; + auto transition = visible ? anim::bumpy(1.0625) : anim::linear; _visibility.start(_updateCallback, from, to, _st.duration, transition); } diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index 6f9985e9a..1217c08c5 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -361,9 +361,13 @@ void PopupMenu::startShowAnimation() { void PopupMenu::opacityAnimationCallback() { update(); - if (_hiding && !_a_opacity.animating()) { - _hiding = false; - hideFinished(); + if (!_a_opacity.animating()) { + if (_hiding) { + _hiding = false; + hideFinished(); + } else { + showChildren(); + } } } diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index db60dba29..85ad525d0 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -323,3 +323,19 @@ defaultDropdownMenu: DropdownMenu { } menu: defaultMenu; } + +historyToDown: icon { + { "history_down_shadow", #00000040 }, + { "history_down_circle", #ffffff, point(4px, 4px) }, +}; + +contactsAddIcon: icon { + { "history_down_shadow", #00000020 }, + { "history_down_circle", activeButtonBg, point(4px, 4px) }, + { "contacts_add", activeButtonFg, point(18px, 18px) }, +}; +contactsAddIconOver: icon { + { "history_down_shadow", #00000020 }, + { "history_down_circle", activeButtonBgOver, point(4px, 4px) }, + { "contacts_add", activeButtonFg, point(18px, 18px) }, +}; diff --git a/Telegram/build/version b/Telegram/build/version index 577f8e9c5..eb7121d34 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019003 +BetaVersion 10019004 From 27ff8d4bf5b8e97a9852b4b1ebecb8acfff09b9f Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 10 Nov 2016 22:03:56 +0300 Subject: [PATCH 031/100] Closed beta 10019004: improved new dropdown animation on macOS. --- Telegram/SourceFiles/ui/widgets/popup_menu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index 1217c08c5..cba26c3df 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -455,13 +455,13 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou setOrigin(origin); _menu->setShowSource(source); + startShowAnimation(); + psUpdateOverlayed(this); show(); psShowOverAll(this); windowHandle()->requestActivate(); activateWindow(); - - startShowAnimation(); } PopupMenu::~PopupMenu() { From bd2be4e0c1ab82aa19b6bff17ece78fcbee30988 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Nov 2016 10:51:53 +0300 Subject: [PATCH 032/100] Build fixed for Linux, checking for compositing manager running. Backported QX11Info::isCompositingManagerRunning from Qt 5.7. --- Telegram/Patches/qtbase_5_6_2.diff | 46 +++++++++++++++++++ .../platform/linux/main_window_linux.cpp | 16 ++++--- .../platform/linux/main_window_linux.h | 2 + Telegram/SourceFiles/pspecific.h | 1 + Telegram/SourceFiles/pspecific_linux.cpp | 27 +++++++++++ Telegram/SourceFiles/pspecific_mac.cpp | 4 ++ Telegram/SourceFiles/pspecific_win.cpp | 4 ++ .../SourceFiles/ui/widgets/popup_menu.cpp | 18 +++----- Telegram/SourceFiles/ui/widgets/popup_menu.h | 2 +- 9 files changed, 102 insertions(+), 18 deletions(-) diff --git a/Telegram/Patches/qtbase_5_6_2.diff b/Telegram/Patches/qtbase_5_6_2.diff index aeb46ca3e..076b22bbb 100644 --- a/Telegram/Patches/qtbase_5_6_2.diff +++ b/Telegram/Patches/qtbase_5_6_2.diff @@ -11896,6 +11896,52 @@ index 6fffa1e..cb1c9c1 100644 void destroyWindow(); inline bool isDropSiteEnabled() const { return m_dropTarget != 0; } void setDropSiteEnabled(bool enabled); +diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +index 09e7ecf..c0f15a4 100644 +--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp ++++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +@@ -79,7 +79,10 @@ static int resourceType(const QByteArray &key) + QByteArrayLiteral("rootwindow"), + QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"), + QByteArrayLiteral("nofonthinting"), +- QByteArrayLiteral("atspibus") ++ QByteArrayLiteral("atspibus"), ++ ++ // Patch: Backport compositing manager check from Qt 5.7 ++ QByteArrayLiteral("compositingenabled") + }; + const QByteArray *end = names + sizeof(names) / sizeof(names[0]); + const QByteArray *result = std::find(names, end, key); +@@ -252,6 +255,13 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr + case RootWindow: + result = reinterpret_cast(xcbScreen->root()); + break; ++ ++ // Patch: Backport compositing manager check from Qt 5.7 ++ case CompositingEnabled: ++ if (QXcbVirtualDesktop *vd = xcbScreen->virtualDesktop()) ++ result = vd->compositingActive() ? this : Q_NULLPTR; ++ break; ++ + default: + break; + } +diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h +index f88b710..6f818a5 100644 +--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h ++++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h +@@ -68,7 +68,10 @@ public: + ScreenSubpixelType, + ScreenAntialiasingEnabled, + NoFontHinting, +- AtspiBus ++ AtspiBus, ++ ++ // Patch: Backport compositing manager check from Qt 5.7 ++ CompositingEnabled + }; + + QXcbNativeInterface(); diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index bc2de89..aa8f8df 100644 --- a/src/widgets/dialogs/qfiledialog.cpp diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 208b4d3d5..01fe867b6 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -61,7 +61,7 @@ void _trayIconActivate(GtkStatusIcon *status_icon, gpointer popup_menu) { gboolean _trayIconResized(GtkStatusIcon *status_icon, gint size, gpointer popup_menu) { _trayIconSize = size; - if (App::wnd()) App::wnd()->psUpdateCounter(); + if (Global::started()) Notify::unreadCounterUpdated(); return FALSE; } @@ -166,7 +166,7 @@ static gboolean _trayIconCheck(gpointer/* pIn*/) { cSetSupportTray(true); if (App::wnd()) { App::wnd()->psUpdateWorkmode(); - App::wnd()->psUpdateCounter(); + Notify::unreadCounterUpdated(); App::wnd()->updateTrayMenu(); } } @@ -239,7 +239,7 @@ void MainWindow::psTrayMenuUpdated() { void MainWindow::psSetupTrayIcon() { if (noQtTrayIcon) { if (!cSupportTray()) return; - psUpdateCounter(); + updateIconCounters(); } else { LOG(("Using Qt tray icon.")); if (!trayIcon) { @@ -266,7 +266,7 @@ void MainWindow::psSetupTrayIcon() { App::wnd()->updateTrayMenu(); } - psUpdateCounter(); + updateIconCounters(); trayIcon->show(); } @@ -315,12 +315,16 @@ void MainWindow::psUpdateIndicator() { } } -void MainWindow::psUpdateCounter() { +void MainWindow::unreadCounterChangedHook() { + setWindowTitle(titleText()); + updateIconCounters(); +} + +void MainWindow::updateIconCounters() { setWindowIcon(wndIcon); int32 counter = App::histories().unreadBadge(); - setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); #ifndef TDESKTOP_DISABLE_UNITY_INTEGRATION if (_psUnityLauncherEntry) { if (counter > 0) { diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 824005b5c..95fc18b69 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -74,6 +74,7 @@ protected: virtual void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) = 0; private: + void updateIconCounters(); void psCreateTrayIcon(); QTimer _psCheckStatusIconTimer; @@ -81,6 +82,7 @@ private: QTimer _psUpdateIndicatorTimer; uint64 _psLastIndicatorUpdate = 0; + }; } // namespace Platform diff --git a/Telegram/SourceFiles/pspecific.h b/Telegram/SourceFiles/pspecific.h index 961515c98..812ae842d 100644 --- a/Telegram/SourceFiles/pspecific.h +++ b/Telegram/SourceFiles/pspecific.h @@ -36,6 +36,7 @@ void start(); void finish(); void SetWatchingMediaKeys(bool watching); +bool TransparentWindowsSupported(QPoint globalPosition); namespace ThirdParty { diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp index 488160a87..84ed14603 100644 --- a/Telegram/SourceFiles/pspecific_linux.cpp +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -34,6 +34,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include +#include + using namespace Platform; namespace { @@ -409,6 +411,31 @@ void finish() { void SetWatchingMediaKeys(bool watching) { } +bool TransparentWindowsSupported(QPoint globalPosition) { + if (auto app = static_cast(QCoreApplication::instance())) { + if (auto native = app->platformNativeInterface()) { + if (auto desktop = QApplication::desktop()) { + auto index = desktop->screenNumber(globalPosition); + auto screens = QGuiApplication::screens(); + if (auto screen = (index >= 0 && index < screens.size()) ? screens[index] : QGuiApplication::primaryScreen()) { + if (native->nativeResourceForScreen(QByteArray("compositingEnabled"), screen)) { + return true; + } + + static OrderedSet WarnedAbout; + if (!WarnedAbout.contains(index)) { + WarnedAbout.insert(index); + LOG(("WARNING: Compositing is disabled for screen index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); + } + } else { + LOG(("WARNING: Could not get screen for index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); + } + } + } + } + return false; +} + namespace ThirdParty { void start() { diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index d9029e508..e680442bf 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -405,6 +405,10 @@ void finish() { objc_finish(); } +bool TransparentWindowsSupported(QPoint globalPosition) { + return true; +} + namespace ThirdParty { void start() { diff --git a/Telegram/SourceFiles/pspecific_win.cpp b/Telegram/SourceFiles/pspecific_win.cpp index 7558b4c5a..0c13c4787 100644 --- a/Telegram/SourceFiles/pspecific_win.cpp +++ b/Telegram/SourceFiles/pspecific_win.cpp @@ -732,6 +732,10 @@ void finish() { void SetWatchingMediaKeys(bool watching) { } +bool TransparentWindowsSupported(QPoint globalPosition) { + return true; +} + namespace ThirdParty { void start() { diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index cba26c3df..ccb7420a2 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -68,7 +68,7 @@ void PopupMenu::init() { } void PopupMenu::handleCompositingUpdate() { - _padding = _compositing ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); + _padding = _useTransaprency ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); _menu->moveToLeft(_padding.left() + _st.scrollPadding.left(), _padding.top() + _st.scrollPadding.top()); handleMenuResize(); } @@ -126,7 +126,7 @@ void PopupMenu::paintEvent(QPaintEvent *e) { } void PopupMenu::paintBg(Painter &p) { - if (_compositing) { + if (_useTransaprency) { Shadow::paint(p, _inner, width(), _st.shadow); App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small); } else { @@ -307,7 +307,7 @@ void PopupMenu::prepareCache() { void PopupMenu::startOpacityAnimation(bool hiding) { _hiding = false; - if (!_compositing) { + if (!_useTransaprency) { _a_opacity.finish(); if (hiding) { hideFinished(); @@ -334,7 +334,7 @@ void PopupMenu::showStarted() { } void PopupMenu::startShowAnimation() { - if (!_compositing) { + if (!_useTransaprency) { _a_show.finish(); update(); return; @@ -347,7 +347,7 @@ void PopupMenu::startShowAnimation() { _showAnimation = std_::make_unique(_st.animation, _origin); _showAnimation->setFinalImage(std_::move(cache), _inner); - if (_compositing) { + if (_useTransaprency) { auto corners = App::cornersMask(ImageRoundRadius::Small); _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); } else { @@ -382,7 +382,7 @@ QImage PopupMenu::grabForPanelAnimation() { result.fill(Qt::transparent); { Painter p(&result); - if (_compositing) { + if (_useTransaprency) { App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small); } else { p.fillRect(_inner, _st.menu.itemBg); @@ -410,11 +410,7 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou auto origin = PanelAnimation::Origin::TopLeft; auto w = p - QPoint(0, _padding.top()); auto r = Sandbox::screenGeometry(p); -#ifdef Q_OS_LINUX - _compositing = QX11Info::isCompositingManagerRunning(QApplication::desktop()->screenNumber(p)); -#else // Q_OS_LINUX - _compositing = true; -#endif // Q_OS_LINUX + _useTransaprency = Platform::TransparentWindowsSupported(p); handleCompositingUpdate(); if (rtl()) { if (w.x() - width() < r.x() - _padding.left()) { diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.h b/Telegram/SourceFiles/ui/widgets/popup_menu.h index b53255376..eb7a0b338 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.h +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.h @@ -117,7 +117,7 @@ private: std_::unique_ptr _showAnimation; FloatAnimation _a_show; - bool _compositing = true; + bool _useTransaprency = true; bool _hiding = false; QPixmap _cache; FloatAnimation _a_opacity; From 55b1ba128d6a2af86f90238348b775f3e7a55e9a Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Nov 2016 11:59:55 +0300 Subject: [PATCH 033/100] Fixed PanelAnimation for Retina, added 1px padding to emoji. --- Telegram/Resources/basic.style | 2 +- Telegram/SourceFiles/historywidget.cpp | 4 +- .../ui/effects/panel_animation.cpp | 30 ++++----- .../SourceFiles/ui/widgets/inner_dropdown.cpp | 3 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 16 ++--- Telegram/SourceFiles/ui/widgets/popup_menu.h | 2 +- .../SourceFiles/window/slide_animation.cpp | 64 ++++++------------- Telegram/SourceFiles/window/slide_animation.h | 8 +-- .../SourceFiles/window/top_bar_widget.cpp | 3 + 9 files changed, 55 insertions(+), 77 deletions(-) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 1a358f229..13f2e1c65 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -30,7 +30,7 @@ semiboldFont: font(fsize semibold); emojiImgSize: 18px; // exceptional value for retina emojiSize: 18px; -emojiPadding: 0px; +emojiPadding: 1px; lineWidth: 1px; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index efa04d70d..9e0332156 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -4219,7 +4219,6 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re if (_list) _list->deleteLater(); _list = nullptr; _scroll.takeWidget(); - updateTopBarSelection(); clearInlineBot(); @@ -4229,6 +4228,9 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _peer = peerId ? App::peer(peerId) : nullptr; _channel = _peer ? peerToChannel(_peer->id) : NoChannel; _canSendMessages = canSendMessages(_peer); + + updateTopBarSelection(); + if (_peer && _peer->isChannel()) { _peer->asChannel()->updateFull(); _joinChannel->setText(lang(_peer->isMegagroup() ? lng_group_invite_join : lng_channel_join).toUpper()); diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.cpp b/Telegram/SourceFiles/ui/effects/panel_animation.cpp index ef783f5ff..c41b3bdba 100644 --- a/Telegram/SourceFiles/ui/effects/panel_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/panel_animation.cpp @@ -30,6 +30,13 @@ void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) { t_assert(!_finalImage.isNull()); _finalWidth = _finalImage.width(); _finalHeight = _finalImage.height(); + _finalInnerLeft = inner.x(); + _finalInnerTop = inner.y(); + _finalInnerWidth = inner.width(); + _finalInnerHeight = inner.height(); + _finalInnerRight = _finalInnerLeft + _finalInnerWidth; + _finalInnerBottom = _finalInnerTop + _finalInnerHeight; + t_assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner)); setStartWidth(); setStartHeight(); @@ -59,20 +66,12 @@ void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) { t_assert(_finalImage.depth() == static_cast(sizeof(uint32) << 3)); t_assert(_finalImage.bytesPerLine() == (_finalIntsPerLine << 2)); t_assert(_finalIntsPerLineAdded >= 0); - - _finalInnerLeft = inner.x(); - _finalInnerTop = inner.y(); - _finalInnerWidth = inner.width(); - _finalInnerHeight = inner.height(); - _finalInnerRight = _finalInnerLeft + _finalInnerWidth; - _finalInnerBottom = _finalInnerTop + _finalInnerHeight; - t_assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner)); } void PanelAnimation::setShadow() { if (_skipShadow) return; - _shadow.extend = _st.shadow.extend; + _shadow.extend = _st.shadow.extend * cIntRetinaFactor(); _shadow.left = cloneImage(_st.shadow.left); if (_shadow.valid()) { _shadow.topLeft = cloneImage(_st.shadow.topLeft); @@ -101,13 +100,13 @@ void PanelAnimation::setShadow() { } void PanelAnimation::setStartWidth() { - _startWidth = qRound(_st.startWidth * _finalImage.width()); - if (_startWidth >= 0) t_assert(_startWidth <= _finalWidth); + _startWidth = qRound(_st.startWidth * _finalInnerWidth); + if (_startWidth >= 0) t_assert(_startWidth <= _finalInnerWidth); } void PanelAnimation::setStartHeight() { - _startHeight = qRound(_st.startHeight * _finalImage.height()); - if (_startHeight >= 0) t_assert(_startHeight <= _finalHeight); + _startHeight = qRound(_st.startHeight * _finalInnerHeight); + if (_startHeight >= 0) t_assert(_startHeight <= _finalInnerHeight); } void PanelAnimation::setStartAlpha() { @@ -116,7 +115,7 @@ void PanelAnimation::setStartAlpha() { } void PanelAnimation::setStartFadeTop() { - _startFadeTop = qRound(_st.startFadeTop * _finalImage.height()); + _startFadeTop = qRound(_st.startFadeTop * _finalInnerHeight); } void PanelAnimation::createFadeMask() { @@ -177,7 +176,8 @@ void PanelAnimation::setCornerMask(Corner &corner, QImage &&image) { QImage PanelAnimation::cloneImage(const style::icon &source) { if (source.empty()) return QImage(); - auto result = QImage(source.width(), source.height(), QImage::Format_ARGB32_Premultiplied); + auto result = QImage(source.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); result.fill(Qt::transparent); { Painter p(&result); diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index 467d7ce48..bc0c2d450 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -237,7 +237,8 @@ void InnerDropdown::startShowAnimation() { _a_opacity = base::take(opacityAnimation); _showAnimation = std_::make_unique(_st.animation, _origin); - _showAnimation->setFinalImage(std_::move(cache), rect().marginsRemoved(_st.padding)); + auto inner = rect().marginsRemoved(_st.padding); + _showAnimation->setFinalImage(std_::move(cache), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); auto corners = App::cornersMask(ImageRoundRadius::Small); _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); _showAnimation->start(); diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index ccb7420a2..91772619f 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -68,7 +68,7 @@ void PopupMenu::init() { } void PopupMenu::handleCompositingUpdate() { - _padding = _useTransaprency ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); + _padding = _useTransparency ? _st.shadow.extend : style::margins(st::lineWidth, st::lineWidth, st::lineWidth, st::lineWidth); _menu->moveToLeft(_padding.left() + _st.scrollPadding.left(), _padding.top() + _st.scrollPadding.top()); handleMenuResize(); } @@ -126,7 +126,7 @@ void PopupMenu::paintEvent(QPaintEvent *e) { } void PopupMenu::paintBg(Painter &p) { - if (_useTransaprency) { + if (_useTransparency) { Shadow::paint(p, _inner, width(), _st.shadow); App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small); } else { @@ -307,7 +307,7 @@ void PopupMenu::prepareCache() { void PopupMenu::startOpacityAnimation(bool hiding) { _hiding = false; - if (!_useTransaprency) { + if (!_useTransparency) { _a_opacity.finish(); if (hiding) { hideFinished(); @@ -334,7 +334,7 @@ void PopupMenu::showStarted() { } void PopupMenu::startShowAnimation() { - if (!_useTransaprency) { + if (!_useTransparency) { _a_show.finish(); update(); return; @@ -346,8 +346,8 @@ void PopupMenu::startShowAnimation() { _a_opacity = base::take(opacityAnimation); _showAnimation = std_::make_unique(_st.animation, _origin); - _showAnimation->setFinalImage(std_::move(cache), _inner); - if (_useTransaprency) { + _showAnimation->setFinalImage(std_::move(cache), QRect(_inner.topLeft() * cIntRetinaFactor(), _inner.size() * cIntRetinaFactor())); + if (_useTransparency) { auto corners = App::cornersMask(ImageRoundRadius::Small); _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); } else { @@ -382,7 +382,7 @@ QImage PopupMenu::grabForPanelAnimation() { result.fill(Qt::transparent); { Painter p(&result); - if (_useTransaprency) { + if (_useTransparency) { App::roundRect(p, _inner, _st.menu.itemBg, ImageRoundRadius::Small); } else { p.fillRect(_inner, _st.menu.itemBg); @@ -410,7 +410,7 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource sou auto origin = PanelAnimation::Origin::TopLeft; auto w = p - QPoint(0, _padding.top()); auto r = Sandbox::screenGeometry(p); - _useTransaprency = Platform::TransparentWindowsSupported(p); + _useTransparency = Platform::TransparentWindowsSupported(p); handleCompositingUpdate(); if (rtl()) { if (w.x() - width() < r.x() - _padding.left()) { diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.h b/Telegram/SourceFiles/ui/widgets/popup_menu.h index eb7a0b338..0eb1e7e1c 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.h +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.h @@ -117,7 +117,7 @@ private: std_::unique_ptr _showAnimation; FloatAnimation _a_show; - bool _useTransaprency = true; + bool _useTransparency = true; bool _hiding = false; QPixmap _cache; FloatAnimation _a_opacity; diff --git a/Telegram/SourceFiles/window/slide_animation.cpp b/Telegram/SourceFiles/window/slide_animation.cpp index b6b2286ac..f8e27c174 100644 --- a/Telegram/SourceFiles/window/slide_animation.cpp +++ b/Telegram/SourceFiles/window/slide_animation.cpp @@ -23,23 +23,21 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Window { -SlideAnimation::SlideAnimation() - : _animation(animation(this, &SlideAnimation::step)) { -} - void SlideAnimation::paintContents(Painter &p, const QRect &update) const { int retina = cIntRetinaFactor(); - _animation.step(getms()); - if (a_coordOver.current() > 0) { - p.drawPixmap(QRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, _cacheUnder.height())); - p.setOpacity(a_progress.current()); - p.fillRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina, st::slideFadeOutBg); + auto progress = _animation.current(getms()); + auto coordUnder = anim::interpolate(0, -st::slideShift, progress); + auto coordOver = anim::interpolate(_cacheOver.width() / cIntRetinaFactor(), 0, progress); + if (coordOver) { + p.drawPixmap(QRect(0, 0, coordOver, _cacheUnder.height() / retina), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, _cacheUnder.height())); + p.setOpacity(progress); + p.fillRect(0, 0, coordOver, _cacheUnder.height() / retina, st::slideFadeOutBg); p.setOpacity(1); } - p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, _cacheOver.height() / retina), _cacheOver, QRect(0, 0, _cacheOver.width(), _cacheOver.height())); - p.setOpacity(a_progress.current()); - st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), _cacheOver.height() / retina)); + p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, _cacheOver.height() / retina), _cacheOver, QRect(0, 0, _cacheOver.width(), _cacheOver.height())); + p.setOpacity(progress); + st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), _cacheOver.height() / retina)); if (_topBarShadowEnabled) { p.setOpacity(1); @@ -69,42 +67,20 @@ void SlideAnimation::setFinishedCallback(FinishedCallback &&callback) { } void SlideAnimation::start() { - int delta = st::slideShift; - if (_direction == SlideDirection::FromLeft) { - a_progress = anim::fvalue(1, 0); - std::swap(_cacheUnder, _cacheOver); - a_coordUnder = anim::ivalue(-delta, 0); - a_coordOver = anim::ivalue(0, _cacheOver.width() / cIntRetinaFactor()); - } else { - a_progress = anim::fvalue(0, 1); - a_coordUnder = anim::ivalue(0, -delta); - a_coordOver = anim::ivalue(_cacheOver.width() / cIntRetinaFactor(), 0); - } - _animation.start(); + auto delta = st::slideShift; + auto fromLeft = (_direction == SlideDirection::FromLeft); + if (fromLeft) std::swap(_cacheUnder, _cacheOver); + _animation.start([this] { animationCallback(); }, fromLeft ? 1. : 0., fromLeft ? 0. : 1., st::slideDuration, transition()); + _repaintCallback(); } -void SlideAnimation::step(float64 ms, bool timer) { - float64 dt = ms / st::slideDuration; - if (dt >= 1) { - dt = 1; - if (timer) { - _animation.stop(); - a_coordUnder.finish(); - a_coordOver.finish(); - - if (_finishedCallback) { - _finishedCallback(); - } - return; +void SlideAnimation::animationCallback() { + _repaintCallback(); + if (!_animation.animating()) { + if (_finishedCallback) { + _finishedCallback(); } } - - a_coordUnder.update(dt, transition()); - a_coordOver.update(dt, transition()); - a_progress.update(dt, transition()); - if (timer && _repaintCallback) { - _repaintCallback(); - } } } // namespace Window diff --git a/Telegram/SourceFiles/window/slide_animation.h b/Telegram/SourceFiles/window/slide_animation.h index ead4c85c2..12f9789db 100644 --- a/Telegram/SourceFiles/window/slide_animation.h +++ b/Telegram/SourceFiles/window/slide_animation.h @@ -29,8 +29,6 @@ enum class SlideDirection { class SlideAnimation { public: - SlideAnimation(); - void paintContents(Painter &p, const QRect &update) const; void setDirection(SlideDirection direction); @@ -50,15 +48,13 @@ public: } private: - void step(float64 ms, bool timer); + void animationCallback(); SlideDirection _direction = SlideDirection::FromRight; bool _topBarShadowEnabled = false; - mutable Animation _animation; + mutable FloatAnimation _animation; QPixmap _cacheUnder, _cacheOver; - anim::ivalue a_coordUnder, a_coordOver; - anim::fvalue a_progress; RepaintCallback _repaintCallback; FinishedCallback _finishedCallback; diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 4bcedc72d..1d5d68439 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -239,6 +239,7 @@ void TopBarWidget::startAnim() { _mediaType->hide(); _search->hide(); _menuToggle->hide(); + _menu.destroy(); if (_membersShowArea) { _membersShowArea->hide(); } @@ -282,6 +283,7 @@ void TopBarWidget::showAll() { _info->setPeer(h); _info->show(); _menuToggle->hide(); + _menu.destroy(); } else { _info->hide(); _menuToggle->show(); @@ -291,6 +293,7 @@ void TopBarWidget::showAll() { _search->hide(); _info->hide(); _menuToggle->hide(); + _menu.destroy(); } if (_membersShowArea) { _membersShowArea->show(); From d4d1d438be8a04c5281d8794ca1858cfcedd5a73 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Nov 2016 12:05:05 +0300 Subject: [PATCH 034/100] Closed beta 10019005: inline bot cancel button fixed. --- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 2 +- Telegram/SourceFiles/historywidget.cpp | 15 ++++++++++----- Telegram/build/version | 2 +- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 5b1c34e86..d757b76cd 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,4 - PRODUCTVERSION 0,10,19,4 + FILEVERSION 0,10,19,5 + PRODUCTVERSION 0,10,19,5 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.19.4" + VALUE "FileVersion", "0.10.19.5" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.4" + VALUE "ProductVersion", "0.10.19.5" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index a51fcdfcc..abf93f304 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,4 - PRODUCTVERSION 0,10,19,4 + FILEVERSION 0,10,19,5 + PRODUCTVERSION 0,10,19,5 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.19.4" + VALUE "FileVersion", "0.10.19.5" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.4" + VALUE "ProductVersion", "0.10.19.5" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 9b9bba03d..169174315 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019004ULL) +#define BETA_VERSION_MACRO (10019005ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 9e0332156..cceba28e3 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3274,8 +3274,13 @@ void HistoryWidget::onTextChange() { _send->hide(); updateMouseTracking(); mouseMoveEvent(0); - } else if (!_field->isHidden() && _send->isHidden()) { - _send->show(); + } else if (!_field->isHidden() && _send->isHidden() && (!_inlineBotCancel || _inlineBotCancel->isHidden())) { + if (_inlineBotCancel) { + _send->hide(); + _inlineBotCancel->show(); + } else { + _send->show(); + } updateMouseTracking(); _a_record.stop(); _inRecord = _inField = false; @@ -4717,7 +4722,7 @@ void HistoryWidget::updateControlsVisibility() { } void HistoryWidget::updateMouseTracking() { - bool trackMouse = !_fieldBarCancel->isHidden() || _pinnedBar || (cHasAudioCapture() && _send->isHidden() && !_field->isHidden()); + bool trackMouse = !_fieldBarCancel->isHidden() || _pinnedBar || (cHasAudioCapture() && _send->isHidden() && (!_inlineBotCancel || _inlineBotCancel->isHidden()) && !_field->isHidden()); setMouseTracking(trackMouse); } @@ -5542,7 +5547,7 @@ void HistoryWidget::animStop() { void HistoryWidget::step_record(float64 ms, bool timer) { float64 dt = ms / st::historyComposeButton.duration; - if (dt >= 1 || !_send->isHidden() || isBotStart() || isBlocked()) { + if (dt >= 1 || !_send->isHidden() || (_inlineBotCancel && !_inlineBotCancel->isHidden()) || isBotStart() || isBlocked()) { _a_record.stop(); a_recordDown.finish(); a_recordCancelActive.finish(); @@ -8748,7 +8753,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { if (_list) { if (!_field->isHidden() || _recording) { drawField(p, r); - if (_send->isHidden()) { + if (_send->isHidden() && (!_inlineBotCancel || _inlineBotCancel->isHidden())) { drawRecordButton(p); if (_recording) drawRecording(p); } diff --git a/Telegram/build/version b/Telegram/build/version index eb7121d34..42cb9bf9d 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019004 +BetaVersion 10019005 From 807bebb3cf029a4a3f064cd0145590bc8b33edd9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Nov 2016 16:46:04 +0300 Subject: [PATCH 035/100] Buttons moved to ui/widgets/buttons module, BoxButton removed. --- Telegram/Resources/basic.style | 78 ----- Telegram/Resources/basic_types.style | 28 -- Telegram/SourceFiles/boxes/aboutbox.cpp | 30 +- Telegram/SourceFiles/boxes/aboutbox.h | 13 +- Telegram/SourceFiles/boxes/abstractbox.cpp | 2 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 9 +- Telegram/SourceFiles/boxes/addcontactbox.h | 28 +- Telegram/SourceFiles/boxes/autolockbox.cpp | 11 +- Telegram/SourceFiles/boxes/autolockbox.h | 3 +- Telegram/SourceFiles/boxes/boxes.style | 31 ++ Telegram/SourceFiles/boxes/confirmbox.cpp | 5 +- Telegram/SourceFiles/boxes/confirmbox.h | 25 +- .../SourceFiles/boxes/confirmphonebox.cpp | 5 +- Telegram/SourceFiles/boxes/confirmphonebox.h | 8 +- Telegram/SourceFiles/boxes/connectionbox.cpp | 3 +- Telegram/SourceFiles/boxes/connectionbox.h | 9 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 4 +- Telegram/SourceFiles/boxes/contactsbox.h | 8 +- .../SourceFiles/boxes/downloadpathbox.cpp | 6 +- Telegram/SourceFiles/boxes/downloadpathbox.h | 8 +- Telegram/SourceFiles/boxes/languagebox.cpp | 11 +- Telegram/SourceFiles/boxes/languagebox.h | 3 +- .../SourceFiles/boxes/localstoragebox.cpp | 4 +- Telegram/SourceFiles/boxes/localstoragebox.h | 8 +- Telegram/SourceFiles/boxes/members_box.cpp | 8 +- .../SourceFiles/boxes/notifications_box.cpp | 3 +- .../SourceFiles/boxes/notifications_box.h | 7 +- Telegram/SourceFiles/boxes/passcodebox.cpp | 281 ++++++++-------- Telegram/SourceFiles/boxes/passcodebox.h | 22 +- Telegram/SourceFiles/boxes/photocropbox.cpp | 15 +- Telegram/SourceFiles/boxes/photocropbox.h | 7 +- Telegram/SourceFiles/boxes/photosendbox.cpp | 3 +- Telegram/SourceFiles/boxes/photosendbox.h | 9 +- Telegram/SourceFiles/boxes/report_box.cpp | 3 +- Telegram/SourceFiles/boxes/report_box.h | 3 +- Telegram/SourceFiles/boxes/sessionsbox.cpp | 34 +- Telegram/SourceFiles/boxes/sessionsbox.h | 6 +- Telegram/SourceFiles/boxes/sharebox.cpp | 1 + Telegram/SourceFiles/boxes/sharebox.h | 7 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 8 +- Telegram/SourceFiles/boxes/stickers_box.h | 5 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 63 ++-- Telegram/SourceFiles/boxes/stickersetbox.h | 6 +- Telegram/SourceFiles/boxes/usernamebox.cpp | 78 ++--- Telegram/SourceFiles/boxes/usernamebox.h | 12 +- Telegram/SourceFiles/dialogs/dialogs.style | 2 +- Telegram/SourceFiles/dialogswidget.cpp | 14 +- Telegram/SourceFiles/dialogswidget.h | 6 +- Telegram/SourceFiles/history/history.style | 39 ++- Telegram/SourceFiles/historywidget.cpp | 25 +- Telegram/SourceFiles/historywidget.h | 46 +-- Telegram/SourceFiles/intro/intro.style | 10 +- Telegram/SourceFiles/intro/introcode.cpp | 3 +- Telegram/SourceFiles/intro/introcode.h | 8 +- Telegram/SourceFiles/intro/introphone.cpp | 3 +- Telegram/SourceFiles/intro/introphone.h | 1 - Telegram/SourceFiles/intro/intropwdcheck.cpp | 5 +- Telegram/SourceFiles/intro/intropwdcheck.h | 10 +- Telegram/SourceFiles/intro/introsignup.cpp | 3 +- Telegram/SourceFiles/intro/introsignup.h | 1 - Telegram/SourceFiles/intro/introstart.cpp | 5 +- Telegram/SourceFiles/intro/introstart.h | 6 +- Telegram/SourceFiles/intro/introwidget.cpp | 2 +- Telegram/SourceFiles/intro/introwidget.h | 5 +- Telegram/SourceFiles/mainwidget.cpp | 2 +- Telegram/SourceFiles/mainwindow.cpp | 13 +- Telegram/SourceFiles/mainwindow.h | 6 +- .../media/player/media_player_button.h | 2 +- .../media/player/media_player_cover.cpp | 6 +- .../player/media_player_volume_controller.cpp | 2 +- .../media/player/media_player_widget.cpp | 6 +- .../media/view/media_clip_controller.cpp | 2 +- .../SourceFiles/media/view/mediaview.style | 2 +- Telegram/SourceFiles/mediaview.cpp | 41 +-- Telegram/SourceFiles/mediaview.h | 5 +- Telegram/SourceFiles/overviewwidget.cpp | 2 +- Telegram/SourceFiles/passcodewidget.cpp | 3 +- Telegram/SourceFiles/passcodewidget.h | 3 +- .../platform/win/window_title_win.cpp | 2 +- .../SourceFiles/profile/profile_cover.cpp | 6 +- Telegram/SourceFiles/profile/profile_cover.h | 4 +- .../SourceFiles/profile/profile_fixed_bar.cpp | 11 +- .../profile/profile_settings_widget.cpp | 2 +- .../profile/profile_userpic_button.cpp | 2 +- .../profile/profile_userpic_button.h | 3 +- .../settings/settings_advanced_widget.cpp | 1 + .../settings/settings_advanced_widget.h | 10 +- .../settings/settings_background_widget.cpp | 7 +- .../settings/settings_background_widget.h | 12 +- .../settings/settings_block_widget.cpp | 11 +- .../settings/settings_block_widget.h | 6 +- .../settings_chat_settings_widget.cpp | 10 +- .../settings/settings_chat_settings_widget.h | 12 +- .../SourceFiles/settings/settings_cover.cpp | 7 +- .../SourceFiles/settings/settings_cover.h | 4 +- .../settings/settings_fixed_bar.cpp | 2 +- .../settings/settings_general_widget.cpp | 8 +- .../settings/settings_general_widget.h | 14 +- .../settings_notifications_widget.cpp | 3 +- .../settings/settings_notifications_widget.h | 2 +- .../settings/settings_privacy_widget.cpp | 9 +- .../settings/settings_privacy_widget.h | 10 +- .../settings/settings_scale_widget.cpp | 2 +- Telegram/SourceFiles/stdafx.h | 1 - Telegram/SourceFiles/stickers/emoji_pan.cpp | 18 +- Telegram/SourceFiles/stickers/emoji_pan.h | 9 +- .../ui/{button.cpp => abstract_button.cpp} | 52 ++- .../ui/{button.h => abstract_button.h} | 35 +- .../ui/buttons/history_down_button.cpp | 10 +- .../ui/buttons/history_down_button.h | 10 +- .../SourceFiles/ui/buttons/icon_button.cpp | 124 ------- Telegram/SourceFiles/ui/buttons/icon_button.h | 65 ---- .../ui/buttons/left_outline_button.cpp | 6 +- .../ui/buttons/left_outline_button.h | 6 +- .../ui/buttons/peer_avatar_button.cpp | 2 +- .../ui/buttons/peer_avatar_button.h | 4 +- .../SourceFiles/ui/buttons/round_button.cpp | 131 -------- .../SourceFiles/ui/buttons/round_button.h | 67 ---- Telegram/SourceFiles/ui/countryinput.h | 1 - Telegram/SourceFiles/ui/flatbutton.cpp | 203 ------------ Telegram/SourceFiles/ui/flatbutton.h | 108 ------ Telegram/SourceFiles/ui/widgets/buttons.cpp | 311 ++++++++++++++++++ Telegram/SourceFiles/ui/widgets/buttons.h | 139 ++++++++ .../ui/{buttons => widgets}/checkbox.cpp | 12 +- .../ui/{buttons => widgets}/checkbox.h | 21 +- .../SourceFiles/ui/widgets/multi_select.cpp | 2 +- Telegram/SourceFiles/ui/widgets/widgets.style | 58 ++-- .../window/notifications_manager_default.cpp | 6 +- .../window/notifications_manager_default.h | 3 +- .../SourceFiles/window/top_bar_widget.cpp | 8 +- .../window/window_theme_warning.cpp | 2 +- .../SourceFiles/window/window_theme_warning.h | 8 +- Telegram/gyp/Telegram.gyp | 16 +- 133 files changed, 1300 insertions(+), 1508 deletions(-) rename Telegram/SourceFiles/ui/{button.cpp => abstract_button.cpp} (64%) rename Telegram/SourceFiles/ui/{button.h => abstract_button.h} (76%) delete mode 100644 Telegram/SourceFiles/ui/buttons/icon_button.cpp delete mode 100644 Telegram/SourceFiles/ui/buttons/icon_button.h delete mode 100644 Telegram/SourceFiles/ui/buttons/round_button.cpp delete mode 100644 Telegram/SourceFiles/ui/buttons/round_button.h delete mode 100644 Telegram/SourceFiles/ui/flatbutton.cpp delete mode 100644 Telegram/SourceFiles/ui/flatbutton.h create mode 100644 Telegram/SourceFiles/ui/widgets/buttons.cpp create mode 100644 Telegram/SourceFiles/ui/widgets/buttons.h rename Telegram/SourceFiles/ui/{buttons => widgets}/checkbox.cpp (95%) rename Telegram/SourceFiles/ui/{buttons => widgets}/checkbox.h (87%) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 13f2e1c65..eaf924394 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -83,18 +83,6 @@ attentionBoxButton: RoundButton(defaultBoxButton) { textBgOver: #fff0ed; } boxButtonPadding: margins(12px, 16px, 22px, 16px); -defaultBoxLinkButton: linkButton { - color: #0080c0; - overColor: #0080c0; - downColor: #0073ad; - font: boxTextFont; - overFont: font(boxFontSize underline); -} -redBoxLinkButton: linkButton(defaultBoxLinkButton) { - color: #d15948; - overColor: #d15948; - downColor: #db6352; -} boxLabel: flatLabel(labelDefFlat) { font: font(boxFontSize); align: align(topleft); @@ -290,13 +278,6 @@ btnNoHover: #777777; linkCropLimit: 360px; linkFont: normalFont; linkOverFont: font(fsize underline); -btnDefLink: linkButton { - color: btnYesColor; - overColor: btnYesColor; - downColor: btnYesHover; - font: linkFont; - overFont: linkOverFont; -} inpDefFont: font(17px); inpDefFlat: flatInput { @@ -368,11 +349,6 @@ boxShadow: icon {{ "box_shadow", windowShadowFg }}; boxShadowShift: 2px; btnSelectSep: #e0e0e0; -btnRedLink: linkButton(btnDefLink) { - color: #d15948; - overColor: #d15948; - downColor: #db6352; -} countryRowHeight: 36px; countryRowNameFont: semiboldFont; @@ -699,41 +675,6 @@ historyScroll: flatScroll(scrollDef) { } textRectMargins: margins(-2px, -1px, -2px, -1px); -reportSpamHide: flatButton { - duration: 200; - cursor: cursor(pointer); - - color: btnYesColor; - overColor: btnYesHover; - - bgColor: transparent; - overBgColor: transparent; - - width: -40px; - height: 46px; - - textTop: 15px; - overTextTop: 15px; - downTextTop: 16px; - - font: font(fsize); - overFont: font(fsize underline); -} -reportSpamButton: flatButton(reportSpamHide) { - textTop: 6px; - overTextTop: 6px; - downTextTop: 7px; - - width: -50px; - height: 30px; - - bgColor: #888888; - overBgColor: #7b7b7b; -} -reportSpamSeparator: 30px; -reportSpamBg: #fffffff0; -reportSpamFg: #000000; - newMsgSound: ":/gui/art/newmsg.wav"; unreadBarHeight: 32px; @@ -798,25 +739,6 @@ connectionPasswordInputField: InputField(defaultInputField) { } connectionIPv6Skip: 11px; -aboutIcon: icon {{ "intro_logo", #008ed5 }}; -aboutWidth: 390px; -aboutVersionTop: -3px; -aboutVersionLink: linkButton(btnDefLink) { - color: #999999; - overColor: #999999; - downColor: #999999; -} -aboutTextTop: 34px; -aboutSkip: 14px; -aboutLabel: flatLabel(labelDefFlat) { - font: normalFont; - width: 330px; - align: align(topleft); -} -aboutTextStyle: textStyle(defaultTextStyle) { - lineHeight: 22px; -} - emojiTextFont: font(15px); emojiReplaceWidth: 52px; emojiReplaceHeight: 56px; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index fdf78866f..82a6f294e 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -30,34 +30,6 @@ textStyle { lineHeight: pixels; } -linkButton { - color: color; - overColor: color; - downColor: color; - font: font; - overFont: font; -} - -flatButton { - color: color; - overColor: color; - - bgColor: color; - overBgColor: color; - - width: pixels; - height: pixels; - - textTop: pixels; - overTextTop: pixels; - downTextTop: pixels; - - font: font; - overFont: font; - duration: int; - cursor: cursor; -} - flatInput { textColor: color; bgColor: color; diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp index dc49c5068..a646a91af 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.cpp +++ b/Telegram/SourceFiles/boxes/aboutbox.cpp @@ -27,6 +27,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "autoupdater.h" #include "boxes/confirmbox.h" #include "application.h" +#include "ui/widgets/buttons.h" +#include "styles/style_boxes.h" AboutBox::AboutBox() : AbstractBox(st::aboutWidth) , _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink) @@ -34,12 +36,12 @@ AboutBox::AboutBox() : AbstractBox(st::aboutWidth) , _text2(this, lang(lng_about_text_2), FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) , _text3(this,st::aboutLabel, st::aboutTextStyle) , _done(this, lang(lng_close), st::defaultBoxButton) { - _text3.setRichText(lng_about_text_3(lt_faq_open, qsl("[a href=\"%1\"]").arg(telegramFaqLink()), lt_faq_close, qsl("[/a]"))); + _text3->setRichText(lng_about_text_3(lt_faq_open, qsl("[a href=\"%1\"]").arg(telegramFaqLink()), lt_faq_close, qsl("[/a]"))); - setMaxHeight(titleHeight() + st::aboutTextTop + _text1.height() + st::aboutSkip + _text2.height() + st::aboutSkip + _text3.height() + st::boxButtonPadding.top() + _done.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::aboutTextTop + _text1->height() + st::aboutSkip + _text2->height() + st::aboutSkip + _text3->height() + st::boxButtonPadding.top() + _done->height() + st::boxButtonPadding.bottom()); - connect(&_version, SIGNAL(clicked()), this, SLOT(onVersion())); - connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_version, SIGNAL(clicked()), this, SLOT(onVersion())); + connect(_done, SIGNAL(clicked()), this, SLOT(onClose())); prepare(); @@ -47,19 +49,19 @@ AboutBox::AboutBox() : AbstractBox(st::aboutWidth) } void AboutBox::showAll() { - _version.show(); - _text1.show(); - _text2.show(); - _text3.show(); - _done.show(); + _version->show(); + _text1->show(); + _text2->show(); + _text3->show(); + _done->show(); } void AboutBox::resizeEvent(QResizeEvent *e) { - _version.moveToLeft(st::boxPadding.left(), titleHeight() + st::aboutVersionTop); - _text1.moveToLeft(st::boxPadding.left(), titleHeight() + st::aboutTextTop); - _text2.moveToLeft(st::boxPadding.left(), _text1.y() + _text1.height() + st::aboutSkip); - _text3.moveToLeft(st::boxPadding.left(), _text2.y() + _text2.height() + st::aboutSkip); - _done.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _done.height()); + _version->moveToLeft(st::boxPadding.left(), titleHeight() + st::aboutVersionTop); + _text1->moveToLeft(st::boxPadding.left(), titleHeight() + st::aboutTextTop); + _text2->moveToLeft(st::boxPadding.left(), _text1->y() + _text1->height() + st::aboutSkip); + _text3->moveToLeft(st::boxPadding.left(), _text2->y() + _text2->height() + st::aboutSkip); + _done->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _done->height()); AbstractBox::resizeEvent(e); } diff --git a/Telegram/SourceFiles/boxes/aboutbox.h b/Telegram/SourceFiles/boxes/aboutbox.h index 99d5571f5..d05cedbdb 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.h +++ b/Telegram/SourceFiles/boxes/aboutbox.h @@ -23,6 +23,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" #include "ui/flatlabel.h" +namespace Ui { +class RoundButton; +class LinkButton; +} // namespace Ui + class AboutBox : public AbstractBox { Q_OBJECT @@ -42,9 +47,11 @@ protected: void showAll() override; private: - LinkButton _version; - FlatLabel _text1, _text2, _text3; - BoxButton _done; + ChildWidget _version; + ChildWidget _text1; + ChildWidget _text2; + ChildWidget _text3; + ChildWidget _done; }; diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index fdafb714f..31c5d348d 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_boxes.h" #include "localstorage.h" #include "lang.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "mainwidget.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index fedcb3376..4e63886b7 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -29,7 +29,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "boxes/photocropbox.h" #include "ui/filedialog.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" @@ -627,7 +628,7 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) { QTextOption option(style::al_left); option.setWrapMode(QTextOption::WrapAnywhere); p.setFont(_linkOver ? st::boxTextFont->underline() : st::boxTextFont); - p.setPen(st::btnDefLink.color); + p.setPen(st::defaultLinkButton.color); p.drawText(_invitationLink, _channel->inviteLink(), option); if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) { p.setOpacity(a_goodOpacity.current()); @@ -1085,7 +1086,7 @@ EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox() , _title(this, st::defaultInputField, lang(lng_dlg_new_channel_name), _channel->name) , _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about()) , _sign(this, lang(lng_edit_sign_messages), channel->addsSignature(), st::defaultBoxCheckbox) -, _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::defaultBoxLinkButton) +, _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::boxLinkButton) , _save(this, lang(lng_settings_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(peerUpdated(PeerData*))); @@ -1404,7 +1405,7 @@ void RevokePublicLinkBox::paintChat(Painter &p, const ChatRow &row, bool selecte row.name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width()); p.setFont(selected ? st::linkOverFont : st::linkFont); - p.setPen(pressed ? st::btnDefLink.downColor : st::btnDefLink.color->p); + p.setPen(pressed ? st::defaultLinkButton.downColor : st::defaultLinkButton.color); p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), lang(lng_channels_too_much_public_revoke), _revokeWidth); p.setPen(st::contactsStatusFg); diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index 805426311..6ce931baa 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -30,6 +30,8 @@ class ConfirmBox; namespace Ui { class Checkbox; class Radiobutton; +class LinkButton; +class RoundButton; } // namespace Ui class AddContactBox : public AbstractBox, public RPCSender { @@ -66,9 +68,9 @@ private: ChildWidget _last; ChildWidget _phone; - ChildWidget _save; - ChildWidget _cancel; - ChildWidget _retry; + ChildWidget _save; + ChildWidget _cancel; + ChildWidget _retry; bool _invertOrder; @@ -123,8 +125,8 @@ private: QImage _photoBig; QPixmap _photoSmall; - ChildWidget _next; - ChildWidget _cancel; + ChildWidget _next; + ChildWidget _cancel; // channel creation int32 _creationRequestId; @@ -190,8 +192,8 @@ private: QRect _invitationLink; bool _linkOver; - ChildWidget _save; - ChildWidget _skip; + ChildWidget _save; + ChildWidget _skip; bool _tooMuchUsernames = false; @@ -237,8 +239,8 @@ private: ChildWidget _first; ChildWidget _last; - ChildWidget _save; - ChildWidget _cancel; + ChildWidget _save; + ChildWidget _cancel; bool _invertOrder = false; @@ -285,10 +287,10 @@ private: ChildWidget _description; ChildWidget _sign; - ChildWidget _publicLink; + ChildWidget _publicLink; - ChildWidget _save; - ChildWidget _cancel; + ChildWidget _save; + ChildWidget _cancel; mtpRequestId _saveTitleRequestId = 0; mtpRequestId _saveDescriptionRequestId = 0; @@ -337,7 +339,7 @@ private: int _revokeWidth = 0; ChildWidget _aboutRevoke; - ChildWidget _cancel; + ChildWidget _cancel; base::lambda_unique _revokeCallback; mtpRequestId _revokeRequestId = 0; diff --git a/Telegram/SourceFiles/boxes/autolockbox.cpp b/Telegram/SourceFiles/boxes/autolockbox.cpp index e6310cda0..f2b025c07 100644 --- a/Telegram/SourceFiles/boxes/autolockbox.cpp +++ b/Telegram/SourceFiles/boxes/autolockbox.cpp @@ -26,7 +26,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "mainwidget.h" #include "mainwindow.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" AutoLockBox::AutoLockBox() : _close(this, lang(lng_box_ok), st::defaultBoxButton) { @@ -35,7 +36,7 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { int32 opts[] = { 60, 300, 3600, 18000 }, cnt = sizeof(opts) / sizeof(opts[0]); - resizeMaxHeight(st::langsWidth, titleHeight() + cnt * (st::boxOptionListPadding.top() + st::langsButton.height) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _close.height() + st::boxButtonPadding.bottom()); + resizeMaxHeight(st::langsWidth, titleHeight() + cnt * (st::boxOptionListPadding.top() + st::langsButton.height) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _close->height() + st::boxButtonPadding.bottom()); int32 y = titleHeight() + st::boxOptionListPadding.top(); _options.reserve(cnt); @@ -47,14 +48,14 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { connect(_options.back(), SIGNAL(changed()), this, SLOT(onChange())); } - connect(&_close, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_close, SIGNAL(clicked()), this, SLOT(onClose())); - _close.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close.height()); + _close->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close->height()); prepare(); } void AutoLockBox::showAll() { - _close.show(); + _close->show(); for (int32 i = 0, l = _options.size(); i < l; ++i) { _options[i]->show(); } diff --git a/Telegram/SourceFiles/boxes/autolockbox.h b/Telegram/SourceFiles/boxes/autolockbox.h index b542042e4..e5b6e6c92 100644 --- a/Telegram/SourceFiles/boxes/autolockbox.h +++ b/Telegram/SourceFiles/boxes/autolockbox.h @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Radiobutton; +class RoundButton; } // namespace Ui class AutoLockBox : public AbstractBox { @@ -42,6 +43,6 @@ protected: private: QVector _options; - BoxButton _close; + ChildWidget _close; }; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index b4a82f04d..3db3c34c3 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -38,6 +38,14 @@ boxBlockTitleClose: IconButton(defaultIconButton) { iconOver: boxBlockTitleCloseIconOver; } +boxLinkButton: LinkButton { + color: #0080c0; + overColor: #0080c0; + downColor: #0073ad; + font: boxTextFont; + overFont: font(boxFontSize underline); +} + confirmInviteTitle: flatLabel(labelDefFlat) { font: font(16px semibold); align: align(center); @@ -265,6 +273,11 @@ sessionTerminate: IconButton { iconPosition: point(3px, 3px); iconPositionDown: point(3px, 4px); } +sessionTerminateAllButton: LinkButton(boxLinkButton) { + color: #d15948; + overColor: #d15948; + downColor: #db6352; +} passcodeHeaderFont: font(19px); passcodeHeaderHeight: 80px; @@ -309,3 +322,21 @@ themeWarningHeight: 150px; themeWarningShadow: boxShadow; themeWarningShadowShift: boxShadowShift; themeWarningTextTop: 60px; + +aboutWidth: 390px; +aboutVersionTop: -3px; +aboutVersionLink: LinkButton(defaultLinkButton) { + color: #999999; + overColor: #999999; + downColor: #999999; +} +aboutTextTop: 34px; +aboutSkip: 14px; +aboutLabel: flatLabel(labelDefFlat) { + font: normalFont; + width: 330px; + align: align(topleft); +} +aboutTextStyle: textStyle(defaultTextStyle) { + lineHeight: 22px; +} diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 951fc3d15..2e2a6bc69 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -27,7 +27,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "apiwrap.h" #include "application.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "core/click_handler_types.h" #include "localstorage.h" @@ -282,7 +283,7 @@ void MaxInviteBox::paintEvent(QPaintEvent *e) { QTextOption option(style::al_left); option.setWrapMode(QTextOption::WrapAnywhere); p.setFont(_linkOver ? st::defaultInputField.font->underline() : st::defaultInputField.font); - p.setPen(st::btnDefLink.color); + p.setPen(st::defaultLinkButton.color); p.drawText(_invitationLink, _link, option); if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) { p.setOpacity(a_goodOpacity.current()); diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index 38b545cdd..e713528af 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Checkbox; +class RoundButton; } // namespace Ui class InformBox; @@ -85,8 +86,8 @@ private: QPoint _lastMousePos; - ChildWidget _confirm; - ChildWidget _cancel; + ChildWidget _confirm; + ChildWidget _cancel; base::lambda_unique _confirmedCallback; @@ -164,7 +165,7 @@ private: void updateSelected(const QPoint &cursorGlobalPosition); void step_good(float64 ms, bool timer); - ChildWidget _close; + ChildWidget _close; Text _text; int32 _textWidth, _textHeight; @@ -205,8 +206,8 @@ private: Text _text, _note; int32 _textWidth, _textHeight; - ChildWidget _convert; - ChildWidget _cancel; + ChildWidget _convert; + ChildWidget _cancel; }; @@ -234,8 +235,8 @@ private: ChildWidget _text; ChildWidget _notify; - ChildWidget _pin; - ChildWidget _cancel; + ChildWidget _pin; + ChildWidget _cancel; mtpRequestId _requestId = 0; @@ -265,8 +266,8 @@ private: ChildWidget _reportSpam; ChildWidget _deleteAll; - ChildWidget _delete; - ChildWidget _cancel; + ChildWidget _delete; + ChildWidget _cancel; }; @@ -298,11 +299,13 @@ protected: void showAll() override; private: - ChildWidget _title, _status; + ChildWidget _title; + ChildWidget _status; ImagePtr _photo; QVector _participants; - ChildWidget _join, _cancel; + ChildWidget _join; + ChildWidget _cancel; int _userWidth = 0; }; diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.cpp b/Telegram/SourceFiles/boxes/confirmphonebox.cpp index 23ca5c067..36d0871a0 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.cpp +++ b/Telegram/SourceFiles/boxes/confirmphonebox.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_boxes.h" #include "boxes/confirmbox.h" +#include "ui/widgets/buttons.h" #include "mainwidget.h" #include "lang.h" @@ -107,8 +108,8 @@ void ConfirmPhoneBox::launch() { _code = new InputField(this, st::confirmPhoneCodeField, lang(lng_code_ph)); - _send = new BoxButton(this, lang(lng_confirm_phone_send), st::defaultBoxButton); - _cancel = new BoxButton(this, lang(lng_cancel), st::cancelBoxButton); + _send.create(this, lang(lng_confirm_phone_send), st::defaultBoxButton); + _cancel.create(this, lang(lng_cancel), st::cancelBoxButton); setMaxHeight(titleHeight() + st::usernamePadding.top() + _code->height() + st::usernameSkip + _about->height() + st::usernameSkip + _send->height() + st::boxButtonPadding.bottom()); diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.h b/Telegram/SourceFiles/boxes/confirmphonebox.h index f759824e9..6c3788262 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.h +++ b/Telegram/SourceFiles/boxes/confirmphonebox.h @@ -24,6 +24,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class FlatLabel; +namespace Ui { +class RoundButton; +} // namespace Ui + class ConfirmPhoneBox : public AbstractBox, public RPCSender { Q_OBJECT @@ -90,8 +94,8 @@ private: mtpRequestId _checkCodeRequestId = 0; ChildWidget _about = { nullptr }; - ChildWidget _send = { nullptr }; - ChildWidget _cancel = { nullptr }; + ChildWidget _send = { nullptr }; + ChildWidget _cancel = { nullptr }; ChildWidget _code = { nullptr }; // Flag for not calling onTextChanged() recursively. diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index 5fc825fbd..cd481d302 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -25,7 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "mainwidget.h" #include "mainwindow.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "history/history_location_manager.h" ConnectionBox::ConnectionBox() : AbstractBox(st::boxWidth) diff --git a/Telegram/SourceFiles/boxes/connectionbox.h b/Telegram/SourceFiles/boxes/connectionbox.h index 6d66477e6..9e0e3de88 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.h +++ b/Telegram/SourceFiles/boxes/connectionbox.h @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Checkbox; class Radiobutton; +class RoundButton; } // namespace Ui class ConnectionBox : public AbstractBox { @@ -55,8 +56,8 @@ private: ChildWidget _tcpProxyRadio; ChildWidget _tryIPv6; - ChildWidget _save; - ChildWidget _cancel; + ChildWidget _save; + ChildWidget _cancel; }; @@ -86,7 +87,7 @@ private: int _sectionHeight; - ChildWidget _save; - ChildWidget _cancel; + ChildWidget _save; + ChildWidget _cancel; }; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 517d9e8f7..9bd4c5119 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -30,8 +30,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "mainwindow.h" #include "application.h" -#include "ui/buttons/checkbox.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "ui/filedialog.h" #include "ui/widgets/multi_select.h" #include "ui/effects/widget_slide_wrap.h" diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 93807e328..41d9520d9 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -31,6 +31,8 @@ class IndexedList; } // namespace Dialogs namespace Ui { +class RoundButton; +class LinkButton; class Checkbox; class MultiSelect; template @@ -93,8 +95,8 @@ private: ChildWidget> _select; ChildWidget _add = { nullptr }; - ChildWidget _next; - ChildWidget _cancel; + ChildWidget _next; + ChildWidget _cancel; MembersFilter _membersFilter; ChildWidget _topShadow; @@ -300,7 +302,7 @@ private: int _byUsernameSel = -1; QPoint _lastMousePos; - ChildWidget _addContactLnk; + ChildWidget _addContactLnk; bool _saving = false; bool _allAdminsChecked = false; diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.cpp b/Telegram/SourceFiles/boxes/downloadpathbox.cpp index 704c916de..8f3051691 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.cpp +++ b/Telegram/SourceFiles/boxes/downloadpathbox.cpp @@ -24,8 +24,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "localstorage.h" #include "ui/filedialog.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "pspecific.h" +#include "styles/style_boxes.h" DownloadPathBox::DownloadPathBox() : AbstractBox() , _path(Global::DownloadPath()) @@ -33,7 +35,7 @@ DownloadPathBox::DownloadPathBox() : AbstractBox() , _default(this, qsl("dir_type"), 0, lang(lng_download_path_default_radio), _path.isEmpty()) , _temp(this, qsl("dir_type"), 1, lang(lng_download_path_temp_radio), _path == qsl("tmp")) , _dir(this, qsl("dir_type"), 2, lang(lng_download_path_dir_radio), !_path.isEmpty() && _path != qsl("tmp")) -, _pathLink(this, QString(), st::defaultBoxLinkButton) +, _pathLink(this, QString(), st::boxLinkButton) , _save(this, lang(lng_connection_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.h b/Telegram/SourceFiles/boxes/downloadpathbox.h index a0fdfe5fd..070e8889b 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.h +++ b/Telegram/SourceFiles/boxes/downloadpathbox.h @@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Radiobutton; +class LinkButton; +class RoundButton; } // namespace Ui class DownloadPathBox : public AbstractBox { @@ -53,9 +55,9 @@ private: ChildWidget _default; ChildWidget _temp; ChildWidget _dir; - ChildWidget _pathLink; + ChildWidget _pathLink; - ChildWidget _save; - ChildWidget _cancel; + ChildWidget _save; + ChildWidget _cancel; }; diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp index 4795030f9..3ac63ad6c 100644 --- a/Telegram/SourceFiles/boxes/languagebox.cpp +++ b/Telegram/SourceFiles/boxes/languagebox.cpp @@ -22,7 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/languagebox.h" #include "lang.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "localstorage.h" #include "boxes/confirmbox.h" #include "mainwidget.h" @@ -56,16 +57,16 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { connect(_langs.back(), SIGNAL(changed()), this, SLOT(onChange())); } - resizeMaxHeight(st::langsWidth, titleHeight() + (languageCount + (haveTestLang ? 1 : 0)) * (st::boxOptionListPadding.top() + st::langsButton.height) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _close.height() + st::boxButtonPadding.bottom()); + resizeMaxHeight(st::langsWidth, titleHeight() + (languageCount + (haveTestLang ? 1 : 0)) * (st::boxOptionListPadding.top() + st::langsButton.height) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _close->height() + st::boxButtonPadding.bottom()); - connect(&_close, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_close, SIGNAL(clicked()), this, SLOT(onClose())); - _close.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close.height()); + _close->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close->height()); prepare(); } void LanguageBox::showAll() { - _close.show(); + _close->show(); for (int32 i = 0, l = _langs.size(); i < l; ++i) { _langs[i]->show(); } diff --git a/Telegram/SourceFiles/boxes/languagebox.h b/Telegram/SourceFiles/boxes/languagebox.h index 9714fcde7..53a5852e0 100644 --- a/Telegram/SourceFiles/boxes/languagebox.h +++ b/Telegram/SourceFiles/boxes/languagebox.h @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Radiobutton; +class RoundButton; } // namespace Ui class LanguageBox : public AbstractBox { @@ -45,6 +46,6 @@ protected: private: QVector _langs; - BoxButton _close; + ChildWidget _close; }; diff --git a/Telegram/SourceFiles/boxes/localstoragebox.cpp b/Telegram/SourceFiles/boxes/localstoragebox.cpp index 2c459fa57..f23f53b7c 100644 --- a/Telegram/SourceFiles/boxes/localstoragebox.cpp +++ b/Telegram/SourceFiles/boxes/localstoragebox.cpp @@ -22,13 +22,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/localstoragebox.h" #include "styles/style_boxes.h" +#include "ui/widgets/buttons.h" #include "localstorage.h" -#include "ui/flatbutton.h" #include "lang.h" #include "mainwindow.h" LocalStorageBox::LocalStorageBox() : AbstractBox() -, _clear(this, lang(lng_local_storage_clear), st::defaultBoxLinkButton) +, _clear(this, lang(lng_local_storage_clear), st::boxLinkButton) , _close(this, lang(lng_box_ok), st::defaultBoxButton) { connect(_clear, SIGNAL(clicked()), this, SLOT(onClear())); connect(_close, SIGNAL(clicked()), this, SLOT(onClose())); diff --git a/Telegram/SourceFiles/boxes/localstoragebox.h b/Telegram/SourceFiles/boxes/localstoragebox.h index ef175bfec..178135d9e 100644 --- a/Telegram/SourceFiles/boxes/localstoragebox.h +++ b/Telegram/SourceFiles/boxes/localstoragebox.h @@ -22,8 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" -class BoxButton; +namespace Ui { +class RoundButton; class LinkButton; +} // namespace Ui class LocalStorageBox : public AbstractBox { Q_OBJECT @@ -53,8 +55,8 @@ private: }; State _state = State::Normal; - ChildWidget _clear; - ChildWidget _close; + ChildWidget _clear; + ChildWidget _close; int _imagesCount = -1; int _audiosCount = -1; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 5dbedd23c..4f0a12f37 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -28,7 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "boxes/contactsbox.h" #include "boxes/confirmbox.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "observer_peer.h" MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll) @@ -248,11 +248,7 @@ void MembersBox::Inner::paintDialog(Painter &p, PeerData *peer, MemberData *data if (data->canKick) { p.setFont((kickSel ? st::linkOverFont : st::linkFont)->f); - if (kickDown) { - p.setPen(st::btnDefLink.downColor->p); - } else { - p.setPen(st::btnDefLink.color->p); - } + p.setPen(kickDown ? st::defaultLinkButton.downColor : st::defaultLinkButton.color); p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), _kickText, _kickWidth); } diff --git a/Telegram/SourceFiles/boxes/notifications_box.cpp b/Telegram/SourceFiles/boxes/notifications_box.cpp index d6312f588..4901b6737 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.cpp +++ b/Telegram/SourceFiles/boxes/notifications_box.cpp @@ -22,8 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/notifications_box.h" #include "lang.h" -#include "ui/buttons/round_button.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/discrete_slider.h" #include "styles/style_boxes.h" #include "styles/style_dialogs.h" diff --git a/Telegram/SourceFiles/boxes/notifications_box.h b/Telegram/SourceFiles/boxes/notifications_box.h index 36bcfdbce..cd9114483 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.h +++ b/Telegram/SourceFiles/boxes/notifications_box.h @@ -22,10 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" -class BoxButton; -class LinkButton; - namespace Ui { +class RoundButton; +class LinkButton; class DiscreteSlider; } // namespace Ui @@ -72,7 +71,7 @@ private: int _oldCount; ChildWidget _countSlider; - ChildWidget _done; + ChildWidget _done; QVector _cornerSamples[4]; diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp index ab956eb0f..92a8a2ebf 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.cpp +++ b/Telegram/SourceFiles/boxes/passcodebox.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "localstorage.h" #include "styles/style_boxes.h" +#include "ui/widgets/buttons.h" PasscodeBox::PasscodeBox(bool turningOff) : AbstractBox(st::boxWidth) , _replacedBy(0) @@ -82,114 +83,114 @@ void PasscodeBox::init() { _aboutHeight = _about.countHeight(st::boxWidth - st::boxPadding.left() * 1.5); textstyleRestore(); if (_turningOff) { - _oldPasscode.show(); + _oldPasscode->show(); _boxTitle = lang(_cloudPwd ? lng_cloud_password_remove : lng_passcode_remove); - setMaxHeight(titleHeight() + st::passcodePadding.top() + _oldPasscode.height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0) + _aboutHeight + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::passcodePadding.top() + _oldPasscode->height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0) + _aboutHeight + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton->height() + st::boxButtonPadding.bottom()); } else { bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode(); if (has) { - _oldPasscode.show(); + _oldPasscode->show(); _boxTitle = lang(_cloudPwd ? lng_cloud_password_change : lng_passcode_change); - setMaxHeight(titleHeight() + st::passcodePadding.top() + _oldPasscode.height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0) + _newPasscode.height() + st::contactSkip + _reenterPasscode.height() + st::passcodeSkip + (_cloudPwd ? _passwordHint.height() + st::contactSkip : 0) + _aboutHeight + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::passcodePadding.top() + _oldPasscode->height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0) + _newPasscode->height() + st::contactSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::contactSkip : 0) + _aboutHeight + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton->height() + st::boxButtonPadding.bottom()); } else { - _oldPasscode.hide(); + _oldPasscode->hide(); _boxTitle = lang(_cloudPwd ? lng_cloud_password_create : lng_passcode_create); - setMaxHeight(titleHeight() + st::passcodePadding.top() + _newPasscode.height() + st::contactSkip + _reenterPasscode.height() + st::passcodeSkip + (_cloudPwd ? _passwordHint.height() + st::contactSkip : 0) + _aboutHeight + (_cloudPwd ? st::contactSkip + _recoverEmail.height() + st::passcodeSkip : st::passcodePadding.bottom()) + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::passcodePadding.top() + _newPasscode->height() + st::contactSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::contactSkip : 0) + _aboutHeight + (_cloudPwd ? st::contactSkip + _recoverEmail->height() + st::passcodeSkip : st::passcodePadding.bottom()) + st::boxButtonPadding.top() + _saveButton->height() + st::boxButtonPadding.bottom()); } } - connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_saveButton, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancelButton, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_oldPasscode, SIGNAL(changed()), this, SLOT(onOldChanged())); - connect(&_newPasscode, SIGNAL(changed()), this, SLOT(onNewChanged())); - connect(&_reenterPasscode, SIGNAL(changed()), this, SLOT(onNewChanged())); - connect(&_passwordHint, SIGNAL(changed()), this, SLOT(onNewChanged())); - connect(&_recoverEmail, SIGNAL(changed()), this, SLOT(onEmailChanged())); + connect(_oldPasscode, SIGNAL(changed()), this, SLOT(onOldChanged())); + connect(_newPasscode, SIGNAL(changed()), this, SLOT(onNewChanged())); + connect(_reenterPasscode, SIGNAL(changed()), this, SLOT(onNewChanged())); + connect(_passwordHint, SIGNAL(changed()), this, SLOT(onNewChanged())); + connect(_recoverEmail, SIGNAL(changed()), this, SLOT(onEmailChanged())); - connect(&_oldPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_newPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_reenterPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_passwordHint, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_recoverEmail, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_oldPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_newPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_reenterPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_passwordHint, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_recoverEmail, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_recover, SIGNAL(clicked()), this, SLOT(onRecoverByEmail())); + connect(_recover, SIGNAL(clicked()), this, SLOT(onRecoverByEmail())); } void PasscodeBox::showAll() { bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode(); if (_turningOff) { - _oldPasscode.show(); + _oldPasscode->show(); if (_cloudPwd && _hasRecovery) { - _recover.show(); + _recover->show(); } else { - _recover.hide(); + _recover->hide(); } - _newPasscode.hide(); - _reenterPasscode.hide(); - _passwordHint.hide(); - _recoverEmail.hide(); + _newPasscode->hide(); + _reenterPasscode->hide(); + _passwordHint->hide(); + _recoverEmail->hide(); } else { if (has) { - _oldPasscode.show(); + _oldPasscode->show(); if (_cloudPwd && _hasRecovery) { - _recover.show(); + _recover->show(); } else { - _recover.hide(); + _recover->hide(); } } else { - _oldPasscode.hide(); - _recover.hide(); + _oldPasscode->hide(); + _recover->hide(); } - _newPasscode.show(); - _reenterPasscode.show(); + _newPasscode->show(); + _reenterPasscode->show(); if (_cloudPwd) { - _passwordHint.show(); + _passwordHint->show(); } else { - _passwordHint.hide(); + _passwordHint->hide(); } if (_cloudPwd && _curSalt.isEmpty()) { - _recoverEmail.show(); + _recoverEmail->show(); } else { - _recoverEmail.hide(); + _recoverEmail->hide(); } } - _saveButton.show(); - _cancelButton.show(); + _saveButton->show(); + _cancelButton->show(); AbstractBox::showAll(); } void PasscodeBox::onSubmit() { bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode(); - if (_oldPasscode.hasFocus()) { + if (_oldPasscode->hasFocus()) { if (_turningOff) { onSave(); } else { - _newPasscode.setFocus(); + _newPasscode->setFocus(); } - } else if (_newPasscode.hasFocus()) { - _reenterPasscode.setFocus(); - } else if (_reenterPasscode.hasFocus()) { - if (has && _oldPasscode.text().isEmpty()) { - _oldPasscode.setFocus(); - _oldPasscode.showError(); - } else if (_newPasscode.text().isEmpty()) { - _newPasscode.setFocus(); - _newPasscode.showError(); - } else if (_reenterPasscode.text().isEmpty()) { - _reenterPasscode.showError(); - } else if (!_passwordHint.isHidden()) { - _passwordHint.setFocus(); + } else if (_newPasscode->hasFocus()) { + _reenterPasscode->setFocus(); + } else if (_reenterPasscode->hasFocus()) { + if (has && _oldPasscode->text().isEmpty()) { + _oldPasscode->setFocus(); + _oldPasscode->showError(); + } else if (_newPasscode->text().isEmpty()) { + _newPasscode->setFocus(); + _newPasscode->showError(); + } else if (_reenterPasscode->text().isEmpty()) { + _reenterPasscode->showError(); + } else if (!_passwordHint->isHidden()) { + _passwordHint->setFocus(); } else { onSave(); } - } else if (_passwordHint.hasFocus()) { - if (_recoverEmail.isHidden()) { + } else if (_passwordHint->hasFocus()) { + if (_recoverEmail->isHidden()) { onSave(); } else { - _recoverEmail.setFocus(); + _recoverEmail->setFocus(); } - } else if (_recoverEmail.hasFocus()) { + } else if (_recoverEmail->hasFocus()) { onSave(); } } @@ -203,27 +204,27 @@ void PasscodeBox::paintEvent(QPaintEvent *e) { textstyleSet(&st::usernameTextStyle); int32 w = st::boxWidth - st::boxPadding.left() * 1.5; - int32 abouty = (_passwordHint.isHidden() ? (_reenterPasscode.isHidden() ? (_oldPasscode.y() + (_hasRecovery && !_hintText.isEmpty() ? st::passcodeSkip : 0)) : _reenterPasscode.y()) + st::passcodeSkip : _passwordHint.y() + st::contactSkip) + _oldPasscode.height(); + int32 abouty = (_passwordHint->isHidden() ? (_reenterPasscode->isHidden() ? (_oldPasscode->y() + (_hasRecovery && !_hintText.isEmpty() ? st::passcodeSkip : 0)) : _reenterPasscode->y()) + st::passcodeSkip : _passwordHint->y() + st::contactSkip) + _oldPasscode->height(); p.setPen(st::boxTextFg); _about.drawLeft(p, st::boxPadding.left(), abouty, w, width()); if (!_hintText.isEmpty() && _oldError.isEmpty()) { - _hintText.drawLeftElided(p, st::boxPadding.left(), _oldPasscode.y() + _oldPasscode.height() + ((st::passcodeSkip - st::normalFont->height) / 2), w, width(), 1, style::al_topleft); + _hintText.drawLeftElided(p, st::boxPadding.left(), _oldPasscode->y() + _oldPasscode->height() + ((st::passcodeSkip - st::normalFont->height) / 2), w, width(), 1, style::al_topleft); } if (!_oldError.isEmpty()) { p.setPen(st::boxTextFgError); - p.drawText(QRect(st::boxPadding.left(), _oldPasscode.y() + _oldPasscode.height(), w, st::passcodeSkip), _oldError, style::al_left); + p.drawText(QRect(st::boxPadding.left(), _oldPasscode->y() + _oldPasscode->height(), w, st::passcodeSkip), _oldError, style::al_left); } if (!_newError.isEmpty()) { p.setPen(st::boxTextFgError); - p.drawText(QRect(st::boxPadding.left(), _reenterPasscode.y() + _reenterPasscode.height(), w, st::passcodeSkip), _newError, style::al_left); + p.drawText(QRect(st::boxPadding.left(), _reenterPasscode->y() + _reenterPasscode->height(), w, st::passcodeSkip), _newError, style::al_left); } if (!_emailError.isEmpty()) { p.setPen(st::boxTextFgError); - p.drawText(QRect(st::boxPadding.left(), _recoverEmail.y() + _recoverEmail.height(), w, st::passcodeSkip), _emailError, style::al_left); + p.drawText(QRect(st::boxPadding.left(), _recoverEmail->y() + _recoverEmail->height(), w, st::passcodeSkip), _emailError, style::al_left); } textstyleRestore(); @@ -232,57 +233,57 @@ void PasscodeBox::paintEvent(QPaintEvent *e) { void PasscodeBox::resizeEvent(QResizeEvent *e) { bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode(); int32 w = st::boxWidth - st::boxPadding.left() - st::boxPadding.right(); - _oldPasscode.resize(w, _oldPasscode.height()); - _oldPasscode.moveToLeft(st::boxPadding.left(), titleHeight() + st::passcodePadding.top()); - _newPasscode.resize(w, _newPasscode.height()); - _newPasscode.moveToLeft(st::boxPadding.left(), _oldPasscode.y() + ((_turningOff || has) ? (_oldPasscode.height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0)) : 0)); - _reenterPasscode.resize(w, _reenterPasscode.height()); - _reenterPasscode.moveToLeft(st::boxPadding.left(), _newPasscode.y() + _newPasscode.height() + st::contactSkip); - _passwordHint.resize(w, _passwordHint.height()); - _passwordHint.moveToLeft(st::boxPadding.left(), _reenterPasscode.y() + _reenterPasscode.height() + st::passcodeSkip); - _recoverEmail.resize(w, _passwordHint.height()); - _recoverEmail.moveToLeft(st::boxPadding.left(), _passwordHint.y() + _passwordHint.height() + st::contactSkip + _aboutHeight + st::contactSkip); + _oldPasscode->resize(w, _oldPasscode->height()); + _oldPasscode->moveToLeft(st::boxPadding.left(), titleHeight() + st::passcodePadding.top()); + _newPasscode->resize(w, _newPasscode->height()); + _newPasscode->moveToLeft(st::boxPadding.left(), _oldPasscode->y() + ((_turningOff || has) ? (_oldPasscode->height() + st::passcodeSkip + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeSkip : 0)) : 0)); + _reenterPasscode->resize(w, _reenterPasscode->height()); + _reenterPasscode->moveToLeft(st::boxPadding.left(), _newPasscode->y() + _newPasscode->height() + st::contactSkip); + _passwordHint->resize(w, _passwordHint->height()); + _passwordHint->moveToLeft(st::boxPadding.left(), _reenterPasscode->y() + _reenterPasscode->height() + st::passcodeSkip); + _recoverEmail->resize(w, _passwordHint->height()); + _recoverEmail->moveToLeft(st::boxPadding.left(), _passwordHint->y() + _passwordHint->height() + st::contactSkip + _aboutHeight + st::contactSkip); - if (!_recover.isHidden()) { - _recover.moveToLeft(st::boxPadding.left(), _oldPasscode.y() + _oldPasscode.height() + (_hintText.isEmpty() ? ((st::passcodeSkip - _recover.height()) / 2) : st::passcodeSkip)); + if (!_recover->isHidden()) { + _recover->moveToLeft(st::boxPadding.left(), _oldPasscode->y() + _oldPasscode->height() + (_hintText.isEmpty() ? ((st::passcodeSkip - _recover->height()) / 2) : st::passcodeSkip)); } - _saveButton.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _saveButton.height()); - _cancelButton.moveToRight(st::boxButtonPadding.right() + _saveButton.width() + st::boxButtonPadding.left(), _saveButton.y()); + _saveButton->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _saveButton->height()); + _cancelButton->moveToRight(st::boxButtonPadding.right() + _saveButton->width() + st::boxButtonPadding.left(), _saveButton->y()); AbstractBox::resizeEvent(e); } void PasscodeBox::doSetInnerFocus() { - if (_skipEmailWarning && !_recoverEmail.isHidden()) { - _recoverEmail.setFocus(); - } else if (_oldPasscode.isHidden()) { - _newPasscode.setFocus(); + if (_skipEmailWarning && !_recoverEmail->isHidden()) { + _recoverEmail->setFocus(); + } else if (_oldPasscode->isHidden()) { + _newPasscode->setFocus(); } else { - _oldPasscode.setFocus(); + _oldPasscode->setFocus(); } } void PasscodeBox::setPasswordDone(const MTPBool &result) { _setRequest = 0; emit reloadPassword(); - ConfirmBox *box = new InformBox(lang(_reenterPasscode.isHidden() ? lng_cloud_password_removed : (_oldPasscode.isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated))); + ConfirmBox *box = new InformBox(lang(_reenterPasscode->isHidden() ? lng_cloud_password_removed : (_oldPasscode->isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated))); Ui::showLayer(box); } bool PasscodeBox::setPasswordFail(const RPCError &error) { if (MTP::isFloodError(error)) { - if (_oldPasscode.isHidden()) return false; + if (_oldPasscode->isHidden()) return false; if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose(); _setRequest = 0; - _oldPasscode.selectAll(); - _oldPasscode.setFocus(); - _oldPasscode.showError(); + _oldPasscode->selectAll(); + _oldPasscode->setFocus(); + _oldPasscode->showError(); _oldError = lang(lng_flood_error); if (_hasRecovery && _hintText.isEmpty()) { - _recover.hide(); + _recover->hide(); } update(); return true; @@ -293,15 +294,15 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) { _setRequest = 0; QString err = error.type(); if (err == qstr("PASSWORD_HASH_INVALID")) { - if (_oldPasscode.isHidden()) { + if (_oldPasscode->isHidden()) { emit reloadPassword(); onClose(); } else { onBadOldPasscode(); } } else if (err == qstr("NEW_PASSWORD_BAD")) { - _newPasscode.setFocus(); - _newPasscode.showError(); + _newPasscode->setFocus(); + _newPasscode->showError(); _newError = lang(lng_cloud_password_bad); update(); } else if (err == qstr("NEW_SALT_INVALID")) { @@ -309,8 +310,8 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) { onClose(); } else if (err == qstr("EMAIL_INVALID")) { _emailError = lang(lng_cloud_password_bad_email); - _recoverEmail.setFocus(); - _recoverEmail.showError(); + _recoverEmail->setFocus(); + _recoverEmail->showError(); update(); } else if (err == qstr("EMAIL_UNCONFIRMED")) { Ui::showLayer(new InformBox(lang(lng_cloud_password_almost))); @@ -322,13 +323,13 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) { void PasscodeBox::onSave(bool force) { if (_setRequest) return; - QString old = _oldPasscode.text(), pwd = _newPasscode.text(), conf = _reenterPasscode.text(); + QString old = _oldPasscode->text(), pwd = _newPasscode->text(), conf = _reenterPasscode->text(); bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode(); if (!_cloudPwd && (_turningOff || has)) { if (!passcodeCanTry()) { _oldError = lang(lng_flood_error); - _oldPasscode.setFocus(); - _oldPasscode.showError(); + _oldPasscode->setFocus(); + _oldPasscode->showError(); update(); return; } @@ -344,37 +345,37 @@ void PasscodeBox::onSave(bool force) { } } if (!_turningOff && pwd.isEmpty()) { - _newPasscode.setFocus(); - _newPasscode.showError(); + _newPasscode->setFocus(); + _newPasscode->showError(); if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose(); return; } if (pwd != conf) { - _reenterPasscode.selectAll(); - _reenterPasscode.setFocus(); - _reenterPasscode.showError(); + _reenterPasscode->selectAll(); + _reenterPasscode->setFocus(); + _reenterPasscode->showError(); if (!conf.isEmpty()) { _newError = lang(_cloudPwd ? lng_cloud_password_differ : lng_passcode_differ); update(); } if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose(); } else if (!_turningOff && has && old == pwd) { - _newPasscode.setFocus(); - _newPasscode.showError(); + _newPasscode->setFocus(); + _newPasscode->showError(); _newError = lang(_cloudPwd ? lng_cloud_password_is_same : lng_passcode_is_same); update(); if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose(); } else if (_cloudPwd) { - QString hint = _passwordHint.getLastText(), email = _recoverEmail.getLastText().trimmed(); - if (_cloudPwd && pwd == hint && !_passwordHint.isHidden() && !_newPasscode.isHidden()) { - _newPasscode.setFocus(); - _newPasscode.showError(); + QString hint = _passwordHint->getLastText(), email = _recoverEmail->getLastText().trimmed(); + if (_cloudPwd && pwd == hint && !_passwordHint->isHidden() && !_newPasscode->isHidden()) { + _newPasscode->setFocus(); + _newPasscode->showError(); _newError = lang(lng_cloud_password_bad); update(); if (isHidden() && _replacedBy && !_replacedBy->isHidden()) _replacedBy->onClose(); return; } - if (!_recoverEmail.isHidden() && email.isEmpty() && !force) { + if (!_recoverEmail->isHidden() && email.isEmpty() && !force) { _skipEmailWarning = true; _replacedBy = new ConfirmBox(lang(lng_cloud_password_about_recover), lang(lng_cloud_password_skip_email), st::attentionBoxButton); connect(_replacedBy, SIGNAL(confirmed()), this, SLOT(onForceNoMail())); @@ -389,13 +390,13 @@ void PasscodeBox::onSave(bool force) { } else { hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data()); } - QByteArray oldPasswordData = _oldPasscode.isHidden() ? QByteArray() : (_curSalt + old.toUtf8() + _curSalt); - QByteArray oldPasswordHash = _oldPasscode.isHidden() ? QByteArray() : QByteArray(32, Qt::Uninitialized); - if (!_oldPasscode.isHidden()) { + QByteArray oldPasswordData = _oldPasscode->isHidden() ? QByteArray() : (_curSalt + old.toUtf8() + _curSalt); + QByteArray oldPasswordHash = _oldPasscode->isHidden() ? QByteArray() : QByteArray(32, Qt::Uninitialized); + if (!_oldPasscode->isHidden()) { hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data()); } MTPDaccount_passwordInputSettings::Flags flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt | MTPDaccount_passwordInputSettings::Flag::f_new_password_hash | MTPDaccount_passwordInputSettings::Flag::f_hint; - if (_oldPasscode.isHidden() || _newPasscode.isHidden()) { + if (_oldPasscode->isHidden() || _newPasscode->isHidden()) { flags |= MTPDaccount_passwordInputSettings::Flag::f_email; } MTPaccount_PasswordInputSettings settings(MTP_account_passwordInputSettings(MTP_flags(flags), MTP_bytes(_newSalt), MTP_bytes(newPasswordHash), MTP_string(hint), MTP_string(email))); @@ -410,12 +411,12 @@ void PasscodeBox::onSave(bool force) { } void PasscodeBox::onBadOldPasscode() { - _oldPasscode.selectAll(); - _oldPasscode.setFocus(); - _oldPasscode.showError(); + _oldPasscode->selectAll(); + _oldPasscode->setFocus(); + _oldPasscode->showError(); _oldError = lang(_cloudPwd ? lng_cloud_password_wrong : lng_passcode_wrong); if (_hasRecovery && _hintText.isEmpty()) { - _recover.hide(); + _recover->hide(); } update(); } @@ -424,7 +425,7 @@ void PasscodeBox::onOldChanged() { if (!_oldError.isEmpty()) { _oldError = QString(); if (_hasRecovery && _hintText.isEmpty()) { - _recover.show(); + _recover->show(); } update(); } @@ -498,21 +499,21 @@ RecoverBox::RecoverBox(const QString &pattern) : AbstractBox(st::boxWidth) , _recoverCode(this, st::defaultInputField, lang(lng_signin_code)) { setBlockTitle(true); - setMaxHeight(titleHeight() + st::passcodePadding.top() + st::passcodeSkip + _recoverCode.height() + st::passcodeSkip + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton.height() + st::boxButtonPadding.bottom()); + setMaxHeight(titleHeight() + st::passcodePadding.top() + st::passcodeSkip + _recoverCode->height() + st::passcodeSkip + st::passcodePadding.bottom() + st::boxButtonPadding.top() + _saveButton->height() + st::boxButtonPadding.bottom()); - connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSubmit())); - connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_saveButton, SIGNAL(clicked()), this, SLOT(onSubmit())); + connect(_cancelButton, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_recoverCode, SIGNAL(changed()), this, SLOT(onCodeChanged())); - connect(&_recoverCode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_recoverCode, SIGNAL(changed()), this, SLOT(onCodeChanged())); + connect(_recoverCode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); prepare(); } void RecoverBox::showAll() { - _recoverCode.show(); - _saveButton.show(); - _cancelButton.show(); + _recoverCode->show(); + _saveButton->show(); + _cancelButton->show(); AbstractBox::showAll(); } @@ -525,35 +526,35 @@ void RecoverBox::paintEvent(QPaintEvent *e) { p.setFont(st::normalFont); p.setPen(st::boxTextFg); int32 w = st::boxWidth - st::boxPadding.left() * 1.5; - p.drawText(QRect(st::boxPadding.left(), _recoverCode.y() - st::passcodeSkip - st::passcodePadding.top(), w, st::passcodePadding.top() + st::passcodeSkip), _pattern, style::al_left); + p.drawText(QRect(st::boxPadding.left(), _recoverCode->y() - st::passcodeSkip - st::passcodePadding.top(), w, st::passcodePadding.top() + st::passcodeSkip), _pattern, style::al_left); if (!_error.isEmpty()) { p.setPen(st::boxTextFgError); - p.drawText(QRect(st::boxPadding.left(), _recoverCode.y() + _recoverCode.height(), w, st::passcodeSkip), _error, style::al_left); + p.drawText(QRect(st::boxPadding.left(), _recoverCode->y() + _recoverCode->height(), w, st::passcodeSkip), _error, style::al_left); } } void RecoverBox::resizeEvent(QResizeEvent *e) { - _recoverCode.resize(st::boxWidth - st::boxPadding.left() - st::boxPadding.right(), _recoverCode.height()); - _recoverCode.moveToLeft(st::boxPadding.left(), titleHeight() + st::passcodePadding.top() + st::passcodeSkip); + _recoverCode->resize(st::boxWidth - st::boxPadding.left() - st::boxPadding.right(), _recoverCode->height()); + _recoverCode->moveToLeft(st::boxPadding.left(), titleHeight() + st::passcodePadding.top() + st::passcodeSkip); - _saveButton.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _saveButton.height()); - _cancelButton.moveToRight(st::boxButtonPadding.right() + _saveButton.width() + st::boxButtonPadding.left(), _saveButton.y()); + _saveButton->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _saveButton->height()); + _cancelButton->moveToRight(st::boxButtonPadding.right() + _saveButton->width() + st::boxButtonPadding.left(), _saveButton->y()); AbstractBox::resizeEvent(e); } void RecoverBox::doSetInnerFocus() { - _recoverCode.setFocus(); + _recoverCode->setFocus(); } void RecoverBox::onSubmit() { if (_submitRequest) return; - QString code = _recoverCode.getLastText().trimmed(); + QString code = _recoverCode->getLastText().trimmed(); if (code.isEmpty()) { - _recoverCode.setFocus(); - _recoverCode.showError(); + _recoverCode->setFocus(); + _recoverCode->showError(); return; } @@ -577,7 +578,7 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) { _submitRequest = 0; _error = lang(lng_flood_error); update(); - _recoverCode.showError(); + _recoverCode->showError(); return true; } if (MTP::isDefaultHandledError(error)) return false; @@ -599,9 +600,9 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) { } else if (err == qstr("CODE_INVALID")) { _error = lang(lng_signin_wrong_code); update(); - _recoverCode.selectAll(); - _recoverCode.setFocus(); - _recoverCode.showError(); + _recoverCode->selectAll(); + _recoverCode->setFocus(); + _recoverCode->showError(); return true; } if (cDebug()) { // internal server error @@ -610,6 +611,6 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) { _error = lang(lng_server_error); } update(); - _recoverCode.setFocus(); + _recoverCode->setFocus(); return false; } diff --git a/Telegram/SourceFiles/boxes/passcodebox.h b/Telegram/SourceFiles/boxes/passcodebox.h index 4e1c212df..53ce16438 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.h +++ b/Telegram/SourceFiles/boxes/passcodebox.h @@ -22,6 +22,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" +namespace Ui { +class LinkButton; +class RoundButton; +} // namespace Ui + class PasscodeBox : public AbstractBox, public RPCSender { Q_OBJECT @@ -74,10 +79,14 @@ private: QString _boxTitle; Text _about, _hintText; - BoxButton _saveButton, _cancelButton; - PasswordField _oldPasscode, _newPasscode, _reenterPasscode; - InputField _passwordHint, _recoverEmail; - LinkButton _recover; + ChildWidget _saveButton; + ChildWidget _cancelButton; + ChildWidget _oldPasscode; + ChildWidget _newPasscode; + ChildWidget _reenterPasscode; + ChildWidget _passwordHint; + ChildWidget _recoverEmail; + ChildWidget _recover; QString _oldError, _newError, _emailError; @@ -112,8 +121,9 @@ private: QString _pattern; - BoxButton _saveButton, _cancelButton; - InputField _recoverCode; + ChildWidget _saveButton; + ChildWidget _cancelButton; + ChildWidget _recoverCode; QString _error; diff --git a/Telegram/SourceFiles/boxes/photocropbox.cpp b/Telegram/SourceFiles/boxes/photocropbox.cpp index 34dc05bf1..755c36dc0 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.cpp +++ b/Telegram/SourceFiles/boxes/photocropbox.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "photocropbox.h" #include "fileuploader.h" +#include "ui/widgets/buttons.h" PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer) : AbstractBox() , _downState(0) @@ -54,8 +55,8 @@ void PhotoCropBox::init(const QImage &img, PeerData *peer) { _title = lang(lng_settings_crop_profile); } - connect(&_done, SIGNAL(clicked()), this, SLOT(onSend())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_done, SIGNAL(clicked()), this, SLOT(onSend())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); if (peerToBareInt(_peerId)) { connect(this, SIGNAL(ready(const QImage&)), this, SLOT(onReady(const QImage&))); } @@ -77,7 +78,7 @@ void PhotoCropBox::init(const QImage &img, PeerData *peer) { _thumby = st::boxPhotoPadding.top(); setMouseTracking(true); - resizeMaxHeight(st::boxWideWidth, st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + st::boxTextFont->height + st::cropSkip + st::boxButtonPadding.top() + _done.height() + st::boxButtonPadding.bottom()); + resizeMaxHeight(st::boxWideWidth, st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + st::boxTextFont->height + st::cropSkip + st::boxButtonPadding.top() + _done->height() + st::boxButtonPadding.bottom()); } void PhotoCropBox::mousePressEvent(QMouseEvent *e) { @@ -257,8 +258,8 @@ void PhotoCropBox::paintEvent(QPaintEvent *e) { } void PhotoCropBox::resizeEvent(QResizeEvent *e) { - _done.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _done.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _done.width() + st::boxButtonPadding.left(), _done.y()); + _done->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _done->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _done->width() + st::boxButtonPadding.left(), _done->y()); AbstractBox::resizeEvent(e); } @@ -304,6 +305,6 @@ void PhotoCropBox::onReady(const QImage &tosend) { } void PhotoCropBox::showAll() { - _done.show(); - _cancel.show(); + _done->show(); + _cancel->show(); } diff --git a/Telegram/SourceFiles/boxes/photocropbox.h b/Telegram/SourceFiles/boxes/photocropbox.h index 2a424bf5e..470344acd 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.h +++ b/Telegram/SourceFiles/boxes/photocropbox.h @@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" +namespace Ui { +class RoundButton; +} // namespace Ui + class PhotoCropBox : public AbstractBox { Q_OBJECT @@ -56,7 +60,8 @@ private: int32 _thumbx, _thumby, _thumbw, _thumbh; int32 _cropx, _cropy, _cropw; int32 _fromposx, _fromposy, _fromcropx, _fromcropy, _fromcropw; - BoxButton _done, _cancel; + ChildWidget _done; + ChildWidget _cancel; QImage _img; QPixmap _thumb; PeerId _peerId; diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 31395b637..3ebb1446a 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -26,7 +26,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "photosendbox.h" #include "history/history_media_types.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "styles/style_history.h" PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxWideWidth) diff --git a/Telegram/SourceFiles/boxes/photosendbox.h b/Telegram/SourceFiles/boxes/photosendbox.h index 8c41c31f1..04b424e4a 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.h +++ b/Telegram/SourceFiles/boxes/photosendbox.h @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Checkbox; +class RoundButton; } // namespace Ui class PhotoSendBox : public AbstractBox { @@ -60,8 +61,8 @@ private: bool _compressedFromSettings; ChildWidget _compressed; - ChildWidget _send; - ChildWidget _cancel; + ChildWidget _send; + ChildWidget _cancel; int32 _thumbx, _thumby, _thumbw, _thumbh; Text _name; @@ -108,8 +109,8 @@ private: QPixmap _thumb; ChildWidget _field = { nullptr }; - ChildWidget _save; - ChildWidget _cancel; + ChildWidget _save; + ChildWidget _cancel; int32 _thumbx, _thumby, _thumbw, _thumbh; Text _name; diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index 8da5e09ac..4b56c3f16 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -25,7 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_boxes.h" #include "styles/style_profile.h" #include "boxes/confirmbox.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "mainwindow.h" ReportBox::ReportBox(ChannelData *channel) : AbstractBox(st::boxWidth) diff --git a/Telegram/SourceFiles/boxes/report_box.h b/Telegram/SourceFiles/boxes/report_box.h index c4f81d358..8e631eb38 100644 --- a/Telegram/SourceFiles/boxes/report_box.h +++ b/Telegram/SourceFiles/boxes/report_box.h @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Radiobutton; +class RoundButton; } // namespace Ui class ReportBox : public AbstractBox, public RPCSender { @@ -60,7 +61,7 @@ private: ChildWidget _reasonOther; ChildWidget _reasonOtherText = { nullptr }; - ChildWidget _report, _cancel; + ChildWidget _report, _cancel; enum Reason { ReasonSpam, diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index 3898aecdd..fb2218d7d 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "countries.h" #include "boxes/confirmbox.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "styles/style_boxes.h" SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll) @@ -38,14 +38,14 @@ SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll) , _shortPollRequest(0) { setMaxHeight(st::sessionsHeight); - connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_done, SIGNAL(clicked()), this, SLOT(onClose())); connect(_inner, SIGNAL(oneTerminated()), this, SLOT(onOneTerminated())); connect(_inner, SIGNAL(allTerminated()), this, SLOT(onAllTerminated())); connect(_inner, SIGNAL(terminateAll()), this, SLOT(onTerminateAll())); connect(App::wnd(), SIGNAL(newAuthorization()), this, SLOT(onNewAuthorization())); connect(&_shortPollTimer, SIGNAL(timeout()), this, SLOT(onShortPollAuthorizations())); - init(_inner, st::boxButtonPadding.bottom() + _done.height() + st::boxButtonPadding.top(), titleHeight()); + init(_inner, st::boxButtonPadding.bottom() + _done->height() + st::boxButtonPadding.top(), titleHeight()); _inner->resize(width(), st::noContactsHeight); prepare(); @@ -55,12 +55,12 @@ SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll) void SessionsBox::resizeEvent(QResizeEvent *e) { ScrollableBox::resizeEvent(e); - _shadow.setGeometry(0, height() - st::boxButtonPadding.bottom() - _done.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); - _done.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _done.height()); + _shadow.setGeometry(0, height() - st::boxButtonPadding.bottom() - _done->height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); + _done->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _done->height()); } void SessionsBox::showAll() { - _done.show(); + _done->show(); if (_loading) { scrollArea()->hide(); _shadow.hide(); @@ -194,7 +194,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) { } } _inner->listUpdated(); - if (!_done.isHidden()) { + if (!_done->isHidden()) { showAll(); update(); } @@ -204,7 +204,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) { void SessionsBox::onOneTerminated() { if (_list.isEmpty()) { - if (!_done.isHidden()) { + if (!_done->isHidden()) { showAll(); update(); } @@ -214,7 +214,7 @@ void SessionsBox::onOneTerminated() { void SessionsBox::onShortPollAuthorizations() { if (!_shortPollRequest) { _shortPollRequest = MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations)); - if (!_done.isHidden()) { + if (!_done->isHidden()) { showAll(); update(); } @@ -236,7 +236,7 @@ void SessionsBox::onAllTerminated() { void SessionsBox::onTerminateAll() { _loading = true; - if (!_done.isHidden()) { + if (!_done->isHidden()) { showAll(); update(); } @@ -246,10 +246,10 @@ SessionsBox::Inner::Inner(QWidget *parent, SessionsBox::List *list, SessionsBox: , _list(list) , _current(current) , _terminating(0) -, _terminateAll(this, lang(lng_sessions_terminate_all), st::redBoxLinkButton) +, _terminateAll(this, lang(lng_sessions_terminate_all), st::sessionTerminateAllButton) , _terminateBox(0) { - connect(&_terminateAll, SIGNAL(clicked()), this, SLOT(onTerminateAll())); - _terminateAll.hide(); + connect(_terminateAll, SIGNAL(clicked()), this, SLOT(onTerminateAll())); + _terminateAll->hide(); setAttribute(Qt::WA_OpaquePaintEvent); } @@ -320,7 +320,7 @@ void SessionsBox::Inner::paintEvent(QPaintEvent *e) { void SessionsBox::Inner::onTerminate() { for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) { - if (i.value()->getState() & Button::StateOver) { + if (i.value()->getState() & Ui::AbstractButton::StateOver) { _terminating = i.key(); if (_terminateBox) _terminateBox->deleteLater(); @@ -399,14 +399,14 @@ bool SessionsBox::Inner::terminateAllFail(const RPCError &error) { } void SessionsBox::Inner::resizeEvent(QResizeEvent *e) { - _terminateAll.moveToLeft(st::sessionPadding.left(), st::sessionCurrentPadding.top() + st::sessionHeight + st::sessionCurrentPadding.bottom()); + _terminateAll->moveToLeft(st::sessionPadding.left(), st::sessionCurrentPadding.top() + st::sessionHeight + st::sessionCurrentPadding.bottom()); } void SessionsBox::Inner::listUpdated() { if (_list->isEmpty()) { - _terminateAll.hide(); + _terminateAll->hide(); } else { - _terminateAll.show(); + _terminateAll->show(); } for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) { i.value()->move(0, -1); diff --git a/Telegram/SourceFiles/boxes/sessionsbox.h b/Telegram/SourceFiles/boxes/sessionsbox.h index 0b32c9bad..b3d8c96df 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.h +++ b/Telegram/SourceFiles/boxes/sessionsbox.h @@ -27,6 +27,8 @@ class ConfirmBox; namespace Ui { class IconButton; +class LinkButton; +class RoundButton; } // namespace Ui class SessionsBox : public ScrollableBox, public RPCSender { @@ -68,7 +70,7 @@ private: class Inner; ChildWidget _inner; ScrollableBoxShadow _shadow; - BoxButton _done; + ChildWidget _done; SingleTimer _shortPollTimer; mtpRequestId _shortPollRequest; @@ -114,7 +116,7 @@ private: TerminateButtons _terminateButtons; uint64 _terminating; - LinkButton _terminateAll; + ChildWidget _terminateAll; ConfirmBox *_terminateBox; }; diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 6969a24b0..f0d1ee119 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -35,6 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/toast/toast.h" #include "ui/widgets/multi_select.h" #include "history/history_media_types.h" +#include "ui/widgets/buttons.h" #include "boxes/contactsbox.h" ShareBox::ShareBox(CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback) : ItemListBox(st::boxScroll) diff --git a/Telegram/SourceFiles/boxes/sharebox.h b/Telegram/SourceFiles/boxes/sharebox.h index 196a6912b..f986e14b6 100644 --- a/Telegram/SourceFiles/boxes/sharebox.h +++ b/Telegram/SourceFiles/boxes/sharebox.h @@ -37,6 +37,7 @@ struct PeerUpdate; namespace Ui { class MultiSelect; +class RoundButton; } // namespace Ui QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId); @@ -90,9 +91,9 @@ private: ChildWidget _inner; ChildWidget _select; - ChildWidget _copy; - ChildWidget _share; - ChildWidget _cancel; + ChildWidget _copy; + ChildWidget _share; + ChildWidget _cancel; ChildWidget _topShadow; ChildWidget _bottomShadow; diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index c0093b7ce..9fe45983f 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -31,6 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_boxes.h" #include "styles/style_stickers.h" +#include "ui/widgets/buttons.h" namespace { @@ -611,11 +612,8 @@ void StickersBox::Inner::paintRow(Painter &p, int32 index) { bool removeDown = removeSel && (index == _actionDown); p.setFont(removeSel ? st::linkOverFont : st::linkFont); - if (removeDown) { - p.setPen(st::btnDefLink.downColor); - } else { - p.setPen(st::btnDefLink.color); - } + p.setPen(removeDown ? st::defaultLinkButton.downColor : st::defaultLinkButton.color); + int32 remWidth = s->recent ? _clearWidth : (s->disabled ? (s->official ? _restoreWidth : _returnWidth) : _removeWidth); QString remText = lang(s->recent ? lng_stickers_clear_recent : (s->disabled ? (s->official ? lng_stickers_restore : lng_stickers_return) : lng_stickers_remove)); p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), remText, remWidth); diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h index 31bc703b1..aed6e99f6 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.h +++ b/Telegram/SourceFiles/boxes/stickers_box.h @@ -26,6 +26,7 @@ class ConfirmBox; namespace Ui { class PlainShadow; +class RoundButton; } // namespace Ui class StickersBox : public ItemListBox, public RPCSender { @@ -81,8 +82,8 @@ private: class Inner; ChildWidget _inner; - ChildWidget _save = { nullptr }; - ChildWidget _cancel = { nullptr }; + ChildWidget _save = { nullptr }; + ChildWidget _cancel = { nullptr }; OrderedSet _disenableRequests; mtpRequestId _reorderRequest = 0; ChildWidget _topShadow = { nullptr }; diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 831962c75..1c4396e3a 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -31,6 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_boxes.h" #include "styles/style_stickers.h" +#include "ui/widgets/buttons.h" StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::stickersScroll) , _inner(this, set) @@ -42,12 +43,12 @@ StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st:: setMaxHeight(st::stickersMaxHeight); connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); - init(_inner, st::boxButtonPadding.bottom() + _cancel.height() + st::boxButtonPadding.top()); + init(_inner, st::boxButtonPadding.bottom() + _cancel->height() + st::boxButtonPadding.top()); - connect(&_add, SIGNAL(clicked()), this, SLOT(onAddStickers())); - connect(&_share, SIGNAL(clicked()), this, SLOT(onShareStickers())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_add, SIGNAL(clicked()), this, SLOT(onAddStickers())); + connect(_share, SIGNAL(clicked()), this, SLOT(onShareStickers())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_done, SIGNAL(clicked()), this, SLOT(onClose())); connect(_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons())); connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); @@ -81,7 +82,7 @@ void StickerSetBox::onShareStickers() { } void StickerSetBox::onUpdateButtons() { - if (!_cancel.isHidden() || !_done.isHidden()) { + if (!_cancel->isHidden() || !_done->isHidden()) { showAll(); } } @@ -98,27 +99,27 @@ void StickerSetBox::showAll() { if (_inner->loaded()) { _shadow.show(); if (_inner->notInstalled()) { - _add.show(); - _cancel.show(); - _share.hide(); - _done.hide(); + _add->show(); + _cancel->show(); + _share->hide(); + _done->hide(); } else if (_inner->official()) { - _add.hide(); - _share.hide(); - _cancel.hide(); - _done.show(); + _add->hide(); + _share->hide(); + _cancel->hide(); + _done->show(); } else { - _share.show(); - _cancel.show(); - _add.hide(); - _done.hide(); + _share->show(); + _cancel->show(); + _add->hide(); + _done->hide(); } } else { _shadow.hide(); - _add.hide(); - _share.hide(); - _cancel.show(); - _done.hide(); + _add->hide(); + _share->hide(); + _cancel->show(); + _done->hide(); } resizeEvent(0); update(); @@ -134,16 +135,16 @@ void StickerSetBox::paintEvent(QPaintEvent *e) { void StickerSetBox::resizeEvent(QResizeEvent *e) { ScrollableBox::resizeEvent(e); _inner->resize(width(), _inner->height()); - _shadow.setGeometry(0, height() - st::boxButtonPadding.bottom() - _cancel.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); - _add.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _add.height()); - _share.moveToRight(st::boxButtonPadding.right(), _add.y()); - _done.moveToRight(st::boxButtonPadding.right(), _add.y()); - if (_add.isHidden() && _share.isHidden()) { - _cancel.moveToRight(st::boxButtonPadding.right(), _add.y()); - } else if (_add.isHidden()) { - _cancel.moveToRight(st::boxButtonPadding.right() + _share.width() + st::boxButtonPadding.left(), _add.y()); + _shadow.setGeometry(0, height() - st::boxButtonPadding.bottom() - _cancel->height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); + _add->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _add->height()); + _share->moveToRight(st::boxButtonPadding.right(), _add->y()); + _done->moveToRight(st::boxButtonPadding.right(), _add->y()); + if (_add->isHidden() && _share->isHidden()) { + _cancel->moveToRight(st::boxButtonPadding.right(), _add->y()); + } else if (_add->isHidden()) { + _cancel->moveToRight(st::boxButtonPadding.right() + _share->width() + st::boxButtonPadding.left(), _add->y()); } else { - _cancel.moveToRight(st::boxButtonPadding.right() + _add.width() + st::boxButtonPadding.left(), _add.y()); + _cancel->moveToRight(st::boxButtonPadding.right() + _add->width() + st::boxButtonPadding.left(), _add->y()); } } diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h index 53932eff3..7c052f622 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.h +++ b/Telegram/SourceFiles/boxes/stickersetbox.h @@ -27,6 +27,7 @@ class ConfirmBox; namespace Ui { class PlainShadow; +class RoundButton; } // namespace Ui class StickerSetBox : public ScrollableBox, public RPCSender { @@ -59,7 +60,10 @@ private: class Inner; ChildWidget _inner; ScrollableBoxShadow _shadow; - BoxButton _add, _share, _cancel, _done; + ChildWidget _add; + ChildWidget _share; + ChildWidget _cancel; + ChildWidget _done; QString _title; }; diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp index aa4365cf6..d4c1a964c 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.cpp +++ b/Telegram/SourceFiles/boxes/usernamebox.cpp @@ -19,18 +19,20 @@ 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/usernamebox.h" +#include "lang.h" #include "application.h" -#include "usernamebox.h" #include "mainwidget.h" #include "mainwindow.h" +#include "ui/widgets/buttons.h" +#include "styles/style_boxes.h" UsernameBox::UsernameBox() : AbstractBox(st::boxWidth), _save(this, lang(lng_settings_save), st::defaultBoxButton), _cancel(this, lang(lng_cancel), st::cancelBoxButton), _username(this, st::defaultInputField, qsl("@username"), App::self()->username, false), -_link(this, QString(), st::defaultBoxLinkButton), +_link(this, QString(), st::boxLinkButton), _saveRequestId(0), _checkRequestId(0), _about(st::boxWidth - st::usernamePadding.left()) { setBlockTitle(true); @@ -39,15 +41,15 @@ _about(st::boxWidth - st::usernamePadding.left()) { textstyleSet(&st::usernameTextStyle); _about.setRichText(st::boxTextFont, lang(lng_username_about)); - resizeMaxHeight(st::boxWidth, titleHeight() + st::usernamePadding.top() + _username.height() + st::usernameSkip + _about.countHeight(st::boxWidth - st::usernamePadding.left()) + 3 * st::usernameTextStyle.lineHeight + st::usernamePadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + resizeMaxHeight(st::boxWidth, titleHeight() + st::usernamePadding.top() + _username->height() + st::usernameSkip + _about.countHeight(st::boxWidth - st::usernamePadding.left()) + 3 * st::usernameTextStyle.lineHeight + st::usernamePadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom()); textstyleRestore(); - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_username, SIGNAL(changed()), this, SLOT(onChanged())); - connect(&_username, SIGNAL(submitted(bool)), this, SLOT(onSave())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_username, SIGNAL(changed()), this, SLOT(onChanged())); + connect(_username, SIGNAL(submitted(bool)), this, SLOT(onSave())); - connect(&_link, SIGNAL(clicked()), this, SLOT(onLinkClick())); + connect(_link, SIGNAL(clicked()), this, SLOT(onLinkClick())); _checkTimer.setSingleShot(true); connect(&_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck())); @@ -56,16 +58,16 @@ _about(st::boxWidth - st::usernamePadding.left()) { } void UsernameBox::showAll() { - _username.show(); - _save.show(); - _cancel.show(); + _username->show(); + _save->show(); + _cancel->show(); updateLinkText(); AbstractBox::showAll(); } void UsernameBox::doSetInnerFocus() { - _username.setFocus(); + _username->setFocus(); } void UsernameBox::paintEvent(QPaintEvent *e) { @@ -77,25 +79,25 @@ void UsernameBox::paintEvent(QPaintEvent *e) { p.setFont(st::boxTextFont); if (!_copiedTextLink.isEmpty()) { p.setPen(st::usernameDefaultFg); - p.drawTextLeft(st::usernamePadding.left(), _username.y() + _username.height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _copiedTextLink); + p.drawTextLeft(st::usernamePadding.left(), _username->y() + _username->height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _copiedTextLink); } else if (!_errorText.isEmpty()) { p.setPen(st::boxTextFgError); - p.drawTextLeft(st::usernamePadding.left(), _username.y() + _username.height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _errorText); + p.drawTextLeft(st::usernamePadding.left(), _username->y() + _username->height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _errorText); } else if (!_goodText.isEmpty()) { p.setPen(st::boxTextFgGood); - p.drawTextLeft(st::usernamePadding.left(), _username.y() + _username.height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _goodText); + p.drawTextLeft(st::usernamePadding.left(), _username->y() + _username->height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _goodText); } else { p.setPen(st::usernameDefaultFg); - p.drawTextLeft(st::usernamePadding.left(), _username.y() + _username.height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), lang(lng_username_choose)); + p.drawTextLeft(st::usernamePadding.left(), _username->y() + _username->height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), lang(lng_username_choose)); } p.setPen(st::boxTextFg); textstyleSet(&st::usernameTextStyle); int32 availw = st::boxWidth - st::usernamePadding.left(), h = _about.countHeight(availw); - _about.drawLeft(p, st::usernamePadding.left(), _username.y() + _username.height() + st::usernameSkip, availw, width()); + _about.drawLeft(p, st::usernamePadding.left(), _username->y() + _username->height() + st::usernameSkip, availw, width()); textstyleRestore(); - int32 linky = _username.y() + _username.height() + st::usernameSkip + h + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2); - if (_link.isHidden()) { + int32 linky = _username->y() + _username->height() + st::usernameSkip + h + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2); + if (_link->isHidden()) { p.drawTextLeft(st::usernamePadding.left(), linky, width(), lang(lng_username_link_willbe)); p.setPen(st::usernameDefaultFg); p.drawTextLeft(st::usernamePadding.left(), linky + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2), width(), qsl("https://telegram.me/username")); @@ -105,17 +107,17 @@ void UsernameBox::paintEvent(QPaintEvent *e) { } void UsernameBox::resizeEvent(QResizeEvent *e) { - _username.resize(width() - st::usernamePadding.left() - st::usernamePadding.right(), _username.height()); - _username.moveToLeft(st::usernamePadding.left(), titleHeight() + st::usernamePadding.top()); + _username->resize(width() - st::usernamePadding.left() - st::usernamePadding.right(), _username->height()); + _username->moveToLeft(st::usernamePadding.left(), titleHeight() + st::usernamePadding.top()); textstyleSet(&st::usernameTextStyle); int32 availw = st::boxWidth - st::usernamePadding.left(), h = _about.countHeight(availw); textstyleRestore(); - int32 linky = _username.y() + _username.height() + st::usernameSkip + h + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2); - _link.moveToLeft(st::usernamePadding.left(), linky + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2)); + int32 linky = _username->y() + _username->height() + st::usernameSkip + h + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2); + _link->moveToLeft(st::usernamePadding.left(), linky + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2)); - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); AbstractBox::resizeEvent(e); } @@ -199,21 +201,21 @@ bool UsernameBox::onUpdateFail(const RPCError &error) { onClose(); return true; } else if (err == qstr("USERNAME_INVALID")) { - _username.setFocus(); - _username.showError(); + _username->setFocus(); + _username->showError(); _copiedTextLink = QString(); _errorText = lang(lng_username_invalid); update(); return true; } else if (err == qstr("USERNAME_OCCUPIED") || err == qstr("USERNAMES_UNAVAILABLE")) { - _username.setFocus(); - _username.showError(); + _username->setFocus(); + _username->showError(); _copiedTextLink = QString(); _errorText = lang(lng_username_occupied); update(); return true; } - _username.setFocus(); + _username->setFocus(); return true; } @@ -245,25 +247,25 @@ bool UsernameBox::onCheckFail(const RPCError &error) { } _goodText = QString(); _copiedTextLink = QString(); - _username.setFocus(); + _username->setFocus(); return true; } QString UsernameBox::getName() const { - return _username.text().replace('@', QString()).trimmed(); + return _username->text().replace('@', QString()).trimmed(); } void UsernameBox::updateLinkText() { QString uname = getName(); - _link.setText(st::boxTextFont->elided(qsl("https://telegram.me/") + uname, st::boxWidth - st::usernamePadding.left() - st::usernamePadding.right())); + _link->setText(st::boxTextFont->elided(qsl("https://telegram.me/") + uname, st::boxWidth - st::usernamePadding.left() - st::usernamePadding.right())); if (uname.isEmpty()) { - if (!_link.isHidden()) { - _link.hide(); + if (!_link->isHidden()) { + _link->hide(); update(); } } else { - if (_link.isHidden()) { - _link.show(); + if (_link->isHidden()) { + _link->show(); update(); } } diff --git a/Telegram/SourceFiles/boxes/usernamebox.h b/Telegram/SourceFiles/boxes/usernamebox.h index 1af61399d..05beb5067 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.h +++ b/Telegram/SourceFiles/boxes/usernamebox.h @@ -22,6 +22,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" +namespace Ui { +class RoundButton; +class LinkButton; +} // namespace Ui + class UsernameBox : public AbstractBox, public RPCSender { Q_OBJECT @@ -53,9 +58,10 @@ private: QString getName() const; void updateLinkText(); - BoxButton _save, _cancel; - UsernameInput _username; - LinkButton _link; + ChildWidget _save; + ChildWidget _cancel; + ChildWidget _username; + ChildWidget _link; mtpRequestId _saveRequestId, _checkRequestId; QString _sentUsername, _checkUsername, _errorText, _goodText, _copiedTextLink; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index a50ba0084..79226dfb5 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -162,7 +162,7 @@ historySendingInvertedIcon: icon {{ "dialogs_sending", #ffffffc8, point(5px, 5px historyViewsSendingIcon: icon {{ "dialogs_sending", #a0adb5, point(3px, 0px) }}; historyViewsSendingInvertedIcon: icon {{ "dialogs_sending", #ffffffc8, point(3px, 0px) }}; -dialogsUpdateButton: flatButton { +dialogsUpdateButton: FlatButton { duration: 0; cursor: cursor(pointer); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 780e1355e..779b7a2a1 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" #include "styles/style_stickers.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" #include "data/data_drafts.h" #include "lang.h" @@ -54,7 +54,7 @@ DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(pare connect(main, SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&))); connect(main, SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(onPeerPhotoChanged(PeerData*))); connect(main, SIGNAL(dialogRowReplaced(Dialogs::Row*,Dialogs::Row*)), this, SLOT(onDialogRowReplaced(Dialogs::Row*,Dialogs::Row*))); - connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); + connect(_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); connect(_cancelSearchInPeer, SIGNAL(clicked()), this, SIGNAL(cancelSearchInPeer())); _cancelSearchInPeer->hide(); @@ -410,7 +410,7 @@ void DialogsInner::mousePressEvent(QMouseEvent *e) { } void DialogsInner::resizeEvent(QResizeEvent *e) { - _addContactLnk.move((width() - _addContactLnk.width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); + _addContactLnk->move((width() - _addContactLnk->width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); _cancelSearchInPeer->move(width() - st::dialogsPadding.x() - st::dialogsCancelSearch.width, (st::dialogsRowHeight - st::dialogsCancelSearch.height) / 2); } @@ -1092,16 +1092,16 @@ void DialogsInner::refresh(bool toTop) { if (shownDialogs()->isEmpty()) { h = st::noContactsHeight; if (cContactsReceived()) { - if (_addContactLnk.isHidden()) _addContactLnk.show(); + if (_addContactLnk->isHidden()) _addContactLnk->show(); } else { - if (!_addContactLnk.isHidden()) _addContactLnk.hide(); + if (!_addContactLnk->isHidden()) _addContactLnk->hide(); } } else { h = dialogsOffset() + shownDialogs()->size() * st::dialogsRowHeight; - if (!_addContactLnk.isHidden()) _addContactLnk.hide(); + if (!_addContactLnk->isHidden()) _addContactLnk->hide(); } } else { - if (!_addContactLnk.isHidden()) _addContactLnk.hide(); + if (!_addContactLnk->isHidden()) _addContactLnk->hide(); if (_state == FilteredState) { h = searchedOffset() + (_searchResults.count() * st::dialogsRowHeight) + ((_searchResults.isEmpty() && !_searchInPeer) ? -st::searchedBarHeight : 0); } else if (_state == SearchedState) { diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index cac76a09b..a708116ca 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -32,6 +32,8 @@ namespace Ui { class IconButton; class PopupMenu; class DropdownMenu; +class FlatButton; +class LinkButton; } // namespace Ui enum DialogsSearchRequestType { @@ -206,7 +208,7 @@ private: void paintDialog(QPainter &p, Dialogs::Row *dialog); - LinkButton _addContactLnk; + ChildWidget _addContactLnk; ChildWidget _cancelSearchInPeer; bool _overDelete = false; @@ -335,7 +337,7 @@ private: ChildWidget _lockUnlock; ChildWidget _scroll; ChildWidget _inner; - ChildWidget _updateTelegram = { nullptr }; + ChildWidget _updateTelegram = { nullptr }; Animation _a_show; QPixmap _cacheUnder, _cacheOver; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 961d89d65..70b84ef5b 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -157,7 +157,7 @@ historyComposeFieldMaxHeight: 224px; historySendPadding: 9px; historySendRight: 2px; -historyComposeButton: flatButton { +historyComposeButton: FlatButton { duration: 200; cursor: cursor(pointer); @@ -177,7 +177,7 @@ historyComposeButton: flatButton { font: semiboldFont; overFont: semiboldFont; } -historyUnblock: flatButton(historyComposeButton) { +historyUnblock: FlatButton(historyComposeButton) { color: #d15948; overColor: #d15948; } @@ -307,3 +307,38 @@ topBarMenuToggle: IconButton(topBarSearch) { iconPosition: point(18px, 17px); iconPositionDown: point(18px, 17px); } +reportSpamHide: FlatButton { + duration: 200; + cursor: cursor(pointer); + + color: btnYesColor; + overColor: btnYesHover; + + bgColor: transparent; + overBgColor: transparent; + + width: -40px; + height: 46px; + + textTop: 15px; + overTextTop: 15px; + downTextTop: 16px; + + font: font(fsize); + overFont: font(fsize underline); +} +reportSpamButton: FlatButton(reportSpamHide) { + textTop: 6px; + overTextTop: 6px; + downTextTop: 7px; + + width: -50px; + height: 30px; + + bgColor: #888888; + overBgColor: #7b7b7b; +} +reportSpamSeparator: 30px; +reportSpamBg: #fffffff0; +reportSpamFg: #000000; + diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index cceba28e3..e250eb2bb 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" #include "ui/toast/toast.h" #include "ui/buttons/history_down_button.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/inner_dropdown.h" #include "ui/widgets/dropdown_menu.h" #include "inline_bots/inline_bot_result.h" @@ -2911,6 +2911,29 @@ HistoryHider::~HistoryHider() { parent()->noHider(this); } +class SilentToggle : public Ui::IconButton, public Ui::AbstractTooltipShower { +public: + SilentToggle(QWidget *parent); + + void setChecked(bool checked); + bool checked() const { + return _checked; + } + + // AbstractTooltipShower interface + QString tooltipText() const override; + QPoint tooltipPos() const override; + +protected: + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + +private: + bool _checked = false; + +}; + SilentToggle::SilentToggle(QWidget *parent) : IconButton(parent, st::historySilentToggle) { setMouseTracking(true); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 37df22358..0224b08ed 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -23,7 +23,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localimageloader.h" #include "ui/effects/rect_shadow.h" #include "ui/widgets/tooltip.h" -#include "ui/buttons/icon_button.h" #include "history/history_common.h" #include "history/field_autocomplete.h" #include "window/section_widget.h" @@ -44,10 +43,14 @@ class PopupMenu; class IconButton; class HistoryDownButton; class EmojiButton; +class FlatButton; +class LinkButton; +class RoundButton; } // namespace Ui class DragArea; class EmojiPan; +class SilentToggle; class HistoryWidget; class HistoryInner : public TWidget, public Ui::AbstractTooltipShower, private base::Subscriber { @@ -350,9 +353,9 @@ protected: void paintEvent(QPaintEvent *e) override; private: - ChildWidget _report; - ChildWidget _hide; - ChildWidget _clear; + ChildWidget _report; + ChildWidget _hide; + ChildWidget _clear; }; @@ -489,8 +492,8 @@ private: QString _shareUrl, _shareText; QString _botAndQuery; - ChildWidget _send; - ChildWidget _cancel; + ChildWidget _send; + ChildWidget _cancel; PeerData *_offered = nullptr; anim::fvalue a_opacity; @@ -511,29 +514,6 @@ private: }; -class SilentToggle : public Ui::IconButton, public Ui::AbstractTooltipShower { -public: - SilentToggle(QWidget *parent); - - void setChecked(bool checked); - bool checked() const { - return _checked; - } - - // AbstractTooltipShower interface - QString tooltipText() const override; - QPoint tooltipPos() const override; - -protected: - void mouseMoveEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void leaveEvent(QEvent *e) override; - -private: - bool _checked = false; - -}; - EntitiesInText entitiesFromTextTags(const TextWithTags::Tags &tags); TextWithTags::Tags textTagsFromEntities(const EntitiesInText &entities); @@ -1099,10 +1079,10 @@ private: ReportSpamPanel _reportSpamPanel; ChildWidget _send; - ChildWidget _unblock; - ChildWidget _botStart; - ChildWidget _joinChannel; - ChildWidget _muteUnmute; + ChildWidget _unblock; + ChildWidget _botStart; + ChildWidget _joinChannel; + ChildWidget _muteUnmute; mtpRequestId _unblockRequest = 0; mtpRequestId _reportSpamRequest = 0; ChildWidget _attachToggle; diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 22709b21b..70881640b 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -43,6 +43,14 @@ introCountry: countryInput { align: align(left); } +introIcon: icon {{ "intro_logo", #008ed5 }}; + +introResetLink: LinkButton(defaultLinkButton) { + color: #d15948; + overColor: #d15948; + downColor: #db6352; +} + introBtnTop: 288px; introSkip: 25px; introFinishSkip: 15px; @@ -51,7 +59,7 @@ introHeaderFont: font(24px); introHeaderSkip: 14px; introIconSkip: 50px; introFont: font(16px); -introLink: linkButton(btnDefLink) { +introLink: LinkButton(defaultLinkButton) { font: introFont; overFont: font(16px underline); } diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index 0c403eb5a..982fe678d 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "intro/introsignup.h" #include "intro/intropwdcheck.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" #include "styles/style_intro.h" CodeInput::CodeInput(QWidget *parent, const style::flatInput &st, const QString &ph) : FlatInput(parent, st, ph) { @@ -86,7 +86,6 @@ IntroCode::IntroCode(IntroWidget *parent) : IntroStep(parent) , _checkRequest(this) { setGeometry(parent->innerRect()); - _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitCode())); connect(_code, SIGNAL(changed()), this, SLOT(onInputChange())); connect(_callTimer, SIGNAL(timeout()), this, SLOT(onSendCall())); diff --git a/Telegram/SourceFiles/intro/introcode.h b/Telegram/SourceFiles/intro/introcode.h index 49e049bcd..d3f894ccb 100644 --- a/Telegram/SourceFiles/intro/introcode.h +++ b/Telegram/SourceFiles/intro/introcode.h @@ -20,13 +20,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include -#include "ui/flatbutton.h" -#include "ui/flatinput.h" #include "intro/introwidget.h" +class FlatInput; + namespace Ui { class RoundButton; +class LinkButton; } // namespace Ui class CodeInput final : public FlatInput { @@ -91,7 +91,7 @@ private: ChildWidget _next; Text _desc; - ChildWidget _noTelegramCode; + ChildWidget _noTelegramCode; mtpRequestId _noTelegramCodeRequestId; QRect _textRect; diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index 9f68aacd5..11c3311b6 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "intro/introcode.h" #include "styles/style_intro.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" namespace { class SignUpClickHandler : public LeftButtonClickHandler { @@ -56,7 +56,6 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) setVisible(false); setGeometry(parent->innerRect()); - _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitPhone())); connect(_phone, SIGNAL(voidBackspace(QKeyEvent*)), _code, SLOT(startErasing(QKeyEvent*))); connect(_country, SIGNAL(codeChanged(const QString &)), _code, SLOT(codeSelected(const QString &))); diff --git a/Telegram/SourceFiles/intro/introphone.h b/Telegram/SourceFiles/intro/introphone.h index cf4786e73..019251705 100644 --- a/Telegram/SourceFiles/intro/introphone.h +++ b/Telegram/SourceFiles/intro/introphone.h @@ -20,7 +20,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/flatbutton.h" #include "ui/countryinput.h" #include "ui/flatlabel.h" #include "intro/introwidget.h" diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index d04c3dadd..0dc6ee140 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "application.h" #include "intro/introsignup.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) @@ -40,12 +40,11 @@ IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent) , _codeField(this, st::inpIntroPassword, lang(lng_signin_code)) , _toRecover(this, lang(lng_signin_recover)) , _toPassword(this, lang(lng_signin_try_password)) -, _reset(this, lang(lng_signin_reset_account), st::btnRedLink) +, _reset(this, lang(lng_signin_reset_account), st::introResetLink) , _checkRequest(this) { setVisible(false); setGeometry(parent->innerRect()); - _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitPwd())); connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); connect(_toRecover, SIGNAL(clicked()), this, SLOT(onToRecover())); diff --git a/Telegram/SourceFiles/intro/intropwdcheck.h b/Telegram/SourceFiles/intro/intropwdcheck.h index 01c37b7cf..8d4d1d1c0 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.h +++ b/Telegram/SourceFiles/intro/intropwdcheck.h @@ -20,11 +20,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/flatinput.h" #include "intro/introwidget.h" +class FlatInput; + namespace Ui { class RoundButton; +class LinkButton; } // namespace Ui class IntroPwdCheck final : public IntroStep { @@ -80,9 +82,9 @@ private: ChildWidget _pwdField; ChildWidget _codeField; - ChildWidget _toRecover; - ChildWidget _toPassword; - ChildWidget _reset; + ChildWidget _toRecover; + ChildWidget _toPassword; + ChildWidget _reset; mtpRequestId _sentRequest = 0; Text _hintText; diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index 0f674e5d5..450f28675 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/photocropbox.h" #include "lang.h" #include "application.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" IntroSignup::IntroSignup(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) @@ -42,7 +42,6 @@ IntroSignup::IntroSignup(IntroWidget *parent) : IntroStep(parent) setVisible(false); setGeometry(parent->innerRect()); - _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitName())); connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); diff --git a/Telegram/SourceFiles/intro/introsignup.h b/Telegram/SourceFiles/intro/introsignup.h index 3b7535ec8..8bfe59bfa 100644 --- a/Telegram/SourceFiles/intro/introsignup.h +++ b/Telegram/SourceFiles/intro/introsignup.h @@ -20,7 +20,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/flatbutton.h" #include "ui/flatinput.h" #include "intro/introwidget.h" diff --git a/Telegram/SourceFiles/intro/introstart.cpp b/Telegram/SourceFiles/intro/introstart.cpp index 6c24ab9e8..34e1d4520 100644 --- a/Telegram/SourceFiles/intro/introstart.cpp +++ b/Telegram/SourceFiles/intro/introstart.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "intro/introphone.h" #include "langloaderplain.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent) , _intro(this, lang(lng_intro), FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle) @@ -53,7 +53,6 @@ IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent) setGeometry(parent->innerRect()); - _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); connect(_next, SIGNAL(clicked()), parent, SLOT(onStepSubmit())); connect(_changeLang, SIGNAL(clicked()), parent, SLOT(onChangeLang())); @@ -74,7 +73,7 @@ void IntroStart::paintEvent(QPaintEvent *e) { p.setPen(st::introHeaderFg); p.drawText((width() - _headerWidth) / 2, hy, qsl("Telegram Desktop")); - st::aboutIcon.paint(p, QPoint((width() - st::aboutIcon.width()) / 2, hy - st::introIconSkip - st::aboutIcon.height()), width()); + st::introIcon.paint(p, QPoint((width() - st::introIcon.width()) / 2, hy - st::introIconSkip - st::introIcon.height()), width()); } void IntroStart::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/intro/introstart.h b/Telegram/SourceFiles/intro/introstart.h index 9ad5733b6..a88be1238 100644 --- a/Telegram/SourceFiles/intro/introstart.h +++ b/Telegram/SourceFiles/intro/introstart.h @@ -21,9 +21,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "intro/introwidget.h" -#include "ui/flatlabel.h" + +class FlatLabel; namespace Ui { +class LinkButton; class RoundButton; } // namespace Ui @@ -39,7 +41,7 @@ public: private: ChildWidget _intro; - ChildWidget _changeLang; + ChildWidget _changeLang; ChildWidget _next; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index aea428d67..c41b8ac55 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -32,7 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "application.h" #include "ui/text/text.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "ui/effects/widget_fade_wrap.h" #include "styles/style_intro.h" #include "autoupdater.h" diff --git a/Telegram/SourceFiles/intro/introwidget.h b/Telegram/SourceFiles/intro/introwidget.h index 24b6bd452..36072d578 100644 --- a/Telegram/SourceFiles/intro/introwidget.h +++ b/Telegram/SourceFiles/intro/introwidget.h @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class IconButton; +class RoundButton; template class WidgetFadeWrap; } // namespace Ui @@ -152,8 +153,8 @@ private: QString _firstname, _lastname; ChildWidget> _back; - ChildWidget _settings; - ChildWidget _update = { nullptr }; + ChildWidget _settings; + ChildWidget _update = { nullptr }; float64 _backFrom = 0.; float64 _backTo = 0.; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 720c031ed..32f8fba2a 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_dialogs.h" #include "ui/buttons/peer_avatar_button.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "window/section_memento.h" #include "window/section_widget.h" diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index e7e665031..bb6f4982b 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_dialogs.h" #include "styles/style_window.h" #include "ui/widgets/popup_menu.h" +#include "ui/widgets/buttons.h" #include "core/zlib_help.h" #include "lang.h" #include "shortcuts.h" @@ -52,7 +53,7 @@ ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const Q , _shadow(st::boxShadow) , _reconnect(this, QString()) { set(text, reconnect); - connect(&_reconnect, SIGNAL(clicked()), this, SLOT(onReconnect())); + connect(_reconnect, SIGNAL(clicked()), this, SLOT(onReconnect())); } void ConnectingWidget::set(const QString &text, const QString &reconnect) { @@ -60,12 +61,12 @@ void ConnectingWidget::set(const QString &text, const QString &reconnect) { _textWidth = st::linkFont->width(_text) + st::linkFont->spacew; int32 _reconnectWidth = 0; if (reconnect.isEmpty()) { - _reconnect.hide(); + _reconnect->hide(); } else { - _reconnect.setText(reconnect); - _reconnect.show(); - _reconnect.move(st::connectingPadding.left() + _textWidth, st::boxShadow.height() + st::connectingPadding.top()); - _reconnectWidth = _reconnect.width(); + _reconnect->setText(reconnect); + _reconnect->show(); + _reconnect->move(st::connectingPadding.left() + _textWidth, st::boxShadow.height() + st::connectingPadding.top()); + _reconnectWidth = _reconnect->width(); } resize(st::connectingPadding.left() + _textWidth + _reconnectWidth + st::connectingPadding.right() + st::boxShadow.width(), st::boxShadow.height() + st::connectingPadding.top() + st::linkFont->height + st::connectingPadding.bottom()); update(); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index c6d032f8a..8f53d7d32 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -47,6 +47,10 @@ class WarningWidget; } // namespace Theme } // namespace Window +namespace Ui { +class LinkButton; +} // namespace Ui + class ConnectingWidget : public TWidget { Q_OBJECT @@ -64,7 +68,7 @@ private: Ui::RectShadow _shadow; QString _text; int32 _textWidth; - LinkButton _reconnect; + ChildWidget _reconnect; }; diff --git a/Telegram/SourceFiles/media/player/media_player_button.h b/Telegram/SourceFiles/media/player/media_player_button.h index 1335e55b8..aabf46968 100644 --- a/Telegram/SourceFiles/media/player/media_player_button.h +++ b/Telegram/SourceFiles/media/player/media_player_button.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/button.h" +#include "ui/abstract_button.h" #include "styles/style_media_player.h" namespace Media { diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp index b2e252b1f..3a2eb7d4b 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.cpp +++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/flatlabel.h" #include "ui/widgets/label_simple.h" #include "ui/widgets/media_slider.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "media/media_audio.h" #include "media/view/media_clip_playback.h" #include "media/player/media_player_button.h" @@ -38,7 +38,7 @@ namespace Player { using State = PlayButtonLayout::State; -class CoverWidget::PlayButton : public Button { +class CoverWidget::PlayButton : public Ui::AbstractButton { public: PlayButton(QWidget *parent); @@ -57,7 +57,7 @@ private: }; -CoverWidget::PlayButton::PlayButton(QWidget *parent) : Button(parent) +CoverWidget::PlayButton::PlayButton(QWidget *parent) : Ui::AbstractButton(parent) , _layout(st::mediaPlayerPanelButton, [this] { update(); }) { resize(st::mediaPlayerPanelButtonSize); setCursor(style::cur_pointer); diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp index 237127fa0..062032e71 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/player/media_player_volume_controller.h" #include "media/media_audio.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/media_slider.h" #include "styles/style_media_player.h" #include "styles/style_widgets.h" diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 5597a4509..4e22f965e 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/label_simple.h" #include "ui/widgets/filled_slider.h" #include "ui/widgets/shadow.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "media/media_audio.h" #include "media/view/media_clip_playback.h" #include "media/player/media_player_button.h" @@ -39,7 +39,7 @@ namespace Player { using State = PlayButtonLayout::State; -class Widget::PlayButton : public Button { +class Widget::PlayButton : public Ui::AbstractButton { public: PlayButton(QWidget *parent); @@ -58,7 +58,7 @@ private: }; -Widget::PlayButton::PlayButton(QWidget *parent) : Button(parent) +Widget::PlayButton::PlayButton(QWidget *parent) : Ui::AbstractButton(parent) , _layout(st::mediaPlayerButton, [this] { update(); }) { resize(st::mediaPlayerButtonSize); setCursor(style::cur_pointer); diff --git a/Telegram/SourceFiles/media/view/media_clip_controller.cpp b/Telegram/SourceFiles/media/view/media_clip_controller.cpp index 9b48227c0..9e66e6d66 100644 --- a/Telegram/SourceFiles/media/view/media_clip_controller.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_controller.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/label_simple.h" #include "ui/widgets/media_slider.h" #include "ui/effects/widget_fade_wrap.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "media/media_audio.h" namespace Media { diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 0d0b3ea6a..895e68351 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -115,7 +115,7 @@ mediaviewFileExtPadding: 10px; mediaviewFileLinksTop: 57px; mediaviewFileIconSize: 80px; -mediaviewFileLink: linkButton(btnDefLink) { +mediaviewFileLink: LinkButton(defaultLinkButton) { color: #4595d3; overColor: #4595d3; downColor: #4595d3; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index c28f02360..434c03b1b 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "ui/filedialog.h" #include "ui/widgets/popup_menu.h" +#include "ui/widgets/buttons.h" #include "media/media_clip_reader.h" #include "media/view/media_clip_controller.h" #include "styles/style_mediaview.h" @@ -130,9 +131,9 @@ MediaView::MediaView() : TWidget(App::wnd()) _controlsHideTimer.setSingleShot(true); connect(&_controlsHideTimer, SIGNAL(timeout()), this, SLOT(onHideControls())); - connect(&_docDownload, SIGNAL(clicked()), this, SLOT(onDownload())); - connect(&_docSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs())); - connect(&_docCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel())); + connect(_docDownload, SIGNAL(clicked()), this, SLOT(onDownload())); + connect(_docSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs())); + connect(_docCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel())); _dropdown->setHiddenCallback([this] { dropdownHidden(); }); } @@ -237,7 +238,7 @@ void MediaView::stopGif() { void MediaView::documentUpdated(DocumentData *doc) { if (_doc && _doc == doc && !fileShown()) { - if ((_doc->loading() && _docCancel.isHidden()) || (!_doc->loading() && !_docCancel.isHidden())) { + if ((_doc->loading() && _docCancel->isHidden()) || (!_doc->loading() && !_docCancel->isHidden())) { updateControls(); } else if (_doc->loading()) { updateDocSize(); @@ -289,29 +290,29 @@ void MediaView::updateDocSize() { void MediaView::updateControls() { if (_doc && !fileShown()) { if (_doc->loading()) { - _docDownload.hide(); - _docSaveAs.hide(); - _docCancel.moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); - _docCancel.show(); + _docDownload->hide(); + _docSaveAs->hide(); + _docCancel->moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); + _docCancel->show(); } else { if (_doc->loaded(DocumentData::FilePathResolveChecked)) { - _docDownload.hide(); - _docSaveAs.moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); - _docSaveAs.show(); - _docCancel.hide(); + _docDownload->hide(); + _docSaveAs->moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); + _docSaveAs->show(); + _docCancel->hide(); } else { - _docDownload.moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); - _docDownload.show(); - _docSaveAs.moveToLeft(_docRect.x() + 2.5 * st::mediaviewFilePadding + st::mediaviewFileIconSize + _docDownload.width(), _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); - _docSaveAs.show(); - _docCancel.hide(); + _docDownload->moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); + _docDownload->show(); + _docSaveAs->moveToLeft(_docRect.x() + 2.5 * st::mediaviewFilePadding + st::mediaviewFileIconSize + _docDownload->width(), _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); + _docSaveAs->show(); + _docCancel->hide(); } } updateDocSize(); } else { - _docDownload.hide(); - _docSaveAs.hide(); - _docCancel.hide(); + _docDownload->hide(); + _docSaveAs->hide(); + _docCancel->hide(); } radialStart(); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index b54900c1e..1cdcdcdcd 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -31,6 +31,7 @@ class Controller; namespace Ui { class PopupMenu; +class LinkButton; } // namespace Ui struct AudioPlaybackState; @@ -242,7 +243,9 @@ private: int _docNameWidth = 0, _docSizeWidth = 0, _docExtWidth = 0; QRect _docRect, _docIconRect; int _docThumbx = 0, _docThumby = 0, _docThumbw = 0; - LinkButton _docDownload, _docSaveAs, _docCancel; + ChildWidget _docDownload; + ChildWidget _docSaveAs; + ChildWidget _docCancel; QRect _photoRadialRect; Ui::RadialAnimation _radial; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index d5b19764c..85f060e5d 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/tooltip.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "window/top_bar_widget.h" #include "window/window_theme.h" #include "lang.h" diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 3cc48564f..0c1d3de2e 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "application.h" #include "ui/text/text.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" #include "styles/style_boxes.h" #include "window/slide_animation.h" @@ -39,7 +39,6 @@ PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) connect(_passcode, SIGNAL(changed()), this, SLOT(onChanged())); connect(_passcode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - _submit->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); _submit->setClickedCallback([this] { onSubmit(); }); _logout->setClickedCallback([] { App::wnd()->onLogout(); }); diff --git a/Telegram/SourceFiles/passcodewidget.h b/Telegram/SourceFiles/passcodewidget.h index 6b682ddcc..3ccd192b7 100644 --- a/Telegram/SourceFiles/passcodewidget.h +++ b/Telegram/SourceFiles/passcodewidget.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once namespace Ui { +class LinkButton; class RoundButton; } // namespace Ui @@ -56,7 +57,7 @@ private: ChildWidget _passcode; ChildWidget _submit; - ChildWidget _logout; + ChildWidget _logout; QString _error; }; diff --git a/Telegram/SourceFiles/platform/win/window_title_win.cpp b/Telegram/SourceFiles/platform/win/window_title_win.cpp index 582e8fb28..e948614b9 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.cpp +++ b/Telegram/SourceFiles/platform/win/window_title_win.cpp @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "platform/win/window_title_win.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "styles/style_window.h" diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index 2608aa84b..122ed6f26 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -25,10 +25,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_window.h" #include "profile/profile_cover_drop_area.h" #include "profile/profile_userpic_button.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" #include "ui/filedialog.h" #include "ui/flatlabel.h" -#include "ui/flatbutton.h" #include "observer_peer.h" #include "boxes/confirmbox.h" #include "boxes/contactsbox.h" @@ -354,7 +353,7 @@ void CoverWidget::refreshStatusText() { _statusText = lang(lng_settings_uploading_photo); _statusTextIsOnline = false; if (!_cancelPhotoUpload) { - _cancelPhotoUpload = new LinkButton(this, lang(lng_cancel), st::btnDefLink); + _cancelPhotoUpload.create(this, lang(lng_cancel), st::defaultLinkButton); connect(_cancelPhotoUpload, SIGNAL(clicked()), this, SLOT(onCancelPhotoUpload())); _cancelPhotoUpload->show(); _cancelPhotoUpload->moveToLeft(_statusPosition.x() + st::profileStatusFont->width(_statusText) + st::profileStatusFont->spacew, _statusPosition.y()); @@ -455,7 +454,6 @@ void CoverWidget::clearButtons() { void CoverWidget::addButton(const QString &text, const char *slot, const style::RoundButton *replacementStyle) { auto &buttonStyle = _buttons.isEmpty() ? st::profilePrimaryButton : st::profileSecondaryButton; auto button = new Ui::RoundButton(this, text, buttonStyle); - button->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); connect(button, SIGNAL(clicked()), this, slot); button->show(); diff --git a/Telegram/SourceFiles/profile/profile_cover.h b/Telegram/SourceFiles/profile/profile_cover.h index 3abd8e66e..6c5e925f7 100644 --- a/Telegram/SourceFiles/profile/profile_cover.h +++ b/Telegram/SourceFiles/profile/profile_cover.h @@ -24,10 +24,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" class FlatLabel; -class LinkButton; namespace Ui { class RoundButton; +class LinkButton; } // namespace Ui namespace Notify { @@ -118,7 +118,7 @@ private: ChildWidget _dropArea = { nullptr }; ChildWidget _name; - ChildWidget _cancelPhotoUpload = { nullptr }; + ChildWidget _cancelPhotoUpload = { nullptr }; QPoint _statusPosition; QString _statusText; diff --git a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp index 584b480d7..03927b938 100644 --- a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp +++ b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_profile.h" #include "styles/style_window.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" #include "lang.h" #include "mainwidget.h" #include "boxes/addcontactbox.h" @@ -33,9 +33,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Profile { -class BackButton final : public Button, private base::Subscriber { +class BackButton final : public Ui::AbstractButton, private base::Subscriber { public: - BackButton(QWidget *parent) : Button(parent) { + BackButton(QWidget *parent) : Ui::AbstractButton(parent) { setCursor(style::cur_pointer); subscribe(Adaptive::Changed(), [this] { updateAdaptiveLayout(); }); @@ -58,8 +58,8 @@ protected: Window::TopBarWidget::paintUnreadCounter(p, width()); } - void onStateChanged(int oldState, ButtonStateChangeSource source) override { - if ((_state & Button::StateDown) && !(oldState & Button::StateDown)) { + void onStateChanged(int oldState, StateChangeSource source) override { + if ((_state & StateDown) && !(oldState & StateDown)) { emit clicked(); } } @@ -186,6 +186,7 @@ void FixedBar::addRightAction(RightActionType type, const QString &text, const c _rightActions[_currentAction].type = type; delete _rightActions[_currentAction].button; _rightActions[_currentAction].button = new Ui::RoundButton(this, text, st::profileFixedBarButton); + _rightActions[_currentAction].button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); connect(_rightActions[_currentAction].button, SIGNAL(clicked()), this, slot); bool showButton = !_animatingMode && (type != RightActionType::ShareContact || !_hideShareContactButton); _rightActions[_currentAction].button->setVisible(showButton); diff --git a/Telegram/SourceFiles/profile/profile_settings_widget.cpp b/Telegram/SourceFiles/profile/profile_settings_widget.cpp index c93ee264c..886cf9c8b 100644 --- a/Telegram/SourceFiles/profile/profile_settings_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_settings_widget.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_profile.h" #include "ui/buttons/left_outline_button.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" #include "boxes/confirmbox.h" #include "boxes/contactsbox.h" #include "observer_peer.h" diff --git a/Telegram/SourceFiles/profile/profile_userpic_button.cpp b/Telegram/SourceFiles/profile/profile_userpic_button.cpp index 547d93a10..267c35b0b 100644 --- a/Telegram/SourceFiles/profile/profile_userpic_button.cpp +++ b/Telegram/SourceFiles/profile/profile_userpic_button.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Profile { -UserpicButton::UserpicButton(QWidget *parent, PeerData *peer) : Button(parent), _peer(peer) { +UserpicButton::UserpicButton(QWidget *parent, PeerData *peer) : AbstractButton(parent), _peer(peer) { resize(st::profilePhotoSize, st::profilePhotoSize); processPeerPhoto(); diff --git a/Telegram/SourceFiles/profile/profile_userpic_button.h b/Telegram/SourceFiles/profile/profile_userpic_button.h index 0bec52b7a..ca2d7076a 100644 --- a/Telegram/SourceFiles/profile/profile_userpic_button.h +++ b/Telegram/SourceFiles/profile/profile_userpic_button.h @@ -20,6 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include "ui/abstract_button.h" #include "core/observer.h" namespace Notify { @@ -28,7 +29,7 @@ struct PeerUpdate; namespace Profile { -class UserpicButton final : public Button, private base::Subscriber { +class UserpicButton : public Ui::AbstractButton, private base::Subscriber { public: UserpicButton(QWidget *parent, PeerData *peer); diff --git a/Telegram/SourceFiles/settings/settings_advanced_widget.cpp b/Telegram/SourceFiles/settings/settings_advanced_widget.cpp index 9502a6b14..e7485d0ae 100644 --- a/Telegram/SourceFiles/settings/settings_advanced_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_advanced_widget.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/aboutbox.h" #include "boxes/localstoragebox.h" #include "mainwindow.h" +#include "ui/widgets/buttons.h" namespace Settings { diff --git a/Telegram/SourceFiles/settings/settings_advanced_widget.h b/Telegram/SourceFiles/settings/settings_advanced_widget.h index a6c8b75a2..5fb013d45 100644 --- a/Telegram/SourceFiles/settings/settings_advanced_widget.h +++ b/Telegram/SourceFiles/settings/settings_advanced_widget.h @@ -49,14 +49,14 @@ private: #endif // !TDESKTOP_DISABLE_NETWORK_PROXY void supportGot(const MTPhelp_Support &support); - ChildWidget _manageLocalStorage = { nullptr }; + ChildWidget _manageLocalStorage = { nullptr }; #ifndef TDESKTOP_DISABLE_NETWORK_PROXY ChildWidget _connectionType = { nullptr }; #endif // !TDESKTOP_DISABLE_NETWORK_PROXY - ChildWidget _askQuestion = { nullptr }; - ChildWidget _telegramFAQ = { nullptr }; - ChildWidget _about = { nullptr }; - ChildWidget _logOut = { nullptr }; + ChildWidget _askQuestion = { nullptr }; + ChildWidget _telegramFAQ = { nullptr }; + ChildWidget _about = { nullptr }; + ChildWidget _logOut = { nullptr }; mtpRequestId _supportGetRequest = 0; diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index e40b51c17..f4a0a18fa 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -26,7 +26,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "boxes/backgroundbox.h" #include "ui/effects/widget_slide_wrap.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "localstorage.h" #include "mainwindow.h" #include "window/window_theme.h" @@ -34,8 +35,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Settings { BackgroundRow::BackgroundRow(QWidget *parent) : TWidget(parent) -, _chooseFromGallery(this, lang(lng_settings_bg_from_gallery), st::defaultBoxLinkButton) -, _chooseFromFile(this, lang(lng_settings_bg_from_file), st::defaultBoxLinkButton) +, _chooseFromGallery(this, lang(lng_settings_bg_from_gallery), st::boxLinkButton) +, _chooseFromFile(this, lang(lng_settings_bg_from_file), st::boxLinkButton) , _radial(animation(this, &BackgroundRow::step_radial)) { updateImage(); diff --git a/Telegram/SourceFiles/settings/settings_background_widget.h b/Telegram/SourceFiles/settings/settings_background_widget.h index 6b7d458f2..5793dfa9c 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.h +++ b/Telegram/SourceFiles/settings/settings_background_widget.h @@ -24,14 +24,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/effects/radial_animation.h" #include "ui/filedialog.h" -class LinkButton; - -namespace Ui { -class Checkbox; -template -class WidgetSlideWrap; -} // namespace Ui; - namespace Settings { class BackgroundRow : public TWidget { @@ -60,8 +52,8 @@ private: void step_radial(uint64 ms, bool timer); QPixmap _background; - ChildWidget _chooseFromGallery; - ChildWidget _chooseFromFile; + ChildWidget _chooseFromGallery; + ChildWidget _chooseFromFile; Ui::RadialAnimation _radial; diff --git a/Telegram/SourceFiles/settings/settings_block_widget.cpp b/Telegram/SourceFiles/settings/settings_block_widget.cpp index edfe760a4..ee8fc62d8 100644 --- a/Telegram/SourceFiles/settings/settings_block_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_block_widget.cpp @@ -22,7 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" #include "styles/style_settings.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" namespace Settings { @@ -85,17 +86,17 @@ void BlockWidget::rowHeightUpdated() { } void BlockWidget::createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, bool checked) { - child = new Ui::Checkbox(this, text, checked, st::defaultBoxCheckbox); + child.create(this, text, checked, st::defaultBoxCheckbox); connect(child, SIGNAL(changed()), this, slot); } void BlockWidget::createChildRow(ChildWidget &child, style::margins &margin, const QString &group, int value, const QString &text, const char *slot, bool checked) { - child = new Ui::Radiobutton(this, group, value, text, checked, st::defaultRadiobutton); + child .create(this, group, value, text, checked, st::defaultRadiobutton); connect(child, SIGNAL(changed()), this, slot); } -void BlockWidget::createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, const style::linkButton &st) { - child = new LinkButton(this, text, st); +void BlockWidget::createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, const style::LinkButton &st) { + child .create(this, text, st); connect(child, SIGNAL(clicked()), this, slot); } diff --git a/Telegram/SourceFiles/settings/settings_block_widget.h b/Telegram/SourceFiles/settings/settings_block_widget.h index 92e28a5ab..8d651f827 100644 --- a/Telegram/SourceFiles/settings/settings_block_widget.h +++ b/Telegram/SourceFiles/settings/settings_block_widget.h @@ -21,10 +21,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "core/observer.h" +#include "styles/style_boxes.h" namespace Ui { class Checkbox; class Radiobutton; +class LinkButton; template class WidgetSlideWrap; } // namespace Ui @@ -88,7 +90,7 @@ private: } void createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, bool checked); void createChildRow(ChildWidget &child, style::margins &margin, const QString &group, int value, const QString &text, const char *slot, bool checked); - void createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, const style::linkButton &st = st::defaultBoxLinkButton); + void createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, const style::LinkButton &st = st::boxLinkButton); void addCreatedRow(TWidget *child, const style::margins &margin); void rowHeightUpdated(); @@ -107,7 +109,7 @@ private: !IsWidgetSlideWrap::value && !std_::is_same::value && !std_::is_same::value && - !std_::is_same::value>; + !std_::is_same::value>; template > void createChildRow(ChildWidget &child, style::margins &margin, Args&&... args) { diff --git a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp index c9527e6c8..6e1109246 100644 --- a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp @@ -24,7 +24,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_settings.h" #include "lang.h" #include "ui/effects/widget_slide_wrap.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "ui/flatlabel.h" #include "localstorage.h" #include "mainwidget.h" @@ -39,13 +40,12 @@ namespace Settings { LabeledLink::LabeledLink(QWidget *parent, const QString &label, const QString &text, Type type, const char *slot) : TWidget(parent) , _label(this, label, FlatLabel::InitType::Simple, (type == Type::Primary) ? st::settingsPrimaryLabel : st::labelDefFlat) -, _link(this, text, (type == Type::Primary) ? st::defaultBoxLinkButton : st::btnDefLink) { +, _link(this, text, (type == Type::Primary) ? st::boxLinkButton : st::defaultLinkButton) { connect(_link, SIGNAL(clicked()), parent, slot); } void LabeledLink::setLink(const QString &text) { - _link.destroy(); - _link = new LinkButton(this, text); + _link.create(this, text); } int LabeledLink::naturalWidth() const { @@ -159,7 +159,7 @@ void ChatSettingsWidget::createControls() { addChildRow(_replaceEmoji, marginSub, lang(lng_settings_replace_emojis), SLOT(onReplaceEmoji()), cReplaceEmojis()); style::margins marginList(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip); - addChildRow(_viewList, marginList, slidedPadding, lang(lng_settings_view_emojis), SLOT(onViewList()), st::btnDefLink); + addChildRow(_viewList, marginList, slidedPadding, lang(lng_settings_view_emojis), SLOT(onViewList()), st::defaultLinkButton); if (!cReplaceEmojis()) { _viewList->hideFast(); } diff --git a/Telegram/SourceFiles/settings/settings_chat_settings_widget.h b/Telegram/SourceFiles/settings/settings_chat_settings_widget.h index 2c1da6c74..71d997226 100644 --- a/Telegram/SourceFiles/settings/settings_chat_settings_widget.h +++ b/Telegram/SourceFiles/settings/settings_chat_settings_widget.h @@ -36,7 +36,7 @@ public: void setLink(const QString &text); - LinkButton *link() { + Ui::LinkButton *link() { return _link; } @@ -47,7 +47,7 @@ protected: private: ChildWidget _label; - ChildWidget _link; + ChildWidget _link; }; @@ -83,7 +83,7 @@ private: State _state = State::Empty; ChildWidget _path; - ChildWidget _clear; + ChildWidget _clear; }; @@ -106,13 +106,13 @@ private: void createControls(); ChildWidget _replaceEmoji = { nullptr }; - ChildWidget> _viewList = { nullptr }; + ChildWidget> _viewList = { nullptr }; ChildWidget _dontAskDownloadPath = { nullptr }; ChildWidget> _downloadPath = { nullptr }; ChildWidget _sendByEnter = { nullptr }; ChildWidget _sendByCtrlEnter = { nullptr }; - ChildWidget _automaticMediaDownloadSettings = { nullptr }; - ChildWidget _manageStickerSets = { nullptr }; + ChildWidget _automaticMediaDownloadSettings = { nullptr }; + ChildWidget _manageStickerSets = { nullptr }; }; diff --git a/Telegram/SourceFiles/settings/settings_cover.cpp b/Telegram/SourceFiles/settings/settings_cover.cpp index 6792f8d05..05390244a 100644 --- a/Telegram/SourceFiles/settings/settings_cover.cpp +++ b/Telegram/SourceFiles/settings/settings_cover.cpp @@ -22,8 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_cover.h" #include "ui/flatlabel.h" -#include "ui/buttons/round_button.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "observer_peer.h" #include "lang.h" #include "application.h" @@ -50,9 +49,7 @@ CoverWidget::CoverWidget(QWidget *parent, UserData *self) : BlockWidget(parent, _name->setSelectable(true); _name->setContextCopyText(lang(lng_profile_copy_fullname)); - _setPhoto->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); connect(_setPhoto, SIGNAL(clicked()), this, SLOT(onSetPhoto())); - _editName->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); connect(_editName, SIGNAL(clicked()), this, SLOT(onEditName())); connect(_editNameInline, SIGNAL(clicked()), this, SLOT(onEditName())); @@ -280,7 +277,7 @@ void CoverWidget::refreshStatusText() { _statusText = lang(lng_settings_uploading_photo); _statusTextIsOnline = false; if (!_cancelPhotoUpload) { - _cancelPhotoUpload = new LinkButton(this, lang(lng_cancel), st::btnDefLink); + _cancelPhotoUpload.create(this, lang(lng_cancel), st::defaultLinkButton); connect(_cancelPhotoUpload, SIGNAL(clicked()), this, SLOT(onCancelPhotoUpload())); _cancelPhotoUpload->show(); _cancelPhotoUpload->moveToLeft(_statusPosition.x() + st::settingsStatusFont->width(_statusText) + st::settingsStatusFont->spacew, _statusPosition.y()); diff --git a/Telegram/SourceFiles/settings/settings_cover.h b/Telegram/SourceFiles/settings/settings_cover.h index 08ca9c0b4..c086fb201 100644 --- a/Telegram/SourceFiles/settings/settings_cover.h +++ b/Telegram/SourceFiles/settings/settings_cover.h @@ -26,8 +26,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" class FlatLabel; -class LinkButton; - namespace Ui { class RoundButton; class IconButton; @@ -95,7 +93,7 @@ private: ChildWidget _name; ChildWidget _editNameInline; - ChildWidget _cancelPhotoUpload = { nullptr }; + ChildWidget _cancelPhotoUpload = { nullptr }; QPoint _statusPosition; QString _statusText; diff --git a/Telegram/SourceFiles/settings/settings_fixed_bar.cpp b/Telegram/SourceFiles/settings/settings_fixed_bar.cpp index 5954df433..079c91343 100644 --- a/Telegram/SourceFiles/settings/settings_fixed_bar.cpp +++ b/Telegram/SourceFiles/settings/settings_fixed_bar.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_settings.h" #include "styles/style_boxes.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "mainwindow.h" #include "lang.h" diff --git a/Telegram/SourceFiles/settings/settings_general_widget.cpp b/Telegram/SourceFiles/settings/settings_general_widget.cpp index 2b38960a7..3225a158d 100644 --- a/Telegram/SourceFiles/settings/settings_general_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_general_widget.cpp @@ -24,8 +24,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_settings.h" #include "lang.h" #include "ui/effects/widget_slide_wrap.h" -#include "ui/flatbutton.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "localstorage.h" #include "pspecific.h" #include "mainwindow.h" @@ -168,7 +168,7 @@ void UpdateStateRow::onFailed() { #endif // !TDESKTOP_DISABLE_AUTOUPDATE GeneralWidget::GeneralWidget(QWidget *parent, UserData *self) : BlockWidget(parent, self, lang(lng_settings_section_general)) -, _changeLanguage(this, lang(lng_settings_change_lang), st::defaultBoxLinkButton) { +, _changeLanguage(this, lang(lng_settings_change_lang), st::boxLinkButton) { connect(_changeLanguage, SIGNAL(clicked()), this, SLOT(onChangeLanguage())); subscribe(Global::RefChooseCustomLang(), [this]() { chooseCustomLang(); }); subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) { @@ -178,7 +178,7 @@ GeneralWidget::GeneralWidget(QWidget *parent, UserData *self) : BlockWidget(pare } int GeneralWidget::resizeGetHeight(int newWidth) { - _changeLanguage->moveToRight(contentLeft(), st::settingsBlockMarginTop + st::settingsBlockTitleTop + st::settingsBlockTitleFont->ascent - st::btnDefLink.font->ascent, newWidth); + _changeLanguage->moveToRight(contentLeft(), st::settingsBlockMarginTop + st::settingsBlockTitleTop + st::settingsBlockTitleFont->ascent - st::defaultLinkButton.font->ascent, newWidth); return BlockWidget::resizeGetHeight(newWidth); } diff --git a/Telegram/SourceFiles/settings/settings_general_widget.h b/Telegram/SourceFiles/settings/settings_general_widget.h index 228cf182a..48ab3cb2a 100644 --- a/Telegram/SourceFiles/settings/settings_general_widget.h +++ b/Telegram/SourceFiles/settings/settings_general_widget.h @@ -23,14 +23,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" #include "ui/filedialog.h" -class LinkButton; - -namespace Ui { -class Checkbox; -template -class WidgetSlideWrap; -} // namespace Ui - namespace Settings { #ifndef TDESKTOP_DISABLE_AUTOUPDATE @@ -73,8 +65,8 @@ private: void setState(State state, bool force = false); void setDownloadProgress(qint64 ready, qint64 total); - ChildWidget _check; - ChildWidget _restart; + ChildWidget _check; + ChildWidget _restart; State _state = State::None; QString _downloadText; @@ -114,7 +106,7 @@ private: void chooseCustomLang(); void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update); - ChildWidget _changeLanguage; + ChildWidget _changeLanguage; #ifndef TDESKTOP_DISABLE_AUTOUPDATE ChildWidget _updateAutomatically = { nullptr }; ChildWidget> _updateRow = { nullptr }; diff --git a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp index 2bd3b7ce8..441b0c4f3 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp @@ -25,7 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "localstorage.h" #include "ui/effects/widget_slide_wrap.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" #include "mainwindow.h" #include "window/notifications_manager.h" #include "boxes/notifications_box.h" diff --git a/Telegram/SourceFiles/settings/settings_notifications_widget.h b/Telegram/SourceFiles/settings/settings_notifications_widget.h index 404457c5c..0a2b44e94 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_widget.h +++ b/Telegram/SourceFiles/settings/settings_notifications_widget.h @@ -51,7 +51,7 @@ private: ChildWidget _nativeNotifications = { nullptr }; ChildWidget _playSound = { nullptr }; ChildWidget _includeMuted = { nullptr }; - ChildWidget> _advanced = { nullptr }; + ChildWidget> _advanced = { nullptr }; }; diff --git a/Telegram/SourceFiles/settings/settings_privacy_widget.cpp b/Telegram/SourceFiles/settings/settings_privacy_widget.cpp index 9d177e243..20cf8ec35 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_widget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_privacy_widget.h" #include "ui/effects/widget_slide_wrap.h" +#include "ui/widgets/buttons.h" #include "styles/style_settings.h" #include "lang.h" #include "application.h" @@ -33,8 +34,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Settings { LocalPasscodeState::LocalPasscodeState(QWidget *parent) : TWidget(parent) -, _edit(this, lang(Global::LocalPasscode() ? lng_passcode_change : lng_passcode_turn_on), st::defaultBoxLinkButton) -, _turnOff(this, lang(lng_passcode_turn_off), st::defaultBoxLinkButton) { +, _edit(this, lang(Global::LocalPasscode() ? lng_passcode_change : lng_passcode_turn_on), st::boxLinkButton) +, _turnOff(this, lang(lng_passcode_turn_off), st::boxLinkButton) { updateControls(); connect(_edit, SIGNAL(clicked()), this, SLOT(onEdit())); connect(_turnOff, SIGNAL(clicked()), this, SLOT(onTurnOff())); @@ -62,8 +63,8 @@ void LocalPasscodeState::updateControls() { } CloudPasswordState::CloudPasswordState(QWidget *parent) : TWidget(parent) -, _edit(this, lang(lng_cloud_password_set), st::defaultBoxLinkButton) -, _turnOff(this, lang(lng_passcode_turn_off), st::defaultBoxLinkButton) { +, _edit(this, lang(lng_cloud_password_set), st::boxLinkButton) +, _turnOff(this, lang(lng_passcode_turn_off), st::boxLinkButton) { _turnOff->hide(); connect(_edit, SIGNAL(clicked()), this, SLOT(onEdit())); connect(_turnOff, SIGNAL(clicked()), this, SLOT(onTurnOff())); diff --git a/Telegram/SourceFiles/settings/settings_privacy_widget.h b/Telegram/SourceFiles/settings/settings_privacy_widget.h index 54d7ca8bb..8bc1a947b 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_widget.h +++ b/Telegram/SourceFiles/settings/settings_privacy_widget.h @@ -41,8 +41,8 @@ private slots: private: void updateControls(); - ChildWidget _edit; - ChildWidget _turnOff; + ChildWidget _edit; + ChildWidget _turnOff; }; @@ -67,8 +67,8 @@ private: void offPasswordDone(const MTPBool &result); bool offPasswordFail(const RPCError &error); - ChildWidget _edit; - ChildWidget _turnOff; + ChildWidget _edit; + ChildWidget _turnOff; QString _waitingConfirm; QByteArray _curPasswordSalt; @@ -95,7 +95,7 @@ private: ChildWidget _localPasscodeState = { nullptr }; ChildWidget> _autoLock = { nullptr }; ChildWidget _cloudPasswordState = { nullptr }; - ChildWidget _showAllSessions = { nullptr }; + ChildWidget _showAllSessions = { nullptr }; }; diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp index 63a9674a7..d16c9af9d 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_scale_widget.h" #include "styles/style_settings.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" #include "lang.h" #include "localstorage.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index 42ef303d6..fde5cb65d 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -72,7 +72,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/twidget.h" #include "ui/flatinput.h" #include "ui/flattextarea.h" -#include "ui/flatbutton.h" #include "ui/scrollarea.h" #include "ui/images.h" #include "ui/text/text.h" diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index f52735d6b..3413ddee3 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_stickers.h" #include "styles/style_intro.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" #include "boxes/stickers_box.h" @@ -804,7 +804,7 @@ StickerPanInner::StickerPanInner() : TWidget() setMouseTracking(true); setAttribute(Qt::WA_OpaquePaintEvent); - connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); + connect(_settings, SIGNAL(clicked()), this, SLOT(onSettings())); _previewTimer.setSingleShot(true); connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); @@ -821,7 +821,7 @@ StickerPanInner::StickerPanInner() : TWidget() void StickerPanInner::setMaxHeight(int32 h) { _maxHeight = h; resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); - _settings.moveToLeft((st::emojiPanWidth - _settings.width()) / 2, height() / 3); + _settings->moveToLeft((st::emojiPanWidth - _settings->width()) / 2, height() / 3); } void StickerPanInner::setVisibleTopBottom(int visibleTop, int visibleBottom) { @@ -1425,9 +1425,9 @@ void StickerPanInner::refreshStickers() { int h = countHeight(); if (h != height()) resize(width(), h); - _settings.setVisible(_section == Section::Stickers && _mySets.isEmpty()); + _settings->setVisible(_section == Section::Stickers && _mySets.isEmpty()); } else { - _settings.hide(); + _settings->hide(); } emit refreshIcons(kRefreshIconsNoAnimation); @@ -1499,7 +1499,7 @@ bool StickerPanInner::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool fo void StickerPanInner::refreshSavedGifs() { if (_section == Section::Gifs) { - _settings.hide(); + _settings->hide(); clearInlineRows(false); auto &saved = cSavedGifs(); @@ -1728,7 +1728,7 @@ void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) { _switchPmStartToken.clear(); } else { if (!_switchPmButton) { - _switchPmButton = std_::make_unique(this, QString(), st::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())); @@ -1768,7 +1768,7 @@ int StickerPanInner::refreshInlineRows(UserData *bot, const InlineCacheEntry *en _inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username)); _section = Section::Inlines; - _settings.hide(); + _settings->hide(); int32 count = entry->results.size(), from = validateExistingInlineRows(entry->results), added = 0; @@ -2492,7 +2492,7 @@ void EmojiPanel::paintEvent(QPaintEvent *e) { p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); } -EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent) +EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : AbstractButton(parent) , _toStickers(toStickers) { setCursor(style::cur_pointer); updateText(); diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index afa4e6374..6fed74829 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/twidget.h" #include "ui/effects/rect_shadow.h" +#include "ui/abstract_button.h" namespace InlineBots { namespace Layout { @@ -32,6 +33,8 @@ class Result; namespace Ui { class IconButton; +class LinkButton; +class RoundButton; } // namesapce Ui namespace internal { @@ -372,7 +375,7 @@ private: QTimer _updateInlineItems; bool _inlineWithThumb = false; - std_::unique_ptr _switchPmButton; + std_::unique_ptr _switchPmButton; QString _switchPmStartToken; typedef QVector InlineItems; @@ -417,7 +420,7 @@ private: QString _addText; int _addWidth; - LinkButton _settings; + ChildWidget _settings; QTimer _previewTimer; bool _previewShown = false; @@ -463,7 +466,7 @@ private: }; -class EmojiSwitchButton : public Button { +class EmojiSwitchButton : public Ui::AbstractButton { public: EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji diff --git a/Telegram/SourceFiles/ui/button.cpp b/Telegram/SourceFiles/ui/abstract_button.cpp similarity index 64% rename from Telegram/SourceFiles/ui/button.cpp rename to Telegram/SourceFiles/ui/abstract_button.cpp index ff58f77d2..0ce112850 100644 --- a/Telegram/SourceFiles/ui/button.cpp +++ b/Telegram/SourceFiles/ui/abstract_button.cpp @@ -19,30 +19,29 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "button.h" +#include "ui/abstract_button.h" -Button::Button(QWidget *parent) : TWidget(parent), _state(StateNone), _acceptBoth(false) { -} +namespace Ui { -void Button::leaveEvent(QEvent *e) { +void AbstractButton::leaveEvent(QEvent *e) { if (_state & StateDown) return; - setOver(false, ButtonByHover); + setOver(false, StateChangeSource::ByHover); setMouseTracking(false); return TWidget::leaveEvent(e); } -void Button::enterEvent(QEvent *e) { - setOver(true, ButtonByHover); +void AbstractButton::enterEvent(QEvent *e) { + setOver(true, StateChangeSource::ByHover); setMouseTracking(true); return TWidget::enterEvent(e); } -void Button::setAcceptBoth(bool acceptBoth) { +void AbstractButton::setAcceptBoth(bool acceptBoth) { _acceptBoth = acceptBoth; } -void Button::mousePressEvent(QMouseEvent *e) { +void AbstractButton::mousePressEvent(QMouseEvent *e) { if (_acceptBoth || e->buttons() & Qt::LeftButton) { if (!(_state & StateOver)) { enterEvent(0); @@ -50,28 +49,26 @@ void Button::mousePressEvent(QMouseEvent *e) { if (!(_state & StateDown)) { int oldState = _state; _state |= StateDown; - emit stateChanged(oldState, ButtonByPress); - onStateChanged(oldState, ButtonByPress); + onStateChanged(oldState, StateChangeSource::ByPress); e->accept(); } } } -void Button::mouseMoveEvent(QMouseEvent *e) { +void AbstractButton::mouseMoveEvent(QMouseEvent *e) { if (rect().contains(e->pos())) { - setOver(true, ButtonByHover); + setOver(true, StateChangeSource::ByHover); } else { - setOver(false, ButtonByHover); + setOver(false, StateChangeSource::ByHover); } } -void Button::mouseReleaseEvent(QMouseEvent *e) { +void AbstractButton::mouseReleaseEvent(QMouseEvent *e) { if (_state & StateDown) { int oldState = _state; _state &= ~StateDown; - emit stateChanged(oldState, ButtonByPress); - onStateChanged(oldState, ButtonByPress); + onStateChanged(oldState, StateChangeSource::ByPress); if (oldState & StateOver) { _modifiers = e->modifiers(); if (_clickedCallback) { @@ -84,40 +81,37 @@ void Button::mouseReleaseEvent(QMouseEvent *e) { } } -void Button::setOver(bool over, ButtonStateChangeSource source) { +void AbstractButton::setOver(bool over, StateChangeSource source) { if (over && !(_state & StateOver)) { int oldState = _state; _state |= StateOver; - emit stateChanged(oldState, source); onStateChanged(oldState, source); } else if (!over && (_state & StateOver)) { int oldState = _state; _state &= ~StateOver; - emit stateChanged(oldState, source); onStateChanged(oldState, source); } } -void Button::setDisabled(bool disabled) { +void AbstractButton::setDisabled(bool disabled) { int oldState = _state; if (disabled && !(_state & StateDisabled)) { _state |= StateDisabled; - emit stateChanged(oldState, ButtonByUser); - onStateChanged(oldState, ButtonByUser); + onStateChanged(oldState, StateChangeSource::ByUser); } else if (!disabled && (_state & StateDisabled)) { _state &= ~StateDisabled; - emit stateChanged(oldState, ButtonByUser); - onStateChanged(oldState, ButtonByUser); + onStateChanged(oldState, StateChangeSource::ByUser); } } -void Button::clearState() { +void AbstractButton::clearState() { int oldState = _state; _state = StateNone; - emit stateChanged(oldState, ButtonByUser); - onStateChanged(oldState, ButtonByUser); + onStateChanged(oldState, StateChangeSource::ByUser); } -int Button::getState() const { +int AbstractButton::getState() const { return _state; } + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/button.h b/Telegram/SourceFiles/ui/abstract_button.h similarity index 76% rename from Telegram/SourceFiles/ui/button.h rename to Telegram/SourceFiles/ui/abstract_button.h index 298bc7e8c..2d68d1d95 100644 --- a/Telegram/SourceFiles/ui/button.h +++ b/Telegram/SourceFiles/ui/abstract_button.h @@ -20,26 +20,28 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include #include "ui/twidget.h" #include "core/lambda_wrap.h" -typedef enum { - ButtonByUser = 0x00, // by clearState() call - ButtonByPress = 0x01, - ButtonByHover = 0x02, -} ButtonStateChangeSource; +namespace Ui { -class Button : public TWidget { +class AbstractButton : public TWidget { Q_OBJECT public: - Button(QWidget *parent); + enum class StateChangeSource { + ByUser = 0x00, + ByPress = 0x01, + ByHover = 0x02, + }; + + AbstractButton(QWidget *parent) : TWidget(parent) { + } enum { - StateNone = 0x00, - StateOver = 0x01, - StateDown = 0x02, + StateNone = 0x00, + StateOver = 0x01, + StateDown = 0x02, StateDisabled = 0x04, }; @@ -51,7 +53,7 @@ public: int getState() const; void setDisabled(bool disabled = true); - void setOver(bool over, ButtonStateChangeSource source = ButtonByUser); + void setOver(bool over, StateChangeSource source = StateChangeSource::ByUser); bool disabled() const { return (_state & StateDisabled); } @@ -71,16 +73,17 @@ protected: signals: void clicked(); - void stateChanged(int oldState, ButtonStateChangeSource source); protected: - virtual void onStateChanged(int oldState, ButtonStateChangeSource source) { + virtual void onStateChanged(int oldState, StateChangeSource source) { } Qt::KeyboardModifiers _modifiers; - int _state; - bool _acceptBoth; + int _state = StateNone; + bool _acceptBoth = false; base::lambda_unique _clickedCallback; }; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp index 9eab4635d..b55e65c22 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp @@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { -HistoryDownButton::HistoryDownButton(QWidget *parent) : Button(parent) +HistoryDownButton::HistoryDownButton(QWidget *parent) : AbstractButton(parent) //, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity) , _a_arrowOver(animation(this, &HistoryDownButton::step_arrowOver)) { setCursor(style::cur_pointer); @@ -78,10 +78,10 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) { } } -void HistoryDownButton::onStateChanged(int oldState, ButtonStateChangeSource source) { +void HistoryDownButton::onStateChanged(int oldState, StateChangeSource source) { //a_arrowOpacity.start((_state & (StateOver | StateDown)) ? st::historyAttachEmoji.overOpacity : st::historyAttachEmoji.opacity); - if (source == ButtonByUser || source == ButtonByPress) { + if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) { _a_arrowOver.stop(); a_arrowOpacity.finish(); update(); @@ -133,7 +133,7 @@ void HistoryDownButton::step_arrowOver(float64 ms, bool timer) { if (timer) update(); } -EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : Button(parent) +EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : AbstractButton(parent) , _st(st) , _a_loading(animation(this, &EmojiButton::step_loading)) { resize(_st.width, _st.height); @@ -183,7 +183,7 @@ void EmojiButton::setLoading(bool loading) { } } -void EmojiButton::onStateChanged(int oldState, ButtonStateChangeSource source) { +void EmojiButton::onStateChanged(int oldState, StateChangeSource source) { auto over = (_state & StateOver); if (over != (oldState & StateOver)) { update(); diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.h b/Telegram/SourceFiles/ui/buttons/history_down_button.h index 70dc27247..87d228302 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.h +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.h @@ -20,12 +20,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/button.h" +#include "ui/abstract_button.h" #include "styles/style_widgets.h" namespace Ui { -class HistoryDownButton : public Button { +class HistoryDownButton : public AbstractButton { public: HistoryDownButton(QWidget *parent); @@ -44,7 +44,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, ButtonStateChangeSource source) override; + void onStateChanged(int oldState, StateChangeSource source) override; private: void toggleAnimated(); @@ -62,7 +62,7 @@ private: }; -class EmojiButton : public Button { +class EmojiButton : public AbstractButton { public: EmojiButton(QWidget *parent, const style::IconButton &st); @@ -70,7 +70,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, ButtonStateChangeSource source) override; + void onStateChanged(int oldState, StateChangeSource source) override; private: const style::IconButton &_st; diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.cpp b/Telegram/SourceFiles/ui/buttons/icon_button.cpp deleted file mode 100644 index f1003d0a1..000000000 --- a/Telegram/SourceFiles/ui/buttons/icon_button.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* -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 "ui/buttons/icon_button.h" - -namespace Ui { - -IconButton::IconButton(QWidget *parent, const style::IconButton &st) : Button(parent) -, _st(st) { - resize(_st.width, _st.height); - setCursor(style::cur_pointer); -} - -void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) { - _iconOverride = icon; - _iconOverrideOver = iconOver; - update(); -} - -void IconButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); - auto overIcon = [this] { - if (_iconOverrideOver) { - return _iconOverrideOver; - } else if (!_st.iconOver.empty()) { - return &_st.iconOver; - } else if (_iconOverride) { - return _iconOverride; - } - return &_st.icon; - }; - auto justIcon = [this] { - if (_iconOverride) { - return _iconOverride; - } - return &_st.icon; - }; - auto icon = (over == 1.) ? overIcon() : justIcon(); - auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition; - if (position.x() < 0) { - position.setX((width() - icon->width()) / 2); - } - if (position.y() < 0) { - position.setY((height() - icon->height()) / 2); - } - icon->paint(p, position, width()); - if (over > 0. && over < 1.) { - auto iconOver = overIcon(); - if (iconOver != icon) { - p.setOpacity(over); - iconOver->paint(p, position, width()); - } - } -} - -void IconButton::onStateChanged(int oldState, ButtonStateChangeSource source) { - auto over = (_state & StateOver); - if (over != (oldState & StateOver)) { - if (_st.duration) { - auto from = over ? 0. : 1.; - auto to = over ? 1. : 0.; - _a_over.start([this] { update(); }, from, to, _st.duration); - } else { - update(); - } - } -} - -MaskButton::MaskButton(QWidget *parent, const style::MaskButton &st) : Button(parent) -, _st(st) { - resize(_st.width, _st.height); - setCursor(style::cur_pointer); - setAttribute(Qt::WA_OpaquePaintEvent); -} - -void MaskButton::onStateChanged(int oldState, ButtonStateChangeSource source) { - auto over = (_state & StateOver); - if (over != (oldState & StateOver)) { - _a_iconOver.start([this] { update(); }, over ? 0. : 1., over ? 1. : 0., _st.duration); - } -} - -void MaskButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto clip = e->rect(); - auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition; - if (position.x() < 0) { - position.setX((width() - _st.icon.width()) / 2); - } - if (position.y() < 0) { - position.setY((height() - _st.icon.height()) / 2); - } - auto icon = myrtlrect(position.x(), position.y(), _st.icon.width(), _st.icon.height()); - if (!icon.contains(clip)) { - p.fillRect(clip, _st.bg); - } - if (icon.intersects(clip)) { - p.fillRect(icon.intersected(clip), anim::brush(_st.iconBg, _st.iconBgOver, _a_iconOver.current(getms(), (_state & StateOver) ? 1. : 0.))); - _st.icon.paint(p, position, width()); - } -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.h b/Telegram/SourceFiles/ui/buttons/icon_button.h deleted file mode 100644 index 7decf5144..000000000 --- a/Telegram/SourceFiles/ui/buttons/icon_button.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -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/button.h" -#include "styles/style_widgets.h" - -namespace Ui { - -class IconButton : public Button { -public: - IconButton(QWidget *parent, const style::IconButton &st); - - // Pass nullptr to restore the default icon. - void setIcon(const style::icon *icon, const style::icon *iconOver = nullptr); - -protected: - void paintEvent(QPaintEvent *e) override; - - void onStateChanged(int oldState, ButtonStateChangeSource source) override; - -private: - const style::IconButton &_st; - const style::icon *_iconOverride = nullptr; - const style::icon *_iconOverrideOver = nullptr; - - FloatAnimation _a_over; - -}; - -class MaskButton : public Button { -public: - MaskButton(QWidget *parent, const style::MaskButton &st); - -protected: - void paintEvent(QPaintEvent *e) override; - - void onStateChanged(int oldState, ButtonStateChangeSource source) override; - -private: - const style::MaskButton &_st; - - FloatAnimation _a_iconOver; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/left_outline_button.cpp b/Telegram/SourceFiles/ui/buttons/left_outline_button.cpp index 04d94a4b2..ba8de2ffc 100644 --- a/Telegram/SourceFiles/ui/buttons/left_outline_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/left_outline_button.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { -LeftOutlineButton::LeftOutlineButton(QWidget *parent, const QString &text, const style::OutlineButton &st) : Button(parent) +LeftOutlineButton::LeftOutlineButton(QWidget *parent, const QString &text, const style::OutlineButton &st) : AbstractButton(parent) , _text(text) , _fullText(text) , _textWidth(st.font->width(_text)) @@ -54,7 +54,7 @@ int LeftOutlineButton::resizeGetHeight(int newWidth) { void LeftOutlineButton::paintEvent(QPaintEvent *e) { Painter p(this); - bool over = (_state & Button::StateOver); + bool over = (_state & StateOver); if (width() > _st.outlineWidth) { p.fillRect(rtlrect(0, 0, _st.outlineWidth, height(), width()), over ? _st.outlineFgOver : _st.outlineFg); p.fillRect(rtlrect(_st.outlineWidth, 0, width() - _st.outlineWidth, height(), width()), over ? _st.textBgOver : _st.textBg); @@ -64,7 +64,7 @@ void LeftOutlineButton::paintEvent(QPaintEvent *e) { p.drawTextLeft(_st.padding.left(), _st.padding.top(), width(), _text, _textWidth); } -void LeftOutlineButton::onStateChanged(int oldState, ButtonStateChangeSource source) { +void LeftOutlineButton::onStateChanged(int oldState, StateChangeSource source) { update(); } diff --git a/Telegram/SourceFiles/ui/buttons/left_outline_button.h b/Telegram/SourceFiles/ui/buttons/left_outline_button.h index 011b4c2ad..590535d32 100644 --- a/Telegram/SourceFiles/ui/buttons/left_outline_button.h +++ b/Telegram/SourceFiles/ui/buttons/left_outline_button.h @@ -20,11 +20,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/button.h" +#include "ui/abstract_button.h" namespace Ui { -class LeftOutlineButton : public Button { +class LeftOutlineButton : public AbstractButton { public: LeftOutlineButton(QWidget *parent, const QString &text, const style::OutlineButton &st = st::defaultLeftOutlineButton); @@ -33,7 +33,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; - void onStateChanged(int oldState, ButtonStateChangeSource source) override; + void onStateChanged(int oldState, StateChangeSource source) override; int resizeGetHeight(int newWidth) override; diff --git a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp index 23acb4609..b37bf850a 100644 --- a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { -PeerAvatarButton::PeerAvatarButton(QWidget *parent, PeerData *peer, const style::PeerAvatarButton &st) : Button(parent) +PeerAvatarButton::PeerAvatarButton(QWidget *parent, PeerData *peer, const style::PeerAvatarButton &st) : AbstractButton(parent) , _peer(peer) , _st(st) { resize(_st.size, _st.size); diff --git a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.h b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.h index eca0f3122..7a31ba9db 100644 --- a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.h +++ b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.h @@ -20,13 +20,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/button.h" +#include "ui/abstract_button.h" class PeerData; namespace Ui { -class PeerAvatarButton : public Button { +class PeerAvatarButton : public AbstractButton { public: PeerAvatarButton(QWidget *parent, PeerData *peer, const style::PeerAvatarButton &st); diff --git a/Telegram/SourceFiles/ui/buttons/round_button.cpp b/Telegram/SourceFiles/ui/buttons/round_button.cpp deleted file mode 100644 index fad19eccc..000000000 --- a/Telegram/SourceFiles/ui/buttons/round_button.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* -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 "ui/buttons/round_button.h" - -namespace Ui { - -RoundButton::RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st) : Button(parent) -, _text(text) -, _fullText(text) -, _textWidth(st.font->width(_text)) -, _st(st) { - resizeToText(); - - setCursor(style::cur_pointer); -} - -void RoundButton::setTextTransform(TextTransform transform) { - _transform = transform; - updateText(); -} - -void RoundButton::setText(const QString &text) { - _fullText = text; - updateText(); -} - -void RoundButton::setSecondaryText(const QString &secondaryText) { - _fullSecondaryText = secondaryText; - updateText(); -} - -void RoundButton::setFullWidth(int newFullWidth) { - _fullWidthOverride = newFullWidth; - resizeToText(); -} - -void RoundButton::updateText() { - if (_transform == TextTransform::ToUpper) { - _text = _fullText.toUpper(); - _secondaryText = _fullSecondaryText.toUpper(); - } else { - _text = _fullText; - _secondaryText = _fullSecondaryText; - } - _textWidth = _text.isEmpty() ? 0 : _st.font->width(_text); - _secondaryTextWidth = _secondaryText.isEmpty() ? 0 : _st.font->width(_secondaryText); - - resizeToText(); -} - -void RoundButton::resizeToText() { - int innerWidth = contentWidth(); - if (_fullWidthOverride < 0) { - resize(innerWidth - _fullWidthOverride, _st.height + _st.padding.top() + _st.padding.bottom()); - } else if (_st.width <= 0) { - resize(innerWidth - _st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom()); - } else { - if (_st.width < innerWidth + (_st.height - _st.font->height)) { - _text = _st.font->elided(_fullText, qMax(_st.width - (_st.height - _st.font->height), 1)); - innerWidth = _st.font->width(_text); - } - resize(_st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom()); - } -} - -int RoundButton::contentWidth() const { - int result = _textWidth + _secondaryTextWidth; - if (_textWidth > 0 && _secondaryTextWidth > 0) { - result += _st.secondarySkip; - } - return result; -} - -void RoundButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - int innerWidth = contentWidth(); - auto rounded = rtlrect(rect().marginsRemoved(_st.padding), width()); - if (_fullWidthOverride < 0) { - rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height()); - } - App::roundRect(p, rounded, _st.textBg, ImageRoundRadius::Small); - - auto over = (_state & StateOver); - if (over) { - App::roundRect(p, rounded, _st.textBgOver, ImageRoundRadius::Small); - } - - p.setFont(_st.font); - int textLeft = _st.padding.left() + ((width() - innerWidth - _st.padding.left() - _st.padding.right()) / 2); - if (_fullWidthOverride < 0) { - textLeft = -_fullWidthOverride / 2; - } - int textTopDelta = (_state & StateDown) ? (_st.downTextTop - _st.textTop) : 0; - int textTop = _st.padding.top() + _st.textTop + textTopDelta; - if (!_text.isEmpty()) { - p.setPen(over ? _st.textFgOver : _st.textFg); - p.drawTextLeft(textLeft, textTop, width(), _text); - } - if (!_secondaryText.isEmpty()) { - textLeft += _textWidth + (_textWidth ? _st.secondarySkip : 0); - p.setPen(over ? _st.secondaryTextFgOver : _st.secondaryTextFg); - p.drawTextLeft(textLeft, textTop, width(), _secondaryText); - } - _st.icon.paint(p, QPoint(_st.padding.left(), _st.padding.right() + textTopDelta), width()); -} - -void RoundButton::onStateChanged(int oldState, ButtonStateChangeSource source) { - update(); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/round_button.h b/Telegram/SourceFiles/ui/buttons/round_button.h deleted file mode 100644 index 406ce1346..000000000 --- a/Telegram/SourceFiles/ui/buttons/round_button.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -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/button.h" - -namespace Ui { - -class RoundButton : public Button { -public: - RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st); - - void setText(const QString &text); - void setSecondaryText(const QString &secondaryText); - - int contentWidth() const; - - void setFullWidth(int newFullWidth); - - enum class TextTransform { - NoTransform, - ToUpper, - }; - void setTextTransform(TextTransform transform); - -protected: - void paintEvent(QPaintEvent *e) override; - - void onStateChanged(int oldState, ButtonStateChangeSource source) override; - -private: - void updateText(); - void resizeToText(); - - QString _text, _fullText; - int _textWidth; - - QString _secondaryText, _fullSecondaryText; - int _secondaryTextWidth = 0; - - int _fullWidthOverride = 0; - - const style::RoundButton &_st; - - TextTransform _transform = TextTransform::NoTransform; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index be20f24ea..d3416c068 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/flatinput.h" #include "ui/scrollarea.h" -#include "ui/flatbutton.h" #include "ui/effects/rect_shadow.h" #include "boxes/abstractbox.h" #include "styles/style_intro.h" diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp deleted file mode 100644 index 5d44c1b74..000000000 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* -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 "ui/flatbutton.h" - -#include "styles/style_history.h" - -FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatButton &st) : Button(parent) -, _text(text) -, _st(st) -, a_over(0) -, _a_appearance(animation(this, &FlatButton::step_appearance)) { - if (_st.width < 0) { - _width = textWidth() - _st.width; - } else if (!_st.width) { - _width = textWidth() + _st.height - _st.font->height; - } else { - _width = _st.width; - } - connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); - resize(_width, _st.height); - setCursor(_st.cursor); -} - -void FlatButton::setOpacity(float64 o) { - _opacity = o; - update(); -} - -float64 FlatButton::opacity() const { - return _opacity; -} - -void FlatButton::setText(const QString &text) { - _text = text; - update(); -} - -void FlatButton::setWidth(int32 w) { - _width = w; - if (_width < 0) { - _width = textWidth() - _st.width; - } else if (!_width) { - _width = textWidth() + _st.height - _st.font->height; - } - resize(_width, height()); -} - -int32 FlatButton::textWidth() const { - return _st.font->width(_text); -} - -void FlatButton::step_appearance(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_appearance.stop(); - a_over.finish(); - } else { - a_over.update(dt, anim::linear); - } - if (timer) update(); -} - -void FlatButton::onStateChange(int oldState, ButtonStateChangeSource source) { - a_over.start((_state & StateOver) ? 1. : 0.); - if (source == ButtonByUser || source == ButtonByPress) { - _a_appearance.stop(); - a_over.finish(); - update(); - } else { - _a_appearance.start(); - } -} - -void FlatButton::paintEvent(QPaintEvent *e) { - QPainter p(this); - - QRect r(0, height() - _st.height, width(), _st.height); - - p.setOpacity(_opacity); - p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current())); - - p.setFont((_state & StateOver) ? _st.overFont : _st.font); - p.setRenderHint(QPainter::TextAntialiasing); - p.setPen(anim::pen(_st.color, _st.overColor, a_over.current())); - - int32 top = (_state & StateOver) ? ((_state & StateDown) ? _st.downTextTop : _st.overTextTop) : _st.textTop; - r.setTop(top); - - p.drawText(r, _text, style::al_top); -} - -LinkButton::LinkButton(QWidget *parent, const QString &text, const style::linkButton &st) : Button(parent) -, _text(text) -, _textWidth(st.font->width(_text)) -, _st(st) { - connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); - resize(_textWidth, _st.font->height); - setCursor(style::cur_pointer); -} - -int LinkButton::naturalWidth() const { - return _textWidth; -} - -void LinkButton::paintEvent(QPaintEvent *e) { - Painter p(this); - auto &font = ((_state & StateOver) ? _st.overFont : _st.font); - auto &pen = ((_state & StateDown) ? _st.downColor : ((_state & StateOver) ? _st.overColor : _st.color)); - p.setFont(font); - p.setPen(pen); - if (_textWidth > width()) { - p.drawText(0, font->ascent, font->elided(_text, width())); - } else { - p.drawText(0, font->ascent, _text); - } -} - -void LinkButton::setText(const QString &text) { - _text = text; - _textWidth = _st.font->width(_text); - resize(_textWidth, _st.font->height); - update(); -} - -void LinkButton::onStateChange(int oldState, ButtonStateChangeSource source) { - update(); -} - -LinkButton::~LinkButton() { -} - -BoxButton::BoxButton(QWidget *parent, const QString &text, const style::RoundButton &st) : Button(parent) -, _text(text.toUpper()) -, _fullText(text.toUpper()) -, _textWidth(st.font->width(_text)) -, _st(st) { - resizeToText(); - - connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); - - setCursor(style::cur_pointer); - - setAttribute(Qt::WA_OpaquePaintEvent); -} - -void BoxButton::setText(const QString &text) { - _text = text; - _fullText = text; - _textWidth = _st.font->width(_text); - resizeToText(); - update(); -} - -void BoxButton::resizeToText() { - if (_st.width <= 0) { - resize(_textWidth - _st.width, _st.height); - } else { - if (_st.width < _textWidth + (_st.height - _st.font->height)) { - _text = _st.font->elided(_fullText, qMax(_st.width - (_st.height - _st.font->height), 1)); - _textWidth = _st.font->width(_text); - } - resize(_st.width, _st.height); - } -} - -void BoxButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto over = (_state & StateOver); - p.fillRect(rect(), _st.textBg); - - if (over) { - App::roundRect(p, rect(), _st.textBgOver, ImageRoundRadius::Small); - } - p.setPen(over ? _st.textFgOver : _st.textFg); - p.setFont(_st.font); - - auto textTop = (_state & StateDown) ? _st.downTextTop : _st.textTop; - p.drawText((width() - _textWidth) / 2, textTop + _st.font->ascent, _text); -} - -void BoxButton::onStateChange(int oldState, ButtonStateChangeSource source) { - update(); -} diff --git a/Telegram/SourceFiles/ui/flatbutton.h b/Telegram/SourceFiles/ui/flatbutton.h deleted file mode 100644 index bb11410cc..000000000 --- a/Telegram/SourceFiles/ui/flatbutton.h +++ /dev/null @@ -1,108 +0,0 @@ -/* -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/button.h" -#include "ui/animation.h" - -class FlatButton : public Button { - Q_OBJECT - -public: - FlatButton(QWidget *parent, const QString &text, const style::flatButton &st); - - void step_appearance(float64 ms, bool timer); - void paintEvent(QPaintEvent *e); - void setOpacity(float64 o); - float64 opacity() const; - - void setText(const QString &text); - void setWidth(int32 w); - - int32 textWidth() const; - - ~FlatButton() { - } - -public slots: - void onStateChange(int oldState, ButtonStateChangeSource source); - -private: - QString _text, _textForAutoSize; - int _width, _textWidth; - - const style::flatButton &_st; - - anim::fvalue a_over; - Animation _a_appearance; - - float64 _opacity = 1.; - -}; - -class LinkButton : public Button { - Q_OBJECT - -public: - LinkButton(QWidget *parent, const QString &text, const style::linkButton &st = st::btnDefLink); - - int naturalWidth() const override; - - void setText(const QString &text); - - ~LinkButton(); - -protected: - void paintEvent(QPaintEvent *e) override; - -public slots: - void onStateChange(int oldState, ButtonStateChangeSource source); - -private: - QString _text; - int _textWidth = 0; - const style::linkButton &_st; - -}; - -class BoxButton : public Button { - Q_OBJECT - -public: - BoxButton(QWidget *parent, const QString &text, const style::RoundButton &st); - - void setText(const QString &text); - void paintEvent(QPaintEvent *e) override; - - void step_over(float64 ms, bool timer); - -public slots: - void onStateChange(int oldState, ButtonStateChangeSource source); - -private: - void resizeToText(); - - QString _text, _fullText; - int32 _textWidth; - - const style::RoundButton &_st; - -}; diff --git a/Telegram/SourceFiles/ui/widgets/buttons.cpp b/Telegram/SourceFiles/ui/widgets/buttons.cpp new file mode 100644 index 000000000..c36909300 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/buttons.cpp @@ -0,0 +1,311 @@ +/* +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 "ui/widgets/buttons.h" + +namespace Ui { + +FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : AbstractButton(parent) +, _text(text) +, _st(st) +, a_over(0) +, _a_appearance(animation(this, &FlatButton::step_appearance)) { + if (_st.width < 0) { + _width = textWidth() - _st.width; + } else if (!_st.width) { + _width = textWidth() + _st.height - _st.font->height; + } else { + _width = _st.width; + } + resize(_width, _st.height); + setCursor(_st.cursor); +} + +void FlatButton::setOpacity(float64 o) { + _opacity = o; + update(); +} + +float64 FlatButton::opacity() const { + return _opacity; +} + +void FlatButton::setText(const QString &text) { + _text = text; + update(); +} + +void FlatButton::setWidth(int32 w) { + _width = w; + if (_width < 0) { + _width = textWidth() - _st.width; + } else if (!_width) { + _width = textWidth() + _st.height - _st.font->height; + } + resize(_width, height()); +} + +int32 FlatButton::textWidth() const { + return _st.font->width(_text); +} + +void FlatButton::step_appearance(float64 ms, bool timer) { + float64 dt = ms / _st.duration; + if (dt >= 1) { + _a_appearance.stop(); + a_over.finish(); + } else { + a_over.update(dt, anim::linear); + } + if (timer) update(); +} + +void FlatButton::onStateChanged(int oldState, StateChangeSource source) { + a_over.start((_state & StateOver) ? 1. : 0.); + if (source == StateChangeSource::ByUser || source == StateChangeSource::ByPress) { + _a_appearance.stop(); + a_over.finish(); + update(); + } else { + _a_appearance.start(); + } +} + +void FlatButton::paintEvent(QPaintEvent *e) { + QPainter p(this); + + QRect r(0, height() - _st.height, width(), _st.height); + + p.setOpacity(_opacity); + p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current())); + + p.setFont((_state & StateOver) ? _st.overFont : _st.font); + p.setRenderHint(QPainter::TextAntialiasing); + p.setPen(anim::pen(_st.color, _st.overColor, a_over.current())); + + int32 top = (_state & StateOver) ? ((_state & StateDown) ? _st.downTextTop : _st.overTextTop) : _st.textTop; + r.setTop(top); + + p.drawText(r, _text, style::al_top); +} + +LinkButton::LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st) : AbstractButton(parent) +, _text(text) +, _textWidth(st.font->width(_text)) +, _st(st) { + resize(_textWidth, _st.font->height); + setCursor(style::cur_pointer); +} + +int LinkButton::naturalWidth() const { + return _textWidth; +} + +void LinkButton::paintEvent(QPaintEvent *e) { + Painter p(this); + auto &font = ((_state & StateOver) ? _st.overFont : _st.font); + auto &pen = ((_state & StateDown) ? _st.downColor : ((_state & StateOver) ? _st.overColor : _st.color)); + p.setFont(font); + p.setPen(pen); + if (_textWidth > width()) { + p.drawText(0, font->ascent, font->elided(_text, width())); + } else { + p.drawText(0, font->ascent, _text); + } +} + +void LinkButton::setText(const QString &text) { + _text = text; + _textWidth = _st.font->width(_text); + resize(_textWidth, _st.font->height); + update(); +} + +void LinkButton::onStateChanged(int oldState, StateChangeSource source) { + update(); +} + +RoundButton::RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st) : AbstractButton(parent) +, _fullText(text) +, _st(st) { + setCursor(style::cur_pointer); + updateText(); +} + +void RoundButton::setTextTransform(TextTransform transform) { + _transform = transform; + updateText(); +} + +void RoundButton::setText(const QString &text) { + _fullText = text; + updateText(); +} + +void RoundButton::setSecondaryText(const QString &secondaryText) { + _fullSecondaryText = secondaryText; + updateText(); +} + +void RoundButton::setFullWidth(int newFullWidth) { + _fullWidthOverride = newFullWidth; + resizeToText(); +} + +void RoundButton::updateText() { + if (_transform == TextTransform::ToUpper) { + _text = _fullText.toUpper(); + _secondaryText = _fullSecondaryText.toUpper(); + } else { + _text = _fullText; + _secondaryText = _fullSecondaryText; + } + _textWidth = _text.isEmpty() ? 0 : _st.font->width(_text); + _secondaryTextWidth = _secondaryText.isEmpty() ? 0 : _st.font->width(_secondaryText); + + resizeToText(); +} + +void RoundButton::resizeToText() { + int innerWidth = contentWidth(); + if (_fullWidthOverride < 0) { + resize(innerWidth - _fullWidthOverride, _st.height + _st.padding.top() + _st.padding.bottom()); + } else if (_st.width <= 0) { + resize(innerWidth - _st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom()); + } else { + if (_st.width < innerWidth + (_st.height - _st.font->height)) { + _text = _st.font->elided(_fullText, qMax(_st.width - (_st.height - _st.font->height), 1)); + innerWidth = _st.font->width(_text); + } + resize(_st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom()); + } +} + +int RoundButton::contentWidth() const { + int result = _textWidth + _secondaryTextWidth; + if (_textWidth > 0 && _secondaryTextWidth > 0) { + result += _st.secondarySkip; + } + return result; +} + +void RoundButton::paintEvent(QPaintEvent *e) { + Painter p(this); + + int innerWidth = contentWidth(); + auto rounded = rtlrect(rect().marginsRemoved(_st.padding), width()); + if (_fullWidthOverride < 0) { + rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height()); + } + App::roundRect(p, rounded, _st.textBg, ImageRoundRadius::Small); + + auto over = (_state & StateOver); + if (over) { + App::roundRect(p, rounded, _st.textBgOver, ImageRoundRadius::Small); + } + + p.setFont(_st.font); + int textLeft = _st.padding.left() + ((width() - innerWidth - _st.padding.left() - _st.padding.right()) / 2); + if (_fullWidthOverride < 0) { + textLeft = -_fullWidthOverride / 2; + } + int textTopDelta = (_state & StateDown) ? (_st.downTextTop - _st.textTop) : 0; + int textTop = _st.padding.top() + _st.textTop + textTopDelta; + if (!_text.isEmpty()) { + p.setPen(over ? _st.textFgOver : _st.textFg); + p.drawTextLeft(textLeft, textTop, width(), _text); + } + if (!_secondaryText.isEmpty()) { + textLeft += _textWidth + (_textWidth ? _st.secondarySkip : 0); + p.setPen(over ? _st.secondaryTextFgOver : _st.secondaryTextFg); + p.drawTextLeft(textLeft, textTop, width(), _secondaryText); + } + _st.icon.paint(p, QPoint(_st.padding.left(), _st.padding.right() + textTopDelta), width()); +} + +void RoundButton::onStateChanged(int oldState, StateChangeSource source) { + update(); +} + +IconButton::IconButton(QWidget *parent, const style::IconButton &st) : AbstractButton(parent) +, _st(st) { + resize(_st.width, _st.height); + setCursor(style::cur_pointer); +} + +void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) { + _iconOverride = icon; + _iconOverrideOver = iconOver; + update(); +} + +void IconButton::paintEvent(QPaintEvent *e) { + Painter p(this); + + auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); + auto overIcon = [this] { + if (_iconOverrideOver) { + return _iconOverrideOver; + } else if (!_st.iconOver.empty()) { + return &_st.iconOver; + } else if (_iconOverride) { + return _iconOverride; + } + return &_st.icon; + }; + auto justIcon = [this] { + if (_iconOverride) { + return _iconOverride; + } + return &_st.icon; + }; + auto icon = (over == 1.) ? overIcon() : justIcon(); + auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition; + if (position.x() < 0) { + position.setX((width() - icon->width()) / 2); + } + if (position.y() < 0) { + position.setY((height() - icon->height()) / 2); + } + icon->paint(p, position, width()); + if (over > 0. && over < 1.) { + auto iconOver = overIcon(); + if (iconOver != icon) { + p.setOpacity(over); + iconOver->paint(p, position, width()); + } + } +} + +void IconButton::onStateChanged(int oldState, StateChangeSource source) { + auto over = (_state & StateOver); + if (over != (oldState & StateOver)) { + if (_st.duration) { + auto from = over ? 0. : 1.; + auto to = over ? 1. : 0.; + _a_over.start([this] { update(); }, from, to, _st.duration); + } else { + update(); + } + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/buttons.h b/Telegram/SourceFiles/ui/widgets/buttons.h new file mode 100644 index 000000000..cf6557d03 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/buttons.h @@ -0,0 +1,139 @@ +/* +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/abstract_button.h" +#include "styles/style_widgets.h" + +namespace Ui { + +class FlatButton : public AbstractButton { +public: + FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st); + + void step_appearance(float64 ms, bool timer); + void paintEvent(QPaintEvent *e); + void setOpacity(float64 o); + float64 opacity() const; + + void setText(const QString &text); + void setWidth(int32 w); + + int32 textWidth() const; + +protected: + void onStateChanged(int oldState, StateChangeSource source) override; + +private: + QString _text, _textForAutoSize; + int _width, _textWidth; + + const style::FlatButton &_st; + + anim::fvalue a_over; + Animation _a_appearance; + + float64 _opacity = 1.; + +}; + +class LinkButton : public AbstractButton { +public: + LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st = st::defaultLinkButton); + + int naturalWidth() const override; + + void setText(const QString &text); + +protected: + void paintEvent(QPaintEvent *e) override; + + void onStateChanged(int oldState, StateChangeSource source) override; + +private: + QString _text; + int _textWidth = 0; + const style::LinkButton &_st; + +}; + +class RoundButton : public AbstractButton { +public: + RoundButton(QWidget *parent, const QString &text, const style::RoundButton &st); + + void setText(const QString &text); + void setSecondaryText(const QString &secondaryText); + + int contentWidth() const; + + void setFullWidth(int newFullWidth); + + enum class TextTransform { + NoTransform, + ToUpper, + }; + void setTextTransform(TextTransform transform); + +protected: + void paintEvent(QPaintEvent *e) override; + + void onStateChanged(int oldState, StateChangeSource source) override; + +private: + void updateText(); + void resizeToText(); + + QString _text, _fullText; + int _textWidth; + + QString _secondaryText, _fullSecondaryText; + int _secondaryTextWidth = 0; + + int _fullWidthOverride = 0; + + const style::RoundButton &_st; + + TextTransform _transform = TextTransform::ToUpper; + +}; + +class IconButton : public AbstractButton { +public: + IconButton(QWidget *parent, const style::IconButton &st); + + // Pass nullptr to restore the default icon. + void setIcon(const style::icon *icon, const style::icon *iconOver = nullptr); + +protected: + void paintEvent(QPaintEvent *e) override; + + void onStateChanged(int oldState, StateChangeSource source) override; + +private: + const style::IconButton &_st; + const style::icon *_iconOverride = nullptr; + const style::icon *_iconOverrideOver = nullptr; + + FloatAnimation _a_over; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/checkbox.cpp b/Telegram/SourceFiles/ui/widgets/checkbox.cpp similarity index 95% rename from Telegram/SourceFiles/ui/buttons/checkbox.cpp rename to Telegram/SourceFiles/ui/widgets/checkbox.cpp index b5188f52d..94af0c601 100644 --- a/Telegram/SourceFiles/ui/buttons/checkbox.cpp +++ b/Telegram/SourceFiles/ui/widgets/checkbox.cpp @@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "ui/buttons/checkbox.h" +#include "ui/widgets/checkbox.h" #include "lang.h" @@ -87,7 +87,7 @@ void RadiobuttonGroup::remove(Radiobutton * const &radio) { } } -Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st) : Button(parent) +Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st) : AbstractButton(parent) , _st(st) , a_over(0) , a_checked(checked ? 1 : 0) @@ -109,7 +109,6 @@ Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const sty _checkRect = myrtlrect(0, 0, _st.diameter, _st.diameter); connect(this, SIGNAL(clicked()), this, SLOT(onClicked())); - connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); setCursor(style::cur_pointer); @@ -222,7 +221,7 @@ void Checkbox::onClicked() { setChecked(!checked()); } -void Checkbox::onStateChange(int oldState, ButtonStateChangeSource source) { +void Checkbox::onStateChanged(int oldState, StateChangeSource source) { if ((_state & StateOver) && !(oldState & StateOver)) { a_over.start(1); _a_over.start(); @@ -237,7 +236,7 @@ void Checkbox::onStateChange(int oldState, ButtonStateChangeSource source) { } } -Radiobutton::Radiobutton(QWidget *parent, const QString &group, int32 value, const QString &text, bool checked, const style::Radiobutton &st) : Button(parent) +Radiobutton::Radiobutton(QWidget *parent, const QString &group, int32 value, const QString &text, bool checked, const style::Radiobutton &st) : AbstractButton(parent) , _st(st) , a_over(0) , a_checked(checked ? 1 : 0) @@ -261,7 +260,6 @@ Radiobutton::Radiobutton(QWidget *parent, const QString &group, int32 value, con _checkRect = myrtlrect(0, 0, _st.diameter, _st.diameter); connect(this, SIGNAL(clicked()), this, SLOT(onClicked())); - connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); setCursor(style::cur_pointer); @@ -381,7 +379,7 @@ void Radiobutton::onClicked() { setChecked(!checked()); } -void Radiobutton::onStateChange(int oldState, ButtonStateChangeSource source) { +void Radiobutton::onStateChanged(int oldState, StateChangeSource source) { if ((_state & StateOver) && !(oldState & StateOver)) { a_over.start(1); _a_over.start(); diff --git a/Telegram/SourceFiles/ui/buttons/checkbox.h b/Telegram/SourceFiles/ui/widgets/checkbox.h similarity index 87% rename from Telegram/SourceFiles/ui/buttons/checkbox.h rename to Telegram/SourceFiles/ui/widgets/checkbox.h index 8b7ccf367..ad5b8c7cc 100644 --- a/Telegram/SourceFiles/ui/buttons/checkbox.h +++ b/Telegram/SourceFiles/ui/widgets/checkbox.h @@ -20,11 +20,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/button.h" +#include "ui/abstract_button.h" namespace Ui { -class Checkbox : public Button { +class Checkbox : public AbstractButton { Q_OBJECT public: @@ -44,9 +44,10 @@ public: protected: void paintEvent(QPaintEvent *e) override; - public slots: + void onStateChanged(int oldState, StateChangeSource source) override; + +public slots: void onClicked(); - void onStateChange(int oldState, ButtonStateChangeSource source); signals: void changed(); @@ -67,7 +68,7 @@ private: }; -class Radiobutton : public Button { +class Radiobutton : public AbstractButton { Q_OBJECT public: @@ -83,13 +84,15 @@ public: void step_over(float64 ms, bool timer); void step_checked(float64 ms, bool timer); - void paintEvent(QPaintEvent *e); - ~Radiobutton(); - public slots: +protected: + void paintEvent(QPaintEvent *e) override; + + void onStateChanged(int oldState, StateChangeSource source) override; + +public slots: void onClicked(); - void onStateChange(int oldState, ButtonStateChangeSource source); signals: void changed(); diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index f803a8c60..dfcbb1b4e 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/multi_select.h" #include "styles/style_widgets.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "lang.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 85ad525d0..f3ba2c336 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -27,6 +27,34 @@ LabelSimple { textFg: color; } +LinkButton { + color: color; + overColor: color; + downColor: color; + font: font; + overFont: font; +} + +FlatButton { + color: color; + overColor: color; + + bgColor: color; + overBgColor: color; + + width: pixels; + height: pixels; + + textTop: pixels; + overTextTop: pixels; + downTextTop: pixels; + + font: font; + overFont: font; + duration: int; + cursor: cursor; +} + IconButton { width: pixels; height: pixels; @@ -53,22 +81,6 @@ Shadow { fallback: color; } -MaskButton { - width: pixels; - height: pixels; - - bg: color; - icon: icon; - - iconBg: color; - iconBgOver: color; - - iconPosition: point; - iconPositionDown: point; - - duration: int; -} - MediaSlider { width: pixels; activeFg: color; @@ -212,17 +224,19 @@ defaultLabelSimple: LabelSimple { textFg: windowTextFg; } +defaultLinkButton: LinkButton { + color: btnYesColor; + overColor: btnYesColor; + downColor: btnYesHover; + font: linkFont; + overFont: linkOverFont; +} + defaultIconButton: IconButton { iconPosition: point(-1px, -1px); iconPositionDown: point(-1px, -1px); } -defaultMaskButton: MaskButton { - iconPosition: point(-1px, -1px); - iconPositionDown: point(-1px, -1px); - duration: 150; -} - widgetSlideDuration: 200; widgetFadeDuration: 200; diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index ca8a35195..40218c278 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "mainwindow.h" #include "lang.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" #include "styles/style_window.h" @@ -919,8 +919,8 @@ void HideAllButton::paintEvent(QPaintEvent *e) { p.fillRect(0, st::notifyBorderWidth, st::notifyBorderWidth, height() - 2 * st::notifyBorderWidth, st::notifyBorder); p.fillRect(width() - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorderWidth, height() - 2 * st::notifyBorderWidth, st::notifyBorder); - p.setFont(st::btnDefLink.font); - p.setPen(st::btnDefLink.color); + p.setFont(st::defaultLinkButton.font); + p.setPen(st::defaultLinkButton.color); p.drawText(rect(), lang(lng_notification_hide_all), style::al_center); } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index a4ba92062..67b5956e6 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class IconButton; +class RoundButton; } // namespace Ui namespace Window { @@ -245,7 +246,7 @@ private: HistoryItem *_item; int _forwardedCount; ChildWidget _close; - ChildWidget _reply; + ChildWidget _reply; ChildWidget _background = { nullptr }; ChildWidget _replyArea = { nullptr }; ChildWidget _replySend = { nullptr }; diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 1d5d68439..76978560f 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -30,10 +30,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "shortcuts.h" #include "lang.h" #include "ui/buttons/peer_avatar_button.h" -#include "ui/buttons/round_button.h" -#include "ui/buttons/icon_button.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/dropdown_menu.h" -#include "ui/flatbutton.h" #include "dialogs/dialogs_layout.h" namespace Window { @@ -47,9 +45,7 @@ TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w) , _mediaType(this, lang(lng_media_type), st::topBarButton) , _search(this, st::topBarSearch) , _menuToggle(this, st::topBarMenuToggle) { - _clearSelection->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); - _forward->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); - _delete->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); + _mediaType->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); _forward->setClickedCallback([this] { onForwardSelection(); }); _delete->setClickedCallback([this] { onDeleteSelection(); }); diff --git a/Telegram/SourceFiles/window/window_theme_warning.cpp b/Telegram/SourceFiles/window/window_theme_warning.cpp index e75a33c05..ffcae6d66 100644 --- a/Telegram/SourceFiles/window/window_theme_warning.cpp +++ b/Telegram/SourceFiles/window/window_theme_warning.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/window_theme_warning.h" #include "styles/style_boxes.h" -#include "ui/buttons/round_button.h" +#include "ui/widgets/buttons.h" #include "window/window_theme.h" #include "lang.h" diff --git a/Telegram/SourceFiles/window/window_theme_warning.h b/Telegram/SourceFiles/window/window_theme_warning.h index 27e4b7ad7..af9529831 100644 --- a/Telegram/SourceFiles/window/window_theme_warning.h +++ b/Telegram/SourceFiles/window/window_theme_warning.h @@ -22,7 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/effects/rect_shadow.h" -class BoxButton; +namespace Ui { +class RoundButton; +} // namespace Ui namespace Window { namespace Theme { @@ -60,8 +62,8 @@ private: QString _text; Ui::RectShadow _shadow; - ChildWidget _keepChanges; - ChildWidget _revert; + ChildWidget _keepChanges; + ChildWidget _revert; base::lambda_unique _hiddenCallback; diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 93504aea6..788c11023 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -449,18 +449,12 @@ '<(src_loc)/stickers/emoji_pan.h', '<(src_loc)/stickers/stickers.cpp', '<(src_loc)/stickers/stickers.h', - '<(src_loc)/ui/buttons/checkbox.cpp', - '<(src_loc)/ui/buttons/checkbox.h', '<(src_loc)/ui/buttons/history_down_button.cpp', '<(src_loc)/ui/buttons/history_down_button.h', - '<(src_loc)/ui/buttons/icon_button.cpp', - '<(src_loc)/ui/buttons/icon_button.h', '<(src_loc)/ui/buttons/left_outline_button.cpp', '<(src_loc)/ui/buttons/left_outline_button.h', '<(src_loc)/ui/buttons/peer_avatar_button.cpp', '<(src_loc)/ui/buttons/peer_avatar_button.h', - '<(src_loc)/ui/buttons/round_button.cpp', - '<(src_loc)/ui/buttons/round_button.h', '<(src_loc)/ui/effects/panel_animation.cpp', '<(src_loc)/ui/effects/panel_animation.h', '<(src_loc)/ui/effects/radial_animation.cpp', @@ -495,6 +489,10 @@ '<(src_loc)/ui/toast/toast_manager.h', '<(src_loc)/ui/toast/toast_widget.cpp', '<(src_loc)/ui/toast/toast_widget.h', + '<(src_loc)/ui/widgets/buttons.cpp', + '<(src_loc)/ui/widgets/buttons.h', + '<(src_loc)/ui/widgets/checkbox.cpp', + '<(src_loc)/ui/widgets/checkbox.h', '<(src_loc)/ui/widgets/continuous_slider.cpp', '<(src_loc)/ui/widgets/continuous_slider.h', '<(src_loc)/ui/widgets/discrete_slider.cpp', @@ -519,18 +517,16 @@ '<(src_loc)/ui/widgets/shadow.h', '<(src_loc)/ui/widgets/tooltip.cpp', '<(src_loc)/ui/widgets/tooltip.h', + '<(src_loc)/ui/abstract_button.cpp', + '<(src_loc)/ui/abstract_button.h', '<(src_loc)/ui/animation.cpp', '<(src_loc)/ui/animation.h', - '<(src_loc)/ui/button.cpp', - '<(src_loc)/ui/button.h', '<(src_loc)/ui/countryinput.cpp', '<(src_loc)/ui/countryinput.h', '<(src_loc)/ui/emoji_config.cpp', '<(src_loc)/ui/emoji_config.h', '<(src_loc)/ui/filedialog.cpp', '<(src_loc)/ui/filedialog.h', - '<(src_loc)/ui/flatbutton.cpp', - '<(src_loc)/ui/flatbutton.h', '<(src_loc)/ui/flatinput.cpp', '<(src_loc)/ui/flatinput.h', '<(src_loc)/ui/flatlabel.cpp', From a74baaea364ae3a64cff82a134a037f6d83e0303 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Nov 2016 22:51:59 +0300 Subject: [PATCH 036/100] Left main menu. --- Telegram/SourceFiles/application.cpp | 2 +- Telegram/SourceFiles/dialogs/dialogs.style | 14 ++- Telegram/SourceFiles/dialogswidget.cpp | 5 +- Telegram/SourceFiles/layerwidget.cpp | 97 +++++++++++++++- Telegram/SourceFiles/layerwidget.h | 6 + Telegram/SourceFiles/mainwindow.cpp | 12 ++ Telegram/SourceFiles/mainwindow.h | 2 + Telegram/SourceFiles/window/main_window.h | 4 +- Telegram/SourceFiles/window/window.style | 34 ++++++ .../SourceFiles/window/window_main_menu.cpp | 105 ++++++++++++++++++ .../SourceFiles/window/window_main_menu.h | 48 ++++++++ Telegram/gyp/Telegram.gyp | 2 + 12 files changed, 320 insertions(+), 11 deletions(-) create mode 100644 Telegram/SourceFiles/window/window_main_menu.cpp create mode 100644 Telegram/SourceFiles/window/window_main_menu.h diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 42953122c..08b1669d1 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -834,7 +834,7 @@ void AppClass::cancelPhotoUpdate(const PeerId &peer) { void AppClass::mtpPause() { MTP::pause(); - _mtpUnpauseTimer.start(st::slideDuration * 2); + _mtpUnpauseTimer.start(st::layerSlideDuration * 2); } void AppClass::mtpUnpause() { diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 79226dfb5..4a68d2998 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -100,18 +100,20 @@ dialogsFilter: flatInput(inpDefGray) { width: 240px; height: 32px; - textMrg: margins(12px, 3px, 12px, 3px); + textMrg: margins(12px, 3px, 30px, 3px); } dialogsCancelSearch: IconButton(dialogsMenuToggle) { icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg, point(0px, 1px) }}; iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver, point(0px, 1px) }}; } -dialogsMenu: DropdownMenu(defaultDropdownMenu) { - menu: Menu(defaultMenu) { - itemIconPosition: point(15px, 8px); - itemPadding: margins(56px, 10px, 56px, 12px); - } +dialogsMenu: Menu(defaultMenu) { + itemFont: semiboldFont; + itemIconPosition: point(28px, 11px); + itemPadding: margins(76px, 14px, 28px, 14px); +} +dialogsMenuWrap: DropdownMenu(defaultDropdownMenu) { + menu: dialogsMenu; } dialogsMenuPosition: point(-3px, -2px); dialogsMenuNewGroup: icon {{ "menu_new_group", menuIconFg }}; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 779b7a2a1..32fd61b7c 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -2012,8 +2012,11 @@ void DialogsWidget::onChooseByDrag() { } void DialogsWidget::showMainMenu() { + App::wnd()->showMainMenu(); + return; + if (!_mainMenu) { - _mainMenu.create(this, st::dialogsMenu); + _mainMenu.create(this, st::dialogsMenuWrap); _mainMenu->addAction(lang(lng_create_group_title), [] { App::wnd()->onShowNewGroup(); }, &st::dialogsMenuNewGroup, &st::dialogsMenuNewGroupOver); diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index f6de4cce5..249929550 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "ui/filedialog.h" #include "styles/style_stickers.h" +#include "window/window_main_menu.h" namespace { @@ -48,6 +49,19 @@ public: , _shadow(st::boxShadow) { } + void setBodyCache(QPixmap &&bodyCache) { + _bodyCache = std_::move(bodyCache); + } + void setMainMenuCache(QPixmap &&mainMenuCache) { + _mainMenuCache = std_::move(mainMenuCache); + if (!_mainMenuCache.isNull()) { + _mainMenuWidth = _mainMenuCache.width() / cIntRetinaFactor(); + _mainMenuRight = 0; + } + } + void setMainMenuRight(int right) { + _mainMenuRight = right; + } void setLayerBox(const QRect &box, const QRect &hiddenSpecialBox) { _box = box; _hiddenSpecialBox = hiddenSpecialBox; @@ -61,6 +75,25 @@ protected: void paintEvent(QPaintEvent *e) override { Painter p(this); + auto hasMainMenuCache = !_mainMenuCache.isNull(); + if (hasMainMenuCache || _mainMenuRight) { + auto boxLeft = _mainMenuRight; + auto cacheWidth = boxLeft * cIntRetinaFactor(); + if (left > 0 && hasMainMenuCache) { + p.drawPixmapLeft(0, 0, width(), _mainMenuCache, rtlrect(_mainMenuCache.width() - cacheWidth, 0, cacheWidth, height() * cIntRetinaFactor(), _mainMenuCache.width())); + } + if (!_bodyCache.isNull()) { + p.drawPixmapLeft(boxLeft, 0, width(), _bodyCache, rtlrect(cacheWidth, 0, _bodyCache.width() - cacheWidth, height() * cIntRetinaFactor(), _bodyCache.width() - cacheWidth)); + } + _shadow.paint(p, QRect(0, 0, boxLeft, height()), 0, Ui::RectShadow::Side::Right); + + p.setOpacity(_opacity); + p.fillRect(myrtlrect(boxLeft, 0, width() - boxLeft, height()), st::layerBg); + return; + } + if (!_bodyCache.isNull()) { + p.drawPixmap(0, 0, _bodyCache); + } p.setOpacity(_opacity); if (_box.isNull()) { p.fillRect(rect(), st::layerBg); @@ -79,6 +112,11 @@ protected: } private: + QPixmap _bodyCache; + QPixmap _mainMenuCache; + int _mainMenuWidth = 0; + int _mainMenuRight = 0; + QRect _box, _hiddenSpecialBox; float64 _opacity = 0.; @@ -224,6 +262,16 @@ void LayerStackWidget::startHide() { } void LayerStackWidget::startAnimation(float64 toOpacity) { + if (_mainMenu) { + setAttribute(Qt::WA_OpaquePaintEvent); + hide(); + _background->setBodyCache(myGrab(App::wnd()->bodyWidget())); + show(); + _mainMenu->hide(); + _background->setMainMenuCache(myGrab(_mainMenu)); + _background->setMainMenuRight(toOpacity ? 0 : _mainMenu->width()); + } + if (App::app()) App::app()->mtpPause(); a_bg.start(toOpacity); a_layer.start(toOpacity); @@ -283,6 +331,9 @@ void LayerStackWidget::resizeEvent(QResizeEvent *e) { if (auto l = layer()) { l->parentResized(); } + if (_mainMenu) { + _mainMenu->resize(_mainMenu->width(), height()); + } updateLayerBox(); } @@ -300,6 +351,28 @@ void LayerStackWidget::showSpecialLayer(LayerWidget *l) { activateLayer(l); } +void LayerStackWidget::showMainMenu() { + clearLayers(); + if (_specialLayer) { + _specialLayer.destroyDelayed(); + } + _mainMenu.create(this); + _mainMenu->setGeometryToLeft(0, 0, _mainMenu->width(), height()); + + _mainMenu->setParent(this); + fixOrder(); + + if (isHidden()) { + startShow(); + } else { + _mainMenu->show(); + if (App::wnd()) App::wnd()->setInnerFocus(); + updateLayerBox(); + } + fixOrder(); + sendFakeMouseEvent(); +} + void LayerStackWidget::appendLayer(LayerWidget *l) { if (auto oldLayer = layer()) { oldLayer->hide(); @@ -338,6 +411,11 @@ void LayerStackWidget::initChildLayer(LayerWidget *l) { } void LayerStackWidget::activateLayer(LayerWidget *l) { + if (_mainMenu) { + _mainMenu.destroyDelayed(); + _background->setMainMenuRight(0); + _background->setMainMenuCache(QPixmap()); + } initChildLayer(l); if (isHidden()) { startShow(); @@ -358,6 +436,9 @@ void LayerStackWidget::fixOrder() { } else if (_specialLayer) { _specialLayer->raise(); } + if (_mainMenu) { + _mainMenu->raise(); + } } void LayerStackWidget::sendFakeMouseEvent() { @@ -371,8 +452,15 @@ void LayerStackWidget::step_background(float64 ms, bool timer) { a_layer.finish(); _a_background.stop(); _layerCache = _hiddenSpecialLayerCache = QPixmap(); + setAttribute(Qt::WA_OpaquePaintEvent, false); + _background->setBodyCache(QPixmap()); if (_hiding) { App::wnd()->layerFinishedHide(this); + if (_mainMenu) { + _background->setMainMenuRight(0); + _background->setMainMenuCache(QPixmap()); + _mainMenu.destroyDelayed(); + } } else { if (_specialLayer) { _specialLayer->show(); @@ -382,7 +470,11 @@ void LayerStackWidget::step_background(float64 ms, bool timer) { l->show(); l->showDone(); } - fixOrder(); + if (_mainMenu) { + _background->setMainMenuRight(_mainMenu->width()); + _background->setMainMenuCache(QPixmap()); + _mainMenu->show(); + } if (App::wnd()) App::wnd()->setInnerFocus(); } updateLayerBox(); @@ -390,6 +482,9 @@ void LayerStackWidget::step_background(float64 ms, bool timer) { } else { a_bg.update(dt, anim::easeOutCirc); a_layer.update(dt, anim::linear); + if (_mainMenu) { + _background->setMainMenuRight(a_bg.current() * _mainMenu->width()); + } } _background->setOpacity(a_bg.current()); if (timer) { diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index b44b4bc32..bd0eb45a1 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -20,6 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +namespace Window { +class MainMenu; +} // namespace Window + #include "ui/effects/rect_shadow.h" class LayerWidget : public TWidget { @@ -65,6 +69,7 @@ public: void showLayer(LayerWidget *l); void showSpecialLayer(LayerWidget *l); + void showMainMenu(); void appendLayer(LayerWidget *l); void prependLayer(LayerWidget *l); @@ -115,6 +120,7 @@ private: Layers _layers; ChildWidget _specialLayer = { nullptr }; + ChildWidget _mainMenu = { nullptr }; class BackgroundWidget; ChildWidget _background; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index bb6f4982b..f6a68ca78 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -48,6 +48,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/notifications_manager.h" #include "window/window_theme.h" #include "window/window_theme_warning.h" +#include "window/window_main_menu.h" ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : TWidget(parent) , _shadow(st::boxShadow) @@ -406,6 +407,17 @@ void MainWindow::showSettings() { _layerBg->showSpecialLayer(_settings); } +void MainWindow::showMainMenu() { + if (_passcode) return; + + if (isHidden()) showFromTray(); + + if (!_layerBg) { + _layerBg.create(bodyWidget()); + } + _layerBg->showMainMenu(); +} + void MainWindow::ui_hideSettingsAndLayer(ShowLayerOptions options) { if (_layerBg) { _layerBg->onClose(); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 8f53d7d32..452b41611 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -157,6 +157,8 @@ public: return contentOverlapped(QRect(w->mapToGlobal(r.boundingRect().topLeft()), r.boundingRect().size())); } + void showMainMenu(); + void ui_showLayer(LayerWidget *box, ShowLayerOptions options); void ui_hideSettingsAndLayer(ShowLayerOptions options); bool ui_isLayerShown(); diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 1e3f11f9b..d287a0370 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -50,7 +50,7 @@ public: virtual ~MainWindow(); - QWidget *bodyWidget() { + TWidget *bodyWidget() { return _body; } @@ -90,7 +90,7 @@ private: bool _positionInited = false; ChildWidget _title = { nullptr }; - ChildWidget _body; + ChildWidget _body; QString _titleText; diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 06a2cdfe0..a6db6a26c 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -84,6 +84,40 @@ notifySendReply: IconButton { titleUnreadCounterTop: 5px; titleUnreadCounterRight: 35px; +mainMenuBg: #ffffff; +mainMenuWidth: 274px; +mainMenuCoverBg: #419fd9; +mainMenuCoverFg: windowActiveFg; +mainMenuCoverHeight: 140px; +mainMenuUserpicLeft: 24px; +mainMenuUserpicTop: 22px; +mainMenuUserpicSize: 48px; +mainMenuCoverTextLeft: 30px; +mainMenuCoverNameTop: 88px; +mainMenuCoverStatusTop: 106px; +mainMenuSkip: 13px; +mainMenuFooterLeft: 30px; +mainMenuTelegramLabel: flatLabel(labelDefFlat) { + font: semiboldFont; + align: align(left); + textFg: windowSubTextFg; +} +mainMenuTelegramStyle: textStyle(defaultTextStyle) { + linkFlags: semiboldFont; + linkFlagsOver: font(fsize semibold underline); + linkFg: windowSubTextFg; + linkFgDown: windowSubTextFg; +} +mainMenuTelegramBottom: 43px; +mainMenuVersionLabel: flatLabel(mainMenuTelegramLabel) { + font: normalFont; +} +mainMenuVersionStyle: textStyle(mainMenuTelegramStyle) { + linkFlags: normalFont; + linkFlagsOver: font(fsize underline); +} +mainMenuVersionBottom: 21px; + // Windows specific title titleHeight: 21px; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp new file mode 100644 index 000000000..1e2cc1e90 --- /dev/null +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -0,0 +1,105 @@ +/* +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 "window/window_main_menu.h" + +#include "styles/style_window.h" +#include "ui/flatlabel.h" +#include "ui/widgets/menu.h" +#include "mainwindow.h" +#include "boxes/contactsbox.h" +#include "boxes/aboutbox.h" +#include "lang.h" +#include "core/click_handler_types.h" +#include "styles/style_dialogs.h" + +namespace Window { +namespace { + +class AboutClickHandler : public ClickHandler { +public: + void onClick(Qt::MouseButton) const override { + Ui::showLayer(new AboutBox()); + } + +}; + +} // namespace + +MainMenu::MainMenu(QWidget *parent) : TWidget(parent) +, _menu(this, st::dialogsMenu) +, _telegram(this, st::mainMenuTelegramLabel, st::mainMenuTelegramStyle) +, _version(this, st::mainMenuVersionLabel, st::mainMenuVersionStyle) { + setAttribute(Qt::WA_OpaquePaintEvent); + resize(st::mainMenuWidth, parentWidget()->height()); + _menu->setTriggeredCallback([](QAction *action, int actionTop, Ui::Menu::TriggeredSource source) { + emit action->triggered(); + }); + _menu->addAction(lang(lng_create_group_title), [] { + App::wnd()->onShowNewGroup(); + }, &st::dialogsMenuNewGroup, &st::dialogsMenuNewGroupOver); + _menu->addAction(lang(lng_create_channel_title), [] { + App::wnd()->onShowNewChannel(); + }, &st::dialogsMenuNewChannel, &st::dialogsMenuNewChannelOver); + _menu->addAction(lang(lng_menu_contacts), [] { + Ui::showLayer(new ContactsBox()); + }, &st::dialogsMenuContacts, &st::dialogsMenuContactsOver); + _menu->addAction(lang(lng_menu_settings), [] { + App::wnd()->showSettings(); + }, &st::dialogsMenuSettings, &st::dialogsMenuSettingsOver); + _menu->addAction(lang(lng_settings_faq), [] { + QDesktopServices::openUrl(telegramFaqLink()); + }, &st::dialogsMenuHelp, &st::dialogsMenuHelpOver); + + _telegram->setRichText(textcmdLink(1, qsl("Telegram Desktop"))); + _telegram->setLink(1, ClickHandlerPtr(new UrlClickHandler(qsl("https://desktop.telegram.org")))); + _version->setRichText(textcmdLink(1, qsl("Version 1.2.3")) + QChar(' ') + QChar(8211) + QChar(' ') + textcmdLink(2, qsl("About"))); + _version->setLink(1, ClickHandlerPtr(new UrlClickHandler(qsl("https://desktop.telegram.org/?_hash=changelog")))); + _version->setLink(2, ClickHandlerPtr(new AboutClickHandler())); +} + +void MainMenu::resizeEvent(QResizeEvent *e) { + _menu->setGeometry(0, st::mainMenuCoverHeight + st::mainMenuSkip, width(), _menu->height()); + _telegram->moveToLeft(st::mainMenuFooterLeft, height() - st::mainMenuTelegramBottom - _telegram->height()); + _version->moveToLeft(st::mainMenuFooterLeft, height() - st::mainMenuVersionBottom - _version->height()); +} + +void MainMenu::paintEvent(QPaintEvent *e) { + Painter p(this); + auto cover = QRect(0, 0, width(), st::mainMenuCoverHeight).intersected(e->rect()); + if (!cover.isEmpty()) { + p.fillRect(cover, st::mainMenuCoverBg); + p.setPen(st::mainMenuCoverFg); + p.setFont(st::semiboldFont); + if (auto self = App::self()) { + self->paintUserpicLeft(p, st::mainMenuUserpicSize, st::mainMenuUserpicLeft, st::mainMenuUserpicTop, width()); + self->nameText.drawLeftElided(p, st::mainMenuCoverTextLeft, st::mainMenuCoverNameTop, width() - 2 * st::mainMenuCoverTextLeft, width()); + p.setFont(st::normalFont); + p.drawTextLeft(st::mainMenuCoverTextLeft, st::mainMenuCoverStatusTop, width(), qsl("online")); + } + } + auto other = QRect(0, st::mainMenuCoverHeight, width(), height() - st::mainMenuCoverHeight); + if (!other.isEmpty()) { + p.fillRect(other, st::mainMenuBg); + } +} + +} // namespace Window diff --git a/Telegram/SourceFiles/window/window_main_menu.h b/Telegram/SourceFiles/window/window_main_menu.h new file mode 100644 index 000000000..62f68b4a4 --- /dev/null +++ b/Telegram/SourceFiles/window/window_main_menu.h @@ -0,0 +1,48 @@ +/* +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/effects/rect_shadow.h" + +class FlatLabel; + +namespace Ui { +class Menu; +} // namespace Ui + +namespace Window { + +class MainMenu : public TWidget { +public: + MainMenu(QWidget *parent); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + ChildWidget _menu; + ChildWidget _telegram; + ChildWidget _version; + +}; + +} // namespace Window diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 788c11023..dc7818dfd 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -555,6 +555,8 @@ '<(src_loc)/window/slide_animation.h', '<(src_loc)/window/top_bar_widget.cpp', '<(src_loc)/window/top_bar_widget.h', + '<(src_loc)/window/window_main_menu.cpp', + '<(src_loc)/window/window_main_menu.h', '<(src_loc)/window/window_theme.cpp', '<(src_loc)/window/window_theme.h', '<(src_loc)/window/window_theme_warning.cpp', From 78f55c10e912cda2b43f50ff7e45b32663bec29e Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Nov 2016 23:01:00 +0300 Subject: [PATCH 037/100] Closed beta 10019006: left main menu. --- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 2 +- Telegram/SourceFiles/ui/widgets/buttons.h | 5 +++-- Telegram/build/version | 2 +- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index d757b76cd..4b0dbe013 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,5 - PRODUCTVERSION 0,10,19,5 + FILEVERSION 0,10,19,6 + PRODUCTVERSION 0,10,19,6 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.19.5" + VALUE "FileVersion", "0.10.19.6" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.5" + VALUE "ProductVersion", "0.10.19.6" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index abf93f304..99ce4ed04 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,5 - PRODUCTVERSION 0,10,19,5 + FILEVERSION 0,10,19,6 + PRODUCTVERSION 0,10,19,6 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.19.5" + VALUE "FileVersion", "0.10.19.6" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.5" + VALUE "ProductVersion", "0.10.19.6" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 169174315..e9ddd6088 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019005ULL) +#define BETA_VERSION_MACRO (10019006ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/ui/widgets/buttons.h b/Telegram/SourceFiles/ui/widgets/buttons.h index cf6557d03..f7c1011a5 100644 --- a/Telegram/SourceFiles/ui/widgets/buttons.h +++ b/Telegram/SourceFiles/ui/widgets/buttons.h @@ -30,7 +30,6 @@ public: FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st); void step_appearance(float64 ms, bool timer); - void paintEvent(QPaintEvent *e); void setOpacity(float64 o); float64 opacity() const; @@ -40,11 +39,13 @@ public: int32 textWidth() const; protected: + void paintEvent(QPaintEvent *e) override; + void onStateChanged(int oldState, StateChangeSource source) override; private: QString _text, _textForAutoSize; - int _width, _textWidth; + int _width; const style::FlatButton &_st; diff --git a/Telegram/build/version b/Telegram/build/version index 42cb9bf9d..10659728d 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019005 +BetaVersion 10019006 From 48eb72a9c2439e0ee33f82abc8614fa39a0c9530 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 12 Nov 2016 18:02:19 +0300 Subject: [PATCH 038/100] Optimized dropdown animation. Dropdown animation for EmojiPan done. --- Telegram/SourceFiles/app.cpp | 88 +- Telegram/SourceFiles/app.h | 39 +- Telegram/SourceFiles/core/basic_types.h | 1 + Telegram/SourceFiles/core/build_config.h | 82 ++ .../SourceFiles/media/media_clip_reader.cpp | 7 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 922 ++++++++++++------ Telegram/SourceFiles/stickers/emoji_pan.h | 65 +- Telegram/SourceFiles/stickers/stickers.style | 6 + Telegram/SourceFiles/ui/animation.h | 184 +++- .../ui/effects/panel_animation.cpp | 680 ++++++------- .../SourceFiles/ui/effects/panel_animation.h | 95 +- Telegram/SourceFiles/ui/style/style_core.cpp | 10 +- .../SourceFiles/ui/widgets/inner_dropdown.cpp | 10 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 4 +- Telegram/gyp/Telegram.gyp | 1 + 15 files changed, 1407 insertions(+), 787 deletions(-) create mode 100644 Telegram/SourceFiles/core/build_config.h diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index c62caffd5..982c51c90 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2732,40 +2732,74 @@ namespace { } return ::cornersMaskSmall; } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, const CornersPixmaps &c, const style::color *sh) { - int32 cw = c.p[0]->width() / cIntRetinaFactor(), ch = c.p[0]->height() / cIntRetinaFactor(); - if (w < 2 * cw || h < 2 * ch) return; - if (w > 2 * cw) { - p.fillRect(QRect(x + cw, y, w - 2 * cw, ch), bg->b); - p.fillRect(QRect(x + cw, y + h - ch, w - 2 * cw, ch), bg->b); - if (sh) p.fillRect(QRect(x + cw, y + h, w - 2 * cw, st::msgShadow), (*sh)->b); + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) { + auto cornerWidth = corner.p[0]->width() / cIntRetinaFactor(); + auto cornerHeight = corner.p[0]->height() / cIntRetinaFactor(); + if (w < 2 * cornerWidth || h < 2 * cornerHeight) return; + if (w > 2 * cornerWidth) { + if (parts & RectPart::Top) { + p.fillRect(x + cornerWidth, y, w - 2 * cornerWidth, cornerHeight, bg); + } + if (parts & RectPart::Bottom) { + p.fillRect(x + cornerWidth, y + h - cornerHeight, w - 2 * cornerWidth, cornerHeight, bg); + if (shadow) { + p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, *shadow); + } + } } - if (h > 2 * ch) { - p.fillRect(QRect(x, y + ch, w, h - 2 * ch), bg->b); + if (h > 2 * cornerHeight) { + if ((parts & RectPart::NoTopBottom) == qFlags(RectPart::NoTopBottom)) { + p.fillRect(x, y + cornerHeight, w, h - 2 * cornerHeight, bg); + } else { + if (parts & RectPart::Left) { + p.fillRect(x, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, bg); + } + if ((parts & RectPart::Center) && w > 2 * cornerWidth) { + p.fillRect(x + cornerWidth, y + cornerHeight, w - 2 * cornerWidth, h - 2 * cornerHeight, bg); + } + if (parts & RectPart::Right) { + p.fillRect(x + w - cornerWidth, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, bg); + } + } + } + if (parts & RectPart::TopLeft) { + p.drawPixmap(x, y, *corner.p[0]); + } + if (parts & RectPart::TopRight) { + p.drawPixmap(x + w - cornerWidth, y, *corner.p[1]); + } + if (parts & RectPart::BottomLeft) { + p.drawPixmap(x, y + h - cornerHeight, *corner.p[2]); + } + if (parts & RectPart::BottomRight) { + p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight, *corner.p[3]); } - p.drawPixmap(QPoint(x, y), *c.p[0]); - p.drawPixmap(QPoint(x + w - cw, y), *c.p[1]); - p.drawPixmap(QPoint(x, y + h - ch), *c.p[2]); - p.drawPixmap(QPoint(x + w - cw, y + h - ch), *c.p[3]); } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh) { - roundRect(p, x, y, w, h, bg, ::corners[index], sh); + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *shadow, RectParts parts) { + roundRect(p, x, y, w, h, bg, ::corners[index], shadow, parts); } - void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index) { - const CornersPixmaps &c = ::corners[index]; - int32 cw = c.p[0]->width() / cIntRetinaFactor(), ch = c.p[0]->height() / cIntRetinaFactor(); - p.fillRect(x + cw, y + h, w - 2 * cw, st::msgShadow, sh->b); - p.fillRect(x, y + h - ch, cw, st::msgShadow, sh->b); - p.fillRect(x + w - cw, y + h - ch, cw, st::msgShadow, sh->b); - p.drawPixmap(x, y + h - ch + st::msgShadow, *c.p[2]); - p.drawPixmap(x + w - cw, y + h - ch + st::msgShadow, *c.p[3]); + void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &shadow, RoundCorners index, RectParts parts) { + auto &corner = ::corners[index]; + auto cornerWidth = corner.p[0]->width() / cIntRetinaFactor(); + auto cornerHeight = corner.p[0]->height() / cIntRetinaFactor(); + if (parts & RectPart::Bottom) { + p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, shadow); + } + if (parts & RectPart::BottomLeft) { + p.fillRect(x, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow); + p.drawPixmap(x, y + h - cornerHeight + st::msgShadow, *corner.p[2]); + } + if (parts & RectPart::BottomRight) { + p.fillRect(x + w - cornerWidth, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow); + p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight + st::msgShadow, *corner.p[3]); + } } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius) { - uint32 colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24) | ((uint32(bg->c.red()) & 0xFF) << 16) | ((uint32(bg->c.green()) & 0xFF) << 8) | ((uint32(bg->c.blue()) & 0xFF) << 24); - CornersMap::const_iterator i = cornersMap.find(colorKey); + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius, RectParts parts) { + auto colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24) | ((uint32(bg->c.red()) & 0xFF) << 16) | ((uint32(bg->c.green()) & 0xFF) << 8) | ((uint32(bg->c.blue()) & 0xFF) << 24); + auto i = cornersMap.find(colorKey); if (i == cornersMap.cend()) { QImage images[4]; switch (radius) { @@ -2781,7 +2815,7 @@ namespace { } i = cornersMap.insert(colorKey, pixmaps); } - roundRect(p, x, y, w, h, bg, i.value(), 0); + roundRect(p, x, y, w, h, bg, i.value(), nullptr, parts); } WallPapers gServerBackgrounds; diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 2e14e7d94..f80783141 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -279,18 +279,39 @@ namespace App { #endif // !TDESKTOP_DISABLE_NETWORK_PROXY void setProxySettings(QTcpSocket &socket); + enum class RectPart { + TopLeft = 0x001, + Top = 0x002, + TopRight = 0x004, + Left = 0x008, + Center = 0x010, + Right = 0x020, + BottomLeft = 0x040, + Bottom = 0x080, + BottomRight = 0x100, + TopFull = 0x007, + LeftFull = 0x049, + RightFull = 0x124, + BottomFull = 0x1c0, + NoTopBottom = 0x038, + NoLeftRight = 0x092, + Full = 0x1ff, + }; + Q_DECLARE_FLAGS(RectParts, RectPart); + Q_DECLARE_OPERATORS_FOR_FLAGS(RectParts); + QImage **cornersMask(ImageRoundRadius radius); - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh = 0); - inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *sh = 0) { - return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, sh); + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full); + inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) { + return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, shadow, parts); } - void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index); - inline void roundShadow(Painter &p, const QRect &rect, const style::color &sh, RoundCorners index) { - return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), sh, index); + void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &shadow, RoundCorners index, RectParts parts = RectPart::Full); + inline void roundShadow(Painter &p, const QRect &rect, const style::color &shadow, RoundCorners index, RectParts parts = RectPart::Full) { + return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), shadow, index, parts); } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius); - inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, ImageRoundRadius radius) { - return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius); + void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius, RectParts parts = RectPart::Full); + inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, ImageRoundRadius radius, RectParts parts = RectPart::Full) { + return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius, parts); } struct WallPaper { diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h index 9487daec9..9c368655b 100644 --- a/Telegram/SourceFiles/core/basic_types.h +++ b/Telegram/SourceFiles/core/basic_types.h @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include +#include "core/build_config.h" #include "core/stl_subset.h" #include "core/ordered_set.h" diff --git a/Telegram/SourceFiles/core/build_config.h b/Telegram/SourceFiles/core/build_config.h new file mode 100644 index 000000000..49c89041e --- /dev/null +++ b/Telegram/SourceFiles/core/build_config.h @@ -0,0 +1,82 @@ +/* +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 + +// thanks Chromium + +#if defined(__APPLE__) +#define OS_MAC 1 +#elif defined(__linux__) // __APPLE__ +#define OS_LINUX 1 +#elif defined(_WIN32) // __APPLE__ || __linux__ +#define OS_WIN 1 +#else // __APPLE__ || __linux__ || _WIN32 +#error Please add support for your platform in core/build_config.h +#endif // else for __APPLE__ || __linux__ || _WIN32 + +// For access to standard POSIXish features, use OS_POSIX instead of a +// more specific macro. +#if defined(OS_MAC) || defined(OS_LINUX) +#define OS_POSIX 1 +#endif // OS_MAC || OS_LINUX + +// Compiler detection. +#if defined(__clang__) +#define COMPILER_CLANG 1 +#elif defined(__GNUC__) // __clang__ +#define COMPILER_GCC 1 +#elif defined(_MSC_VER) // __clang__ || __GNUC__ +#define COMPILER_MSVC 1 +#else // _MSC_VER || __clang__ || __GNUC__ +#error Please add support for your compiler in core/build_config.h +#endif // else for _MSC_VER || __clang__ || __GNUC__ + +// Processor architecture detection. +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86_64 1 +#define ARCH_CPU_64_BITS 1 +#elif defined(_M_IX86) || defined(__i386__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86 1 +#define ARCH_CPU_32_BITS 1 +#else +#error Please add support for your architecture in core/build_config.h +#endif + +#if defined(COMPILER_GCC) || defined(COMPILER_CLANG) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED_RESULT +#endif + +#if defined(__GNUC__) +#define FORCE_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define FORCE_INLINE __forceinline +#else +#define FORCE_INLINE inline +#endif + +#include +static_assert(CHAR_BIT == 8, "Not supported char size."); diff --git a/Telegram/SourceFiles/media/media_clip_reader.cpp b/Telegram/SourceFiles/media/media_clip_reader.cpp index 2c45b2084..7e965c8ec 100644 --- a/Telegram/SourceFiles/media/media_clip_reader.cpp +++ b/Telegram/SourceFiles/media/media_clip_reader.cpp @@ -208,7 +208,8 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, auto frame = frameToShow(); t_assert(frame != nullptr); - if (ms) { + auto shouldBePaused = !ms; + if (!shouldBePaused) { frame->displayed.storeRelease(1); if (_autoPausedGif.loadAcquire()) { _autoPausedGif.storeRelease(0); @@ -218,10 +219,10 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, } } } else { - frame->displayed.storeRelease(-1); // displayed, but should be paused + frame->displayed.storeRelease(-1); } - int32 factor(cIntRetinaFactor()); + auto factor = cIntRetinaFactor(); if (frame->pix.width() == outerw * factor && frame->pix.height() == outerh * factor) { moveToNextShow(); return frame->pix; diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 3413ddee3..faa8bf959 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -22,8 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stickers/emoji_pan.h" #include "styles/style_stickers.h" -#include "styles/style_intro.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/shadow.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" #include "boxes/stickers_box.h" @@ -103,8 +103,8 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) { } } else { p.drawPixmap(r.left(), r.top(), _cache); + p.setOpacity(1.); } - } void EmojiColorPicker::enterEvent(QEvent *e) { @@ -286,7 +286,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { 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.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)); @@ -295,7 +295,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { EmojiPanInner::EmojiPanInner() : TWidget() , _maxHeight(int(st::emojiPanMaxHeight) - st::emojiCategory.height) , _a_selected(animation(this, &EmojiPanInner::step_selected)) { - resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); + resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight()); setMouseTracking(true); setAttribute(Qt::WA_OpaquePaintEvent); @@ -394,7 +394,7 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { 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.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)); } @@ -820,7 +820,7 @@ StickerPanInner::StickerPanInner() : TWidget() void StickerPanInner::setMaxHeight(int32 h) { _maxHeight = h; - resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); + resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight()); _settings->moveToLeft((st::emojiPanWidth - _settings->width()) / 2, height() / 3); } @@ -911,18 +911,22 @@ StickerPanInner::~StickerPanInner() { deleteUnusedInlineLayouts(); } +int StickerPanInner::stickersLeft() const { + return (st::stickerPanPadding - st::buttonRadius); +} + QRect StickerPanInner::stickerRect(int tab, int sel) { int x = 0, y = 0; if (_section == Section::Featured) { y += st::emojiPanHeader + (tab * featuredRowHeight()) + st::stickersTrendingHeader; - x = st::stickerPanPadding + (sel * st::stickerPanSize.width()); + x = stickersLeft() + (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()); + x = stickersLeft() + ((sel % StickerPanPerRow) * st::stickerPanSize.width()); break; } else { int cnt = sets[i].pack.size(); @@ -968,7 +972,7 @@ void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) { auto &inlineRow = _inlineRows[row]; if (top >= r.top() + r.height()) break; if (top + inlineRow.height > r.top()) { - int left = st::inlineResultsLeft; + int left = st::inlineResultsLeft - st::buttonRadius; if (row == rows - 1) context.lastRow = true; for (int col = 0, cols = inlineRow.items.size(); col < cols; ++col) { if (left >= tox) break; @@ -991,8 +995,8 @@ void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) { } 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); + int32 fromcol = floorclamp(r.x() - stickersLeft(), st::stickerPanSize.width(), 0, StickerPanPerRow); + int32 tocol = ceilclamp(r.x() + r.width() - stickersLeft(), st::stickerPanSize.width(), 0, StickerPanPerRow); if (rtl()) { qSwap(fromcol, tocol); fromcol = StickerPanPerRow - fromcol; @@ -1013,7 +1017,7 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) { int size = set.pack.size(); - int widthForTitle = featuredContentWidth() - st::emojiPanHeaderLeft; + int widthForTitle = featuredContentWidth() - (st::emojiPanHeaderLeft - st::buttonRadius); if (featuredHasAddButton(c)) { auto add = featuredAddRect(c); auto selected = (_selectedFeaturedSetAdd == c); @@ -1044,20 +1048,20 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) { } p.setFont(st::stickersTrendingHeaderFont); p.setPen(st::stickersTrendingHeaderFg); - p.drawTextLeft(st::emojiPanHeaderLeft, y + st::stickersTrendingHeaderTop, width(), titleText, titleWidth); + p.drawTextLeft(st::emojiPanHeaderLeft - st::buttonRadius, y + st::stickersTrendingHeaderTop, width(), titleText, titleWidth); if (set.flags & MTPDstickerSet_ClientFlag::f_unread) { p.setPen(Qt::NoPen); p.setBrush(st::stickersFeaturedUnreadBg); p.setRenderHint(QPainter::HighQualityAntialiasing, true); - p.drawEllipse(rtlrect(st::emojiPanHeaderLeft + titleWidth + st::stickersFeaturedUnreadSkip, y + st::stickersTrendingHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); + p.drawEllipse(rtlrect(st::emojiPanHeaderLeft - st::buttonRadius + titleWidth + st::stickersFeaturedUnreadSkip, y + st::stickersTrendingHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); p.setRenderHint(QPainter::HighQualityAntialiasing, false); } p.setFont(st::stickersTrendingSubheaderFont); p.setPen(st::stickersTrendingSubheaderFg); - p.drawTextLeft(st::emojiPanHeaderLeft, y + st::stickersTrendingSubheaderTop, width(), lng_stickers_count(lt_count, size)); + p.drawTextLeft(st::emojiPanHeaderLeft - st::buttonRadius, y + st::stickersTrendingSubheaderTop, width(), lng_stickers_count(lt_count, size)); y += st::stickersTrendingHeader; if (y >= r.y() + r.height()) break; @@ -1104,13 +1108,13 @@ void StickerPanInner::paintSticker(Painter &p, Set &set, int y, int index) { int row = (index / StickerPanPerRow), col = (index % StickerPanPerRow); - QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), y + row * st::stickerPanSize.height()); + QPoint pos(stickersLeft() + 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); + p.setOpacity(1.); } bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); @@ -1138,7 +1142,7 @@ void StickerPanInner::paintSticker(Painter &p, Set &set, int y, int index) { QPoint xPos = pos + QPoint(st::stickerPanSize.width() - st::stickerPanDelete.width(), 0); p.setOpacity(hover * (xHover + (1 - xHover) * st::stickerPanDeleteOpacity)); st::stickerPanDelete.paint(p, xPos, width()); - p.setOpacity(1); + p.setOpacity(1.); } } @@ -1151,7 +1155,7 @@ bool StickerPanInner::featuredHasAddButton(int index) const { } int StickerPanInner::featuredContentWidth() const { - return st::stickerPanPadding + (StickerPanPerRow * st::stickerPanSize.width()); + return stickersLeft() + (StickerPanPerRow * st::stickerPanSize.width()); } QRect StickerPanInner::featuredAddRect(int index) const { @@ -1634,7 +1638,7 @@ StickerPanInner::InlineRow &StickerPanInner::layoutInlineRow(InlineRow &row, int }); row.height = 0; - int availw = width() - st::inlineResultsLeft; + int availw = width() - (st::inlineResultsLeft - st::buttonRadius); 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(); @@ -1730,7 +1734,7 @@ void StickerPanInner::refreshSwitchPmButton(const InlineCacheEntry *entry) { if (!_switchPmButton) { _switchPmButton = std_::make_unique(this, QString(), st::switchPmButton); _switchPmButton->show(); - _switchPmButton->move(st::inlineResultsLeft, st::emojiPanHeader); + _switchPmButton->move(st::inlineResultsLeft - st::buttonRadius, st::emojiPanHeader); connect(_switchPmButton.get(), SIGNAL(clicked()), this, SLOT(onSwitchPm())); } _switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper() @@ -2082,7 +2086,7 @@ void StickerPanInner::updateSelected() { auto p = mapFromGlobal(_lastMousePos); if (showingInlineItems()) { - int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft; + int sx = (rtl() ? width() - p.x() : p.x()) - (st::inlineResultsLeft - st::buttonRadius); int sy = p.y() - st::emojiPanHeader; if (_switchPmButton) { sy -= _switchPmButton->height() + st::inlineResultsSkip; @@ -2157,7 +2161,7 @@ void StickerPanInner::updateSelected() { int selectedFeaturedSetAdd = -1; auto featured = (_section == Section::Featured); auto &sets = shownSets(); - int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding; + int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - stickersLeft(); if (featured) { ytill += st::emojiPanHeader; } @@ -2426,12 +2430,12 @@ EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool , _special(special) , _deleteVisible(false) , _delete(special ? 0 : new Ui::IconButton(this, st::hashtagClose)) { // Stickers::NoneSetId if in emoji - resize(st::emojiPanWidth, st::emojiPanHeader); + resize(st::emojiPanWidth - 2 * st::buttonRadius, st::emojiPanHeader); setMouseTracking(true); setText(text); if (_delete) { _delete->hide(); - _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::hashtagClose.icon.width()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); + _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::hashtagClose.icon.width()) / 2) - st::buttonRadius, (st::emojiPanHeader - _delete->height()) / 2, width()); connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); } } @@ -2489,7 +2493,7 @@ void EmojiPanel::paintEvent(QPaintEvent *e) { } p.setFont(st::emojiPanHeaderFont); p.setPen(st::emojiPanHeaderFg); - p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); + p.drawTextLeft(st::emojiPanHeaderLeft - st::buttonRadius, st::emojiPanHeaderTop, width(), _text); } EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : AbstractButton(parent) @@ -2512,7 +2516,7 @@ void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { 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)))); + accumulate_max(maxw, st::emojiPanHeaderFont->width(lang(LangKey(lng_emoji_category0 + c)))); } maxw += st::emojiPanHeaderLeft + st::emojiSwitchSkip + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); if (_textWidth > st::emojiPanWidth - maxw) { @@ -2521,7 +2525,7 @@ void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { } } - int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); + int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip) - st::buttonRadius; resize(w, st::emojiPanHeader); } @@ -2541,14 +2545,264 @@ void EmojiSwitchButton::paintEvent(QPaintEvent *e) { } // namespace internal +namespace { + +FORCE_INLINE uint32 twoImagesOnBgWithAlpha( + const anim::Shifted shiftedBg, + const uint32 source1Alpha, + const uint32 source2Alpha, + const uint32 source1, + const uint32 source2, + const uint32 alpha) { + auto source1Pattern = anim::reshifted(anim::shifted(source1) * source1Alpha); + auto bg1Alpha = 256 - anim::getAlpha(source1Pattern); + auto mixed1Pattern = anim::reshifted(shiftedBg * bg1Alpha) + source1Pattern; + auto source2Pattern = anim::reshifted(anim::shifted(source2) * source2Alpha); + auto bg2Alpha = 256 - anim::getAlpha(source2Pattern); + auto mixed2Pattern = anim::reshifted(mixed1Pattern * bg2Alpha) + source2Pattern; + return anim::unshifted(mixed2Pattern * alpha); +} + +FORCE_INLINE uint32 oneImageOnBgWithAlpha( + const anim::Shifted shiftedBg, + const uint32 sourceAlpha, + const uint32 source, + const uint32 alpha) { + auto sourcePattern = anim::reshifted(anim::shifted(source) * sourceAlpha); + auto bgAlpha = 256 - anim::getAlpha(sourcePattern); + auto mixedPattern = anim::reshifted(shiftedBg * bgAlpha) + sourcePattern; + return anim::unshifted(mixedPattern * alpha); +}; + +} // namespace + +class EmojiPan::SlideAnimation : public Ui::RoundShadowAnimation { +public: + enum class Direction { + LeftToRight, + RightToLeft, + }; + void setFinalImages(Direction direction, QImage &&left, QImage &&right, QRect inner); + + void start(); + void paintFrame(QPainter &p, float64 dt, float64 opacity); + +private: + Direction _direction = Direction::LeftToRight; + QPixmap _leftImage, _rightImage; + int _width = 0; + int _height = 0; + int _innerLeft = 0; + int _innerTop = 0; + int _innerRight = 0; + int _innerBottom = 0; + int _innerWidth = 0; + int _innerHeight = 0; + + int _painterInnerLeft = 0; + int _painterInnerTop = 0; + int _painterInnerWidth = 0; + int _painterInnerBottom = 0; + int _painterCategoriesTop = 0; + int _painterInnerHeight = 0; + int _painterInnerRight = 0; + + int _frameIntsPerLineAdd = 0; + +}; + +void EmojiPan::SlideAnimation::setFinalImages(Direction direction, QImage &&left, QImage &&right, QRect inner) { + t_assert(!started()); + _direction = direction; + _leftImage = QPixmap::fromImage(std_::move(left).convertToFormat(QImage::Format_ARGB32_Premultiplied), Qt::ColorOnly); + _rightImage = QPixmap::fromImage(std_::move(right).convertToFormat(QImage::Format_ARGB32_Premultiplied), Qt::ColorOnly); + + t_assert(!_leftImage.isNull()); + t_assert(!_rightImage.isNull()); + _width = _leftImage.width(); + _height = _rightImage.height(); + t_assert(!(_width % cIntRetinaFactor())); + t_assert(!(_height % cIntRetinaFactor())); + t_assert(_leftImage.devicePixelRatio() == _rightImage.devicePixelRatio()); + t_assert(_rightImage.width() == _width); + t_assert(_rightImage.height() == _height); + t_assert(QRect(0, 0, _width, _height).contains(inner)); + _innerLeft = inner.x(); + _innerTop = inner.y(); + _innerWidth = inner.width(); + _innerHeight = inner.height(); + t_assert(!(_innerLeft % cIntRetinaFactor())); + t_assert(!(_innerTop % cIntRetinaFactor())); + t_assert(!(_innerWidth % cIntRetinaFactor())); + t_assert(!(_innerHeight % cIntRetinaFactor())); + _innerRight = _innerLeft + _innerWidth; + _innerBottom = _innerTop + _innerHeight; + + _painterInnerLeft = _innerLeft / cIntRetinaFactor(); + _painterInnerTop = _innerTop / cIntRetinaFactor(); + _painterInnerRight = _innerRight / cIntRetinaFactor(); + _painterInnerBottom = _innerBottom / cIntRetinaFactor(); + _painterInnerWidth = _innerWidth / cIntRetinaFactor(); + _painterInnerHeight = _innerHeight / cIntRetinaFactor(); + _painterCategoriesTop = _painterInnerBottom - st::emojiCategory.height; + + setShadow(st::emojiPanAnimation.shadow); +} + +void EmojiPan::SlideAnimation::start() { + t_assert(!_leftImage.isNull()); + t_assert(!_rightImage.isNull()); + RoundShadowAnimation::start(_width, _height, _leftImage.devicePixelRatio()); + auto checkCorner = [this](const Corner &corner) { + if (!corner.valid()) return; + t_assert(corner.width <= _innerWidth); + t_assert(corner.height <= _innerHeight); + }; + checkCorner(_topLeft); + checkCorner(_topRight); + checkCorner(_bottomLeft); + checkCorner(_bottomRight); + _frameIntsPerLineAdd = (_width - _innerWidth) + _frameIntsPerLineAdded; +} + +void EmojiPan::SlideAnimation::paintFrame(QPainter &p, float64 dt, float64 opacity) { + t_assert(started()); + t_assert(dt >= 0.); + + auto &transition = anim::easeOutCirc; + _frameAlpha = anim::interpolate(1, 256, opacity); + + auto frameInts = _frameInts + _innerLeft + _innerTop * _frameIntsPerLine; + + auto leftToRight = (_direction == Direction::LeftToRight); + + auto easeOut = anim::easeOutCirc(1., dt); + auto easeIn = anim::easeInCirc(1., dt); + + auto arrivingCoord = anim::interpolate(_innerWidth, 0, easeOut); + auto departingCoord = anim::interpolate(0, _innerWidth, easeIn); + if (auto decrease = (arrivingCoord % cIntRetinaFactor())) { + arrivingCoord -= decrease; + } + if (auto decrease = (departingCoord % cIntRetinaFactor())) { + departingCoord -= decrease; + } + auto arrivingAlpha = easeIn; + auto departingAlpha = 1. - easeOut; + auto leftCoord = (leftToRight ? arrivingCoord : departingCoord) * -1; + auto leftAlpha = (leftToRight ? arrivingAlpha : departingAlpha); + auto rightCoord = (leftToRight ? departingCoord : arrivingCoord); + auto rightAlpha = (leftToRight ? departingAlpha : arrivingAlpha); + + // _innerLeft ..(left).. leftTo ..(both).. bothTo ..(none).. noneTo ..(right).. _innerRight + auto leftTo = _innerLeft + snap(_innerWidth + leftCoord, 0, _innerWidth); + auto rightFrom = _innerLeft + snap(rightCoord, 0, _innerWidth); + auto painterRightFrom = rightFrom / cIntRetinaFactor(); + if (opacity < 1.) { + _frame.fill(Qt::transparent); + } + { + Painter p(&_frame); + p.setOpacity(opacity); + p.fillRect(_painterInnerLeft, _painterInnerTop, _painterInnerWidth, _painterCategoriesTop - _painterInnerTop, st::emojiPanBg); + p.fillRect(_painterInnerLeft, _painterCategoriesTop, _painterInnerWidth, _painterInnerBottom - _painterCategoriesTop, st::emojiPanCategories); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + if (leftTo > _innerLeft) { + p.setOpacity(opacity * leftAlpha); + p.drawPixmap(_painterInnerLeft, _painterInnerTop, _leftImage, _innerLeft - leftCoord, _innerTop, leftTo - _innerLeft, _innerHeight); + } + if (rightFrom < _innerRight) { + p.setOpacity(opacity * rightAlpha); + p.drawPixmap(painterRightFrom, _painterInnerTop, _rightImage, _innerLeft, _innerTop, _innerRight - rightFrom, _innerHeight); + } + } + + // Draw corners + paintCorner(_topLeft, _innerLeft, _innerTop); + paintCorner(_topRight, _innerRight - _topRight.width, _innerTop); + paintCorner(_bottomLeft, _innerLeft, _innerBottom - _bottomLeft.height); + paintCorner(_bottomRight, _innerRight - _bottomRight.width, _innerBottom - _bottomRight.height); + + // Draw shadow upon the transparent + auto outerLeft = _innerLeft; + auto outerTop = _innerTop; + auto outerRight = _innerRight; + auto outerBottom = _innerBottom; + if (_shadow.valid()) { + outerLeft -= _shadow.extend.left(); + outerTop -= _shadow.extend.top(); + outerRight += _shadow.extend.right(); + outerBottom += _shadow.extend.bottom(); + } + if (cIntRetinaFactor() > 1) { + if (auto skipLeft = (outerLeft % cIntRetinaFactor())) { + outerLeft -= skipLeft; + } + if (auto skipTop = (outerTop % cIntRetinaFactor())) { + outerTop -= skipTop; + } + if (auto skipRight = (outerRight % cIntRetinaFactor())) { + outerRight += (cIntRetinaFactor() - skipRight); + } + if (auto skipBottom = (outerBottom % cIntRetinaFactor())) { + outerBottom += (cIntRetinaFactor() - skipBottom); + } + } + + if (opacity == 1.) { + // Fill above the frame top with transparent. + auto fillTopInts = (_frameInts + outerTop * _frameIntsPerLine + outerLeft); + auto fillWidth = (outerRight - outerLeft) * sizeof(uint32); + for (auto fillTop = _innerTop - outerTop; fillTop != 0; --fillTop) { + memset(fillTopInts, 0, fillWidth); + fillTopInts += _frameIntsPerLine; + } + + // Fill to the left and to the right of the frame with transparent. + auto fillLeft = (_innerLeft - outerLeft) * sizeof(uint32); + auto fillRight = (outerRight - _innerRight) * sizeof(uint32); + if (fillLeft || fillRight) { + auto fillInts = _frameInts + _innerTop * _frameIntsPerLine; + for (auto y = _innerTop; y != _innerBottom; ++y) { + memset(fillInts + outerLeft, 0, fillLeft); + memset(fillInts + _innerRight, 0, fillRight); + fillInts += _frameIntsPerLine; + } + } + + // Fill below the frame bottom with transparent. + auto fillBottomInts = (_frameInts + _innerBottom * _frameIntsPerLine + outerLeft); + for (auto fillBottom = outerBottom - _innerBottom; fillBottom != 0; --fillBottom) { + memset(fillBottomInts, 0, fillWidth); + fillBottomInts += _frameIntsPerLine; + } + } + if (_shadow.valid()) { + paintShadow(outerLeft, outerTop, outerRight, outerBottom); + } + + // Debug + //frameInts = _frameInts; + //auto pattern = anim::shifted((static_cast(0xFF) << 24) | (static_cast(0xFF) << 16) | (static_cast(0xFF) << 8) | static_cast(0xFF)); + //for (auto y = 0; y != _finalHeight; ++y) { + // for (auto x = 0; x != _finalWidth; ++x) { + // auto source = *frameInts; + // auto sourceAlpha = (source >> 24); + // *frameInts = anim::unshifted(anim::shifted(source) * 256 + pattern * (256 - sourceAlpha)); + // ++frameInts; + // } + // frameInts += _frameIntsPerLineAdded; + //} + + p.drawImage(outerLeft / cIntRetinaFactor(), outerTop / cIntRetinaFactor(), _frame, outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop); +} + EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) , _maxHeight(st::emojiPanMaxHeight) , _contentMaxHeight(st::emojiPanMaxHeight) , _contentHeight(_contentMaxHeight) , _contentHeightEmoji(_contentHeight - st::emojiCategory.height) , _contentHeightStickers(_contentHeight - st::emojiCategory.height) -, _a_appearance(animation(this, &EmojiPan::step_appearance)) -, _shadow(st::defaultDropdownShadow) , _recent(this, st::emojiCategoryRecent) , _people(this, st::emojiCategoryPeople) , _nature(this, st::emojiCategoryNature) @@ -2558,31 +2812,30 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) , _objects(this, st::emojiCategoryObjects) , _symbols(this, st::emojiCategorySymbols) , _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) { - _width = st::defaultDropdownPadding.left() + st::emojiPanWidth + st::defaultDropdownPadding.right(); - _height = st::defaultDropdownPadding.top() + _contentHeight + st::defaultDropdownPadding.bottom(); + resize(QRect(0, 0, st::emojiPanWidth, _contentHeight).marginsAdded(innerPadding()).size()); + _width = width(); + _height = height(); _bottom = 0; - resize(_width, _height); - e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); + e_scroll.resize(st::emojiPanWidth - st::buttonRadius, _contentHeightEmoji); + s_scroll.resize(st::emojiPanWidth - st::buttonRadius, _contentHeightStickers); - e_scroll.move(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top()); + e_scroll.move(verticalRect().topLeft()); e_scroll.setWidget(&e_inner); - s_scroll.move(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top()); + s_scroll.move(verticalRect().topLeft()); 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::defaultDropdownPadding.left() + (st::emojiPanWidth - 8 * st::emojiCategory.width) / 2; - int32 top = _iconsTop = st::defaultDropdownPadding.top() + _contentHeight - st::emojiCategory.height; + int32 left = _iconsLeft = innerRect().x() + (st::emojiPanWidth - 8 * st::emojiCategory.width) / 2; + int32 top = _iconsTop = innerRect().y() + innerRect().height() - st::emojiCategory.height; prepareTab(left, top, _width, _recent, dbietRecent); prepareTab(left, top, _width, _people, dbietPeople); prepareTab(left, top, _width, _nature, dbietNature); @@ -2617,8 +2870,8 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) 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); + s_switch.moveToRight(st::buttonRadius, 0, st::emojiPanWidth); + e_switch.moveToRight(st::buttonRadius, 0, st::emojiPanWidth); connect(&s_inner, SIGNAL(displaySet(quint64)), this, SLOT(onDisplaySet(quint64))); connect(&s_inner, SIGNAL(installSet(quint64)), this, SLOT(onInstallSet(quint64))); @@ -2660,24 +2913,23 @@ void EmojiPan::updateContentHeight() { _contentHeightEmoji = he; _contentHeightStickers = hs; - _height = st::defaultDropdownPadding.top() + _contentHeight + st::defaultDropdownPadding.bottom(); - - resize(_width, _height); - move(x(), _bottom - height()); + resize(QRect(0, 0, innerRect().width(), _contentHeight).marginsAdded(innerPadding()).size()); + _height = height(); + move(x(), _bottom - _height); if (was > _contentHeight || (was == _contentHeight && wass > _contentHeightStickers)) { - e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); + e_scroll.resize(e_scroll.width(), _contentHeightEmoji); + s_scroll.resize(s_scroll.width(), _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); + e_scroll.resize(e_scroll.width(), _contentHeightEmoji); + s_scroll.resize(s_scroll.width(), _contentHeightStickers); } - _iconsTop = st::defaultDropdownPadding.top() + _contentHeight - st::emojiCategory.height; + _iconsTop = innerRect().y() + innerRect().height() - st::emojiCategory.height; _recent->move(_recent->x(), _iconsTop); _people->move(_people->x(), _iconsTop); _nature->move(_nature->x(), _iconsTop); @@ -2730,113 +2982,114 @@ void EmojiPan::paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const { void EmojiPan::paintEvent(QPaintEvent *e) { Painter p(this); - float64 o = 1; - if (!_cache.isNull()) { - p.setOpacity(o = a_opacity.current()); - } - - QRect r(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top(), _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom()); - - _shadow.paint(p, r, st::defaultDropdownShadowShift); - - 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::emojiPanBg); - if (_stickersShown && s_inner.showSectionIcons()) { - p.fillRect(r.left(), _iconsTop, r.width(), st::emojiCategory.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::emojiCategory.width - x, st::emojiCategory.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::emojiSavedGifsActive : &st::emojiSavedGifs; - } else if (setId == Stickers::FeaturedSetId) { - return active ? &st::stickersTrendingActive : &st::stickersTrending; - } - return active ? &st::emojiRecentActive : &st::emojiRecent; - }; - - int i = 0; - i += _iconsX.current() / int(st::emojiCategory.width); - x -= _iconsX.current() % int(st::emojiCategory.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::emojiCategory.width - s.pixw) / 2, _iconsTop + (st::emojiCategory.height - s.pixh) / 2, width(), pix); - x += st::emojiCategory.width; - } else { - getSpecialSetIcon(s.setId, false)->paint(p, x + st::emojiCategory.iconPosition.x(), _iconsTop + st::emojiCategory.iconPosition.y(), width()); - if (s.setId == Stickers::FeaturedSetId) { - paintFeaturedStickerSetsBadge(p, x); - } - x += st::emojiCategory.width; - } - } - - if (rtl()) selx = width() - selx - st::emojiCategory.width; - p.setOpacity(1.); - p.fillRect(selx, _iconsTop + st::emojiCategory.height - st::stickerIconPadding, st::emojiCategory.width, st::stickerIconSel, st::stickerIconSelColor); - - float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.width(), 0., 1.); - if (o_left > 0) { - p.setOpacity(o_left); - st::stickerIconLeft.fill(p, rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiCategory.height, width())); - } - float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), 0., 1.); - if (o_right > 0) { - p.setOpacity(o_right); - st::stickerIconRight.fill(p, rtlrect(_iconsLeft + 7 * st::emojiCategory.width - st::stickerIconRight.width(), _iconsTop, st::stickerIconRight.width(), st::emojiCategory.height, width())); - } - } - } 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::emojiCategory.height, st::emojiPanBg); - } else { - p.fillRect(r.left(), _iconsTop, r.width(), st::emojiCategory.height, st::emojiPanCategories); - } - } else { - p.fillRect(r, st::emojiPanBg); - p.drawPixmap(r.left(), r.top(), _cache); + auto ms = getms(); + auto opacityAnimating = _a_opacity.animating(ms); + auto switching = (_slideAnimation != nullptr); + if (_a_show.animating(ms)) { + if (auto opacity = _a_opacity.current(_hiding ? 0. : 1.)) { + _showAnimation->paintFrame(p, 0, 0, width(), _a_show.current(1.), opacity); + } + } else if (!switching && opacityAnimating) { + p.setOpacity(_a_opacity.current(_hiding ? 0. : 1.)); + p.drawPixmap(0, 0, _cache); + } else if ((!switching && _hiding) || isHidden()) { + hideFinished(); + } else if (_showAnimation) { + _showAnimation->paintFrame(p, 0, 0, width(), 1., 1.); + _showAnimation.reset(); + if (!switching && !opacityAnimating) showAll(); + } else if (switching) { + auto slideDt = _a_slide.current(ms, 1.); + _slideAnimation->paintFrame(p, slideDt, _a_opacity.current(_hiding ? 0. : 1.)); + if (!_a_slide.animating()) { + _slideAnimation.reset(); + if (!opacityAnimating) showAll(); } } else { - p.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::emojiCategory.height), st::emojiPanBg); - p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::emojiCategory.height), st::emojiPanCategories); - 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()); + if (!_cache.isNull()) _cache = QPixmap(); + if (!_inPanelGrab) Ui::Shadow::paint(p, innerRect(), width(), st::emojiPanAnimation.shadow); + paintContent(p); + } +} + +void EmojiPan::paintContent(Painter &p) { + auto inner = innerRect(); + App::roundRect(p, inner, st::emojiPanBg, ImageRoundRadius::Small, App::RectPart::TopFull); + + auto showSectionIcons = _emojiShown || s_inner.showSectionIcons(); + auto &bottomBg = showSectionIcons ? st::emojiPanCategories : st::emojiPanBg; + auto bottomParts = showSectionIcons ? (App::RectPart::NoTopBottom | App::RectPart::BottomFull) : App::RectPart::BottomFull; + App::roundRect(p, inner.x(), _iconsTop - st::buttonRadius, inner.width(), st::emojiCategory.height + st::buttonRadius, bottomBg, ImageRoundRadius::Small, bottomParts); + + auto horizontal = horizontalRect(); + auto sidesTop = horizontal.y(); + auto sidesHeight = e_scroll.y() + e_scroll.height() - sidesTop; + p.fillRect(myrtlrect(inner.x() + inner.width() - st::emojiScroll.width, sidesTop, st::emojiScroll.width, sidesHeight), st::emojiPanBg); + p.fillRect(myrtlrect(inner.x(), sidesTop, st::buttonRadius, sidesHeight), st::emojiPanBg); + if (_emojiShown) { + auto vertical = verticalRect(); + p.fillRect(vertical.x(), _iconsTop, vertical.width(), st::emojiCategory.height - st::buttonRadius, st::emojiPanCategories); + } else if (showSectionIcons) { + paintStickerSettingsIcon(p); + + if (!_icons.isEmpty()) { + int x = _iconsLeft, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current(); + + QRect clip(x, _iconsTop, _iconsLeft + 7 * st::emojiCategory.width - x, st::emojiCategory.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::emojiSavedGifsActive : &st::emojiSavedGifs; + } else if (setId == Stickers::FeaturedSetId) { + return active ? &st::stickersTrendingActive : &st::stickersTrending; + } + return active ? &st::emojiRecentActive : &st::emojiRecent; + }; + + int i = 0; + i += _iconsX.current() / int(st::emojiCategory.width); + x -= _iconsX.current() % int(st::emojiCategory.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::emojiCategory.width - s.pixw) / 2, _iconsTop + (st::emojiCategory.height - s.pixh) / 2, width(), pix); + x += st::emojiCategory.width; + } else { + getSpecialSetIcon(s.setId, false)->paint(p, x + st::emojiCategory.iconPosition.x(), _iconsTop + st::emojiCategory.iconPosition.y(), width()); + if (s.setId == Stickers::FeaturedSetId) { + paintFeaturedStickerSetsBadge(p, x); + } + x += st::emojiCategory.width; + } } - 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()); + + if (rtl()) selx = width() - selx - st::emojiCategory.width; + p.fillRect(selx, _iconsTop + st::emojiCategory.height - st::stickerIconPadding, st::emojiCategory.width, st::stickerIconSel, st::stickerIconSelColor); + + float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.width(), 0., 1.); + if (o_left > 0) { + p.setOpacity(o_left); + st::stickerIconLeft.fill(p, rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiCategory.height, width())); + p.setOpacity(1.); } - p.drawPixmap(toDst, _toCache, toSrc); + float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), 0., 1.); + if (o_right > 0) { + p.setOpacity(o_right); + st::stickerIconRight.fill(p, rtlrect(_iconsLeft + 7 * st::emojiCategory.width - st::stickerIconRight.width(), _iconsTop, st::stickerIconRight.width(), st::emojiCategory.height, width())); + p.setOpacity(1.); + } + + p.setClipRect(QRect()); } + } else { + p.fillRect(myrtlrect(inner.x() + inner.width() - st::emojiScroll.width, _iconsTop, st::emojiScroll.width, st::emojiCategory.height - st::buttonRadius), st::emojiPanBg); + p.fillRect(myrtlrect(inner.x(), _iconsTop, st::buttonRadius, st::emojiCategory.height - st::buttonRadius), st::emojiPanBg); } } @@ -2846,16 +3099,18 @@ void EmojiPan::moveBottom(int32 bottom, bool force) { move(x(), _bottom - height()); return; } - if (_stickersShown && s_inner.inlineResultsShown()) { + if (!_emojiShown && s_inner.inlineResultsShown()) { + setOrigin(Ui::PanelAnimation::Origin::BottomLeft); moveToLeft(0, _bottom - height()); } else { + setOrigin(Ui::PanelAnimation::Origin::BottomRight); moveToRight(0, _bottom - height()); } } void EmojiPan::enterEvent(QEvent *e) { _hideTimer.stop(); - if (_hiding) showAnimated(); + if (_hiding) showAnimated(_origin); } bool EmojiPan::preventAutoHide() const { @@ -2864,21 +3119,24 @@ bool EmojiPan::preventAutoHide() const { void EmojiPan::leaveEvent(QEvent *e) { if (preventAutoHide() || s_inner.inlineResultsShown()) return; - if (_a_appearance.animating()) { - hideByTimerOrLeave(); + auto ms = getms(); + if (_a_show.animating(ms) || _a_opacity.animating(ms)) { + hideAnimated(); } else { _hideTimer.start(300); } + return TWidget::leaveEvent(e); } void EmojiPan::otherEnter() { _hideTimer.stop(); - showAnimated(); + showAnimated(_origin); } void EmojiPan::otherLeave() { if (preventAutoHide() || s_inner.inlineResultsShown()) return; - if (_a_appearance.animating()) { + auto ms = getms(); + if (_a_opacity.animating(ms)) { hideByTimerOrLeave(); } else { _hideTimer.start(0); @@ -2886,7 +3144,7 @@ void EmojiPan::otherLeave() { } void EmojiPan::mousePressEvent(QMouseEvent *e) { - if (!_stickersShown || e->button() != Qt::LeftButton) return; + if (_emojiShown || e->button() != Qt::LeftButton) return; _iconsMousePos = e ? e->globalPos() : QCursor::pos(); updateSelected(); @@ -2900,7 +3158,7 @@ void EmojiPan::mousePressEvent(QMouseEvent *e) { } void EmojiPan::mouseMoveEvent(QMouseEvent *e) { - if (!_stickersShown) return; + if (_emojiShown) return; _iconsMousePos = e ? e->globalPos() : QCursor::pos(); updateSelected(); @@ -2921,7 +3179,7 @@ void EmojiPan::mouseMoveEvent(QMouseEvent *e) { } void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { - if (!_stickersShown || _icons.isEmpty()) return; + if (_emojiShown || _icons.isEmpty()) return; int32 wasDown = _iconDown; _iconDown = -1; @@ -2975,18 +3233,17 @@ bool EmojiPan::event(QEvent *e) { } void EmojiPan::hideFast() { - if (_a_appearance.animating()) { - _a_appearance.stop(); - } - a_opacity = anim::fvalue(0, 0); + if (isHidden()) return; + _hideTimer.stop(); - hide(); - _cache = QPixmap(); + _hiding = false; + _a_opacity.finish(); + hideFinished(); } void EmojiPan::refreshStickers() { s_inner.refreshStickers(); - if (!_stickersShown) { + if (_emojiShown) { s_inner.preloadImages(); } update(); @@ -2994,9 +3251,9 @@ void EmojiPan::refreshStickers() { void EmojiPan::refreshSavedGifs() { e_switch.updateText(); - e_switch.moveToRight(0, 0, st::emojiPanWidth); + e_switch.moveToRight(st::buttonRadius, 0, st::emojiPanWidth); s_inner.refreshSavedGifs(); - if (!_stickersShown) { + if (_emojiShown) { s_inner.preloadImages(); } } @@ -3022,7 +3279,7 @@ void EmojiPan::onRefreshIcons(bool scrollAnimation) { } updatePanelsPositions(s_panels, s_scroll.scrollTop()); updateSelected(); - if (_stickersShown) { + if (!_emojiShown) { validateSelectedIcon(scrollAnimation ? ValidateIconAnimations::Scroll : ValidateIconAnimations::None); updateContentHeight(); } @@ -3030,17 +3287,17 @@ void EmojiPan::onRefreshIcons(bool scrollAnimation) { } void EmojiPan::onRefreshPanels() { - s_inner.refreshPanels(s_panels); e_inner.refreshPanels(e_panels); - if (_stickersShown) { - updatePanelsPositions(s_panels, s_scroll.scrollTop()); - } else { + s_inner.refreshPanels(s_panels); + if (_emojiShown) { updatePanelsPositions(e_panels, e_scroll.scrollTop()); + } else { + updatePanelsPositions(s_panels, s_scroll.scrollTop()); } } void EmojiPan::leaveToChildEvent(QEvent *e, QWidget *child) { - if (!_stickersShown) return; + if (_emojiShown) return; _iconsMousePos = QCursor::pos(); updateSelected(); } @@ -3089,14 +3346,14 @@ void EmojiPan::updateSelected() { } void EmojiPan::updateIcons() { - if (!_stickersShown || !s_inner.showSectionIcons()) return; + if (_emojiShown || !s_inner.showSectionIcons()) return; QRect r(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top(), _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom()); update(r.left(), _iconsTop, r.width(), st::emojiCategory.height); } void EmojiPan::step_icons(uint64 ms, bool timer) { - if (!_stickersShown) { + if (_emojiShown) { _a_icons.stop(); return; } @@ -3135,46 +3392,16 @@ void EmojiPan::step_icons(uint64 ms, bool timer) { } } -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, anim::easeInCirc); - a_fromAlpha.update(dt1, anim::easeOutCirc); - a_toCoord.update(dt2, anim::easeOutCirc); - a_toAlpha.update(dt2, anim::easeInCirc); - } - if (timer) update(); -} - -void EmojiPan::step_appearance(float64 ms, bool timer) { - if (_cache.isNull()) { - _a_appearance.stop(); - return; - } - - float64 dt = ms / st::defaultDropdownDuration; - if (dt >= 1) { - _a_appearance.stop(); - a_opacity.finish(); +void EmojiPan::opacityAnimationCallback() { + update(); + if (!_a_opacity.animating()) { if (_hiding) { - hideFinish(); - } else { - _cache = QPixmap(); - if (_toCache.isNull()) showAll(); + _hiding = false; + hideFinished(); + } else if (!_a_show.animating() && !_a_slide.animating()) { + showAll(); } - } else { - a_opacity.update(dt, anim::linear); } - if (timer) update(); } void EmojiPan::hideByTimerOrLeave() { @@ -3183,32 +3410,84 @@ void EmojiPan::hideByTimerOrLeave() { hideAnimated(); } -void EmojiPan::prepareShowHideCache() { - if (_cache.isNull()) { - QPixmap from = _fromCache, to = _toCache; - _fromCache = _toCache = QPixmap(); - showAll(); - _cache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding)); - _fromCache = from; _toCache = to; +void EmojiPan::prepareCache() { + if (_a_opacity.animating()) return; + + auto showAnimation = base::take(_a_show); + auto showAnimationData = base::take(_showAnimation); + auto slideAnimation = base::take(_slideAnimation); + showAll(); + _cache = myGrab(this); + _slideAnimation = base::take(slideAnimation); + _showAnimation = base::take(showAnimationData); + _a_show = base::take(showAnimation); + if (_a_show.animating()) { + hideAll(); } } -void EmojiPan::hideAnimated() { - if (_hiding) return; - - prepareShowHideCache(); +void EmojiPan::startOpacityAnimation(bool hiding) { + _hiding = false; + prepareCache(); + _hiding = hiding; hideAll(); - _hiding = true; - a_opacity.start(0); - _a_appearance.start(); + _a_opacity.start([this] { opacityAnimationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., st::emojiPanDuration); } -void EmojiPan::hideFinish() { +void EmojiPan::startShowAnimation() { + if (!_a_show.animating()) { + auto cache = base::take(_cache); + auto opacityAnimation = base::take(_a_opacity); + auto slideAnimationData = base::take(_slideAnimation); + auto slideAnimation = base::take(_a_slide); + showAll(); + auto image = grabForPanelAnimation(); + _a_slide = base::take(slideAnimation); + _slideAnimation = base::take(slideAnimationData); + _a_opacity = base::take(opacityAnimation); + _cache = base::take(_cache); + + _showAnimation = std_::make_unique(st::emojiPanAnimation, _origin); + auto inner = rect().marginsRemoved(st::defaultDropdownPadding); + _showAnimation->setFinalImage(std_::move(image), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); + auto corners = App::cornersMask(ImageRoundRadius::Small); + _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + _showAnimation->start(); + } + hideAll(); + _a_show.start([this] { update(); }, 0., 1., st::emojiPanShowDuration); +} + +QImage EmojiPan::grabForPanelAnimation() { + myEnsureResized(this); + auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + _inPanelGrab = true; + render(&result); + _inPanelGrab = false; + return std_::move(result); +} + +void EmojiPan::hideAnimated() { + if (isHidden()) return; + if (_hiding) return; + + _hideTimer.stop(); + startOpacityAnimation(true); +} + +EmojiPan::~EmojiPan() = default; + +void EmojiPan::hideFinished() { hide(); e_inner.hideFinish(); s_inner.hideFinish(true); - _cache = _toCache = _fromCache = QPixmap(); - _a_slide.stop(); + _a_show.finish(); + _showAnimation.reset(); + _a_slide.finish(); + _slideAnimation.reset(); + _cache = QPixmap(); _horizontal = false; _hiding = false; @@ -3227,37 +3506,43 @@ void EmojiPan::hideFinish() { Notify::clipStopperHidden(ClipStopperSavedGifsPanel); } -void EmojiPan::showAnimated() { - if (!isHidden() && !_hiding) { - return; - } +void EmojiPan::setOrigin(Ui::PanelAnimation::Origin origin) { + _origin = origin; +} + +void EmojiPan::showAnimated(Ui::PanelAnimation::Origin origin) { + setOrigin(origin); + _hideTimer.stop(); + showStarted(); +} + +void EmojiPan::showStarted() { if (isHidden()) { e_inner.refreshRecent(); if (s_inner.inlineResultsShown() && refreshInlineRows()) { - _stickersShown = true; + _emojiShown = false; _shownFromInlineQuery = true; } else { s_inner.refreshRecent(); - _stickersShown = false; + _emojiShown = true; _shownFromInlineQuery = false; _cache = QPixmap(); // clear after refreshInlineRows() } recountContentMaxHeight(); s_inner.preloadImages(); - _fromCache = _toCache = QPixmap(); - _a_slide.stop(); + _a_slide.finish(); + _slideAnimation.reset(); moveBottom(y() + height(), true); - } else if (_hiding) { - if (s_inner.inlineResultsShown() && refreshInlineRows()) { - onSwitch(); - } + show(); + startShowAnimation(); + return; + } else if (!_hiding) { + return; } - prepareShowHideCache(); - hideAll(); - _hiding = false; - show(); - a_opacity.start(1); - _a_appearance.start(); + if (s_inner.inlineResultsShown() && refreshInlineRows()) { + onSwitch(); + } + startOpacityAnimation(false); emit updateStickers(); } @@ -3277,7 +3562,7 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) { if (isHidden() || _hiding) { _hideTimer.stop(); - showAnimated(); + showAnimated(_origin); } else { hideAnimated(); } @@ -3286,59 +3571,46 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { } void EmojiPan::stickersInstalled(uint64 setId) { - _stickersShown = true; + _emojiShown = false; if (isHidden()) { moveBottom(y() + height(), true); + startShowAnimation(); show(); - a_opacity = anim::fvalue(0, 1); - a_opacity.update(0, anim::linear); - _cache = _fromCache = _toCache = QPixmap(); } showAll(); s_inner.showStickerSet(setId); updateContentHeight(); - showAnimated(); + showAnimated(Ui::PanelAnimation::Origin::BottomRight); } void EmojiPan::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { + if (!_emojiShown && !isHidden()) { s_inner.notify_inlineItemLayoutChanged(layout); } } void EmojiPan::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { + if (!_emojiShown && !isHidden()) { s_inner.ui_repaintInlineItem(layout); } } bool EmojiPan::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) { - if (_stickersShown && !isHidden()) { + if (!_emojiShown && !isHidden()) { return s_inner.ui_isInlineItemVisible(layout); } return false; } bool EmojiPan::ui_isInlineItemBeingChosen() { - if (_stickersShown && !isHidden()) { + if (!_emojiShown && !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 { + if (_emojiShown) { s_scroll.hide(); _recent->show(); _people->show(); @@ -3349,6 +3621,17 @@ void EmojiPan::showAll() { _objects->show(); _symbols->show(); e_scroll.show(); + } else { + s_scroll.show(); + _recent->hide(); + _people->hide(); + _nature->hide(); + _food->hide(); + _activity->hide(); + _travel->hide(); + _objects->hide(); + _symbols->hide(); + e_scroll.hide(); } } @@ -3454,11 +3737,65 @@ void EmojiPan::validateSelectedIcon(ValidateIconAnimations animations) { } } +style::margins EmojiPan::innerPadding() const { + return st::defaultDropdownPadding; +} + +QRect EmojiPan::innerRect() const { + return rect().marginsRemoved(innerPadding()); +} + +QRect EmojiPan::horizontalRect() const { + return innerRect().marginsRemoved(style::margins(0, st::buttonRadius, 0, st::buttonRadius)); +} + +QRect EmojiPan::verticalRect() const { + return innerRect().marginsRemoved(style::margins(st::buttonRadius, 0, st::buttonRadius, 0)); +} + void EmojiPan::onSwitch() { - QPixmap cache = _cache; - _fromCache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding)); - _stickersShown = !_stickersShown; - if (!_stickersShown) { + auto cache = base::take(_cache); + auto opacityAnimation = base::take(_a_opacity); + auto showAnimationData = base::take(_showAnimation); + auto showAnimation = base::take(_a_show); + + showAll(); + auto leftImage = grabForPanelAnimation(); + performSwitch(); + showAll(); + auto rightImage = grabForPanelAnimation(); + if (_emojiShown) { + std_::swap_moveable(leftImage, rightImage); + } + + _a_show = base::take(showAnimation); + _showAnimation = base::take(showAnimationData); + _a_opacity = base::take(opacityAnimation); + _cache = base::take(_cache); + + auto direction = _emojiShown ? SlideAnimation::Direction::LeftToRight : SlideAnimation::Direction::RightToLeft; + _slideAnimation = std_::make_unique(); + auto inner = rect().marginsRemoved(st::defaultDropdownPadding); + _slideAnimation->setFinalImages(direction, std_::move(leftImage), std_::move(rightImage), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); + auto corners = App::cornersMask(ImageRoundRadius::Small); + _slideAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + _slideAnimation->start(); + + hideAll(); + + if (_emojiShown) { + s_inner.hideFinish(false); + } else { + e_inner.hideFinish(); + } + + _a_slide.start([this] { update(); }, 0., 1., st::emojiPanSlideDuration, anim::linear); + update(); +} + +void EmojiPan::performSwitch() { + _emojiShown = !_emojiShown; + if (_emojiShown) { Notify::clipStopperHidden(ClipStopperSavedGifsPanel); } else { if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { @@ -3478,27 +3815,6 @@ void EmojiPan::onSwitch() { _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); _iconAnimations.clear(); _a_icons.stop(); - - _cache = QPixmap(); - showAll(); - _toCache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding)); - _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) { @@ -3593,11 +3909,11 @@ void EmojiPan::onDelayedHide() { void EmojiPan::clearInlineBot() { inlineBotChanged(); e_switch.updateText(); - e_switch.moveToRight(0, 0, st::emojiPanWidth); + e_switch.moveToRight(st::buttonRadius, 0, st::emojiPanWidth); } bool EmojiPan::hideOnNoInlineResults() { - return _inlineBot && _stickersShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername()); + return _inlineBot && !_emojiShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername()); } void EmojiPan::inlineBotChanged() { @@ -3743,7 +4059,7 @@ bool EmojiPan::refreshInlineRows(int32 *added) { } _inlineNextOffset = i.value()->nextOffset; } - if (!entry) prepareShowHideCache(); + if (!entry) prepareCache(); int32 result = s_inner.refreshInlineRows(_inlineBot, entry, false); if (added) *added = result; return (entry != nullptr); @@ -3770,8 +4086,8 @@ int32 EmojiPan::showInlineRows(bool newResults) { } else { _hideTimer.stop(); if (hidden || _hiding) { - showAnimated(); - } else if (!_stickersShown) { + showAnimated(_origin); + } else if (_emojiShown) { onSwitch(); } } diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index 6fed74829..be927c8cc 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/twidget.h" #include "ui/effects/rect_shadow.h" #include "ui/abstract_button.h" +#include "ui/effects/panel_animation.h" namespace InlineBots { namespace Layout { @@ -60,7 +61,6 @@ class EmojiColorPicker : public TWidget { Q_OBJECT public: - EmojiColorPicker(); void showEmoji(uint32 code); @@ -345,6 +345,7 @@ private: void appendSet(Sets &to, uint64 setId, AppendSkip skip); void selectEmoji(EmojiPtr emoji); + int stickersLeft() const; QRect stickerRect(int tab, int sel); int32 _maxHeight; @@ -510,8 +511,6 @@ public: 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); @@ -535,13 +534,12 @@ public: bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout); bool ui_isInlineItemBeingChosen(); - bool inlineResultsShown() const { - return s_inner.inlineResultsShown(); - } - - void showAnimated(); + void setOrigin(Ui::PanelAnimation::Origin origin); + void showAnimated(Ui::PanelAnimation::Origin origin); void hideAnimated(); + ~EmojiPan(); + public slots: void refreshStickers(); @@ -549,8 +547,6 @@ private slots: void hideByTimerOrLeave(); void refreshSavedGifs(); - void hideFinish(); - void onWndActiveChanged(); void onScrollEmoji(); @@ -581,6 +577,33 @@ signals: void updateStickers(); private: + void paintContent(Painter &p); + void performSwitch(); + + style::margins innerPadding() const; + + // Rounded rect which has shadow around it. + QRect innerRect() const; + + // Inner rect with removed st::buttonRadius from top and bottom. + // This one is allowed to be not rounded. + QRect horizontalRect() const; + + // Inner rect with removed st::buttonRadius from left and right. + // This one is allowed to be not rounded. + QRect verticalRect() const; + + QImage grabForPanelAnimation(); + void startShowAnimation(); + void startOpacityAnimation(bool hiding); + void prepareCache(); + + class Container; + void opacityAnimationCallback(); + + void hideFinished(); + void showStarted(); + bool preventAutoHide() const; void installSetDone(const MTPmessages_StickerSetInstallResult &result); bool installSetFail(uint64 setId, const RPCError &error); @@ -599,7 +622,6 @@ private: void updateContentHeight(); void leaveToChildEvent(QEvent *e, QWidget *child); - void prepareShowHideCache(); void updateSelected(); void updateIcons(); @@ -614,15 +636,20 @@ private: bool _horizontal = false; int32 _width, _height, _bottom; + + Ui::PanelAnimation::Origin _origin = Ui::PanelAnimation::Origin::BottomRight; + std_::unique_ptr _showAnimation; + FloatAnimation _a_show; + bool _hiding = false; QPixmap _cache; - - anim::fvalue a_opacity = { 0. }; - Animation _a_appearance; - + FloatAnimation _a_opacity; QTimer _hideTimer; + bool _inPanelGrab = false; - Ui::RectShadow _shadow; + class SlideAnimation; + std_::unique_ptr _slideAnimation; + FloatAnimation _a_slide; ChildWidget _recent; ChildWidget _people; @@ -651,12 +678,8 @@ private: anim::ivalue _iconSelX = { 0, 0 }; uint64 _iconsStartAnim = 0; - bool _stickersShown = false; + bool _emojiShown = true; 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; diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index d2616bb60..c751dcdc7 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -117,12 +117,18 @@ emojiCategoryTravel: IconButton(emojiCategory) { icon: emojiTravel; } emojiCategoryObjects: IconButton(emojiCategory) { icon: emojiObjects; } emojiCategorySymbols: IconButton(emojiCategory) { icon: emojiSymbols; } +emojiPanAnimation: PanelAnimation(defaultPanelAnimation) { + fadeBg: emojiPanBg; +} emojiPanPadding: 12px; emojiPanSize: size(45px, 41px); emojiPanWidth: 345px; emojiPanMaxHeight: 366px; +emojiPanShowDuration: 200; emojiPanDuration: 200; emojiPanHover: #f0f4f7; +emojiPanSlideDuration: 200; +emojiPanSlideDelta: 0; // between hide start and show start emojiPanHeader: 42px; emojiPanHeaderFont: semiboldFont; diff --git a/Telegram/SourceFiles/ui/animation.h b/Telegram/SourceFiles/ui/animation.h index 8b5ff5460..81370ef6d 100644 --- a/Telegram/SourceFiles/ui/animation.h +++ b/Telegram/SourceFiles/ui/animation.h @@ -213,7 +213,110 @@ void startManager(); void stopManager(); void registerClipManager(Media::Clip::Manager *manager); -inline uint64 shifted(uint32 components) { +FORCE_INLINE int interpolate(int a, int b, float64 b_ratio) { + return qRound(a + float64(b - a) * b_ratio); +} + +#ifdef ARCH_CPU_32_BITS +#define SHIFTED_USE_32BIT +#endif // ARCH_CPU_32_BITS + +#ifdef SHIFTED_USE_32BIT + +using ShiftedMultiplier = uint32; + +struct Shifted { + Shifted() = default; + Shifted(uint32 low, uint32 high) : low(low), high(high) { + } + uint32 low = 0; + uint32 high = 0; +}; + +FORCE_INLINE Shifted operator+(Shifted a, Shifted b) { + return Shifted(a.low + b.low, a.high + b.high); +} + +FORCE_INLINE Shifted operator*(Shifted shifted, ShiftedMultiplier multiplier) { + return Shifted(shifted.low * multiplier, shifted.high * multiplier); +} + +FORCE_INLINE Shifted operator*(ShiftedMultiplier multiplier, Shifted shifted) { + return Shifted(shifted.low * multiplier, shifted.high * multiplier); +} + +FORCE_INLINE Shifted shifted(uint32 components) { + return Shifted( + (components & 0x000000FFU) | ((components & 0x0000FF00U) << 8), + ((components & 0x00FF0000U) >> 16) | ((components & 0xFF000000U) >> 8)); +} + +FORCE_INLINE uint32 unshifted(Shifted components) { + return ((components.low & 0x0000FF00U) >> 8) + | ((components.low & 0xFF000000U) >> 16) + | ((components.high & 0x0000FF00U) << 8) + | (components.high & 0xFF000000U); +} + +FORCE_INLINE Shifted reshifted(Shifted components) { + return Shifted((components.low >> 8) & 0x00FF00FFU, (components.high >> 8) & 0x00FF00FFU); +} + +FORCE_INLINE Shifted shifted(QColor color) { + // Make it premultiplied. + auto alpha = static_cast((color.alpha() & 0xFF) + 1); + auto components = Shifted(static_cast(color.blue() & 0xFF) | (static_cast(color.green() & 0xFF) << 16), + static_cast(color.red() & 0xFF) | (static_cast(255) << 16)); + return reshifted(components * alpha); +} + +FORCE_INLINE uint32 getAlpha(Shifted components) { + return (components.high & 0x00FF0000U) >> 16; +} + +FORCE_INLINE Shifted non_premultiplied(QColor color) { + return Shifted(static_cast(color.blue() & 0xFF) | (static_cast(color.green() & 0xFF) << 16), + static_cast(color.red() & 0xFF) | (static_cast(color.alpha() & 0xFF) << 16)); +} + +FORCE_INLINE QColor color(QColor a, QColor b, float64 b_ratio) { + auto bOpacity = snap(interpolate(0, 255, b_ratio), 0, 255) + 1; + auto aOpacity = (256 - bOpacity); + auto components = (non_premultiplied(a) * aOpacity + non_premultiplied(b) * bOpacity); + return { + static_cast((components.high >> 8) & 0xFF), + static_cast((components.low >> 24) & 0xFF), + static_cast((components.low >> 8) & 0xFF), + static_cast((components.high >> 24) & 0xFF), + }; +} + +#else // SHIFTED_USE_32BIT + +using ShiftedMultiplier = uint64; + +struct Shifted { + Shifted() = default; + Shifted(uint32 value) : value(value) { + } + Shifted(uint64 value) : value(value) { + } + uint64 value = 0; +}; + +FORCE_INLINE Shifted operator+(Shifted a, Shifted b) { + return Shifted(a.value + b.value); +} + +FORCE_INLINE Shifted operator*(Shifted shifted, ShiftedMultiplier multiplier) { + return Shifted(shifted.value * multiplier); +} + +FORCE_INLINE Shifted operator*(ShiftedMultiplier multiplier, Shifted shifted) { + return Shifted(shifted.value * multiplier); +} + +FORCE_INLINE Shifted shifted(uint32 components) { auto wide = static_cast(components); return (wide & 0x00000000000000FFULL) | ((wide & 0x000000000000FF00ULL) << 8) @@ -221,82 +324,93 @@ inline uint64 shifted(uint32 components) { | ((wide & 0x00000000FF000000ULL) << 24); } -inline uint32 unshifted(uint64 components) { - return static_cast((components & 0x000000000000FF00ULL) >> 8) - | static_cast((components & 0x00000000FF000000ULL) >> 16) - | static_cast((components & 0x0000FF0000000000ULL) >> 24) - | static_cast((components & 0xFF00000000000000ULL) >> 32); +FORCE_INLINE uint32 unshifted(Shifted components) { + return static_cast((components.value & 0x000000000000FF00ULL) >> 8) + | static_cast((components.value & 0x00000000FF000000ULL) >> 16) + | static_cast((components.value & 0x0000FF0000000000ULL) >> 24) + | static_cast((components.value & 0xFF00000000000000ULL) >> 32); } -inline uint64 reshifted(uint64 components) { - return (components >> 8) & 0x00FF00FF00FF00FFULL; +FORCE_INLINE Shifted reshifted(Shifted components) { + return (components.value >> 8) & 0x00FF00FF00FF00FFULL; } -inline int interpolate(int a, int b, float64 b_ratio) { - return qRound(a + float64(b - a) * b_ratio); +FORCE_INLINE Shifted shifted(QColor color) { + // Make it premultiplied. + auto alpha = static_cast((color.alpha() & 0xFF) + 1); + auto components = static_cast(color.blue() & 0xFF) + | (static_cast(color.green() & 0xFF) << 16) + | (static_cast(color.red() & 0xFF) << 32) + | (static_cast(255) << 48); + return reshifted(components * alpha); } -inline QColor color(QColor a, QColor b, float64 b_ratio) { +FORCE_INLINE uint32 getAlpha(Shifted components) { + return (components.value & 0x00FF000000000000ULL) >> 48; +} + +FORCE_INLINE Shifted non_premultiplied(QColor color) { + return static_cast(color.blue() & 0xFF) + | (static_cast(color.green() & 0xFF) << 16) + | (static_cast(color.red() & 0xFF) << 32) + | (static_cast(color.alpha() & 0xFF) << 48); +} + +FORCE_INLINE QColor color(QColor a, QColor b, float64 b_ratio) { auto bOpacity = snap(interpolate(0, 255, b_ratio), 0, 255) + 1; auto aOpacity = (256 - bOpacity); - auto bBits = static_cast(b.alpha() & 0xFF) - | (static_cast(b.red() & 0xFF) << 16) - | (static_cast(b.green() & 0xFF) << 32) - | (static_cast(b.blue() & 0xFF) << 48); - auto aBits = static_cast(a.alpha() & 0xFF) - | (static_cast(a.red() & 0xFF) << 16) - | (static_cast(a.green() & 0xFF) << 32) - | (static_cast(a.blue() & 0xFF) << 48); - auto resultBits = (aBits * aOpacity + bBits * bOpacity) >> 8; + auto components = (non_premultiplied(a) * aOpacity + non_premultiplied(b) * bOpacity); return { - static_cast((resultBits >> 16) & 0xFF), - static_cast((resultBits >> 32) & 0xFF), - static_cast((resultBits >> 48) & 0xFF), - static_cast(resultBits & 0xFF), + static_cast((components.value >> 40) & 0xFF), + static_cast((components.value >> 24) & 0xFF), + static_cast((components.value >> 8) & 0xFF), + static_cast((components.value >> 56) & 0xFF), }; } -inline QColor color(const style::color &a, QColor b, float64 b_ratio) { +#endif // SHIFTED_USE_32BIT + +FORCE_INLINE QColor color(const style::color &a, QColor b, float64 b_ratio) { return color(a->c, b, b_ratio); } -inline QColor color(QColor a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QColor color(QColor a, const style::color &b, float64 b_ratio) { return color(a, b->c, b_ratio); } -inline QColor color(const style::color &a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QColor color(const style::color &a, const style::color &b, float64 b_ratio) { return color(a->c, b->c, b_ratio); } -inline QPen pen(QColor a, QColor b, float64 b_ratio) { +FORCE_INLINE QPen pen(QColor a, QColor b, float64 b_ratio) { return color(a, b, b_ratio); } -inline QPen pen(const style::color &a, QColor b, float64 b_ratio) { +FORCE_INLINE QPen pen(const style::color &a, QColor b, float64 b_ratio) { return (b_ratio > 0) ? pen(a->c, b, b_ratio) : a; } -inline QPen pen(QColor a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QPen pen(QColor a, const style::color &b, float64 b_ratio) { return (b_ratio < 1) ? pen(a, b->c, b_ratio) : b; } -inline QPen pen(const style::color &a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QPen pen(const style::color &a, const style::color &b, float64 b_ratio) { return (b_ratio > 0) ? ((b_ratio < 1) ? pen(a->c, b->c, b_ratio) : b) : a; } -inline QBrush brush(QColor a, QColor b, float64 b_ratio) { +FORCE_INLINE QBrush brush(QColor a, QColor b, float64 b_ratio) { return color(a, b, b_ratio); } -inline QBrush brush(const style::color &a, QColor b, float64 b_ratio) { +FORCE_INLINE QBrush brush(const style::color &a, QColor b, float64 b_ratio) { return (b_ratio > 0) ? brush(a->c, b, b_ratio) : a; } -inline QBrush brush(QColor a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QBrush brush(QColor a, const style::color &b, float64 b_ratio) { return (b_ratio < 1) ? brush(a, b->c, b_ratio) : b; } -inline QBrush brush(const style::color &a, const style::color &b, float64 b_ratio) { +FORCE_INLINE QBrush brush(const style::color &a, const style::color &b, float64 b_ratio) { return (b_ratio > 0) ? ((b_ratio < 1) ? brush(a->c, b->c, b_ratio) : b) : a; } diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.cpp b/Telegram/SourceFiles/ui/effects/panel_animation.cpp index c41b3bdba..28b3efa84 100644 --- a/Telegram/SourceFiles/ui/effects/panel_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/panel_animation.cpp @@ -23,64 +23,31 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { -void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) { +void RoundShadowAnimation::start(int frameWidth, int frameHeight, float64 devicePixelRatio) { t_assert(!started()); - _finalImage = std_::move(finalImage).convertToFormat(QImage::Format_ARGB32_Premultiplied); - - t_assert(!_finalImage.isNull()); - _finalWidth = _finalImage.width(); - _finalHeight = _finalImage.height(); - _finalInnerLeft = inner.x(); - _finalInnerTop = inner.y(); - _finalInnerWidth = inner.width(); - _finalInnerHeight = inner.height(); - _finalInnerRight = _finalInnerLeft + _finalInnerWidth; - _finalInnerBottom = _finalInnerTop + _finalInnerHeight; - t_assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner)); - - setStartWidth(); - setStartHeight(); - setStartAlpha(); - setStartFadeTop(); - createFadeMask(); - setWidthDuration(); - setHeightDuration(); - setAlphaDuration(); - setShadow(); - - auto checkCorner = [this, inner](Corner &corner) { - if (!corner.valid()) return; - if ((_startWidth >= 0 && _startWidth < _finalWidth) - || (_startHeight >= 0 && _startHeight < _finalHeight)) { - t_assert(corner.width <= inner.width()); - t_assert(corner.height <= inner.height()); - } - }; - checkCorner(_topLeft); - checkCorner(_topRight); - checkCorner(_bottomLeft); - checkCorner(_bottomRight); - _finalInts = reinterpret_cast(_finalImage.constBits()); - _finalIntsPerLine = (_finalImage.bytesPerLine() >> 2); - _finalIntsPerLineAdded = _finalIntsPerLine - _finalWidth; - t_assert(_finalImage.depth() == static_cast(sizeof(uint32) << 3)); - t_assert(_finalImage.bytesPerLine() == (_finalIntsPerLine << 2)); - t_assert(_finalIntsPerLineAdded >= 0); + _frameWidth = frameWidth; + _frameHeight = frameHeight; + _frame = QImage(_frameWidth, _frameHeight, QImage::Format_ARGB32_Premultiplied); + _frame.setDevicePixelRatio(devicePixelRatio); + _frameIntsPerLine = (_frame.bytesPerLine() >> 2); + _frameInts = reinterpret_cast(_frame.bits()); + _frameIntsPerLineAdded = _frameIntsPerLine - _frameWidth; + t_assert(_frame.depth() == static_cast(sizeof(uint32) << 3)); + t_assert(_frame.bytesPerLine() == (_frameIntsPerLine << 2)); + t_assert(_frameIntsPerLineAdded >= 0); } -void PanelAnimation::setShadow() { - if (_skipShadow) return; - - _shadow.extend = _st.shadow.extend * cIntRetinaFactor(); - _shadow.left = cloneImage(_st.shadow.left); +void RoundShadowAnimation::setShadow(const style::Shadow &st) { + _shadow.extend = st.extend * cIntRetinaFactor(); + _shadow.left = cloneImage(st.left); if (_shadow.valid()) { - _shadow.topLeft = cloneImage(_st.shadow.topLeft); - _shadow.top = cloneImage(_st.shadow.top); - _shadow.topRight = cloneImage(_st.shadow.topRight); - _shadow.right = cloneImage(_st.shadow.right); - _shadow.bottomRight = cloneImage(_st.shadow.bottomRight); - _shadow.bottom = cloneImage(_st.shadow.bottom); - _shadow.bottomLeft = cloneImage(_st.shadow.bottomLeft); + _shadow.topLeft = cloneImage(st.topLeft); + _shadow.top = cloneImage(st.top); + _shadow.topRight = cloneImage(st.topRight); + _shadow.right = cloneImage(st.right); + _shadow.bottomRight = cloneImage(st.bottomRight); + _shadow.bottom = cloneImage(st.bottom); + _shadow.bottomLeft = cloneImage(st.bottomLeft); t_assert(!_shadow.topLeft.isNull() && !_shadow.top.isNull() && !_shadow.topRight.isNull() @@ -99,6 +66,214 @@ void PanelAnimation::setShadow() { } } +void RoundShadowAnimation::setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight) { + setCornerMask(_topLeft, std_::move(topLeft)); + setCornerMask(_topRight, std_::move(topRight)); + setCornerMask(_bottomLeft, std_::move(bottomLeft)); + setCornerMask(_bottomRight, std_::move(bottomRight)); +} + +void RoundShadowAnimation::setCornerMask(Corner &corner, QImage &&image) { + t_assert(!started()); + corner.image = std_::move(image); + if (corner.valid()) { + corner.width = corner.image.width(); + corner.height = corner.image.height(); + corner.bytes = corner.image.constBits(); + corner.bytesPerPixel = (corner.image.depth() >> 3); + corner.bytesPerLineAdded = corner.image.bytesPerLine() - corner.width * corner.bytesPerPixel; + t_assert(corner.image.depth() == (corner.bytesPerPixel << 3)); + t_assert(corner.bytesPerLineAdded >= 0); + } else { + corner.width = corner.height = 0; + } +} + +QImage RoundShadowAnimation::cloneImage(const style::icon &source) { + if (source.empty()) return QImage(); + + auto result = QImage(source.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + { + Painter p(&result); + source.paint(p, 0, 0, source.width()); + } + return std_::move(result); +} + +void RoundShadowAnimation::paintCorner(Corner &corner, int left, int top) { + auto mask = corner.bytes; + auto bytesPerPixel = corner.bytesPerPixel; + auto bytesPerLineAdded = corner.bytesPerLineAdded; + auto frameInts = _frameInts + top * _frameIntsPerLine + left; + auto frameIntsPerLineAdd = _frameIntsPerLine - corner.width; + for (auto y = 0; y != corner.height; ++y) { + for (auto x = 0; x != corner.width; ++x) { + auto alpha = static_cast(*mask) + 1; + *frameInts = anim::unshifted(anim::shifted(*frameInts) * alpha); + ++frameInts; + mask += bytesPerPixel; + } + frameInts += frameIntsPerLineAdd; + mask += bytesPerLineAdded; + } +} + +void RoundShadowAnimation::paintShadow(int left, int top, int right, int bottom) { + paintShadowCorner(left, top, _shadow.topLeft); + paintShadowCorner(right - _shadow.topRight.width(), top, _shadow.topRight); + paintShadowCorner(right - _shadow.bottomRight.width(), bottom - _shadow.bottomRight.height(), _shadow.bottomRight); + paintShadowCorner(left, bottom - _shadow.bottomLeft.height(), _shadow.bottomLeft); + paintShadowVertical(left, top + _shadow.topLeft.height(), bottom - _shadow.bottomLeft.height(), _shadow.left); + paintShadowVertical(right - _shadow.right.width(), top + _shadow.topRight.height(), bottom - _shadow.bottomRight.height(), _shadow.right); + paintShadowHorizontal(left + _shadow.topLeft.width(), right - _shadow.topRight.width(), top, _shadow.top); + paintShadowHorizontal(left + _shadow.bottomLeft.width(), right - _shadow.bottomRight.width(), bottom - _shadow.bottom.height(), _shadow.bottom); +} + +void RoundShadowAnimation::paintShadowCorner(int left, int top, const QImage &image) { + auto imageWidth = image.width(); + auto imageHeight = image.height(); + auto imageInts = reinterpret_cast(image.constBits()); + auto imageIntsPerLine = (image.bytesPerLine() >> 2); + auto imageIntsPerLineAdded = imageIntsPerLine - imageWidth; + if (left < 0) { + auto shift = -base::take(left); + imageWidth -= shift; + imageInts += shift; + } + if (top < 0) { + auto shift = -base::take(top); + imageHeight -= shift; + imageInts += shift * imageIntsPerLine; + } + if (left + imageWidth > _frameWidth) { + imageWidth = _frameWidth - left; + } + if (top + imageHeight > _frameHeight) { + imageHeight = _frameHeight - top; + } + if (imageWidth < 0 || imageHeight < 0) return; + + auto frameInts = _frameInts + top * _frameIntsPerLine + left; + auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth; + for (auto y = 0; y != imageHeight; ++y) { + for (auto x = 0; x != imageWidth; ++x) { + auto source = *frameInts; + auto shadowAlpha = qMax(_frameAlpha - int(source >> 24), 0); + *frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha); + ++frameInts; + ++imageInts; + } + frameInts += frameIntsPerLineAdd; + imageInts += imageIntsPerLineAdded; + } +} + +void RoundShadowAnimation::paintShadowVertical(int left, int top, int bottom, const QImage &image) { + auto imageWidth = image.width(); + auto imageInts = reinterpret_cast(image.constBits()); + if (left < 0) { + auto shift = -base::take(left); + imageWidth -= shift; + imageInts += shift; + } + if (top < 0) top = 0; + accumulate_min(bottom, _frameHeight); + accumulate_min(imageWidth, _frameWidth - left); + if (imageWidth < 0 || bottom <= top) return; + + auto frameInts = _frameInts + top * _frameIntsPerLine + left; + auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth; + for (auto y = top; y != bottom; ++y) { + for (auto x = 0; x != imageWidth; ++x) { + auto source = *frameInts; + auto shadowAlpha = _frameAlpha - (source >> 24); + *frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha); + ++frameInts; + ++imageInts; + } + frameInts += frameIntsPerLineAdd; + imageInts -= imageWidth; + } +} + +void RoundShadowAnimation::paintShadowHorizontal(int left, int right, int top, const QImage &image) { + auto imageHeight = image.height(); + auto imageInts = reinterpret_cast(image.constBits()); + auto imageIntsPerLine = (image.bytesPerLine() >> 2); + if (top < 0) { + auto shift = -base::take(top); + imageHeight -= shift; + imageInts += shift * imageIntsPerLine; + } + if (left < 0) left = 0; + accumulate_min(right, _frameWidth); + accumulate_min(imageHeight, _frameHeight - top); + if (imageHeight < 0 || right <= left) return; + + auto frameInts = _frameInts + top * _frameIntsPerLine + left; + auto frameIntsPerLineAdd = _frameIntsPerLine - (right - left); + for (auto y = 0; y != imageHeight; ++y) { + auto imagePattern = anim::shifted(*imageInts); + for (auto x = left; x != right; ++x) { + auto source = *frameInts; + auto shadowAlpha = _frameAlpha - (source >> 24); + *frameInts = anim::unshifted(anim::shifted(source) * 256 + imagePattern * shadowAlpha); + ++frameInts; + } + frameInts += frameIntsPerLineAdd; + imageInts += imageIntsPerLine; + } +} + +void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) { + t_assert(!started()); + _finalImage = QPixmap::fromImage(std_::move(finalImage).convertToFormat(QImage::Format_ARGB32_Premultiplied), Qt::ColorOnly); + + t_assert(!_finalImage.isNull()); + _finalWidth = _finalImage.width(); + _finalHeight = _finalImage.height(); + t_assert(!(_finalWidth % cIntRetinaFactor())); + t_assert(!(_finalHeight % cIntRetinaFactor())); + _finalInnerLeft = inner.x(); + _finalInnerTop = inner.y(); + _finalInnerWidth = inner.width(); + _finalInnerHeight = inner.height(); + t_assert(!(_finalInnerLeft % cIntRetinaFactor())); + t_assert(!(_finalInnerTop % cIntRetinaFactor())); + t_assert(!(_finalInnerWidth % cIntRetinaFactor())); + t_assert(!(_finalInnerHeight % cIntRetinaFactor())); + _finalInnerRight = _finalInnerLeft + _finalInnerWidth; + _finalInnerBottom = _finalInnerTop + _finalInnerHeight; + t_assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner)); + + setStartWidth(); + setStartHeight(); + setStartAlpha(); + setStartFadeTop(); + createFadeMask(); + setWidthDuration(); + setHeightDuration(); + setAlphaDuration(); + if (!_skipShadow) { + setShadow(_st.shadow); + } + + auto checkCorner = [this, inner](Corner &corner) { + if (!corner.valid()) return; + if ((_startWidth >= 0 && _startWidth < _finalWidth) + || (_startHeight >= 0 && _startHeight < _finalHeight)) { + t_assert(corner.width <= inner.width()); + t_assert(corner.height <= inner.height()); + } + }; + checkCorner(_topLeft); + checkCorner(_topRight); + checkCorner(_bottomLeft); + checkCorner(_bottomRight); +} + void PanelAnimation::setStartWidth() { _startWidth = qRound(_st.startWidth * _finalInnerWidth); if (_startWidth >= 0) t_assert(_startWidth <= _finalInnerWidth); @@ -120,30 +295,30 @@ void PanelAnimation::setStartFadeTop() { void PanelAnimation::createFadeMask() { auto resultHeight = qRound(_finalImage.height() * _st.fadeHeight); + if (auto remove = (resultHeight % cIntRetinaFactor())) { + resultHeight -= remove; + } auto finalAlpha = qRound(_st.fadeOpacity * 255); t_assert(finalAlpha >= 0 && finalAlpha < 256); - auto result = QImage(1, resultHeight, QImage::Format_ARGB32_Premultiplied); + auto result = QImage(cIntRetinaFactor(), resultHeight, QImage::Format_ARGB32_Premultiplied); auto ints = reinterpret_cast(result.bits()); - auto intsPerLine = (result.bytesPerLine() >> 2); + auto intsPerLineAdded = (result.bytesPerLine() >> 2) - cIntRetinaFactor(); auto up = (_origin == PanelAnimation::Origin::BottomLeft || _origin == PanelAnimation::Origin::BottomRight); auto from = up ? resultHeight : 0, to = resultHeight - from, delta = up ? -1 : 1; + auto fadeFirstAlpha = up ? (finalAlpha + 1) : 1; + auto fadeLastAlpha = up ? 1 : (finalAlpha + 1); + _fadeFirst = QBrush(QColor(_st.fadeBg->c.red(), _st.fadeBg->c.green(), _st.fadeBg->c.blue(), (_st.fadeBg->c.alpha() * fadeFirstAlpha) >> 8)); + _fadeLast = QBrush(QColor(_st.fadeBg->c.red(), _st.fadeBg->c.green(), _st.fadeBg->c.blue(), (_st.fadeBg->c.alpha() * fadeLastAlpha) >> 8)); for (auto y = from; y != to; y += delta) { auto alpha = static_cast(finalAlpha * y) / resultHeight; - *ints = (0xFFU << 24) | (alpha << 16) | (alpha << 8) | alpha; - ints += intsPerLine; + auto value = (0xFFU << 24) | (alpha << 16) | (alpha << 8) | alpha; + for (auto x = 0; x != cIntRetinaFactor(); ++x) { + *ints++ = value; + } + ints += intsPerLineAdded; } - _fadeMask = style::colorizeImage(result, _st.fadeBg); + _fadeMask = QPixmap::fromImage(style::colorizeImage(result, _st.fadeBg), Qt::ColorOnly); _fadeHeight = _fadeMask.height(); - _fadeInts = reinterpret_cast(_fadeMask.constBits()); - _fadeIntsPerLine = (_fadeMask.bytesPerLine() >> 2); - t_assert(_fadeMask.bytesPerLine() == (_fadeIntsPerLine << 2)); -} - -void PanelAnimation::setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight) { - setCornerMask(_topLeft, std_::move(topLeft)); - setCornerMask(_topRight, std_::move(topRight)); - setCornerMask(_bottomLeft, std_::move(bottomLeft)); - setCornerMask(_bottomRight, std_::move(bottomRight)); } void PanelAnimation::setSkipShadow(bool skipShadow) { @@ -151,41 +326,6 @@ void PanelAnimation::setSkipShadow(bool skipShadow) { _skipShadow = skipShadow; } -void PanelAnimation::setCornerMask(Corner &corner, QImage &&image) { - t_assert(!started()); - corner.image = std_::move(image); - if (corner.valid()) { - corner.width = corner.image.width(); - corner.height = corner.image.height(); - corner.bytes = corner.image.constBits(); - corner.bytesPerPixel = (corner.image.depth() >> 3); - corner.bytesPerLineAdded = corner.image.bytesPerLine() - corner.width * corner.bytesPerPixel; - t_assert(corner.image.depth() == (corner.bytesPerPixel << 3)); - t_assert(corner.bytesPerLineAdded >= 0); - if (_startWidth >= 0) t_assert(corner.width <= _startWidth); - if (_startHeight >= 0) t_assert(corner.height <= _startHeight); - if (!_finalImage.isNull()) { - t_assert(corner.width <= _finalInnerWidth); - t_assert(corner.height <= _finalInnerHeight); - } - } else { - corner.width = corner.height = 0; - } -} - -QImage PanelAnimation::cloneImage(const style::icon &source) { - if (source.empty()) return QImage(); - - auto result = QImage(source.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); - result.fill(Qt::transparent); - { - Painter p(&result); - source.paint(p, 0, 0, source.width()); - } - return std_::move(result); -} - void PanelAnimation::setWidthDuration() { _widthDuration = _st.widthDuration; t_assert(_widthDuration >= 0.); @@ -207,35 +347,46 @@ void PanelAnimation::setAlphaDuration() { } void PanelAnimation::start() { - t_assert(!started()); t_assert(!_finalImage.isNull()); - _frame = QImage(_finalWidth, _finalHeight, QImage::Format_ARGB32_Premultiplied); - _frame.setDevicePixelRatio(_finalImage.devicePixelRatio()); - _frameIntsPerLine = (_frame.bytesPerLine() >> 2); - _frameInts = reinterpret_cast(_frame.bits()); - _frameIntsPerLineAdded = _frameIntsPerLine - _finalWidth; - t_assert(_frame.depth() == static_cast(sizeof(uint32) << 3)); - t_assert(_frame.bytesPerLine() == (_frameIntsPerLine << 2)); - t_assert(_frameIntsPerLineAdded >= 0); + RoundShadowAnimation::start(_finalWidth, _finalHeight, _finalImage.devicePixelRatio()); + auto checkCorner = [this](const Corner &corner) { + if (!corner.valid()) return; + if (_startWidth >= 0) t_assert(corner.width <= _startWidth); + if (_startHeight >= 0) t_assert(corner.height <= _startHeight); + t_assert(corner.width <= _finalInnerWidth); + t_assert(corner.height <= _finalInnerHeight); + }; + checkCorner(_topLeft); + checkCorner(_topRight); + checkCorner(_bottomLeft); + checkCorner(_bottomRight); } -const QImage &PanelAnimation::getFrame(float64 dt, float64 opacity) { +void PanelAnimation::paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity) { t_assert(started()); t_assert(dt >= 0.); auto &transition = anim::easeOutCirc; - constexpr auto finalAlpha = 256; - auto alpha = (dt >= _alphaDuration) ? finalAlpha : anim::interpolate(_startAlpha + 1, finalAlpha, transition(1., dt / _alphaDuration)); - _frameAlpha = anim::interpolate(0, alpha, opacity); + if (dt < _alphaDuration) opacity *= transition(1., dt / _alphaDuration); + _frameAlpha = anim::interpolate(1, 256, opacity); auto frameWidth = (_startWidth < 0 || dt >= _widthDuration) ? _finalInnerWidth : anim::interpolate(_startWidth, _finalInnerWidth, transition(1., dt / _widthDuration)); auto frameHeight = (_startHeight < 0 || dt >= _heightDuration) ? _finalInnerHeight : anim::interpolate(_startHeight, _finalInnerHeight, transition(1., dt / _heightDuration)); + if (auto decrease = (frameWidth % cIntRetinaFactor())) { + frameWidth -= decrease; + } + if (auto decrease = (frameHeight % cIntRetinaFactor())) { + frameHeight -= decrease; + } auto frameLeft = (_origin == Origin::TopLeft || _origin == Origin::BottomLeft) ? _finalInnerLeft : (_finalInnerRight - frameWidth); auto frameTop = (_origin == Origin::TopLeft || _origin == Origin::TopRight) ? _finalInnerTop : (_finalInnerBottom - frameHeight); auto frameRight = frameLeft + frameWidth; auto frameBottom = frameTop + frameHeight; auto fadeTop = (_fadeHeight > 0) ? snap(anim::interpolate(_startFadeTop, _finalInnerHeight, transition(1., dt)), 0, frameHeight) : frameHeight; + if (auto decrease = (fadeTop % cIntRetinaFactor())) { + fadeTop -= decrease; + } auto fadeBottom = (fadeTop < frameHeight) ? qMin(fadeTop + _fadeHeight, frameHeight) : frameHeight; auto fadeSkipLines = 0; if (_origin == Origin::BottomLeft || _origin == Origin::BottomRight) { @@ -247,97 +398,97 @@ const QImage &PanelAnimation::getFrame(float64 dt, float64 opacity) { fadeTop += frameTop; fadeBottom += frameTop; - auto finalInts = _finalInts + frameLeft + frameTop * _finalIntsPerLine; + if (opacity < 1.) { + _frame.fill(Qt::transparent); + } + { + Painter p(&_frame); + p.setOpacity(opacity); + auto painterFrameLeft = frameLeft / cIntRetinaFactor(); + auto painterFrameTop = frameTop / cIntRetinaFactor(); + auto painterFadeBottom = fadeBottom / cIntRetinaFactor(); + p.drawPixmap(painterFrameLeft, painterFrameTop, _finalImage, frameLeft, frameTop, frameWidth, frameHeight); + if (_fadeHeight) { + if (frameTop != fadeTop) { + p.fillRect(painterFrameLeft, painterFrameTop, frameWidth, fadeTop - frameTop, _fadeFirst); + } + if (fadeTop != fadeBottom) { + auto painterFadeTop = fadeTop / cIntRetinaFactor(); + auto painterFrameWidth = frameWidth / cIntRetinaFactor(); + auto painterFrameHeight = frameHeight / cIntRetinaFactor(); + p.drawPixmap(painterFrameLeft, painterFadeTop, painterFrameWidth, painterFadeBottom - painterFadeTop, _fadeMask, 0, fadeSkipLines, cIntRetinaFactor(), fadeBottom - fadeTop); + } + if (fadeBottom != frameBottom) { + p.fillRect(painterFrameLeft, painterFadeBottom, frameWidth, frameBottom - fadeBottom, _fadeLast); + } + } + } auto frameInts = _frameInts + frameLeft + frameTop * _frameIntsPerLine; - auto finalIntsPerLineAdd = (_finalWidth - frameWidth) + _finalIntsPerLineAdded; auto frameIntsPerLineAdd = (_finalWidth - frameWidth) + _frameIntsPerLineAdded; - // Draw frameWidth x fadeTop with fade first color. - auto fadeInts = _fadeInts + fadeSkipLines * _fadeIntsPerLine; - auto fadeWithMasterAlpha = [this](uint32 fade) { - auto fadeAlphaAddition = (256 - (fade >> 24)); - auto fadePattern = anim::shifted(fade); - return [this, fadeAlphaAddition, fadePattern](uint32 source) { - auto sourceAlpha = (source >> 24) + 1; - auto sourcePattern = anim::shifted(source); - auto mixedPattern = anim::reshifted(fadePattern * sourceAlpha + sourcePattern * fadeAlphaAddition); - return anim::unshifted(mixedPattern * _frameAlpha); - }; - }; - if (frameTop != fadeTop) { - // Take the fade components from the first line of the fade mask. - auto withMasterAlpha = fadeWithMasterAlpha(_fadeInts ? *_fadeInts : 0); - for (auto y = frameTop; y != fadeTop; ++y) { - for (auto x = frameLeft; x != frameRight; ++x) { - *frameInts++ = withMasterAlpha(*finalInts++); - } - finalInts += finalIntsPerLineAdd; - frameInts += frameIntsPerLineAdd; - } - } - - // Draw frameWidth x (fadeBottom - fadeTop) with fade gradient. - for (auto y = fadeTop; y != fadeBottom; ++y) { - auto withMasterAlpha = fadeWithMasterAlpha(*fadeInts); - for (auto x = frameLeft; x != frameRight; ++x) { - *frameInts++ = withMasterAlpha(*finalInts++); - } - finalInts += finalIntsPerLineAdd; - frameInts += frameIntsPerLineAdd; - fadeInts += _fadeIntsPerLine; - } - - // Draw frameWidth x (frameBottom - fadeBottom) with fade final color. - if (fadeBottom != frameBottom) { - // Take the fade components from the last line of the fade mask. - auto withMasterAlpha = fadeWithMasterAlpha(*(fadeInts - _fadeIntsPerLine)); - for (auto y = fadeBottom; y != frameBottom; ++y) { - for (auto x = frameLeft; x != frameRight; ++x) { - *frameInts++ = withMasterAlpha(*finalInts++); - } - finalInts += finalIntsPerLineAdd; - frameInts += frameIntsPerLineAdd; - } - } - // Draw corners - auto innerLeft = qMax(_finalInnerLeft, frameLeft); - auto innerTop = qMax(_finalInnerTop, frameTop); - auto innerRight = qMin(_finalInnerRight, frameRight); - auto innerBottom = qMin(_finalInnerBottom, frameBottom); - if (innerLeft != _finalInnerLeft || innerTop != _finalInnerTop) { - paintCorner(_topLeft, innerLeft, innerTop); - } - if (innerRight != _finalInnerRight || innerTop != _finalInnerTop) { - paintCorner(_topRight, innerRight - _topRight.width, innerTop); - } - if (innerLeft != _finalInnerLeft || innerBottom != _finalInnerBottom) { - paintCorner(_bottomLeft, innerLeft, innerBottom - _bottomLeft.height); - } - if (innerRight != _finalInnerRight || innerBottom != _finalInnerBottom) { - paintCorner(_bottomRight, innerRight - _bottomRight.width, innerBottom - _bottomRight.height); - } + paintCorner(_topLeft, frameLeft, frameTop); + paintCorner(_topRight, frameRight - _topRight.width, frameTop); + paintCorner(_bottomLeft, frameLeft, frameBottom - _bottomLeft.height); + paintCorner(_bottomRight, frameRight - _bottomRight.width, frameBottom - _bottomRight.height); - // Fill the rest with transparent - if (frameTop) { - memset(_frameInts, 0, _frameIntsPerLine * frameTop * sizeof(uint32)); + // Draw shadow upon the transparent + auto outerLeft = frameLeft; + auto outerTop = frameTop; + auto outerRight = frameRight; + auto outerBottom = frameBottom; + if (_shadow.valid()) { + outerLeft -= _shadow.extend.left(); + outerTop -= _shadow.extend.top(); + outerRight += _shadow.extend.right(); + outerBottom += _shadow.extend.bottom(); } - auto widthLeft = (_finalWidth - frameRight); - if (frameLeft || widthLeft) { - auto frameInts = _frameInts + frameTop * _frameIntsPerLine; - for (auto y = frameTop; y != frameBottom; ++y) { - memset(frameInts, 0, frameLeft * sizeof(uint32)); - memset(frameInts + frameLeft + frameWidth, 0, widthLeft * sizeof(uint32)); - frameInts += _frameIntsPerLine; + if (cIntRetinaFactor() > 1) { + if (auto skipLeft = (outerLeft % cIntRetinaFactor())) { + outerLeft -= skipLeft; + } + if (auto skipTop = (outerTop % cIntRetinaFactor())) { + outerTop -= skipTop; + } + if (auto skipRight = (outerRight % cIntRetinaFactor())) { + outerRight += (cIntRetinaFactor() - skipRight); + } + if (auto skipBottom = (outerBottom % cIntRetinaFactor())) { + outerBottom += (cIntRetinaFactor() - skipBottom); } } - if (auto heightLeft = (_finalHeight - frameBottom)) { - memset(_frameInts + frameBottom * _frameIntsPerLine, 0, _frameIntsPerLine * heightLeft * sizeof(uint32)); + + if (opacity == 1.) { + // Fill above the frame top with transparent. + auto fillTopInts = (_frameInts + outerTop * _frameIntsPerLine + outerLeft); + auto fillWidth = (outerRight - outerLeft) * sizeof(uint32); + for (auto fillTop = frameTop - outerTop; fillTop != 0; --fillTop) { + memset(fillTopInts, 0, fillWidth); + fillTopInts += _frameIntsPerLine; + } + + // Fill to the left and to the right of the frame with transparent. + auto fillLeft = (frameLeft - outerLeft) * sizeof(uint32); + auto fillRight = (outerRight - frameRight) * sizeof(uint32); + if (fillLeft || fillRight) { + auto fillInts = _frameInts + frameTop * _frameIntsPerLine; + for (auto y = frameTop; y != frameBottom; ++y) { + memset(fillInts + outerLeft, 0, fillLeft); + memset(fillInts + frameRight, 0, fillRight); + fillInts += _frameIntsPerLine; + } + } + + // Fill below the frame bottom with transparent. + auto fillBottomInts = (_frameInts + frameBottom * _frameIntsPerLine + outerLeft); + for (auto fillBottom = outerBottom - frameBottom; fillBottom != 0; --fillBottom) { + memset(fillBottomInts, 0, fillWidth); + fillBottomInts += _frameIntsPerLine; + } } - // Draw shadow if (_shadow.valid()) { - paintShadow(innerLeft, innerTop, innerRight, innerBottom); + paintShadow(outerLeft, outerTop, outerRight, outerBottom); } // Debug @@ -353,140 +504,7 @@ const QImage &PanelAnimation::getFrame(float64 dt, float64 opacity) { // frameInts += _frameIntsPerLineAdded; //} - return _frame; -} - -void PanelAnimation::paintCorner(Corner &corner, int left, int top) { - auto mask = corner.bytes; - auto bytesPerPixel = corner.bytesPerPixel; - auto bytesPerLineAdded = corner.bytesPerLineAdded; - auto frameInts = _frameInts + top * _frameIntsPerLine + left; - auto frameIntsPerLineAdd = _frameIntsPerLine - corner.width; - for (auto y = 0; y != corner.height; ++y) { - for (auto x = 0; x != corner.width; ++x) { - auto alpha = static_cast(*mask) + 1; - *frameInts = anim::unshifted(anim::shifted(*frameInts) * alpha); - ++frameInts; - mask += bytesPerPixel; - } - frameInts += frameIntsPerLineAdd; - mask += bytesPerLineAdded; - } -} - -void PanelAnimation::paintShadow(int left, int top, int right, int bottom) { - left -= _shadow.extend.left(); - top -= _shadow.extend.top(); - right += _shadow.extend.right(); - bottom += _shadow.extend.bottom(); - paintShadowCorner(left, top, _shadow.topLeft); - paintShadowCorner(right - _shadow.topRight.width(), top, _shadow.topRight); - paintShadowCorner(right - _shadow.bottomRight.width(), bottom - _shadow.bottomRight.height(), _shadow.bottomRight); - paintShadowCorner(left, bottom - _shadow.bottomLeft.height(), _shadow.bottomLeft); - paintShadowVertical(left, top + _shadow.topLeft.height(), bottom - _shadow.bottomLeft.height(), _shadow.left); - paintShadowVertical(right - _shadow.right.width(), top + _shadow.topRight.height(), bottom - _shadow.bottomRight.height(), _shadow.right); - paintShadowHorizontal(left + _shadow.topLeft.width(), right - _shadow.topRight.width(), top, _shadow.top); - paintShadowHorizontal(left + _shadow.bottomLeft.width(), right - _shadow.bottomRight.width(), bottom - _shadow.bottom.height(), _shadow.bottom); -} - -void PanelAnimation::paintShadowCorner(int left, int top, const QImage &image) { - auto imageWidth = image.width(); - auto imageHeight = image.height(); - auto imageInts = reinterpret_cast(image.constBits()); - auto imageIntsPerLine = (image.bytesPerLine() >> 2); - auto imageIntsPerLineAdded = imageIntsPerLine - imageWidth; - if (left < 0) { - auto shift = -base::take(left); - imageWidth -= shift; - imageInts += shift; - } - if (top < 0) { - auto shift = -base::take(top); - imageHeight -= shift; - imageInts += shift * imageIntsPerLine; - } - if (left + imageWidth > _finalWidth) { - imageWidth = _finalWidth - left; - } - if (top + imageHeight > _finalHeight) { - imageHeight = _finalHeight - top; - } - if (imageWidth < 0 || imageHeight < 0) return; - - auto frameInts = _frameInts + top * _frameIntsPerLine + left; - auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth; - for (auto y = 0; y != imageHeight; ++y) { - for (auto x = 0; x != imageWidth; ++x) { - auto source = *frameInts; - auto shadowAlpha = _frameAlpha - (source >> 24); - *frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha); - ++frameInts; - ++imageInts; - } - frameInts += frameIntsPerLineAdd; - imageInts += imageIntsPerLineAdded; - } -} - -void PanelAnimation::paintShadowVertical(int left, int top, int bottom, const QImage &image) { - auto imageWidth = image.width(); - auto imageInts = reinterpret_cast(image.constBits()); - if (left < 0) { - auto shift = -base::take(left); - imageWidth -= shift; - imageInts += shift; - } - if (top < 0) top = 0; - if (bottom > _finalHeight) bottom = _finalHeight; - if (left + imageWidth > _finalWidth) { - imageWidth = _finalWidth - left; - } - if (imageWidth < 0 || bottom <= top) return; - - auto frameInts = _frameInts + top * _frameIntsPerLine + left; - auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth; - for (auto y = top; y != bottom; ++y) { - for (auto x = 0; x != imageWidth; ++x) { - auto source = *frameInts; - auto shadowAlpha = _frameAlpha - (source >> 24); - *frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha); - ++frameInts; - ++imageInts; - } - frameInts += frameIntsPerLineAdd; - imageInts -= imageWidth; - } -} - -void PanelAnimation::paintShadowHorizontal(int left, int right, int top, const QImage &image) { - auto imageHeight = image.height(); - auto imageInts = reinterpret_cast(image.constBits()); - auto imageIntsPerLine = (image.bytesPerLine() >> 2); - if (top < 0) { - auto shift = -base::take(top); - imageHeight -= shift; - imageInts += shift * imageIntsPerLine; - } - if (left < 0) left = 0; - if (right > _finalWidth) right = _finalWidth; - if (top + imageHeight > _finalHeight) { - imageHeight = _finalHeight - top; - } - if (imageHeight < 0 || right <= left) return; - - auto frameInts = _frameInts + top * _frameIntsPerLine + left; - auto frameIntsPerLineAdd = _frameIntsPerLine - (right - left); - for (auto y = 0; y != imageHeight; ++y) { - auto imagePattern = anim::shifted(*imageInts); - for (auto x = left; x != right; ++x) { - auto source = *frameInts; - auto shadowAlpha = _frameAlpha - (source >> 24); - *frameInts = anim::unshifted(anim::shifted(source) * 256 + imagePattern * shadowAlpha); - ++frameInts; - } - frameInts += frameIntsPerLineAdd; - imageInts += imageIntsPerLine; - } + p.drawImage(rtlpoint(x + (outerLeft / cIntRetinaFactor()), y + (outerTop / cIntRetinaFactor()), outerWidth), _frame, QRect(outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop)); } } // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.h b/Telegram/SourceFiles/ui/effects/panel_animation.h index 988315483..1e7af2caa 100644 --- a/Telegram/SourceFiles/ui/effects/panel_animation.h +++ b/Telegram/SourceFiles/ui/effects/panel_animation.h @@ -24,34 +24,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { -class PanelAnimation { +class RoundShadowAnimation { public: - enum class Origin { - TopLeft, - TopRight, - BottomLeft, - BottomRight, - }; - PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) { - } - - void setFinalImage(QImage &&finalImage, QRect inner); void setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight); - void setSkipShadow(bool skipShadow); - void start(); - const QImage &getFrame(float64 dt, float64 opacity); - -private: - void setStartWidth(); - void setStartHeight(); - void setStartAlpha(); - void setStartFadeTop(); - void createFadeMask(); - void setWidthDuration(); - void setHeightDuration(); - void setAlphaDuration(); - void setShadow(); +protected: + void start(int frameWidth, int frameHeight, float64 devicePixelRatio); + void setShadow(const style::Shadow &st); bool started() const { return !_frame.isNull(); @@ -86,13 +65,54 @@ private: void paintShadowVertical(int left, int top, int bottom, const QImage &image); void paintShadowHorizontal(int left, int right, int top, const QImage &image); + Shadow _shadow; + + Corner _topLeft; + Corner _topRight; + Corner _bottomLeft; + Corner _bottomRight; + + QImage _frame; + uint32 *_frameInts = nullptr; + int _frameWidth = 0; + int _frameHeight = 0; + int _frameAlpha = 0; // recounted each getFrame() + int _frameIntsPerLine = 0; + int _frameIntsPerLineAdded = 0; + +}; + +class PanelAnimation : public RoundShadowAnimation { +public: + enum class Origin { + TopLeft, + TopRight, + BottomLeft, + BottomRight, + }; + PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) { + } + + void setFinalImage(QImage &&finalImage, QRect inner); + void setSkipShadow(bool skipShadow); + + void start(); + void paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity); + +private: + void setStartWidth(); + void setStartHeight(); + void setStartAlpha(); + void setStartFadeTop(); + void createFadeMask(); + void setWidthDuration(); + void setHeightDuration(); + void setAlphaDuration(); + const style::PanelAnimation &_st; Origin _origin = Origin::TopLeft; - QImage _finalImage; - const uint32 *_finalInts = nullptr; - int _finalIntsPerLine = 0; - int _finalIntsPerLineAdded = 0; + QPixmap _finalImage; int _finalWidth = 0; int _finalHeight = 0; int _finalInnerLeft = 0; @@ -102,33 +122,20 @@ private: int _finalInnerWidth = 0; int _finalInnerHeight = 0; - Shadow _shadow; bool _skipShadow = false; int _startWidth = -1; int _startHeight = -1; int _startAlpha = 0; int _startFadeTop = 0; - QImage _fadeMask; + QPixmap _fadeMask; int _fadeHeight = 0; - const uint32 *_fadeInts = nullptr; - int _fadeIntsPerLine = 0; - - Corner _topLeft; - Corner _topRight; - Corner _bottomLeft; - Corner _bottomRight; + QBrush _fadeFirst, _fadeLast; float64 _widthDuration = 1.; float64 _heightDuration = 1.; float64 _alphaDuration = 1.; - QImage _frame; - uint32 *_frameInts = nullptr; - int _frameAlpha = 0; // recounted each getFrame() - int _frameIntsPerLine = 0; - int _frameIntsPerLineAdded = 0; - }; } // namespace Ui diff --git a/Telegram/SourceFiles/ui/style/style_core.cpp b/Telegram/SourceFiles/ui/style/style_core.cpp index 98ff07b11..f375af726 100644 --- a/Telegram/SourceFiles/ui/style/style_core.cpp +++ b/Telegram/SourceFiles/ui/style/style_core.cpp @@ -85,15 +85,7 @@ void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect auto height = srcRect.height(); t_assert(outResult && outResult->rect().contains(QRect(dstPoint, srcRect.size()))); - auto initialAlpha = c.alpha() + 1; - auto red = (c.red() * initialAlpha) >> 8; - auto green = (c.green() * initialAlpha) >> 8; - auto blue = (c.blue() * initialAlpha) >> 8; - auto alpha = (255 * initialAlpha) >> 8; - auto pattern = static_cast(blue) - | (static_cast(green) << 16) - | (static_cast(red) << 32) - | (static_cast(alpha) << 48); + auto pattern = anim::shifted(c); auto resultBytesPerPixel = (src.depth() >> 3); auto resultIntsPerPixel = 1; diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index bc0c2d450..7f0a4f37f 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -110,7 +110,7 @@ void InnerDropdown::paintEvent(QPaintEvent *e) { auto ms = getms(); if (_a_show.animating(ms)) { if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) { - p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity)); + _showAnimation->paintFrame(p, 0, 0, width(), _a_show.current(1.), opacity); } } else if (_a_opacity.animating(ms)) { p.setOpacity(_a_opacity.current(0.)); @@ -118,10 +118,11 @@ void InnerDropdown::paintEvent(QPaintEvent *e) { } else if (_hiding || isHidden()) { hideFinished(); } else if (_showAnimation) { - p.drawImage(0, 0, _showAnimation->getFrame(1., 1.)); + _showAnimation->paintFrame(p, 0, 0, width(), 1., 1.); _showAnimation.reset(); showChildren(); } else { + if (!_cache.isNull()) _cache = QPixmap(); auto inner = rect().marginsRemoved(_st.padding); Shadow::paint(p, inner, width(), _st.shadow); App::roundRect(p, inner, _st.bg, ImageRoundRadius::Small); @@ -207,6 +208,9 @@ void InnerDropdown::prepareCache() { _cache = myGrab(this); _showAnimation = base::take(showAnimationData); _a_show = base::take(showAnimation); + if (_a_show.animating()) { + hideChildren(); + } } void InnerDropdown::startOpacityAnimation(bool hiding) { @@ -270,7 +274,7 @@ void InnerDropdown::opacityAnimationCallback() { if (_hiding) { _hiding = false; hideFinished(); - } else { + } else if (!_a_show.animating()) { showChildren(); } } diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index 91772619f..d83687937 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -109,7 +109,7 @@ void PopupMenu::paintEvent(QPaintEvent *e) { auto ms = getms(); if (_a_show.animating(ms)) { if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) { - p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity)); + _showAnimation->paintFrame(p, 0, 0, width(), _a_show.current(1.), opacity); } } else if (_a_opacity.animating(ms)) { p.setOpacity(_a_opacity.current(0.)); @@ -117,7 +117,7 @@ void PopupMenu::paintEvent(QPaintEvent *e) { } else if (_hiding || isHidden()) { hideFinished(); } else if (_showAnimation) { - p.drawImage(0, 0, _showAnimation->getFrame(1., 1.)); + _showAnimation->paintFrame(p, 0, 0, width(), 1., 1.); _showAnimation.reset(); showChildren(); } else { diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index dc7818dfd..d19b437ca 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -201,6 +201,7 @@ '<(src_loc)/boxes/stickers_box.h', '<(src_loc)/boxes/usernamebox.cpp', '<(src_loc)/boxes/usernamebox.h', + '<(src_loc)/core/build_config.h', '<(src_loc)/core/basic_types.h', '<(src_loc)/core/click_handler.cpp', '<(src_loc)/core/click_handler.h', From 3186e1e495d0f7a6573c05746ef3c0c06e90ed37 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 15 Nov 2016 14:56:49 +0300 Subject: [PATCH 039/100] Ripple animations done for IconButton, FlatButton and RoundButton. Also moved input field classes to ui/widgets/input_fields module. --- Telegram/Resources/basic.style | 289 ---- Telegram/Resources/basic_types.style | 183 -- Telegram/Resources/colors.palette | 8 + Telegram/Resources/sample.tdesktop-theme | 7 + Telegram/Resources/winrc/Telegram.rc | 8 +- Telegram/Resources/winrc/Updater.rc | 8 +- Telegram/SourceFiles/application.cpp | 6 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 1 + Telegram/SourceFiles/boxes/addcontactbox.h | 24 +- Telegram/SourceFiles/boxes/autolockbox.cpp | 1 + Telegram/SourceFiles/boxes/backgroundbox.cpp | 1 + Telegram/SourceFiles/boxes/boxes.style | 88 +- Telegram/SourceFiles/boxes/confirmbox.h | 5 + .../SourceFiles/boxes/confirmphonebox.cpp | 7 +- Telegram/SourceFiles/boxes/confirmphonebox.h | 7 +- Telegram/SourceFiles/boxes/connectionbox.cpp | 2 + Telegram/SourceFiles/boxes/connectionbox.h | 11 +- Telegram/SourceFiles/boxes/languagebox.cpp | 1 + Telegram/SourceFiles/boxes/passcodebox.cpp | 1 + Telegram/SourceFiles/boxes/passcodebox.h | 14 +- Telegram/SourceFiles/boxes/photocropbox.cpp | 1 + Telegram/SourceFiles/boxes/photosendbox.cpp | 8 +- Telegram/SourceFiles/boxes/photosendbox.h | 5 +- Telegram/SourceFiles/boxes/report_box.cpp | 5 +- Telegram/SourceFiles/boxes/report_box.h | 3 +- Telegram/SourceFiles/boxes/usernamebox.cpp | 1 + Telegram/SourceFiles/boxes/usernamebox.h | 3 +- .../SourceFiles/codegen/style/generator.cpp | 15 +- Telegram/SourceFiles/core/version.h | 2 +- Telegram/SourceFiles/data/data_drafts.cpp | 8 + Telegram/SourceFiles/data/data_drafts.h | 12 +- Telegram/SourceFiles/dialogs/dialogs.style | 22 +- Telegram/SourceFiles/dialogswidget.cpp | 3 +- Telegram/SourceFiles/dialogswidget.h | 3 +- Telegram/SourceFiles/history.h | 25 +- Telegram/SourceFiles/history/history.style | 40 +- Telegram/SourceFiles/historywidget.cpp | 44 +- Telegram/SourceFiles/historywidget.h | 9 +- Telegram/SourceFiles/intro/intro.style | 12 +- Telegram/SourceFiles/intro/introcode.cpp | 4 +- Telegram/SourceFiles/intro/introcode.h | 7 +- Telegram/SourceFiles/intro/introphone.cpp | 6 +- Telegram/SourceFiles/intro/introphone.h | 6 +- Telegram/SourceFiles/intro/intropwdcheck.cpp | 6 +- Telegram/SourceFiles/intro/intropwdcheck.h | 7 +- Telegram/SourceFiles/intro/introsignup.cpp | 5 +- Telegram/SourceFiles/intro/introsignup.h | 6 +- Telegram/SourceFiles/intro/introwidget.cpp | 1 + Telegram/SourceFiles/localimageloader.cpp | 8 +- Telegram/SourceFiles/localstorage.cpp | 9 +- Telegram/SourceFiles/localstorage.h | 1 - Telegram/SourceFiles/mainwidget.cpp | 2 + Telegram/SourceFiles/mainwindow.cpp | 1 + .../SourceFiles/media/media_clip_reader.h | 11 +- Telegram/SourceFiles/overview/overview.style | 2 +- Telegram/SourceFiles/overviewwidget.cpp | 43 +- Telegram/SourceFiles/overviewwidget.h | 3 +- Telegram/SourceFiles/passcodewidget.cpp | 1 + Telegram/SourceFiles/passcodewidget.h | 3 +- .../platform/mac/main_window_mac.mm | 12 +- Telegram/SourceFiles/profile/profile.style | 2 + .../profile/profile_actions_widget.cpp | 1 + .../profile/profile_actions_widget.h | 8 + .../profile/profile_block_widget.cpp | 1 + Telegram/SourceFiles/profile/profile_cover.h | 4 + .../SourceFiles/profile/profile_fixed_bar.cpp | 1 + .../profile/profile_settings_widget.cpp | 4 +- Telegram/SourceFiles/stdafx.h | 2 - Telegram/SourceFiles/stickers/stickers.style | 7 + .../ui/buttons/left_outline_button.h | 1 + .../ui/buttons/peer_avatar_button.h | 1 + Telegram/SourceFiles/ui/countryinput.cpp | 2 +- Telegram/SourceFiles/ui/countryinput.h | 1 - .../ui/effects/panel_animation.cpp | 4 +- .../ui/effects/ripple_animation.cpp | 157 ++ .../SourceFiles/ui/effects/ripple_animation.h | 62 + Telegram/SourceFiles/ui/flattextarea.cpp | 1426 ---------------- Telegram/SourceFiles/ui/flattextarea.h | 261 --- Telegram/SourceFiles/ui/widgets/buttons.cpp | 156 +- Telegram/SourceFiles/ui/widgets/buttons.h | 23 + Telegram/SourceFiles/ui/widgets/checkbox.h | 1 + .../input_fields.cpp} | 1487 ++++++++++++++++- .../{flatinput.h => widgets/input_fields.h} | 248 ++- .../SourceFiles/ui/widgets/multi_select.cpp | 1 + .../SourceFiles/ui/widgets/multi_select.h | 5 +- Telegram/SourceFiles/ui/widgets/widgets.style | 380 +++++ Telegram/SourceFiles/window/main_window.cpp | 1 + .../window/notifications_manager_default.cpp | 16 +- .../window/notifications_manager_default.h | 3 +- .../SourceFiles/window/slide_animation.cpp | 2 + .../SourceFiles/window/top_bar_widget.cpp | 29 +- Telegram/SourceFiles/window/window.style | 75 +- Telegram/build/version | 2 +- Telegram/gyp/Telegram.gyp | 8 +- 94 files changed, 2970 insertions(+), 2449 deletions(-) create mode 100644 Telegram/SourceFiles/ui/effects/ripple_animation.cpp create mode 100644 Telegram/SourceFiles/ui/effects/ripple_animation.h delete mode 100644 Telegram/SourceFiles/ui/flattextarea.cpp delete mode 100644 Telegram/SourceFiles/ui/flattextarea.h rename Telegram/SourceFiles/ui/{flatinput.cpp => widgets/input_fields.cpp} (63%) rename Telegram/SourceFiles/ui/{flatinput.h => widgets/input_fields.h} (68%) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index eaf924394..6efd12541 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -57,156 +57,12 @@ boxTitlePosition: point(26px, 28px); boxTitleHeight: 54px; boxButtonFont: font(boxFontSize semibold); -defaultBoxButton: RoundButton { - textFg: #2f9fea; - textFgOver: #2f9fea; - secondaryTextFg: #2f9fea; - secondaryTextFgOver: #2f9fea; - textBg: boxBg; - textBgOver: lightButtonBgOver; - - width: -24px; - height: 36px; - padding: margins(0px, 0px, 0px, 0px); - - textTop: 8px; - downTextTop: 9px; - - font: boxButtonFont; -} -cancelBoxButton: RoundButton(defaultBoxButton) { - textFg: #aeaeae; -} -attentionBoxButton: RoundButton(defaultBoxButton) { - textFg: #ea4b2f; - textFgOver: #ea4b2f; - textBgOver: #fff0ed; -} boxButtonPadding: margins(12px, 16px, 22px, 16px); boxLabel: flatLabel(labelDefFlat) { font: font(boxFontSize); align: align(topleft); } -defaultLeftOutlineButton: OutlineButton { - outlineWidth: 3px; - outlineFg: windowBg; - outlineFgOver: windowActiveBg; - - textBg: windowBg; - textBgOver: #f2f7fa; - - textFg: windowActiveTextFg; - textFgOver: windowActiveTextFg; - - font: normalFont; - padding: margins(11px, 5px, 11px, 5px); -} -attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) { - outlineFgOver: #e43f3f; - - textBgOver: #faf2f2; - - textFg: #d15948; - textFgOver: #d15948; -} - -defaultInputArea: InputArea { - textBg: windowBg; - textFg: windowTextFg; - textMargins: margins(5px, 6px, 5px, 4px); - - placeholderFg: #999999; - placeholderFgActive: #aaaaaa; - placeholderMargins: margins(2px, 0px, 2px, 0px); - placeholderAlign: align(topleft); - placeholderShift: 50px; - duration: 120; - - borderFg: #e0e0e0; - borderFgActive: #62c0f7; - borderFgError: #e48383; - - border: 1px; - borderActive: 2px; - borderError: 2px; - - font: boxTextFont; - - heightMin: 32px; - heightMax: 128px; -} -defaultInputField: InputField { - textBg: windowBg; - textFg: windowTextFg; - textMargins: margins(0px, 6px, 0px, 4px); - textAlign: align(topleft); - - placeholderFg: #999999; - placeholderFgActive: #aaaaaa; - placeholderMargins: margins(2px, 0px, 2px, 0px); - placeholderAlign: align(topleft); - placeholderShift: 50px; - duration: 120; - - borderFg: #e0e0e0; - borderFgActive: #62c0f7; - borderFgError: #e48383; - - border: 1px; - borderActive: 2px; - borderError: 2px; - - font: boxTextFont; - - height: 32px; -} -defaultCheckboxIcon: icon {{ "default_checkbox_check", windowActiveFg, point(4px, 7px) }}; -defaultCheckbox: Checkbox { - textFg: windowTextFg; - textBg: windowBg; - - checkBg: #ffffff; - checkFg: #b3b3b3; - checkFgOver: #b3b3b3; - checkFgActive: windowActiveBg; - - width: -44px; - height: 22px; - - textPosition: point(32px, 2px); - diameter: 22px; - thickness: 2px; - checkIcon: defaultCheckboxIcon; - - font: normalFont; - duration: 120; -} -defaultBoxCheckbox: Checkbox(defaultCheckbox) { - width: -46px; - textPosition: point(34px, 1px); - font: boxTextFont; -} -defaultRadiobutton: Radiobutton { - textFg: windowTextFg; - textBg: windowBg; - - checkBg: #ffffff; - checkFg: #b3b3b3; - checkFgOver: #bfbfbf; - checkFgActive: #4eb3ee; - - width: -46px; - height: 22px; - - textPosition: point(34px, 0px); - diameter: 22px; - thickness: 2px; - checkSkip: 65px; // * 0.1 - - font: boxTextFont; - duration: 120; -} solidScroll: flatScroll { barColor: #3f729734; bgColor: #214f751a; @@ -279,40 +135,6 @@ linkCropLimit: 360px; linkFont: normalFont; linkOverFont: font(fsize underline); -inpDefFont: font(17px); -inpDefFlat: flatInput { - textColor: #000000; - bgColor: #ffffff; - bgActive: #ffffff; - width: 210px; - height: 40px; - align: align(left); - textMrg: margins(5px, 5px, 5px, 5px); - font: inpDefFont; - cursor: cursor(text); - - borderWidth: 0px; - borderColor: transparent; - borderActive: transparent; - borderError: transparent; - - phColor: #949494; - phFocusColor: #aaaaaa; - phAlign: align(left); - phPos: point(2px, 0px); - phShift: 50px; - phDuration: 100; -} - -inpDefGray: flatInput(inpDefFlat) { - bgColor: #f2f2f2; - borderWidth: 2px; - borderColor: #f2f2f2; - borderActive: #54c3f3; - borderError: #ed8080; - phColor: #808080; -} - scrollDef: flatScroll { barColor: #00000053; bgColor: #0000001a; @@ -375,63 +197,6 @@ noContactsHeight: 100px; noContactsFont: font(fsize); noContactsColor: #777777; -topBarHeight: 54px; -topBarDuration: 200; -topBarBackward: icon {{ "title_back", #a3a3a3 }}; -topBarForwardAlpha: 0.6; -topBarBack: icon {{ "title_back", #259fd8 }}; -topBarBackAlpha: 0.8; -topBarBackColor: #005faf; -topBarBackFont: font(16px); -topBarArrowPadding: margins(39px, 8px, 17px, 8px); -topBarMinPadding: 5px; -topBarButton: RoundButton { - textFg: btnYesColor; - textFgOver: btnYesColor; - secondaryTextFg: btnYesColor; - secondaryTextFgOver: btnYesColor; - textBg: windowBg; - textBgOver: #edf4f7; - - width: -22px; - height: 28px; - padding: margins(0px, 14px, 12px, 12px); - - textTop: 6px; - downTextTop: 7px; - - font: font(fsize); -} -defaultActiveButton: RoundButton { - textFg: activeButtonFg; - textFgOver: activeButtonFgOver; - secondaryTextFg: activeButtonSecondaryFg; - secondaryTextFgOver: activeButtonSecondaryFgOver; - textBg: activeButtonBg; - textBgOver: activeButtonBgOver; - - secondarySkip: 7px; - - width: -34px; - height: 34px; - padding: margins(0px, 0px, 0px, 0px); - - textTop: 8px; - downTextTop: 9px; - - font: semiboldFont; -} -defaultLightButton: RoundButton(defaultActiveButton) { - textFg: lightButtonFg; - textFgOver: lightButtonFgOver; - textBg: lightButtonBg; - textBgOver: lightButtonBgOver; -} -topBarClearButton: RoundButton(defaultLightButton) { - width: -18px; -} -topBarActionSkip: 10px; - activeFadeInDuration: 500; activeFadeOutDuration: 3000; @@ -704,13 +469,6 @@ boxPhotoTextFg: #808080; cropPointSize: 10px; cropSkip: 13px; cropMinSize: 20px; -confirmCaptionArea: InputArea(defaultInputArea) { - textMargins: margins(1px, 6px, 1px, 4px); - heightMax: 56px; -} -confirmBg: #f2f2f2; -confirmMaxHeight: 245px; -confirmCompressedSkip: 10px; profileMaxWidth: 410px; profilePadding: margins(28px, 30px, 28px, 0px); @@ -725,20 +483,6 @@ forwardFont: font(16px); forwardBg: #0000004c; forwardFg: #ffffff; -connectionHostInputField: InputField(defaultInputField) { - width: 160px; -} -connectionPortInputField: InputField(defaultInputField) { - width: 55px; -} -connectionUserInputField: InputField(defaultInputField) { - width: 95px; -} -connectionPasswordInputField: InputField(defaultInputField) { - width: 120px; -} -connectionIPv6Skip: 11px; - emojiTextFont: font(15px); emojiReplaceWidth: 52px; emojiReplaceHeight: 56px; @@ -784,12 +528,6 @@ botKbScroll: flatScroll(solidScroll) { deltax: 3px; width: 10px; } -switchPmButton: RoundButton(defaultBoxButton) { - width: 320px; - height: 34px; - textTop: 7px; - downTextTop: 8px; -} minPhotoSize: 100px; maxMediaSize: 420px; @@ -867,23 +605,6 @@ videoIcon: icon { }; locationSize: size(320px, 240px); -boxOptionListPadding: margins(2px, 20px, 2px, 2px); - -langsWidth: 256px; -langsButton: Radiobutton(defaultRadiobutton) { - width: 200px; -} - -backgroundPadding: 10px; -backgroundSize: size(108px, 193px); -backgroundScroll: flatScroll(boxScroll) { - round: 2px; - width: 10px; - deltax: 3px; - deltat: 10px; - deltab: 0px; -} - mentionHeight: 40px; mentionScroll: flatScroll(scrollDef) { topsh: 0px; @@ -934,11 +655,6 @@ inlineRowFileDescriptionTop: 23px; inlineResultsMinWidth: 64px; inlineDurationMargin: 3px; -editTextArea: InputArea(defaultInputArea) { - textMargins: margins(1px, 6px, 1px, 4px); - heightMax: 256px; -} - toastFont: normalFont; toastMaxWidth: 480px; toastMinMargin: 13px; @@ -948,11 +664,6 @@ toastPadding: margins(19px, 13px, 19px, 12px); toastFadeInDuration: 200; toastFadeOutDuration: 1000; -infoButton: PeerAvatarButton { - size: topBarHeight; - photoSize: 42px; -} - // forward declaration for single "title_back" usage. profileTopBarBackIconFg: #0290d7; profileTopBarBackIcon: icon {{ "title_back", profileTopBarBackIconFg }}; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index 82a6f294e..09967422d 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -30,49 +30,6 @@ textStyle { lineHeight: pixels; } -flatInput { - textColor: color; - bgColor: color; - bgActive: color; - width: pixels; - height: pixels; - textMrg: margins; - align: align; - font: font; - cursor: cursor; - - icon: icon; - - borderWidth: pixels; - borderColor: color; - borderActive: color; - borderError: color; - - phColor: color; - phFocusColor: color; - phPos: point; - phAlign: align; - phShift: pixels; - phDuration: int; -} - -flatTextarea { - textColor: color; - bgColor: color; - width: pixels; - textMrg: margins; - align: align; - font: font; - cursor: cursor; - - phColor: color; - phFocusColor: color; - phPos: point; - phAlign: align; - phShift: pixels; - phDuration: int; -} - flatScroll { barColor: color; bgColor: color; @@ -125,143 +82,3 @@ botKeyboardButton { textTop: pixels; downTextTop: pixels; } - -RoundButton { - textFg: color; - textFgOver: color; - textBg: color; // rect of textBg with rounded rect of textBgOver upon it - textBgOver: color; - - secondaryTextFg: color; - secondaryTextFgOver: color; - secondarySkip: pixels; - - width: pixels; - height: pixels; - padding: margins; - - textTop: pixels; - downTextTop: pixels; - - icon: icon; - - font: font; -} - -Checkbox { - textFg: color; - textBg: color; - - checkBg: color; - checkFg: color; - checkFgOver: color; - checkFgActive: color; - - width: pixels; - height: pixels; - - textPosition: point; - diameter: pixels; - thickness: pixels; - checkIcon: icon; - - font: font; - duration: int; -} - -Radiobutton { - textFg: color; - textBg: color; - - checkBg: color; - checkFg: color; - checkFgOver: color; - checkFgActive: color; - - width: pixels; - height: pixels; - - textPosition: point; - diameter: pixels; - thickness: pixels; - checkSkip: pixels; - - font: font; - duration: int; -} - -InputArea { - textBg: color; - textFg: color; - textMargins: margins; - - placeholderFg: color; - placeholderFgActive: color; - placeholderMargins: margins; - placeholderAlign: align; - placeholderShift: pixels; - - duration: int; - - borderFg: color; - borderFgActive: color; - borderFgError: color; - - border: pixels; - borderActive: pixels; - borderError: pixels; - - font: font; - - width: pixels; - heightMin: pixels; - heightMax: pixels; -} - -InputField { - textBg: color; - textFg: color; - textMargins: margins; - textAlign: align; - - placeholderFg: color; - placeholderFgActive: color; - placeholderMargins: margins; - placeholderAlign: align; - placeholderShift: pixels; - - duration: int; - - borderFg: color; - borderFgActive: color; - borderFgError: color; - - border: pixels; - borderActive: pixels; - borderError: pixels; - - font: font; - - width: pixels; - height: pixels; -} - -PeerAvatarButton { - size: pixels; - photoSize: pixels; -} - -OutlineButton { - outlineWidth: pixels; - outlineFg: color; - outlineFgOver: color; - - textBg: color; - textBgOver: color; - - textFg: color; - textFgOver: color; - - font: font; - padding: margins; -} diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 80a2b9a87..14b8d9d20 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -37,6 +37,7 @@ imageBgTransparent: #ffffff; // widgets activeButtonBg: windowActiveBg; activeButtonBgOver: #46b4eb; +activeButtonBgRipple: #177eb2; activeButtonFg: windowActiveFg; activeButtonFgOver: activeButtonFg; activeButtonSecondaryFg: #cceeff; @@ -44,6 +45,7 @@ activeButtonSecondaryFgOver: activeButtonSecondaryFg; lightButtonBg: windowBg; lightButtonBgOver: #edf7ff; +lightButtonBgRipple: #c7e1f6; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; @@ -92,6 +94,11 @@ boxBlockTitleAdditionalFg: #808080; boxBlockTitleCloseFg: cancelIconFg; boxBlockTitleCloseFgOver: cancelIconFgOver; +attentionBoxButtonFg: #ea4b2f; +attentionBoxButtonFgOver: #ea4b2f; +attentionBoxButtonBgOver: #fff0ed; +attentionBoxButtonBgRipple: #efbcb2; + membersAboutLimitFg: windowSubTextFg; contactsBg: windowBg; @@ -188,6 +195,7 @@ historyReplyCancelFgOver: cancelIconFgOver; historyComposeButtonBg: historyComposeAreaBg; historyComposeButtonBgOver: #f5f5f5; +historyComposeButtonBgRipple: #e7e7e7; historyTextInFg: windowTextFg; historyTextOutFg: windowTextFg; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index 533981196..e1f7da4ea 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -35,12 +35,14 @@ imageBg: #000000; imageBgTransparent: #ffffff; activeButtonBg: windowActiveBg; activeButtonBgOver: #46b4eb; +activeButtonBgRipple: #177eb2; activeButtonFg: windowActiveFg; activeButtonFgOver: activeButtonFg; activeButtonSecondaryFg: #cceeff; activeButtonSecondaryFgOver: activeButtonSecondaryFg; lightButtonBg: windowBg; lightButtonBgOver: #edf7ff; +lightButtonBgRipple: #c7e1f6; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; menuBg: windowBg; @@ -77,6 +79,10 @@ boxBlockTitleFg: boxTitleFg; boxBlockTitleAdditionalFg: #808080; boxBlockTitleCloseFg: cancelIconFg; boxBlockTitleCloseFgOver: cancelIconFgOver; +attentionBoxButtonFg: #ea4b2f; +attentionBoxButtonFgOver: #ea4b2f; +attentionBoxButtonBgOver: #fff0ed; +attentionBoxButtonBgRipple: #efbcb2; membersAboutLimitFg: windowSubTextFg; contactsBg: windowBg; contactsBgOver: windowOverBg; @@ -155,6 +161,7 @@ historyReplyCancelFg: cancelIconFg; historyReplyCancelFgOver: cancelIconFgOver; historyComposeButtonBg: historyComposeAreaBg; historyComposeButtonBgOver: #f5f5f5; +historyComposeButtonBgRipple: #e7e7e7; historyTextInFg: windowTextFg; historyTextOutFg: windowTextFg; historyCaptionInFg: historyTextInFg; diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 4b0dbe013..f0e42625a 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,6 - PRODUCTVERSION 0,10,19,6 + FILEVERSION 0,10,19,7 + PRODUCTVERSION 0,10,19,7 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.19.6" + VALUE "FileVersion", "0.10.19.7" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.6" + VALUE "ProductVersion", "0.10.19.7" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 99ce4ed04..16592aafb 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,6 - PRODUCTVERSION 0,10,19,6 + FILEVERSION 0,10,19,7 + PRODUCTVERSION 0,10,19,7 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.19.6" + VALUE "FileVersion", "0.10.19.7" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.6" + VALUE "ProductVersion", "0.10.19.7" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 08b1669d1..05a25793a 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -1037,15 +1037,15 @@ void AppClass::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId) { PreparedPhotoThumbs photoThumbs; QVector photoSizes; - QPixmap thumb = App::pixmapFromImageInPlace(tosend.scaled(160, 160, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + auto thumb = App::pixmapFromImageInPlace(tosend.scaled(160, 160, Qt::KeepAspectRatio, Qt::SmoothTransformation)); photoThumbs.insert('a', thumb); photoSizes.push_back(MTP_photoSize(MTP_string("a"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); - QPixmap medium = App::pixmapFromImageInPlace(tosend.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + auto medium = App::pixmapFromImageInPlace(tosend.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation)); photoThumbs.insert('b', medium); photoSizes.push_back(MTP_photoSize(MTP_string("b"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); - QPixmap full = QPixmap::fromImage(tosend, Qt::ColorOnly); + auto full = QPixmap::fromImage(tosend, Qt::ColorOnly); photoThumbs.insert('c', full); photoSizes.push_back(MTP_photoSize(MTP_string("c"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0))); diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 4e63886b7..1d9dcc0ea 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -31,6 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index 6ce931baa..70f77c745 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -28,6 +28,10 @@ class FlatLabel; class ConfirmBox; namespace Ui { +class InputField; +class PhoneInput; +class InputArea; +class UsernameInput; class Checkbox; class Radiobutton; class LinkButton; @@ -64,9 +68,9 @@ private: UserData *_user = nullptr; QString _boxTitle; - ChildWidget _first; - ChildWidget _last; - ChildWidget _phone; + ChildWidget _first; + ChildWidget _last; + ChildWidget _phone; ChildWidget _save; ChildWidget _cancel; @@ -119,8 +123,8 @@ private: Animation _a_photoOver; bool _photoOver; - ChildWidget _title; - ChildWidget _description; + ChildWidget _title; + ChildWidget _description; QImage _photoBig; QPixmap _photoSmall; @@ -187,7 +191,7 @@ private: int32 _aboutPublicWidth, _aboutPublicHeight; Text _aboutPublic, _aboutPrivate; - ChildWidget _link; + ChildWidget _link; QRect _invitationLink; bool _linkOver; @@ -236,8 +240,8 @@ private: PeerData *_peer; QString _boxTitle; - ChildWidget _first; - ChildWidget _last; + ChildWidget _first; + ChildWidget _last; ChildWidget _save; ChildWidget _cancel; @@ -283,8 +287,8 @@ private: ChannelData *_channel; - ChildWidget _title; - ChildWidget _description; + ChildWidget _title; + ChildWidget _description; ChildWidget _sign; ChildWidget _publicLink; diff --git a/Telegram/SourceFiles/boxes/autolockbox.cpp b/Telegram/SourceFiles/boxes/autolockbox.cpp index f2b025c07..feaf0e0fe 100644 --- a/Telegram/SourceFiles/boxes/autolockbox.cpp +++ b/Telegram/SourceFiles/boxes/autolockbox.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" +#include "styles/style_boxes.h" AutoLockBox::AutoLockBox() : _close(this, lang(lng_box_ok), st::defaultBoxButton) { diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index 30a0851b2..6a20c8ecb 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "window/window_theme.h" #include "styles/style_overview.h" +#include "styles/style_boxes.h" BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll) , _inner(this) { diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 3db3c34c3..c8af5320a 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -23,6 +23,48 @@ using "basic.style"; using "ui/widgets/widgets.style"; using "intro/intro.style"; +defaultBoxButton: RoundButton { + textFg: #2f9fea; + textFgOver: #2f9fea; + secondaryTextFg: #2f9fea; + secondaryTextFgOver: #2f9fea; + textBg: boxBg; + textBgOver: lightButtonBgOver; + + width: -24px; + height: 36px; + padding: margins(0px, 0px, 0px, 0px); + + textTop: 8px; + downTextTop: 8px; + + font: boxButtonFont; + + ripple: RippleAnimation(defaultRippleAnimation) { + color: lightButtonBgRipple; + } +} + +cancelBoxButton: RoundButton(defaultBoxButton) { + textFg: #aeaeae; +} + +attentionBoxButton: RoundButton(defaultBoxButton) { + textFg: attentionBoxButtonFg; + textFgOver: attentionBoxButtonFgOver; + textBgOver: attentionBoxButtonBgOver; + + ripple: RippleAnimation(defaultRippleAnimation) { + color: attentionBoxButtonBgRipple; + } +} + +defaultBoxCheckbox: Checkbox(defaultCheckbox) { + width: -46px; + textPosition: point(34px, 1px); + font: boxTextFont; +} + boxBlockTitleHeight: 48px; boxBlockTitlePosition: point(18px, 14px); boxBlockTitleFont: font(boxFontSize semibold); @@ -46,6 +88,8 @@ boxLinkButton: LinkButton { overFont: font(boxFontSize underline); } +boxOptionListPadding: margins(2px, 20px, 2px, 2px); + confirmInviteTitle: flatLabel(labelDefFlat) { font: font(16px semibold); align: align(center); @@ -281,7 +325,7 @@ sessionTerminateAllButton: LinkButton(boxLinkButton) { passcodeHeaderFont: font(19px); passcodeHeaderHeight: 80px; -passcodeInput: flatInput(inpIntroPhone) { +passcodeInput: FlatInput(introPhone) { } passcodeSubmit: RoundButton(introNextButton) { width: 225px; @@ -340,3 +384,45 @@ aboutLabel: flatLabel(labelDefFlat) { aboutTextStyle: textStyle(defaultTextStyle) { lineHeight: 22px; } + +editTextArea: InputArea(defaultInputArea) { + textMargins: margins(1px, 6px, 1px, 4px); + heightMax: 256px; +} + +confirmCaptionArea: InputArea(defaultInputArea) { + textMargins: margins(1px, 6px, 1px, 4px); + heightMax: 56px; +} +confirmBg: #f2f2f2; +confirmMaxHeight: 245px; +confirmCompressedSkip: 10px; + +connectionHostInputField: InputField(defaultInputField) { + width: 160px; +} +connectionPortInputField: InputField(defaultInputField) { + width: 55px; +} +connectionUserInputField: InputField(defaultInputField) { + width: 95px; +} +connectionPasswordInputField: InputField(defaultInputField) { + width: 120px; +} +connectionIPv6Skip: 11px; + +langsWidth: 256px; +langsButton: Radiobutton(defaultRadiobutton) { + width: 200px; +} + +backgroundPadding: 10px; +backgroundSize: size(108px, 193px); +backgroundScroll: flatScroll(boxScroll) { + round: 2px; + width: 10px; + deltax: 3px; + deltat: 10px; + deltab: 0px; +} diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index e713528af..ab5234175 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -29,6 +29,11 @@ class Checkbox; class RoundButton; } // namespace Ui +namespace st { +extern const style::RoundButton &defaultBoxButton; +extern const style::RoundButton &cancelBoxButton; +} // namespace style + class InformBox; class ConfirmBox : public AbstractBox, public ClickHandlerHost { Q_OBJECT diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.cpp b/Telegram/SourceFiles/boxes/confirmphonebox.cpp index 36d0871a0..1f0b6a16e 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.cpp +++ b/Telegram/SourceFiles/boxes/confirmphonebox.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_boxes.h" #include "boxes/confirmbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "mainwidget.h" #include "lang.h" @@ -106,7 +107,7 @@ void ConfirmPhoneBox::launch() { } _about->setMarkedText(aboutText); - _code = new InputField(this, st::confirmPhoneCodeField, lang(lng_code_ph)); + _code.create(this, st::confirmPhoneCodeField, lang(lng_code_ph)); _send.create(this, lang(lng_confirm_phone_send), st::defaultBoxButton); _cancel.create(this, lang(lng_cancel), st::cancelBoxButton); @@ -300,6 +301,10 @@ void ConfirmPhoneBox::resizeEvent(QResizeEvent *e) { AbstractBox::resizeEvent(e); } +void ConfirmPhoneBox::doSetInnerFocus() { + _code->setFocus(); +} + ConfirmPhoneBox::~ConfirmPhoneBox() { if (_sendCodeRequestId) { MTP::cancel(_sendCodeRequestId); diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.h b/Telegram/SourceFiles/boxes/confirmphonebox.h index 6c3788262..a48baf6f6 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.h +++ b/Telegram/SourceFiles/boxes/confirmphonebox.h @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class FlatLabel; namespace Ui { +class InputField; class RoundButton; } // namespace Ui @@ -47,9 +48,7 @@ protected: void showAll() override { showChildren(); } - void doSetInnerFocus() override { - _code->setFocus(); - } + void doSetInnerFocus() override; private: ConfirmPhoneBox(QWidget *parent, const QString &phone, const QString &hash); @@ -96,7 +95,7 @@ private: ChildWidget _about = { nullptr }; ChildWidget _send = { nullptr }; ChildWidget _cancel = { nullptr }; - ChildWidget _code = { nullptr }; + ChildWidget _code = { nullptr }; // Flag for not calling onTextChanged() recursively. bool _fixing = false; diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index cd481d302..5e22dc7b5 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -27,7 +27,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "history/history_location_manager.h" +#include "styles/style_boxes.h" ConnectionBox::ConnectionBox() : AbstractBox(st::boxWidth) , _hostInput(this, st::connectionHostInputField, lang(lng_connection_host_ph), Global::ConnectionProxy().host) diff --git a/Telegram/SourceFiles/boxes/connectionbox.h b/Telegram/SourceFiles/boxes/connectionbox.h index 9e0e3de88..65ae47359 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.h +++ b/Telegram/SourceFiles/boxes/connectionbox.h @@ -23,6 +23,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" namespace Ui { +class InputField; +class PortInput; +class PasswordInput; class Checkbox; class Radiobutton; class RoundButton; @@ -47,10 +50,10 @@ protected: void doSetInnerFocus() override; private: - ChildWidget _hostInput; - ChildWidget _portInput; - ChildWidget _userInput; - ChildWidget _passwordInput; + ChildWidget _hostInput; + ChildWidget _portInput; + ChildWidget _userInput; + ChildWidget _passwordInput; ChildWidget _autoRadio; ChildWidget _httpProxyRadio; ChildWidget _tcpProxyRadio; diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp index 3ac63ad6c..e07104148 100644 --- a/Telegram/SourceFiles/boxes/languagebox.cpp +++ b/Telegram/SourceFiles/boxes/languagebox.cpp @@ -29,6 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "mainwindow.h" #include "langloaderplain.h" +#include "styles/style_boxes.h" LanguageBox::LanguageBox() : _close(this, lang(lng_box_ok), st::defaultBoxButton) { diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp index 92a8a2ebf..54e305519 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.cpp +++ b/Telegram/SourceFiles/boxes/passcodebox.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "styles/style_boxes.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" PasscodeBox::PasscodeBox(bool turningOff) : AbstractBox(st::boxWidth) , _replacedBy(0) diff --git a/Telegram/SourceFiles/boxes/passcodebox.h b/Telegram/SourceFiles/boxes/passcodebox.h index 53ce16438..9c15dbb94 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.h +++ b/Telegram/SourceFiles/boxes/passcodebox.h @@ -23,6 +23,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" namespace Ui { +class InputField; +class PasswordInput; class LinkButton; class RoundButton; } // namespace Ui @@ -81,11 +83,11 @@ private: ChildWidget _saveButton; ChildWidget _cancelButton; - ChildWidget _oldPasscode; - ChildWidget _newPasscode; - ChildWidget _reenterPasscode; - ChildWidget _passwordHint; - ChildWidget _recoverEmail; + ChildWidget _oldPasscode; + ChildWidget _newPasscode; + ChildWidget _reenterPasscode; + ChildWidget _passwordHint; + ChildWidget _recoverEmail; ChildWidget _recover; QString _oldError, _newError, _emailError; @@ -123,7 +125,7 @@ private: ChildWidget _saveButton; ChildWidget _cancelButton; - ChildWidget _recoverCode; + ChildWidget _recoverCode; QString _error; diff --git a/Telegram/SourceFiles/boxes/photocropbox.cpp b/Telegram/SourceFiles/boxes/photocropbox.cpp index 755c36dc0..60e3d031d 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.cpp +++ b/Telegram/SourceFiles/boxes/photocropbox.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "photocropbox.h" #include "fileuploader.h" #include "ui/widgets/buttons.h" +#include "styles/style_boxes.h" PhotoCropBox::PhotoCropBox(const QImage &img, const PeerId &peer) : AbstractBox() , _downState(0) diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 3ebb1446a..24acd18f0 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -28,7 +28,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "history/history_media_types.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "styles/style_history.h" +#include "styles/style_boxes.h" PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxWideWidth) , _file(file) @@ -141,7 +143,7 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW updateBoxSize(); _caption->setMaxLength(MaxPhotoCaption); - _caption->setCtrlEnterSubmit(CtrlEnterSubmitBoth); + _caption->setCtrlEnterSubmit(Ui::CtrlEnterSubmitBoth); connect(_compressed, SIGNAL(changed()), this, SLOT(onCompressedChange())); connect(_caption, SIGNAL(resized()), this, SLOT(onCaptionResized())); connect(_caption, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); @@ -493,13 +495,13 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) if (_animated || _photo || _doc) { _field.create(this, st::confirmCaptionArea, lang(lng_photo_caption), caption); _field->setMaxLength(MaxPhotoCaption); - _field->setCtrlEnterSubmit(CtrlEnterSubmitBoth); + _field->setCtrlEnterSubmit(Ui::CtrlEnterSubmitBoth); } else { auto original = msg->originalText(); QString text = textApplyEntities(original.text, original.entities); _field.create(this, st::editTextArea, lang(lng_photo_caption), text); // _field->setMaxLength(MaxMessageSize); // entities can make text in input field larger but still valid - _field->setCtrlEnterSubmit(cCtrlEnter() ? CtrlEnterSubmitCtrlEnter : CtrlEnterSubmitEnter); + _field->setCtrlEnterSubmit(cCtrlEnter() ? Ui::CtrlEnterSubmitCtrlEnter : Ui::CtrlEnterSubmitEnter); } updateBoxSize(); connect(_field, SIGNAL(submitted(bool)), this, SLOT(onSave(bool))); diff --git a/Telegram/SourceFiles/boxes/photosendbox.h b/Telegram/SourceFiles/boxes/photosendbox.h index 04b424e4a..64dd01048 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.h +++ b/Telegram/SourceFiles/boxes/photosendbox.h @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Checkbox; class RoundButton; +class InputArea; } // namespace Ui class PhotoSendBox : public AbstractBox { @@ -57,7 +58,7 @@ private: QPixmap _thumb; - ChildWidget _caption; + ChildWidget _caption; bool _compressedFromSettings; ChildWidget _compressed; @@ -108,7 +109,7 @@ private: QPixmap _thumb; - ChildWidget _field = { nullptr }; + ChildWidget _field = { nullptr }; ChildWidget _save; ChildWidget _cancel; diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index 4b56c3f16..7f4e34cc5 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "mainwindow.h" ReportBox::ReportBox(ChannelData *channel) : AbstractBox(st::boxWidth) @@ -75,9 +76,9 @@ void ReportBox::resizeEvent(QResizeEvent *e) { void ReportBox::onChange() { if (_reasonOther->checked()) { if (!_reasonOtherText) { - _reasonOtherText = new InputArea(this, st::profileReportReasonOther, lang(lng_report_reason_description)); + _reasonOtherText.create(this, st::profileReportReasonOther, lang(lng_report_reason_description)); _reasonOtherText->show(); - _reasonOtherText->setCtrlEnterSubmit(CtrlEnterSubmitBoth); + _reasonOtherText->setCtrlEnterSubmit(Ui::CtrlEnterSubmitBoth); _reasonOtherText->setMaxLength(MaxPhotoCaption); _reasonOtherText->resize(width() - (st::boxPadding.left() + st::boxOptionListPadding.left() + st::boxPadding.right()), _reasonOtherText->height()); diff --git a/Telegram/SourceFiles/boxes/report_box.h b/Telegram/SourceFiles/boxes/report_box.h index 8e631eb38..b02bee021 100644 --- a/Telegram/SourceFiles/boxes/report_box.h +++ b/Telegram/SourceFiles/boxes/report_box.h @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class Radiobutton; class RoundButton; +class InputArea; } // namespace Ui class ReportBox : public AbstractBox, public RPCSender { @@ -59,7 +60,7 @@ private: ChildWidget _reasonViolence; ChildWidget _reasonPornography; ChildWidget _reasonOther; - ChildWidget _reasonOtherText = { nullptr }; + ChildWidget _reasonOtherText = { nullptr }; ChildWidget _report, _cancel; diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp index d4c1a964c..b0772ea66 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.cpp +++ b/Telegram/SourceFiles/boxes/usernamebox.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "mainwindow.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "styles/style_boxes.h" UsernameBox::UsernameBox() : AbstractBox(st::boxWidth), diff --git a/Telegram/SourceFiles/boxes/usernamebox.h b/Telegram/SourceFiles/boxes/usernamebox.h index 05beb5067..d26a62fc2 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.h +++ b/Telegram/SourceFiles/boxes/usernamebox.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" namespace Ui { +class UsernameInput; class RoundButton; class LinkButton; } // namespace Ui @@ -60,7 +61,7 @@ private: ChildWidget _save; ChildWidget _cancel; - ChildWidget _username; + ChildWidget _username; ChildWidget _link; mtpRequestId _saveRequestId, _checkRequestId; diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 379022939..ed05284d9 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -651,10 +651,19 @@ bool Generator::writeIncludesInSource() { return true; } - bool result = module_.enumIncludes([this](const Module &module) -> bool { - source_->include(moduleBaseName(module) + ".h"); + auto includes = QStringList(); + std::function collector = [this, &collector, &includes](const Module &module) { + module.enumIncludes(collector); + auto base = moduleBaseName(module); + if (!includes.contains(base)) { + includes.push_back(base); + } return true; - }); + }; + auto result = module_.enumIncludes(collector); + for (auto base : includes) { + source_->include(base + ".h"); + } source_->newline(); return result; } diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index e9ddd6088..fb6796711 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019006ULL) +#define BETA_VERSION_MACRO (10019007ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/data/data_drafts.cpp b/Telegram/SourceFiles/data/data_drafts.cpp index 6179e45b2..9cd3c617d 100644 --- a/Telegram/SourceFiles/data/data_drafts.cpp +++ b/Telegram/SourceFiles/data/data_drafts.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "data/data_drafts.h" +#include "ui/widgets/input_fields.h" #include "historywidget.h" #include "mainwidget.h" #include "localstorage.h" @@ -30,6 +31,13 @@ namespace { } // namespace +Draft::Draft(const Ui::FlatTextarea *field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId) + : textWithTags(field->getTextWithTags()) + , msgId(msgId) + , cursor(field) + , previewCancelled(previewCancelled) { +} + void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) { auto history = App::history(peerId); auto text = qs(draft.vmessage); diff --git a/Telegram/SourceFiles/data/data_drafts.h b/Telegram/SourceFiles/data/data_drafts.h index 858207248..d5b369bc7 100644 --- a/Telegram/SourceFiles/data/data_drafts.h +++ b/Telegram/SourceFiles/data/data_drafts.h @@ -20,6 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +namespace Ui { +class FlatTextarea; +} // namespace Ui + namespace Data { void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft); @@ -35,12 +39,8 @@ struct Draft { , previewCancelled(previewCancelled) , saveRequestId(saveRequestId) { } - Draft(const FlatTextarea *field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId = 0) - : textWithTags(field->getTextWithTags()) - , msgId(msgId) - , cursor(field) - , previewCancelled(previewCancelled) { - } + Draft(const Ui::FlatTextarea *field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId = 0); + QDateTime date; TextWithTags textWithTags; MsgId msgId = 0; // replyToId for message draft, editMsgId for edit draft diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 4a68d2998..d4399ceed 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -84,6 +84,10 @@ dialogsMenuToggle: IconButton { iconOver: icon {{ "dialogs_menu", dialogsMenuIconFgOver }}; iconPosition: point(6px, 6px); iconPositionDown: point(6px, 6px); + + rippleAreaPosition: point(0px, 0px); + rippleAreaSize: 32px; + ripple: defaultRippleAnimation; } dialogsLock: IconButton(dialogsMenuToggle) { icon: icon {{ "dialogs_lock", dialogsMenuIconFg }}; @@ -92,7 +96,7 @@ dialogsLock: IconButton(dialogsMenuToggle) { dialogsUnlockIcon: icon {{ "dialogs_unlock", dialogsMenuIconFg }}; dialogsUnlockIconOver: icon {{ "dialogs_unlock", dialogsMenuIconFgOver }}; -dialogsFilter: flatInput(inpDefGray) { +dialogsFilter: FlatInput(defaultFlatInput) { font: font(fsize); bgColor: #f2f2f2; phColor: #949494; @@ -102,9 +106,15 @@ dialogsFilter: flatInput(inpDefGray) { height: 32px; textMrg: margins(12px, 3px, 30px, 3px); } -dialogsCancelSearch: IconButton(dialogsMenuToggle) { - icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg, point(0px, 1px) }}; - iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver, point(0px, 1px) }}; +dialogsCancelSearchInPeer: IconButton(dialogsMenuToggle) { + icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg }}; + iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver }}; + + iconPosition: point(7px, 7px); + iconPositionDown: point(7px, 7px); +} +dialogsCancelSearch: IconButton(dialogsCancelSearchInPeer) { + rippleAreaSize: 0px; } dialogsMenu: Menu(defaultMenu) { @@ -183,6 +193,10 @@ dialogsUpdateButton: FlatButton { font: semiboldFont; overFont: semiboldFont; + + ripple: RippleAnimation(defaultRippleAnimation) { + color: activeButtonBgRipple; + } } dialogsForwardHeight: 32px; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 32fd61b7c..182930f38 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -40,6 +40,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "apiwrap.h" #include "ui/widgets/dropdown_menu.h" +#include "ui/widgets/input_fields.h" #include "autoupdater.h" DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(parent) @@ -47,7 +48,7 @@ DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(pare , contactsNoDialogs(std_::make_unique(Dialogs::SortMode::Name)) , contacts(std_::make_unique(Dialogs::SortMode::Name)) , _addContactLnk(this, lang(lng_add_contact_button)) -, _cancelSearchInPeer(this, st::dialogsCancelSearch) { +, _cancelSearchInPeer(this, st::dialogsCancelSearchInPeer) { if (Global::DialogsModeEnabled()) { importantDialogs = std_::make_unique(Dialogs::SortMode::Date); } diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index a708116ca..897416fc1 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -34,6 +34,7 @@ class PopupMenu; class DropdownMenu; class FlatButton; class LinkButton; +class FlatInput; } // namespace Ui enum DialogsSearchRequestType { @@ -332,7 +333,7 @@ private: ChildWidget _forwardCancel = { nullptr }; ChildWidget _mainMenuToggle; ChildWidget _mainMenu = { nullptr }; - ChildWidget _filter; + ChildWidget _filter; ChildWidget _cancelSearch; ChildWidget _lockUnlock; ChildWidget _scroll; diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 6dd45ecd8..5cc99a617 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -152,7 +152,30 @@ struct SendAction { int32 progress; }; -using TextWithTags = FlatTextarea::TextWithTags; +struct TextWithTags { + struct Tag { + int offset, length; + QString id; + }; + using Tags = QVector; + + QString text; + Tags tags; +}; + +inline bool operator==(const TextWithTags::Tag &a, const TextWithTags::Tag &b) { + return (a.offset == b.offset) && (a.length == b.length) && (a.id == b.id); +} +inline bool operator!=(const TextWithTags::Tag &a, const TextWithTags::Tag &b) { + return !(a == b); +} + +inline bool operator==(const TextWithTags &a, const TextWithTags &b) { + return (a.text == b.text) && (a.tags == b.tags); +} +inline bool operator!=(const TextWithTags &a, const TextWithTags &b) { + return !(a == b); +} namespace Data { struct Draft; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 70b84ef5b..32eb6c452 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -136,7 +136,7 @@ historyPeer8UserpicBg: #f7b37c; historyPeer8UserpicFg: #de8d62; historyPeer8UserpicPerson: icon {{ size(120px, 120px), historyPeer8UserpicBg }, { "userpic_person", historyPeer8UserpicFg }}; -historyComposeField: flatTextarea { +historyComposeField: FlatTextarea { textColor: #000000; bgColor: historyComposeAreaBg; align: align(left); @@ -170,12 +170,16 @@ historyComposeButton: FlatButton { width: -32px; height: 46px; - textTop: 12px; - overTextTop: 12px; - downTextTop: 13px; + textTop: 14px; + overTextTop: 14px; + downTextTop: 14px; font: semiboldFont; overFont: semiboldFont; + + ripple: RippleAnimation(defaultRippleAnimation) { + color: historyComposeButtonBgRipple; + } } historyUnblock: FlatButton(historyComposeButton) { color: #d15948; @@ -290,23 +294,6 @@ historyInlineBotCancel: IconButton(historyReplyCancel) { height: 46px; } -topBarSearch: IconButton { - width: 44px; - height: topBarHeight; - - icon: icon {{ "title_search", menuIconFg }}; - iconOver: icon {{ "title_search", menuIconFgOver }}; - - iconPosition: point(13px, 18px); - iconPositionDown: point(13px, 18px); -} -topBarMenuToggle: IconButton(topBarSearch) { - icon: icon {{ "title_menu_dots", menuIconFg }}; - iconOver: icon {{ "title_menu_dots", menuIconFgOver }}; - - iconPosition: point(18px, 17px); - iconPositionDown: point(18px, 17px); -} reportSpamHide: FlatButton { duration: 200; cursor: cursor(pointer); @@ -327,17 +314,6 @@ reportSpamHide: FlatButton { font: font(fsize); overFont: font(fsize underline); } -reportSpamButton: FlatButton(reportSpamHide) { - textTop: 6px; - overTextTop: 6px; - downTextTop: 7px; - - width: -50px; - height: 30px; - - bgColor: #888888; - overBgColor: #7b7b7b; -} reportSpamSeparator: 30px; reportSpamBg: #fffffff0; reportSpamFg: #000000; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index e250eb2bb..ba8a3b016 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -23,6 +23,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_history.h" #include "styles/style_dialogs.h" +#include "styles/style_window.h" +#include "styles/style_boxes.h" #include "boxes/confirmbox.h" #include "boxes/photosendbox.h" #include "boxes/sharebox.h" @@ -77,13 +79,13 @@ QMimeData *mimeDataFromTextWithEntities(const TextWithEntities &forClipboard) { for (auto &tag : tags) { tag.id = mimeTagFromTag(tag.id); } - result->setData(FlatTextarea::tagsMimeType(), FlatTextarea::serializeTagsList(tags)); + result->setData(Ui::FlatTextarea::tagsMimeType(), Ui::FlatTextarea::serializeTagsList(tags)); } return result; } // For mention tags save and validate userId, ignore tags for different userId. -class FieldTagMimeProcessor : public FlatTextarea::TagMimeProcessor { +class FieldTagMimeProcessor : public Ui::FlatTextarea::TagMimeProcessor { public: QString mimeTagFromTag(const QString &tagId) override { return ::mimeTagFromTag(tagId); @@ -2302,7 +2304,7 @@ void HistoryInner::onParentGeometryChanged() { } } -MessageField::MessageField(HistoryWidget *history, const style::flatTextarea &st, const QString &ph, const QString &val) : FlatTextarea(history, st, ph, val), history(history) { +MessageField::MessageField(HistoryWidget *history, const style::FlatTextarea &st, const QString &ph, const QString &val) : Ui::FlatTextarea(history, st, ph, val), history(history) { setMinHeight(st::historySend.height - 2 * st::historySendPadding); setMaxHeight(st::historyComposeFieldMaxHeight); } @@ -2976,7 +2978,7 @@ QPoint SilentToggle::tooltipPos() const { return QCursor::pos(); } -EntitiesInText entitiesFromTextTags(const FlatTextarea::TagList &tags) { +EntitiesInText entitiesFromTextTags(const TextWithTags::Tags &tags) { EntitiesInText result; if (tags.isEmpty()) { return result; @@ -3229,14 +3231,14 @@ void HistoryWidget::updateInlineBotQuery() { MTP::cancel(_inlineBotResolveRequestId); _inlineBotResolveRequestId = 0; } - if (bot == LookingUpInlineBot) { - _inlineBot = LookingUpInlineBot; + if (bot == Ui::LookingUpInlineBot) { + _inlineBot = Ui::LookingUpInlineBot; // Notify::inlineBotRequesting(true); _inlineBotResolveRequestId = MTP::send(MTPcontacts_ResolveUsername(MTP_string(_inlineBotUsername)), rpcDone(&HistoryWidget::inlineBotResolveDone), rpcFail(&HistoryWidget::inlineBotResolveFail, _inlineBotUsername)); return; } - } else if (bot == LookingUpInlineBot) { - if (_inlineBot == LookingUpInlineBot) { + } else if (bot == Ui::LookingUpInlineBot) { + if (_inlineBot == Ui::LookingUpInlineBot) { return; } bot = _inlineBot; @@ -4389,11 +4391,11 @@ void HistoryWidget::updateAfterDrag() { } void HistoryWidget::updateFieldSubmitSettings() { - FlatTextarea::SubmitSettings settings = FlatTextarea::SubmitSettings::Enter; + auto settings = Ui::FlatTextarea::SubmitSettings::Enter; if (_inlineBotCancel) { - settings = FlatTextarea::SubmitSettings::None; + settings = Ui::FlatTextarea::SubmitSettings::None; } else if (cCtrlEnter()) { - settings = FlatTextarea::SubmitSettings::CtrlEnter; + settings = Ui::FlatTextarea::SubmitSettings::CtrlEnter; } _field->setSubmitSettings(settings); } @@ -5156,9 +5158,9 @@ void HistoryWidget::preloadHistoryIfNeeded() { void HistoryWidget::onInlineBotCancel() { auto &textWithTags = _field->getTextWithTags(); if (textWithTags.text.size() > _inlineBotUsername.size() + 2) { - setFieldText({ '@' + _inlineBotUsername + ' ', TextWithTags::Tags() }, TextUpdateEvent::SaveDraft, FlatTextarea::AddToUndoHistory); + setFieldText({ '@' + _inlineBotUsername + ' ', TextWithTags::Tags() }, TextUpdateEvent::SaveDraft, Ui::FlatTextarea::AddToUndoHistory); } else { - clearFieldText(TextUpdateEvent::SaveDraft, FlatTextarea::AddToUndoHistory); + clearFieldText(TextUpdateEvent::SaveDraft, Ui::FlatTextarea::AddToUndoHistory); } } @@ -5883,7 +5885,7 @@ bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) { auto &textWithTags = _field->getTextWithTags(); if (specialGif) { if (textWithTags.text.trimmed() == '@' + cInlineGifBotUsername() && textWithTags.text.at(0) == '@') { - clearFieldText(TextUpdateEvent::SaveDraft, FlatTextarea::AddToUndoHistory); + clearFieldText(TextUpdateEvent::SaveDraft, Ui::FlatTextarea::AddToUndoHistory); } } else { TextWithTags textWithTagsToSet; @@ -5905,7 +5907,7 @@ bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) { } } else { if (!specialGif || _field->isEmpty()) { - setFieldText({ toInsert, TextWithTags::Tags() }, TextUpdateEvent::SaveDraft, FlatTextarea::AddToUndoHistory); + setFieldText({ toInsert, TextWithTags::Tags() }, TextUpdateEvent::SaveDraft, Ui::FlatTextarea::AddToUndoHistory); _field->setFocus(); return true; } @@ -6027,7 +6029,7 @@ void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result) QString inlineBotUsername; auto query = _field->getInlineBotQuery(&bot, &inlineBotUsername); if (inlineBotUsername == _inlineBotUsername) { - if (bot == LookingUpInlineBot) { + if (bot == Ui::LookingUpInlineBot) { bot = resolvedBot; } } else { @@ -6223,7 +6225,7 @@ void HistoryWidget::onKbToggle(bool manual) { } void HistoryWidget::onCmdStart() { - setFieldText({ qsl("/"), TextWithTags::Tags() }, 0, FlatTextarea::AddToUndoHistory); + setFieldText({ qsl("/"), TextWithTags::Tags() }, 0, Ui::FlatTextarea::AddToUndoHistory); } void HistoryWidget::contextMenuEvent(QContextMenuEvent *e) { @@ -6502,7 +6504,7 @@ void HistoryWidget::clearInlineBot() { } void HistoryWidget::inlineBotChanged() { - bool isInlineBot = _inlineBot && (_inlineBot != LookingUpInlineBot); + bool isInlineBot = _inlineBot && (_inlineBot != Ui::LookingUpInlineBot); if (isInlineBot && !_inlineBotCancel) { _inlineBotCancel = std_::make_unique(this, st::historyInlineBotCancel); connect(_inlineBotCancel.get(), SIGNAL(clicked()), this, SLOT(onInlineBotCancel())); @@ -6532,7 +6534,7 @@ void HistoryWidget::onCheckFieldAutocomplete() { if (!_history || _a_show.animating()) return; bool start = false; - bool isInlineBot = _inlineBot && (_inlineBot != LookingUpInlineBot); + bool isInlineBot = _inlineBot && (_inlineBot != Ui::LookingUpInlineBot); QString query = isInlineBot ? QString() : _field->getMentionHashtagBotCommandPart(start); if (!query.isEmpty()) { if (query.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtagsAndBots(); @@ -6547,7 +6549,7 @@ void HistoryWidget::updateFieldPlaceholder() { _field->setPlaceholder(lang(lng_edit_message_text)); _send->setIcon(&st::historyEditSaveIcon, &st::historyEditSaveIconOver); } else { - if (_inlineBot && _inlineBot != LookingUpInlineBot) { + if (_inlineBot && _inlineBot != Ui::LookingUpInlineBot) { _field->setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); } else { _field->setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_silent->checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_message_ph)); @@ -7770,7 +7772,7 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption) _field->setFocus(); } -void HistoryWidget::setFieldText(const TextWithTags &textWithTags, TextUpdateEvents events, FlatTextarea::UndoHistoryAction undoHistoryAction) { +void HistoryWidget::setFieldText(const TextWithTags &textWithTags, TextUpdateEvents events, Ui::FlatTextarea::UndoHistoryAction undoHistoryAction) { _textUpdateEvents = events; _field->setTextWithTags(textWithTags, undoHistoryAction); _field->moveCursor(QTextCursor::End); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 0224b08ed..8d689301b 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localimageloader.h" #include "ui/effects/rect_shadow.h" #include "ui/widgets/tooltip.h" +#include "ui/widgets/input_fields.h" #include "history/history_common.h" #include "history/field_autocomplete.h" #include "window/section_widget.h" @@ -310,11 +311,11 @@ private: }; -class MessageField : public FlatTextarea { +class MessageField : public Ui::FlatTextarea { Q_OBJECT public: - MessageField(HistoryWidget *history, const style::flatTextarea &st, const QString &ph = QString(), const QString &val = QString()); + MessageField(HistoryWidget *history, const style::FlatTextarea &st, const QString &ph = QString(), const QString &val = QString()); void dropEvent(QDropEvent *e); bool canInsertFromMimeData(const QMimeData *source) const; void insertFromMimeData(const QMimeData *source); @@ -1014,8 +1015,8 @@ private: void writeDrafts(Data::Draft **localDraft, Data::Draft **editDraft); void writeDrafts(History *history); - void setFieldText(const TextWithTags &textWithTags, TextUpdateEvents events = 0, FlatTextarea::UndoHistoryAction undoHistoryAction = FlatTextarea::ClearUndoHistory); - void clearFieldText(TextUpdateEvents events = 0, FlatTextarea::UndoHistoryAction undoHistoryAction = FlatTextarea::ClearUndoHistory) { + void setFieldText(const TextWithTags &textWithTags, TextUpdateEvents events = 0, Ui::FlatTextarea::UndoHistoryAction undoHistoryAction = Ui::FlatTextarea::ClearUndoHistory); + void clearFieldText(TextUpdateEvents events = 0, Ui::FlatTextarea::UndoHistoryAction undoHistoryAction = Ui::FlatTextarea::ClearUndoHistory) { setFieldText(TextWithTags(), events, undoHistoryAction); } diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 70881640b..a39394f9c 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -39,7 +39,7 @@ introCountry: countryInput { bgColor: #f2f2f2; ptrSize: size(15px, 8px); textMrg: margins(16px, 5px, 16px, 15px); - font: inpDefFont; + font: defaultInputFont; align: align(left); } @@ -89,17 +89,17 @@ introNextButton: RoundButton(defaultActiveButton) { } introPhoneTop: 8px; -inpIntroCountryCode: flatInput(inpDefGray) { +introCountryCode: FlatInput(defaultFlatInput) { width: 70px; height: 41px; align: align(center); } -inpIntroPhone: flatInput(inpDefGray) { +introPhone: FlatInput(defaultFlatInput) { textMrg: margins(12px, 5px, 12px, 6px); width: 225px; height: 41px; } -inpIntroCode: flatInput(inpDefGray) { +introCode: FlatInput(defaultFlatInput) { textMrg: margins(12px, 5px, 12px, 6px); width: 106px; height: 41px; @@ -109,10 +109,10 @@ inpIntroCode: flatInput(inpDefGray) { phAlign: align(center); phShift: 0px; } -inpIntroName: flatInput(inpIntroPhone) { +introName: FlatInput(introPhone) { width: 192px; } -inpIntroPassword: flatInput(inpIntroPhone) { +introPassword: FlatInput(introPhone) { width: 300px; } diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index 982fe678d..88b75a2c0 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -28,7 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/buttons.h" #include "styles/style_intro.h" -CodeInput::CodeInput(QWidget *parent, const style::flatInput &st, const QString &ph) : FlatInput(parent, st, ph) { +CodeInput::CodeInput(QWidget *parent, const style::FlatInput &st, const QString &ph) : Ui::FlatInput(parent, st, ph) { } void CodeInput::correctValue(const QString &was, QString &now) { @@ -80,7 +80,7 @@ IntroCode::IntroCode(IntroWidget *parent) : IntroStep(parent) , _desc(st::introTextSize.width()) , _noTelegramCode(this, lang(lng_code_no_telegram), st::introLink) , _noTelegramCodeRequestId(0) -, _code(this, st::inpIntroCode, lang(lng_code_ph)) +, _code(this, st::introCode, lang(lng_code_ph)) , _callTimer(this) , _callStatus(intro()->getCallStatus()) , _checkRequest(this) { diff --git a/Telegram/SourceFiles/intro/introcode.h b/Telegram/SourceFiles/intro/introcode.h index d3f894ccb..eb8a76a9d 100644 --- a/Telegram/SourceFiles/intro/introcode.h +++ b/Telegram/SourceFiles/intro/introcode.h @@ -21,19 +21,18 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "intro/introwidget.h" - -class FlatInput; +#include "ui/widgets/input_fields.h" namespace Ui { class RoundButton; class LinkButton; } // namespace Ui -class CodeInput final : public FlatInput { +class CodeInput final : public Ui::FlatInput { Q_OBJECT public: - CodeInput(QWidget *parent, const style::flatInput &st, const QString &ph); + CodeInput(QWidget *parent, const style::FlatInput &st, const QString &ph); signals: void codeEntered(); diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index 11c3311b6..1eedf740a 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -49,8 +49,8 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) , _a_error(animation(this, &IntroPhone::step_error)) , _next(this, lang(lng_intro_next), st::introNextButton) , _country(this, st::introCountry) -, _phone(this, st::inpIntroPhone) -, _code(this, st::inpIntroCountryCode) +, _phone(this, st::introPhone) +, _code(this, st::introCountryCode) , _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), FlatLabel::InitType::Rich, st::introErrorLabel, st::introErrorLabelTextStyle) , _checkRequest(this) { setVisible(false); @@ -110,7 +110,7 @@ void IntroPhone::resizeEvent(QResizeEvent *e) { _next->move((width() - _next->width()) / 2, st::introBtnTop); _country->move((width() - _country->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); int phoneTop = _country->y() + _country->height() + st::introPhoneTop; - _phone->move((width() - _country->width()) / 2 + _country->width() - st::inpIntroPhone.width, phoneTop); + _phone->move((width() - _country->width()) / 2 + _country->width() - st::introPhone.width, phoneTop); _code->move((width() - _country->width()) / 2, phoneTop); } _signup->move((width() - _signup->width()) / 2, _next->y() + _next->height() + st::introErrorTop - ((st::introErrorLabelTextStyle.lineHeight - st::introErrorFont->height) / 2)); diff --git a/Telegram/SourceFiles/intro/introphone.h b/Telegram/SourceFiles/intro/introphone.h index 019251705..f4ebedf4c 100644 --- a/Telegram/SourceFiles/intro/introphone.h +++ b/Telegram/SourceFiles/intro/introphone.h @@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/introwidget.h" namespace Ui { +class PhonePartInput; +class CountryCodeInput; class RoundButton; } // namespace Ui @@ -76,8 +78,8 @@ private: QRect _textRect; ChildWidget _country; - ChildWidget _phone; - ChildWidget _code; + ChildWidget _phone; + ChildWidget _code; ChildWidget _signup; QPixmap _signupCache; diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index 0dc6ee140..f1e974cda 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -22,12 +22,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/intropwdcheck.h" #include "styles/style_intro.h" +#include "styles/style_boxes.h" #include "ui/filedialog.h" #include "boxes/confirmbox.h" #include "lang.h" #include "application.h" #include "intro/introsignup.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) @@ -36,8 +38,8 @@ IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent) , _salt(parent->getPwdSalt()) , _hasRecovery(parent->getHasRecovery()) , _hint(parent->getPwdHint()) -, _pwdField(this, st::inpIntroPassword, lang(lng_signin_password)) -, _codeField(this, st::inpIntroPassword, lang(lng_signin_code)) +, _pwdField(this, st::introPassword, lang(lng_signin_password)) +, _codeField(this, st::introPassword, lang(lng_signin_code)) , _toRecover(this, lang(lng_signin_recover)) , _toPassword(this, lang(lng_signin_try_password)) , _reset(this, lang(lng_signin_reset_account), st::introResetLink) diff --git a/Telegram/SourceFiles/intro/intropwdcheck.h b/Telegram/SourceFiles/intro/intropwdcheck.h index 8d4d1d1c0..b25fca221 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.h +++ b/Telegram/SourceFiles/intro/intropwdcheck.h @@ -22,9 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/introwidget.h" -class FlatInput; - namespace Ui { +class FlatInput; class RoundButton; class LinkButton; } // namespace Ui @@ -80,8 +79,8 @@ private: bool _hasRecovery; QString _hint, _emailPattern; - ChildWidget _pwdField; - ChildWidget _codeField; + ChildWidget _pwdField; + ChildWidget _codeField; ChildWidget _toRecover; ChildWidget _toPassword; ChildWidget _reset; diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index 450f28675..df3e6ed58 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "application.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" IntroSignup::IntroSignup(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) @@ -35,8 +36,8 @@ IntroSignup::IntroSignup(IntroWidget *parent) : IntroStep(parent) , _a_error(animation(this, &IntroSignup::step_error)) , _a_photo(animation(this, &IntroSignup::step_photo)) , _next(this, lang(lng_intro_finish), st::introNextButton) -, _first(this, st::inpIntroName, lang(lng_signup_firstname)) -, _last(this, st::inpIntroName, lang(lng_signup_lastname)) +, _first(this, st::introName, lang(lng_signup_firstname)) +, _last(this, st::introName, lang(lng_signup_lastname)) , _invertOrder(langFirstNameGoesSecond()) , _checkRequest(this) { setVisible(false); diff --git a/Telegram/SourceFiles/intro/introsignup.h b/Telegram/SourceFiles/intro/introsignup.h index 8bfe59bfa..bbb60453a 100644 --- a/Telegram/SourceFiles/intro/introsignup.h +++ b/Telegram/SourceFiles/intro/introsignup.h @@ -20,11 +20,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/flatinput.h" #include "intro/introwidget.h" namespace Ui { class RoundButton; +class FlatInput; } // namespace Ui class IntroSignup final : public IntroStep { @@ -72,8 +72,8 @@ private: QPixmap _photoSmall; int32 _phLeft, _phTop; - ChildWidget _first; - ChildWidget _last; + ChildWidget _first; + ChildWidget _last; QString _firstName, _lastName; mtpRequestId _sentRequest = 0; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index c41b8ac55..f75a11e42 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -37,6 +37,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_intro.h" #include "autoupdater.h" #include "window/slide_animation.h" +#include "styles/style_boxes.h" IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent) , _a_stage(animation(this, &IntroWidget::step_stage)) diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index 4dcf409f0..0cb6d7232 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -353,22 +353,22 @@ void FileLoadTask::process() { } if (!fullimage.isNull() && fullimage.width() > 0 && !song && !gif && !voice) { - int32 w = fullimage.width(), h = fullimage.height(); + auto w = fullimage.width(), h = fullimage.height(); attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h))); if (w < 20 * h && h < 20 * w) { if (animated) { attributes.push_back(MTP_documentAttributeAnimated()); } else if (_type != PrepareDocument) { - QPixmap thumb = (w > 100 || h > 100) ? App::pixmapFromImageInPlace(fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); + auto thumb = (w > 100 || h > 100) ? App::pixmapFromImageInPlace(fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); photoThumbs.insert('s', thumb); photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); - QPixmap medium = (w > 320 || h > 320) ? App::pixmapFromImageInPlace(fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); + auto medium = (w > 320 || h > 320) ? App::pixmapFromImageInPlace(fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); photoThumbs.insert('m', medium); photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); - QPixmap full = (w > 1280 || h > 1280) ? App::pixmapFromImageInPlace(fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); + auto full = (w > 1280 || h > 1280) ? App::pixmapFromImageInPlace(fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); photoThumbs.insert('y', full); photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0))); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index d56a65240..e9f776bc6 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "lang.h" #include "media/media_audio.h" +#include "ui/widgets/input_fields.h" #include "application.h" #include "apiwrap.h" @@ -2369,8 +2370,8 @@ void writeDrafts(const PeerId &peer, const MessageDraft &localDraft, const Messa _writeMap(WriteMapFast); } - auto msgTags = FlatTextarea::serializeTagsList(localDraft.textWithTags.tags); - auto editTags = FlatTextarea::serializeTagsList(editDraft.textWithTags.tags); + auto msgTags = Ui::FlatTextarea::serializeTagsList(localDraft.textWithTags.tags); + auto editTags = Ui::FlatTextarea::serializeTagsList(editDraft.textWithTags.tags); int size = sizeof(quint64); size += Serialize::stringSize(localDraft.textWithTags.text) + Serialize::bytearraySize(msgTags) + 2 * sizeof(qint32); @@ -2476,8 +2477,8 @@ void readDraftsWithCursors(History *h) { return; } - msgData.tags = FlatTextarea::deserializeTagsList(msgTagsSerialized, msgData.text.size()); - editData.tags = FlatTextarea::deserializeTagsList(editTagsSerialized, editData.text.size()); + msgData.tags = Ui::FlatTextarea::deserializeTagsList(msgTagsSerialized, msgData.text.size()); + editData.tags = Ui::FlatTextarea::deserializeTagsList(editTagsSerialized, editData.text.size()); MessageCursor msgCursor, editCursor; _readDraftCursors(peer, msgCursor, editCursor); diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index ddcddf5a3..d871cc80a 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -84,7 +84,6 @@ int32 oldMapVersion(); int32 oldSettingsVersion(); -using TextWithTags = FlatTextarea::TextWithTags; struct MessageDraft { MessageDraft(MsgId msgId = 0, TextWithTags textWithTags = TextWithTags(), bool previewCancelled = false) : msgId(msgId) diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 32f8fba2a..17eddd37b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "styles/style_dialogs.h" +#include "styles/style_history.h" #include "ui/buttons/peer_avatar_button.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" @@ -58,6 +59,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/qthelp_url.h" #include "window/window_theme.h" #include "window/player_wrap_widget.h" +#include "styles/style_boxes.h" StackItemSection::StackItemSection(std_::unique_ptr &&memento) : StackItem(nullptr) , _memento(std_::move(memento)) { diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index f6a68ca78..13b474c53 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" #include "styles/style_window.h" +#include "styles/style_boxes.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/buttons.h" #include "core/zlib_help.h" diff --git a/Telegram/SourceFiles/media/media_clip_reader.h b/Telegram/SourceFiles/media/media_clip_reader.h index 0b3a8a395..925d0d3f3 100644 --- a/Telegram/SourceFiles/media/media_clip_reader.h +++ b/Telegram/SourceFiles/media/media_clip_reader.h @@ -78,11 +78,12 @@ public: void start(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius); QPixmap current(int framew, int frameh, int outerw, int outerh, uint64 ms); QPixmap frameOriginal() const { - Frame *frame = frameToShow(); - if (!frame) return QPixmap(); - QPixmap result(frame ? QPixmap::fromImage(frame->original) : QPixmap()); - result.detach(); - return result; + if (auto frame = frameToShow()) { + auto result = QPixmap::fromImage(frame->original); + result.detach(); + return result; + } + return QPixmap(); } bool currentDisplayed() const { Frame *frame = frameToShow(); diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index a9fb2ac68..40750a84b 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -118,7 +118,7 @@ overviewLinksChecked: icon { { "overview_links_check", #ffffff, point(4px, 5px) }, }; -overviewFilter: flatInput(inpDefGray) { +overviewFilter: FlatInput(defaultFlatInput) { font: font(fsize); bgColor: #f2f2f2; phColor: #949494; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 85f060e5d..0626583bd 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/popup_menu.h" #include "ui/widgets/tooltip.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "window/top_bar_widget.h" #include "window/window_theme.h" #include "lang.h" @@ -77,17 +78,17 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD setMouseTracking(true); connect(_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); - connect(&_search, SIGNAL(cancelled()), this, SLOT(onCancel())); - connect(&_search, SIGNAL(changed()), this, SLOT(onSearchUpdate())); + connect(_search, SIGNAL(cancelled()), this, SLOT(onCancel())); + connect(_search, SIGNAL(changed()), this, SLOT(onSearchUpdate())); _searchTimer.setSingleShot(true); connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchMessages())); _cancelSearch->hide(); if (_type == OverviewLinks || _type == OverviewFiles) { - _search.show(); + _search->show(); } else { - _search.hide(); + _search->hide(); } } @@ -176,7 +177,7 @@ void OverviewInner::fixItemIndex(int32 ¤t, MsgId msgId) const { } void OverviewInner::searchReceived(SearchRequestType type, const MTPmessages_Messages &result, mtpRequestId req) { - if (!_search.text().isEmpty()) { + if (!_search->text().isEmpty()) { if (type == SearchFromStart) { SearchQueries::iterator i = _searchQueries.find(req); if (i != _searchQueries.cend()) { @@ -683,7 +684,7 @@ QPoint OverviewInner::mapMouseToItem(QPoint p, MsgId itemId, int32 itemIndex) { void OverviewInner::activate() { if (_type == OverviewLinks || _type == OverviewFiles) { - _search.setFocus(); + _search->setFocus(); } else { setFocus(); } @@ -1111,7 +1112,7 @@ void OverviewInner::mouseReleaseEvent(QMouseEvent *e) { } void OverviewInner::keyPressEvent(QKeyEvent *e) { - if ((_search.isHidden() || !_search.hasFocus()) && !_overview->isHidden() && e->key() == Qt::Key_Escape) { + if ((_search->isHidden() || !_search->hasFocus()) && !_overview->isHidden() && e->key() == Qt::Key_Escape) { onCancel(); } else if (e->key() == Qt::Key_Back) { App::main()->showBackFromStack(); @@ -1282,8 +1283,8 @@ int32 OverviewInner::resizeToWidth(int32 nwidth, int32 scrollTop, int32 minHeigh } _rowsLeft = (_width - _rowWidth) / 2; - _search.setGeometry(_rowsLeft, st::linksSearchMargin.top(), _rowWidth, _search.height()); - _cancelSearch->moveToLeft(_rowsLeft + _rowWidth - _cancelSearch->width(), _search.y()); + _search->setGeometry(_rowsLeft, st::linksSearchMargin.top(), _rowWidth, _search->height()); + _cancelSearch->moveToLeft(_rowsLeft + _rowWidth - _cancelSearch->width(), _search->y()); if (_type == OverviewPhotos || _type == OverviewVideos) { for (int32 i = 0, l = _items.size(); i < l; ++i) { @@ -1334,14 +1335,14 @@ void OverviewInner::switchType(MediaOverviewType type) { _type = type; _reversed = (_type != OverviewLinks && _type != OverviewFiles); if (_type == OverviewLinks || _type == OverviewFiles) { - _search.show(); + _search->show(); } else { - _search.hide(); + _search->hide(); } - if (!_search.getLastText().isEmpty()) { - _search.setText(QString()); - _search.updatePlaceholder(); + if (!_search->getLastText().isEmpty()) { + _search->setText(QString()); + _search->updatePlaceholder(); onSearchUpdate(); } _cancelSearch->hide(); @@ -1425,7 +1426,7 @@ void OverviewInner::saveContextFile() { bool OverviewInner::onSearchMessages(bool searchCache) { _searchTimer.stop(); - QString q = _search.text().trimmed(); + QString q = _search->text().trimmed(); if (q.isEmpty()) { if (_searchRequest) { _searchRequest = 0; @@ -1462,7 +1463,7 @@ void OverviewInner::onNeedSearchMessages() { } void OverviewInner::onSearchUpdate() { - QString filterText = (_type == OverviewLinks || _type == OverviewFiles) ? _search.text().trimmed() : QString(); + QString filterText = (_type == OverviewLinks || _type == OverviewFiles) ? _search->text().trimmed() : QString(); bool inSearch = !filterText.isEmpty(), changed = (inSearch != _inSearch); _inSearch = inSearch; @@ -1495,11 +1496,11 @@ void OverviewInner::onCancel() { } bool OverviewInner::onCancelSearch() { - if (_search.isHidden()) return false; - bool clearing = !_search.text().isEmpty(); + if (_search->isHidden()) return false; + bool clearing = !_search->text().isEmpty(); _cancelSearch->hide(); - _search.clear(); - _search.updatePlaceholder(); + _search->clear(); + _search->updatePlaceholder(); onSearchUpdate(); return clearing; } @@ -1796,7 +1797,7 @@ void OverviewInner::recountMargins() { _marginTop = st::playlistPadding; _marginBottom = qMax(_minHeight - _height - _marginTop, int32(st::playlistPadding)); } else if (_type == OverviewLinks || _type == OverviewFiles) { - _marginTop = st::linksSearchMargin.top() + _search.height() + st::linksSearchMargin.bottom(); + _marginTop = st::linksSearchMargin.top() + _search->height() + st::linksSearchMargin.bottom(); _marginBottom = qMax(_minHeight - _height - _marginTop, int32(st::playlistPadding)); } else { _marginBottom = st::playlistPadding; diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index aebbe33cb..b5480bf13 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -35,6 +35,7 @@ namespace Ui { class PlainShadow; class PopupMenu; class IconButton; +class FlatInput; } // namespace Ui class OverviewWidget; @@ -179,7 +180,7 @@ private: Overview::Layout::AbstractItem *layoutPrepare(const QDate &date, bool month); int32 setLayoutItem(int32 index, Overview::Layout::AbstractItem *item, int32 top); - FlatInput _search; + ChildWidget _search; ChildWidget _cancelSearch; QVector _results; int32 _itemsToBeLoaded; diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 0c1d3de2e..62e89c118 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "ui/text/text.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "styles/style_boxes.h" #include "window/slide_animation.h" diff --git a/Telegram/SourceFiles/passcodewidget.h b/Telegram/SourceFiles/passcodewidget.h index 3ccd192b7..ab41cde0a 100644 --- a/Telegram/SourceFiles/passcodewidget.h +++ b/Telegram/SourceFiles/passcodewidget.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once namespace Ui { +class FlatInput; class LinkButton; class RoundButton; } // namespace Ui @@ -55,7 +56,7 @@ private: anim::ivalue a_coordUnder, a_coordOver; anim::fvalue a_shadow; - ChildWidget _passcode; + ChildWidget _passcode; ChildWidget _submit; ChildWidget _logout; QString _error; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 1a8a56a23..110d5326a 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -436,7 +436,7 @@ void MainWindow::createGlobalMenu() { namespace { void _sendKeySequence(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier) { QWidget *focused = QApplication::focusWidget(); - if (qobject_cast(focused) || qobject_cast(focused) || qobject_cast(focused)) { + if (qobject_cast(focused) || qobject_cast(focused) || qobject_cast(focused)) { QApplication::postEvent(focused, new QKeyEvent(QEvent::KeyPress, key, modifiers)); QApplication::postEvent(focused, new QKeyEvent(QEvent::KeyRelease, key, modifiers)); } @@ -498,11 +498,11 @@ void MainWindow::psMacUpdateMenu() { canUndo = edit->isUndoAvailable(); canRedo = edit->isRedoAvailable(); canPaste = !Application::clipboard()->text().isEmpty(); - } else if (auto edit = qobject_cast(focused)) { + } else if (auto edit = qobject_cast(focused)) { canCut = canCopy = canDelete = edit->textCursor().hasSelection(); - canSelectAll = !edit->isEmpty(); - canUndo = edit->isUndoAvailable(); - canRedo = edit->isRedoAvailable(); + canSelectAll = !edit->document()->isEmpty(); + canUndo = edit->document()->isUndoAvailable(); + canRedo = edit->document()->isRedoAvailable(); canPaste = !Application::clipboard()->text().isEmpty(); } else if (auto list = qobject_cast(focused)) { canCopy = list->canCopySelected(); @@ -534,7 +534,7 @@ bool MainWindow::psFilterNativeEvent(void *event) { bool MainWindow::eventFilter(QObject *obj, QEvent *evt) { QEvent::Type t = evt->type(); if (t == QEvent::FocusIn || t == QEvent::FocusOut) { - if (qobject_cast(obj) || qobject_cast(obj) || qobject_cast(obj)) { + if (qobject_cast(obj) || qobject_cast(obj) || qobject_cast(obj)) { psMacUpdateMenu(); } } diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index efc6ff1ed..521453932 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org using "basic.style"; using "basic_types.style"; +using "window/window.style"; + profileBg: windowBg; profileTopBarHeight: topBarHeight; diff --git a/Telegram/SourceFiles/profile/profile_actions_widget.cpp b/Telegram/SourceFiles/profile/profile_actions_widget.cpp index ef4ee8465..a62c4967f 100644 --- a/Telegram/SourceFiles/profile/profile_actions_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_actions_widget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_actions_widget.h" #include "styles/style_profile.h" +#include "styles/style_boxes.h" #include "ui/buttons/left_outline_button.h" #include "boxes/confirmbox.h" #include "boxes/report_box.h" diff --git a/Telegram/SourceFiles/profile/profile_actions_widget.h b/Telegram/SourceFiles/profile/profile_actions_widget.h index 0043ccfb7..014312c42 100644 --- a/Telegram/SourceFiles/profile/profile_actions_widget.h +++ b/Telegram/SourceFiles/profile/profile_actions_widget.h @@ -22,6 +22,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_block_widget.h" +namespace style { +struct OutlineButton; +} // namespace style + +namespace st { +extern const style::OutlineButton &defaultLeftOutlineButton; +} // namespace st + namespace Ui { class LeftOutlineButton; } // namespace Ui diff --git a/Telegram/SourceFiles/profile/profile_block_widget.cpp b/Telegram/SourceFiles/profile/profile_block_widget.cpp index 1b287cc6e..95e0eabb9 100644 --- a/Telegram/SourceFiles/profile/profile_block_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_block_widget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_block_widget.h" #include "styles/style_profile.h" +#include "styles/style_widgets.h" namespace Profile { diff --git a/Telegram/SourceFiles/profile/profile_cover.h b/Telegram/SourceFiles/profile/profile_cover.h index 6c5e925f7..7d96fdd32 100644 --- a/Telegram/SourceFiles/profile/profile_cover.h +++ b/Telegram/SourceFiles/profile/profile_cover.h @@ -25,6 +25,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class FlatLabel; +namespace style { +struct RoundButton; +} // namespace style + namespace Ui { class RoundButton; class LinkButton; diff --git a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp index 03927b938..eba4f65fc 100644 --- a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp +++ b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "observer_peer.h" #include "window/top_bar_widget.h" +#include "styles/style_boxes.h" namespace Profile { diff --git a/Telegram/SourceFiles/profile/profile_settings_widget.cpp b/Telegram/SourceFiles/profile/profile_settings_widget.cpp index 886cf9c8b..ed9c84cc4 100644 --- a/Telegram/SourceFiles/profile/profile_settings_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_settings_widget.cpp @@ -129,7 +129,7 @@ void SettingsWidget::refreshManageAdminsButton() { }; _manageAdmins.destroy(); if (hasManageAdmins()) { - _manageAdmins = new Ui::LeftOutlineButton(this, lang(lng_profile_manage_admins), st::defaultLeftOutlineButton); + _manageAdmins.create(this, lang(lng_profile_manage_admins), st::defaultLeftOutlineButton); _manageAdmins->show(); connect(_manageAdmins, SIGNAL(clicked()), this, SLOT(onManageAdmins())); } @@ -152,7 +152,7 @@ void SettingsWidget::refreshInviteLinkButton() { if (inviteLinkText.isEmpty()) { _inviteLink.destroy(); } else { - _inviteLink = new Ui::LeftOutlineButton(this, inviteLinkText, st::defaultLeftOutlineButton); + _inviteLink.create(this, inviteLinkText, st::defaultLeftOutlineButton); _inviteLink->show(); connect(_inviteLink, SIGNAL(clicked()), this, SLOT(onInviteLink())); } diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index fde5cb65d..83ad17611 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -70,8 +70,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/animation.h" #include "ui/twidget.h" -#include "ui/flatinput.h" -#include "ui/flattextarea.h" #include "ui/scrollarea.h" #include "ui/images.h" #include "ui/text/text.h" diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index c751dcdc7..d0a6534b5 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -23,6 +23,13 @@ using "basic.style"; using "boxes/boxes.style"; using "ui/widgets/widgets.style"; +switchPmButton: RoundButton(defaultBoxButton) { + width: 320px; + height: 34px; + textTop: 7px; + downTextTop: 8px; +} + stickersTrendingHeader: 45px; stickersTrendingSkip: 15px; diff --git a/Telegram/SourceFiles/ui/buttons/left_outline_button.h b/Telegram/SourceFiles/ui/buttons/left_outline_button.h index 590535d32..33db20cb2 100644 --- a/Telegram/SourceFiles/ui/buttons/left_outline_button.h +++ b/Telegram/SourceFiles/ui/buttons/left_outline_button.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/abstract_button.h" +#include "styles/style_widgets.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.h b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.h index 7a31ba9db..ccad1341c 100644 --- a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.h +++ b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/abstract_button.h" +#include "styles/style_window.h" class PeerData; diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index e43b4eade..1c833447c 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -111,7 +111,7 @@ CountryInput::CountryInput(QWidget *parent, const style::countryInput &st) : QWi } _arrow = App::pixmapFromImageInPlace(std_::move(trImage)); _inner = QRect(0, 0, _st.width, _st.height); - _arrowRect = QRect((st::inpIntroCountryCode.width - _arrow.width() - 1) / 2, _st.height, _arrow.width(), _arrow.height()); + _arrowRect = QRect((st::introCountryCode.width - _arrow.width() - 1) / 2, _st.height, _arrow.width(), _arrow.height()); } void CountryInput::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index d3416c068..a5a8d376e 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -20,7 +20,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/flatinput.h" #include "ui/scrollarea.h" #include "ui/effects/rect_shadow.h" #include "boxes/abstractbox.h" diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.cpp b/Telegram/SourceFiles/ui/effects/panel_animation.cpp index 28b3efa84..91d373f00 100644 --- a/Telegram/SourceFiles/ui/effects/panel_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/panel_animation.cpp @@ -229,7 +229,7 @@ void RoundShadowAnimation::paintShadowHorizontal(int left, int right, int top, c void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) { t_assert(!started()); - _finalImage = QPixmap::fromImage(std_::move(finalImage).convertToFormat(QImage::Format_ARGB32_Premultiplied), Qt::ColorOnly); + _finalImage = App::pixmapFromImageInPlace(std_::move(finalImage).convertToFormat(QImage::Format_ARGB32_Premultiplied)); t_assert(!_finalImage.isNull()); _finalWidth = _finalImage.width(); @@ -317,7 +317,7 @@ void PanelAnimation::createFadeMask() { } ints += intsPerLineAdded; } - _fadeMask = QPixmap::fromImage(style::colorizeImage(result, _st.fadeBg), Qt::ColorOnly); + _fadeMask = App::pixmapFromImageInPlace(style::colorizeImage(result, _st.fadeBg)); _fadeHeight = _fadeMask.height(); } diff --git a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp new file mode 100644 index 000000000..71823d0d9 --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp @@ -0,0 +1,157 @@ +/* +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 "ui/effects/ripple_animation.h" + +namespace Ui { + +class RippleAnimation::Ripple { +public: + Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, UpdateCallback update); + + void paint(QPainter &p, const QPixmap &mask, uint64 ms); + + void stop(); + bool finished() const { + return _hiding && !_hide.animating(); + } + +private: + const style::RippleAnimation &_st; + UpdateCallback _update; + + QPoint _origin; + int _radiusFrom = 0; + int _radiusTo = 0; + + bool _hiding = false; + FloatAnimation _show; + FloatAnimation _hide; + QPixmap _cache; + QImage _frame; + +}; + +RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, UpdateCallback update) +: _st(st) +, _update(update) +, _origin(origin) +, _radiusFrom(startRadius) +, _frame(mask.size(), QImage::Format_ARGB32_Premultiplied) { + _frame.setDevicePixelRatio(mask.devicePixelRatio()); + + QPoint points[] = { + { 0, 0 }, + { _frame.width() / cIntRetinaFactor(), 0 }, + { _frame.width() / cIntRetinaFactor(), _frame.height() / cIntRetinaFactor() }, + { 0, _frame.height() / cIntRetinaFactor() }, + }; + for (auto point : points) { + accumulate_max(_radiusTo, style::point::dotProduct(_origin - point, _origin - point)); + } + _radiusTo = qRound(sqrt(_radiusTo)); + + _show.start(UpdateCallback(_update), 0., 1., _st.showDuration); +} + +void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, uint64 ms) { + auto opacity = _hide.current(ms, _hiding ? 0. : 1.); + if (opacity == 0.) { + return; + } + + if (_cache.isNull()) { + auto radius = anim::interpolate(_radiusFrom, _radiusTo, _show.current(ms, 1.)); + _frame.fill(Qt::transparent); + { + Painter p(&_frame); + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.setPen(Qt::NoPen); + p.setBrush(_st.color); + p.drawEllipse(_origin, radius, radius); + + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + p.drawPixmap(0, 0, mask); + } + if (radius == _radiusTo) { + _cache = App::pixmapFromImageInPlace(std_::move(_frame)); + } + } + auto saved = p.opacity(); + if (opacity != 1.) p.setOpacity(saved * opacity); + if (_cache.isNull()) { + p.drawImage(0, 0, _frame); + } else { + p.drawPixmap(0, 0, _cache); + } + if (opacity != 1.) p.setOpacity(saved); +} + +void RippleAnimation::Ripple::stop() { + _hiding = true; + _hide.start(UpdateCallback(_update), 1., 0., _st.hideDuration); +} + +RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, UpdateCallback callback) +: _st(st) +, _mask(App::pixmapFromImageInPlace(std_::move(mask))) +, _update(std_::move(callback)) { +} + + +void RippleAnimation::add(QPoint origin, int startRadius) { + _ripples.push_back(new Ripple(_st, origin, startRadius, _mask, _update)); +} + +void RippleAnimation::stopLast() { + if (!_ripples.isEmpty()) { + _ripples.back()->stop(); + } +} + +void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, uint64 ms) { + if (_ripples.isEmpty()) { + return; + } + + if (rtl()) x = outerWidth - x - (_mask.width() / cIntRetinaFactor()); + p.translate(x, y); + for (auto ripple : _ripples) { + ripple->paint(p, _mask, ms); + } + p.translate(-x, -y); + clearFinished(); +} + +void RippleAnimation::clearFinished() { + while (!_ripples.isEmpty() && _ripples.front()->finished()) { + delete base::take(_ripples.front()); + _ripples.pop_front(); + } +} + +void RippleAnimation::clear() { + for (auto ripple : base::take(_ripples)) { + delete ripple; + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/ripple_animation.h b/Telegram/SourceFiles/ui/effects/ripple_animation.h new file mode 100644 index 000000000..ed531a3ff --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/ripple_animation.h @@ -0,0 +1,62 @@ +/* +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 "styles/style_widgets.h" + +namespace Ui { + +class RippleAnimation { +public: + using UpdateCallback = base::lambda_wrap; + + // White upon transparent mask, like colorizeImage(black-white-mask, white). + RippleAnimation(const style::RippleAnimation &st, QImage mask, UpdateCallback update); + + void setMask(QImage &&mask); + + void add(QPoint origin, int startRadius = 0); + void stopLast(); + + void paint(QPainter &p, int x, int y, int outerWidth, uint64 ms); + + bool empty() const { + return _ripples.isEmpty(); + } + + ~RippleAnimation() { + clear(); + } + +private: + void clear(); + void clearFinished(); + + const style::RippleAnimation &_st; + QPixmap _mask; + UpdateCallback _update; + + class Ripple; + QList _ripples; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/flattextarea.cpp b/Telegram/SourceFiles/ui/flattextarea.cpp deleted file mode 100644 index f8495a4a7..000000000 --- a/Telegram/SourceFiles/ui/flattextarea.cpp +++ /dev/null @@ -1,1426 +0,0 @@ -/* -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 "flattextarea.h" - -#include "ui/widgets/popup_menu.h" -#include "mainwindow.h" - -QByteArray FlatTextarea::serializeTagsList(const TagList &tags) { - if (tags.isEmpty()) { - return QByteArray(); - } - - QByteArray tagsSerialized; - { - QBuffer buffer(&tagsSerialized); - buffer.open(QIODevice::WriteOnly); - QDataStream stream(&buffer); - stream.setVersion(QDataStream::Qt_5_1); - stream << qint32(tags.size()); - for_const (auto &tag, tags) { - stream << qint32(tag.offset) << qint32(tag.length) << tag.id; - } - } - return tagsSerialized; -} - -FlatTextarea::TagList FlatTextarea::deserializeTagsList(QByteArray data, int textLength) { - TagList result; - if (data.isEmpty()) { - return result; - } - - QBuffer buffer(&data); - buffer.open(QIODevice::ReadOnly); - - QDataStream stream(&buffer); - stream.setVersion(QDataStream::Qt_5_1); - - qint32 tagCount = 0; - stream >> tagCount; - if (stream.status() != QDataStream::Ok) { - return result; - } - if (tagCount <= 0 || tagCount > textLength) { - return result; - } - - for (int i = 0; i < tagCount; ++i) { - qint32 offset = 0, length = 0; - QString id; - stream >> offset >> length >> id; - if (stream.status() != QDataStream::Ok) { - return result; - } - if (offset < 0 || length <= 0 || offset + length > textLength) { - return result; - } - result.push_back({ offset, length, id }); - } - return result; -} - -QString FlatTextarea::tagsMimeType() { - return qsl("application/x-td-field-tags"); -} - -FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v, const TagList &tags) : QTextEdit(parent) -, _phVisible(!v.length()) -, a_phLeft(_phVisible ? 0 : st.phShift) -, a_phAlpha(_phVisible ? 1 : 0) -, a_phColorFocused(0) -, _a_appearance(animation(this, &FlatTextarea::step_appearance)) -, _lastTextWithTags { v, tags } -, _st(st) { - setAcceptRichText(false); - resize(_st.width, _st.font->height); - - setFont(_st.font->f); - setAlignment(_st.align); - - setPlaceholder(pholder); - - QPalette p(palette()); - p.setColor(QPalette::Text, _st.textColor->c); - setPalette(p); - - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - setFrameStyle(QFrame::NoFrame | QFrame::Plain); - viewport()->setAutoFillBackground(false); - - setContentsMargins(0, 0, 0, 0); - - switch (cScale()) { - case dbisOneAndQuarter: _fakeMargin = 1; break; - case dbisOneAndHalf: _fakeMargin = 2; break; - case dbisTwo: _fakeMargin = 4; break; - } - setStyleSheet(qsl("QTextEdit { margin: %1px; }").arg(_fakeMargin)); - - viewport()->setAttribute(Qt::WA_AcceptTouchEvents); - _touchTimer.setSingleShot(true); - connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); - - connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onDocumentContentsChange(int,int,int))); - connect(document(), SIGNAL(contentsChanged()), this, SLOT(onDocumentContentsChanged())); - connect(this, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool))); - connect(this, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool))); - if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); - - if (!_lastTextWithTags.text.isEmpty()) { - setTextWithTags(_lastTextWithTags, ClearUndoHistory); - } -} - -FlatTextarea::TextWithTags FlatTextarea::getTextWithTagsPart(int start, int end) { - TextWithTags result; - result.text = getTextPart(start, end, &result.tags); - return result; -} - -void FlatTextarea::setTextWithTags(const TextWithTags &textWithTags, UndoHistoryAction undoHistoryAction) { - _insertedTags = textWithTags.tags; - _insertedTagsAreFromMime = false; - _realInsertPosition = 0; - _realCharsAdded = textWithTags.text.size(); - auto doc = document(); - auto cursor = QTextCursor(doc->docHandle(), 0); - if (undoHistoryAction == ClearUndoHistory) { - doc->setUndoRedoEnabled(false); - cursor.beginEditBlock(); - } else if (undoHistoryAction == MergeWithUndoHistory) { - cursor.joinPreviousEditBlock(); - } else { - cursor.beginEditBlock(); - } - cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); - cursor.insertText(textWithTags.text); - cursor.movePosition(QTextCursor::End); - cursor.endEditBlock(); - if (undoHistoryAction == ClearUndoHistory) { - doc->setUndoRedoEnabled(true); - } - _insertedTags.clear(); - _realInsertPosition = -1; - finishPlaceholder(); -} - -void FlatTextarea::finishPlaceholder() { - if (_a_appearance.animating()) { - a_phLeft.finish(); - a_phAlpha.finish(); - _a_appearance.stop(); - update(); - } -} - -void FlatTextarea::setMaxLength(int32 maxLength) { - _maxLength = maxLength; -} - -void FlatTextarea::setMinHeight(int32 minHeight) { - _minHeight = minHeight; - heightAutoupdated(); -} - -void FlatTextarea::setMaxHeight(int32 maxHeight) { - _maxHeight = maxHeight; - heightAutoupdated(); -} - -bool FlatTextarea::heightAutoupdated() { - if (_minHeight < 0 || _maxHeight < 0 || _inHeightCheck) return false; - _inHeightCheck = true; - - myEnsureResized(this); - - int newh = ceil(document()->size().height()) + 2 * fakeMargin(); - if (newh > _maxHeight) { - newh = _maxHeight; - } else if (newh < _minHeight) { - newh = _minHeight; - } - if (height() != newh) { - resize(width(), newh); - _inHeightCheck = false; - return true; - } - _inHeightCheck = false; - return false; -} - -void FlatTextarea::onTouchTimer() { - _touchRightButton = true; -} - -bool FlatTextarea::viewportEvent(QEvent *e) { - if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) { - QTouchEvent *ev = static_cast(e); - if (ev->device()->type() == QTouchDevice::TouchScreen) { - touchEvent(ev); - return QTextEdit::viewportEvent(e); - } - } - return QTextEdit::viewportEvent(e); -} - -void FlatTextarea::touchEvent(QTouchEvent *e) { - switch (e->type()) { - case QEvent::TouchBegin: - if (_touchPress || e->touchPoints().isEmpty()) return; - _touchTimer.start(QApplication::startDragTime()); - _touchPress = true; - _touchMove = _touchRightButton = false; - _touchStart = e->touchPoints().cbegin()->screenPos().toPoint(); - break; - - case QEvent::TouchUpdate: - if (!_touchPress || e->touchPoints().isEmpty()) return; - if (!_touchMove && (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart).manhattanLength() >= QApplication::startDragDistance()) { - _touchMove = true; - } - break; - - case QEvent::TouchEnd: - if (!_touchPress) return; - if (!_touchMove && window()) { - Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton); - QPoint mapped(mapFromGlobal(_touchStart)), winMapped(window()->mapFromGlobal(_touchStart)); - - if (_touchRightButton) { - QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, mapped, _touchStart); - contextMenuEvent(&contextEvent); - } - } - _touchTimer.stop(); - _touchPress = _touchMove = _touchRightButton = false; - break; - - case QEvent::TouchCancel: - _touchPress = false; - _touchTimer.stop(); - break; - } -} - -QRect FlatTextarea::getTextRect() const { - return rect().marginsRemoved(_st.textMrg + st::textRectMargins); -} - -int32 FlatTextarea::fakeMargin() const { - return _fakeMargin; -} - -void FlatTextarea::paintEvent(QPaintEvent *e) { - QPainter p(viewport()); - QRect r(rect().intersected(e->rect())); - p.fillRect(r, _st.bgColor->b); - bool phDraw = _phVisible; - if (_a_appearance.animating()) { - p.setOpacity(a_phAlpha.current()); - phDraw = true; - } - if (phDraw) { - p.save(); - p.setClipRect(r); - p.setFont(_st.font); - p.setPen(anim::pen(_st.phColor, _st.phFocusColor, a_phColorFocused.current())); - if (_st.phAlign == style::al_topleft && _phAfter > 0) { - int skipWidth = placeholderSkipWidth(); - p.drawText(_st.textMrg.left() - _fakeMargin + a_phLeft.current() + skipWidth, _st.textMrg.top() - _fakeMargin - st::lineWidth + _st.font->ascent, _ph); - } else { - QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom()); - p.drawText(phRect, _ph, QTextOption(_st.phAlign)); - } - p.restore(); - p.setOpacity(1); - } - QTextEdit::paintEvent(e); -} - -int FlatTextarea::placeholderSkipWidth() const { - if (!_phAfter) { - return 0; - } - auto text = getTextWithTags().text; - auto result = _st.font->width(text.mid(0, _phAfter)); - if (_phAfter > text.size()) { - result += _st.font->spacew; - } - return result; -} - -void FlatTextarea::focusInEvent(QFocusEvent *e) { - a_phColorFocused.start(1.); - _a_appearance.start(); - QTextEdit::focusInEvent(e); -} - -void FlatTextarea::focusOutEvent(QFocusEvent *e) { - a_phColorFocused.start(0.); - _a_appearance.start(); - QTextEdit::focusOutEvent(e); -} - -QSize FlatTextarea::sizeHint() const { - return geometry().size(); -} - -QSize FlatTextarea::minimumSizeHint() const { - return geometry().size(); -} - -EmojiPtr FlatTextarea::getSingleEmoji() const { - QString text; - QTextFragment fragment; - - getSingleEmojiFragment(text, fragment); - - if (!text.isEmpty()) { - QTextCharFormat format = fragment.charFormat(); - QString imageName = static_cast(&format)->name(); - if (imageName.startsWith(qstr("emoji://e."))) { - return emojiFromUrl(imageName); - } - } - return nullptr; -} - -QString FlatTextarea::getInlineBotQuery(UserData **outInlineBot, QString *outInlineBotUsername) const { - t_assert(outInlineBot != nullptr); - t_assert(outInlineBotUsername != nullptr); - - auto &text = getTextWithTags().text; - auto textLength = text.size(); - - int inlineUsernameStart = 1, inlineUsernameLength = 0; - if (textLength > 2 && text.at(0) == '@' && text.at(1).isLetter()) { - inlineUsernameLength = 1; - for (int i = inlineUsernameStart + 1; i != textLength; ++i) { - if (text.at(i).isLetterOrNumber() || text.at(i).unicode() == '_') { - ++inlineUsernameLength; - continue; - } - if (!text.at(i).isSpace()) { - inlineUsernameLength = 0; - } - break; - } - auto inlineUsernameEnd = inlineUsernameStart + inlineUsernameLength; - auto inlineUsernameEqualsText = (inlineUsernameEnd == textLength); - auto validInlineUsername = false; - if (inlineUsernameEqualsText) { - validInlineUsername = text.endsWith(qstr("bot")); - } else if (inlineUsernameEnd < textLength && inlineUsernameLength) { - validInlineUsername = text.at(inlineUsernameEnd).isSpace(); - } - if (validInlineUsername) { - auto username = text.midRef(inlineUsernameStart, inlineUsernameLength); - if (username != *outInlineBotUsername) { - *outInlineBotUsername = username.toString(); - auto peer = App::peerByName(*outInlineBotUsername); - if (peer) { - if (peer->isUser()) { - *outInlineBot = peer->asUser(); - } else { - *outInlineBot = nullptr; - } - } else { - *outInlineBot = LookingUpInlineBot; - } - } - if (*outInlineBot == LookingUpInlineBot) return QString(); - - if (*outInlineBot && (!(*outInlineBot)->botInfo || (*outInlineBot)->botInfo->inlinePlaceholder.isEmpty())) { - *outInlineBot = nullptr; - } else { - return inlineUsernameEqualsText ? QString() : text.mid(inlineUsernameEnd + 1); - } - } else { - inlineUsernameLength = 0; - } - } - if (inlineUsernameLength < 3) { - *outInlineBot = nullptr; - *outInlineBotUsername = QString(); - } - return QString(); -} - -QString FlatTextarea::getMentionHashtagBotCommandPart(bool &start) const { - start = false; - - int32 pos = textCursor().position(); - if (textCursor().anchor() != pos) return QString(); - - // check mention / hashtag / bot command - QTextDocument *doc(document()); - QTextBlock block = doc->findBlock(pos); - for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) { - QTextFragment fr(iter.fragment()); - if (!fr.isValid()) continue; - - int32 p = fr.position(), e = (p + fr.length()); - if (p >= pos || e < pos) continue; - - QTextCharFormat f = fr.charFormat(); - if (f.isImageFormat()) continue; - - bool mentionInCommand = false; - QString t(fr.text()); - for (int i = pos - p; i > 0; --i) { - if (t.at(i - 1) == '@') { - if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) { - start = (i == 1) && (p == 0); - return t.mid(i - 1, pos - p - i + 1); - } else if ((pos - p - i < 1 || t.at(i).isLetter()) && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) { - mentionInCommand = true; - --i; - continue; - } - return QString(); - } else if (t.at(i - 1) == '#') { - if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) { - start = (i == 1) && (p == 0); - return t.mid(i - 1, pos - p - i + 1); - } - return QString(); - } else if (t.at(i - 1) == '/') { - if (i < 2) { - start = (i == 1) && (p == 0); - return t.mid(i - 1, pos - p - i + 1); - } - return QString(); - } - if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break; - if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break; - } - break; - } - return QString(); -} - -void FlatTextarea::insertTag(const QString &text, QString tagId) { - auto cursor = textCursor(); - int32 pos = cursor.position(); - - auto doc = document(); - auto block = doc->findBlock(pos); - for (auto iter = block.begin(); !iter.atEnd(); ++iter) { - auto fragment = iter.fragment(); - t_assert(fragment.isValid()); - - int fragmentPosition = fragment.position(); - int fragmentEnd = (fragmentPosition + fragment.length()); - if (fragmentPosition >= pos || fragmentEnd < pos) continue; - - auto format = fragment.charFormat(); - if (format.isImageFormat()) continue; - - bool mentionInCommand = false; - auto fragmentText = fragment.text(); - for (int i = pos - fragmentPosition; i > 0; --i) { - auto previousChar = fragmentText.at(i - 1); - if (previousChar == '@' || previousChar == '#' || previousChar == '/') { - if ((i == pos - fragmentPosition || (previousChar == '/' ? fragmentText.at(i).isLetterOrNumber() : fragmentText.at(i).isLetter()) || previousChar == '#') && - (i < 2 || !(fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_'))) { - cursor.setPosition(fragmentPosition + i - 1); - int till = fragmentPosition + i; - for (; (till < fragmentEnd && till < pos); ++till) { - auto ch = fragmentText.at(till - fragmentPosition); - if (!ch.isLetterOrNumber() && ch != '_' && ch != '@') { - break; - } - } - if (till < fragmentEnd && fragmentText.at(till - fragmentPosition) == ' ') { - ++till; - } - cursor.setPosition(till, QTextCursor::KeepAnchor); - break; - } else if ((i == pos - fragmentPosition || fragmentText.at(i).isLetter()) && fragmentText.at(i - 1) == '@' && i > 2 && (fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_') && !mentionInCommand) { - mentionInCommand = true; - --i; - continue; - } - break; - } - if (pos - fragmentPosition - i > 127 || (!mentionInCommand && (pos - fragmentPosition - i > 63))) break; - if (!fragmentText.at(i - 1).isLetterOrNumber() && fragmentText.at(i - 1) != '_') break; - } - break; - } - if (tagId.isEmpty()) { - QTextCharFormat format = cursor.charFormat(); - format.setAnchor(false); - format.setAnchorName(QString()); - format.clearForeground(); - cursor.insertText(text + ' ', format); - } else { - _insertedTags.clear(); - _insertedTags.push_back({ 0, text.size(), tagId }); - _insertedTagsAreFromMime = false; - cursor.insertText(text + ' '); - _insertedTags.clear(); - } -} - -void FlatTextarea::setTagMimeProcessor(std_::unique_ptr &&processor) { - _tagMimeProcessor = std_::move(processor); -} - -void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const { - int32 end = textCursor().position(), start = end - 1; - if (textCursor().anchor() != end) return; - - if (start < 0) start = 0; - - QTextDocument *doc(document()); - QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); - if (till.isValid()) till = till.next(); - - for (QTextBlock b = from; b != till; b = b.next()) { - for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { - QTextFragment fr(iter.fragment()); - if (!fr.isValid()) continue; - - int32 p = fr.position(), e = (p + fr.length()); - if (p >= end || e <= start) { - continue; - } - - QTextCharFormat f = fr.charFormat(); - QString t(fr.text()); - if (p < start) { - t = t.mid(start - p, end - start); - } else if (e > end) { - t = t.mid(0, end - p); - } - if (f.isImageFormat() && !t.isEmpty() && t.at(0).unicode() == QChar::ObjectReplacementCharacter) { - QString imageName = static_cast(&f)->name(); - if (imageName.startsWith(qstr("emoji://e."))) { - fragment = fr; - text = t; - return; - } - } - return; - } - } - return; -} - -void FlatTextarea::removeSingleEmoji() { - QString text; - QTextFragment fragment; - - getSingleEmojiFragment(text, fragment); - - if (!text.isEmpty()) { - QTextCursor t(textCursor()); - t.setPosition(fragment.position()); - t.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor); - t.removeSelectedText(); - setTextCursor(t); - } -} - -namespace { - -class TagAccumulator { -public: - TagAccumulator(FlatTextarea::TagList *tags) : _tags(tags) { - } - - bool changed() const { - return _changed; - } - - void feed(const QString &randomTagId, int currentPosition) { - if (randomTagId == _currentTagId) return; - - if (!_currentTagId.isEmpty()) { - int randomPartPosition = _currentTagId.lastIndexOf('/'); - t_assert(randomPartPosition > 0); - - bool tagChanged = true; - if (_currentTag < _tags->size()) { - auto &alreadyTag = _tags->at(_currentTag); - if (alreadyTag.offset == _currentStart && - alreadyTag.length == currentPosition - _currentStart && - alreadyTag.id == _currentTagId.midRef(0, randomPartPosition)) { - tagChanged = false; - } - } - if (tagChanged) { - _changed = true; - FlatTextarea::Tag tag = { - _currentStart, - currentPosition - _currentStart, - _currentTagId.mid(0, randomPartPosition), - }; - if (_currentTag < _tags->size()) { - (*_tags)[_currentTag] = tag; - } else { - _tags->push_back(tag); - } - } - ++_currentTag; - } - _currentTagId = randomTagId; - _currentStart = currentPosition; - }; - - void finish() { - if (_currentTag < _tags->size()) { - _tags->resize(_currentTag); - _changed = true; - } - } - -private: - FlatTextarea::TagList *_tags; - bool _changed = false; - - int _currentTag = 0; - int _currentStart = 0; - QString _currentTagId; - -}; - -} // namespace - -QString FlatTextarea::getTextPart(int start, int end, TagList *outTagsList, bool *outTagsChanged) const { - if (end >= 0 && end <= start) return QString(); - - if (start < 0) start = 0; - bool full = (start == 0) && (end < 0); - - TagAccumulator tagAccumulator(outTagsList); - - QTextDocument *doc(document()); - QTextBlock from = full ? doc->begin() : doc->findBlock(start), till = (end < 0) ? doc->end() : doc->findBlock(end); - if (till.isValid()) till = till.next(); - - int32 possibleLen = 0; - for (QTextBlock b = from; b != till; b = b.next()) { - possibleLen += b.length(); - } - QString result; - result.reserve(possibleLen + 1); - if (!full && end < 0) { - end = possibleLen; - } - - bool tillFragmentEnd = full; - for (auto b = from; b != till; b = b.next()) { - for (auto iter = b.begin(); !iter.atEnd(); ++iter) { - QTextFragment fragment(iter.fragment()); - if (!fragment.isValid()) continue; - - int32 p = full ? 0 : fragment.position(), e = full ? 0 : (p + fragment.length()); - if (!full) { - tillFragmentEnd = (e <= end); - if (p == end) { - tagAccumulator.feed(fragment.charFormat().anchorName(), result.size()); - } - if (p >= end) { - break; - } - if (e <= start) { - continue; - } - } - if (full || p >= start) { - tagAccumulator.feed(fragment.charFormat().anchorName(), result.size()); - } - - QTextCharFormat f = fragment.charFormat(); - QString emojiText; - QString t(fragment.text()); - if (!full) { - if (p < start) { - t = t.mid(start - p, end - start); - } else if (e > end) { - t = t.mid(0, end - p); - } - } - QChar *ub = t.data(), *uc = ub, *ue = uc + t.size(); - for (; uc != ue; ++uc) { - switch (uc->unicode()) { - case 0xfdd0: // QTextBeginningOfFrame - case 0xfdd1: // QTextEndOfFrame - case QChar::ParagraphSeparator: - case QChar::LineSeparator: - *uc = QLatin1Char('\n'); - break; - case QChar::Nbsp: - *uc = QLatin1Char(' '); - break; - case QChar::ObjectReplacementCharacter: - if (emojiText.isEmpty() && f.isImageFormat()) { - QString imageName = static_cast(&f)->name(); - if (imageName.startsWith(qstr("emoji://e."))) { - if (EmojiPtr emoji = emojiFromUrl(imageName)) { - emojiText = emojiString(emoji); - } - } - } - if (uc > ub) result.append(ub, uc - ub); - if (!emojiText.isEmpty()) result.append(emojiText); - ub = uc + 1; - break; - } - } - if (uc > ub) result.append(ub, uc - ub); - } - result.append('\n'); - } - result.chop(1); - - if (tillFragmentEnd) tagAccumulator.feed(QString(), result.size()); - tagAccumulator.finish(); - - if (outTagsChanged) { - *outTagsChanged = tagAccumulator.changed(); - } - return result; -} - -bool FlatTextarea::hasText() const { - QTextDocument *doc(document()); - QTextBlock from = doc->begin(), till = doc->end(); - - if (from == till) return false; - - for (QTextBlock::Iterator iter = from.begin(); !iter.atEnd(); ++iter) { - QTextFragment fragment(iter.fragment()); - if (!fragment.isValid()) continue; - if (!fragment.text().isEmpty()) return true; - } - return (from.next() != till); -} - -bool FlatTextarea::isUndoAvailable() const { - return _undoAvailable; -} - -bool FlatTextarea::isRedoAvailable() const { - return _redoAvailable; -} - -void FlatTextarea::parseLinks() { // some code is duplicated in text.cpp! - LinkRanges newLinks; - - QString text(toPlainText()); - if (text.isEmpty()) { - if (!_links.isEmpty()) { - _links.clear(); - emit linksChanged(); - } - return; - } - - initLinkSets(); - - int32 len = text.size(); - const QChar *start = text.unicode(), *end = start + text.size(); - for (int32 offset = 0, matchOffset = offset; offset < len;) { - QRegularExpressionMatch m = reDomain().match(text, matchOffset); - if (!m.hasMatch()) break; - - int32 domainOffset = m.capturedStart(); - - QString protocol = m.captured(1).toLower(); - QString topDomain = m.captured(3).toLower(); - - bool isProtocolValid = protocol.isEmpty() || validProtocols().contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); - bool isTopDomainValid = !protocol.isEmpty() || validTopDomains().contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar))); - - if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) { - QString forMailName = text.mid(offset, domainOffset - offset - 1); - QRegularExpressionMatch mMailName = reMailName().match(forMailName); - if (mMailName.hasMatch()) { - offset = matchOffset = m.capturedEnd(); - continue; - } - } - if (!isProtocolValid || !isTopDomainValid) { - offset = matchOffset = m.capturedEnd(); - continue; - } - - QStack parenth; - const QChar *domainEnd = start + m.capturedEnd(), *p = domainEnd; - for (; p < end; ++p) { - QChar ch(*p); - if (chIsLinkEnd(ch)) break; // link finished - if (chIsAlmostLinkEnd(ch)) { - const QChar *endTest = p + 1; - while (endTest < end && chIsAlmostLinkEnd(*endTest)) { - ++endTest; - } - if (endTest >= end || chIsLinkEnd(*endTest)) { - break; // link finished at p - } - p = endTest; - ch = *p; - } - if (ch == '(' || ch == '[' || ch == '{' || ch == '<') { - parenth.push(p); - } else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') { - if (parenth.isEmpty()) break; - const QChar *q = parenth.pop(), open(*q); - if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) { - p = q; - break; - } - } - } - if (p > domainEnd) { // check, that domain ended - if (domainEnd->unicode() != '/' && domainEnd->unicode() != '?') { - matchOffset = domainEnd - start; - continue; - } - } - newLinks.push_back({ domainOffset - 1, static_cast(p - start - domainOffset + 2) }); - offset = matchOffset = p - start; - } - - if (newLinks != _links) { - _links = newLinks; - emit linksChanged(); - } -} - -QStringList FlatTextarea::linksList() const { - QStringList result; - if (!_links.isEmpty()) { - QString text(toPlainText()); - for_const (auto &link, _links) { - result.push_back(text.mid(link.start + 1, link.length - 2)); - } - } - return result; -} - -void FlatTextarea::insertFromMimeData(const QMimeData *source) { - auto mime = tagsMimeType(); - auto text = source->text(); - if (source->hasFormat(mime)) { - auto tagsData = source->data(mime); - _insertedTags = deserializeTagsList(tagsData, text.size()); - _insertedTagsAreFromMime = true; - } else { - _insertedTags.clear(); - } - auto cursor = textCursor(); - _realInsertPosition = qMin(cursor.position(), cursor.anchor()); - _realCharsAdded = text.size(); - QTextEdit::insertFromMimeData(source); - if (!_inDrop) { - emit spacedReturnedPasted(); - _insertedTags.clear(); - _realInsertPosition = -1; - } -} - -void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) { - QTextImageFormat imageFormat; - int32 ew = ESize + st::emojiPadding * cIntRetinaFactor() * 2, eh = _st.font->height * cIntRetinaFactor(); - imageFormat.setWidth(ew / cIntRetinaFactor()); - imageFormat.setHeight(eh / cIntRetinaFactor()); - imageFormat.setName(qsl("emoji://e.") + QString::number(emojiKey(emoji), 16)); - imageFormat.setVerticalAlignment(QTextCharFormat::AlignBaseline); - if (c.charFormat().isAnchor()) { - imageFormat.setAnchor(true); - imageFormat.setAnchorName(c.charFormat().anchorName()); - imageFormat.setForeground(st::defaultTextStyle.linkFg); - } - static QString objectReplacement(QChar::ObjectReplacementCharacter); - c.insertText(objectReplacement, imageFormat); -} - -QVariant FlatTextarea::loadResource(int type, const QUrl &name) { - QString imageName = name.toDisplayString(); - if (imageName.startsWith(qstr("emoji://e."))) { - if (EmojiPtr emoji = emojiFromUrl(imageName)) { - return QVariant(App::emojiSingle(emoji, _st.font->height)); - } - } - return QVariant(); -} - -void FlatTextarea::checkContentHeight() { - if (heightAutoupdated()) { - emit resized(); - } -} - -namespace { - -// Optimization: with null page size document does not re-layout -// on each insertText / mergeCharFormat. -void prepareFormattingOptimization(QTextDocument *document) { - if (!document->pageSize().isNull()) { - document->setPageSize(QSizeF(0, 0)); - } -} - -void removeTags(const style::color &textFg, QTextDocument *document, int from, int end) { - QTextCursor c(document->docHandle(), 0); - c.setPosition(from); - c.setPosition(end, QTextCursor::KeepAnchor); - - QTextCharFormat format; - format.setAnchor(false); - format.setAnchorName(QString()); - format.setForeground(textFg); - c.mergeCharFormat(format); -} - -// Returns the position of the first inserted tag or "changedEnd" value if none found. -int processInsertedTags(const style::color &textFg, QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags, FlatTextarea::TagMimeProcessor *processor) { - int firstTagStart = changedEnd; - int applyNoTagFrom = changedEnd; - for_const (auto &tag, tags) { - int tagFrom = changedPosition + tag.offset; - int tagTo = tagFrom + tag.length; - accumulate_max(tagFrom, changedPosition); - accumulate_min(tagTo, changedEnd); - auto tagId = processor ? processor->tagFromMimeTag(tag.id) : tag.id; - if (tagTo > tagFrom && !tagId.isEmpty()) { - accumulate_min(firstTagStart, tagFrom); - - prepareFormattingOptimization(document); - - if (applyNoTagFrom < tagFrom) { - removeTags(textFg, document, applyNoTagFrom, tagFrom); - } - QTextCursor c(document->docHandle(), 0); - c.setPosition(tagFrom); - c.setPosition(tagTo, QTextCursor::KeepAnchor); - - QTextCharFormat format; - format.setAnchor(true); - format.setAnchorName(tagId + '/' + QString::number(rand_value())); - format.setForeground(st::defaultTextStyle.linkFg); - c.mergeCharFormat(format); - - applyNoTagFrom = tagTo; - } - } - if (applyNoTagFrom < changedEnd) { - removeTags(textFg, document, applyNoTagFrom, changedEnd); - } - - return firstTagStart; -} - -// When inserting a part of text inside a tag we need to have -// a way to know if the insertion replaced the end of the tag -// or it was strictly inside (in the middle) of the tag. -bool wasInsertTillTheEndOfTag(QTextBlock block, QTextBlock::iterator fragmentIt, int insertionEnd) { - auto insertTagName = fragmentIt.fragment().charFormat().anchorName(); - while (true) { - for (; !fragmentIt.atEnd(); ++fragmentIt) { - auto fragment = fragmentIt.fragment(); - bool fragmentOutsideInsertion = (fragment.position() >= insertionEnd); - if (fragmentOutsideInsertion) { - return (fragment.charFormat().anchorName() != insertTagName); - } - int fragmentEnd = fragment.position() + fragment.length(); - bool notFullFragmentInserted = (fragmentEnd > insertionEnd); - if (notFullFragmentInserted) { - return false; - } - } - if (block.isValid()) { - fragmentIt = block.begin(); - block = block.next(); - } else { - break; - } - } - // Insertion goes till the end of the text => not strictly inside a tag. - return true; -} - -struct FormattingAction { - enum class Type { - Invalid, - InsertEmoji, - TildeFont, - RemoveTag, - }; - Type type = Type::Invalid; - EmojiPtr emoji = nullptr; - bool isTilde = false; - int intervalStart = 0; - int intervalEnd = 0; -}; - -} // namespace - -void FlatTextarea::processFormatting(int insertPosition, int insertEnd) { - // Tilde formatting. - auto regularFont = qsl("Open Sans"), semiboldFont = qsl("Open Sans Semibold"); - bool tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == regularFont); - bool isTildeFragment = false; - - // First tag handling (the one we inserted text to). - bool startTagFound = false; - bool breakTagOnNotLetter = false; - - auto doc = document(); - - // Apply inserted tags. - auto insertedTagsProcessor = _insertedTagsAreFromMime ? _tagMimeProcessor.get() : nullptr; - int breakTagOnNotLetterTill = processInsertedTags(_st.textColor, doc, insertPosition, insertEnd, - _insertedTags, insertedTagsProcessor); - using ActionType = FormattingAction::Type; - while (true) { - FormattingAction action; - - auto fromBlock = doc->findBlock(insertPosition); - auto tillBlock = doc->findBlock(insertEnd); - if (tillBlock.isValid()) tillBlock = tillBlock.next(); - - for (auto block = fromBlock; block != tillBlock; block = block.next()) { - for (auto fragmentIt = block.begin(); !fragmentIt.atEnd(); ++fragmentIt) { - auto fragment = fragmentIt.fragment(); - t_assert(fragment.isValid()); - - int fragmentPosition = fragment.position(); - if (insertPosition >= fragmentPosition + fragment.length()) { - continue; - } - int changedPositionInFragment = insertPosition - fragmentPosition; // Can be negative. - int changedEndInFragment = insertEnd - fragmentPosition; - if (changedEndInFragment <= 0) { - break; - } - - auto charFormat = fragment.charFormat(); - if (tildeFormatting) { - isTildeFragment = (charFormat.fontFamily() == semiboldFont); - } - - auto fragmentText = fragment.text(); - auto *textStart = fragmentText.constData(); - auto *textEnd = textStart + fragmentText.size(); - - if (!startTagFound) { - startTagFound = true; - auto tagName = charFormat.anchorName(); - if (!tagName.isEmpty()) { - breakTagOnNotLetter = wasInsertTillTheEndOfTag(block, fragmentIt, insertEnd); - } - } - - auto *ch = textStart + qMax(changedPositionInFragment, 0); - for (; ch < textEnd; ++ch) { - int emojiLength = 0; - if (auto emoji = emojiFromText(ch, textEnd, &emojiLength)) { - // Replace emoji if no current action is prepared. - if (action.type == ActionType::Invalid) { - action.type = ActionType::InsertEmoji; - action.emoji = emoji; - action.intervalStart = fragmentPosition + (ch - textStart); - action.intervalEnd = action.intervalStart + emojiLength; - } - break; - } - - if (breakTagOnNotLetter && !ch->isLetter()) { - // Remove tag name till the end if no current action is prepared. - if (action.type != ActionType::Invalid) { - break; - } - breakTagOnNotLetter = false; - if (fragmentPosition + (ch - textStart) < breakTagOnNotLetterTill) { - action.type = ActionType::RemoveTag; - action.intervalStart = fragmentPosition + (ch - textStart); - action.intervalEnd = breakTagOnNotLetterTill; - break; - } - } - if (tildeFormatting) { // Tilde symbol fix in OpenSans. - bool tilde = (ch->unicode() == '~'); - if ((tilde && !isTildeFragment) || (!tilde && isTildeFragment)) { - if (action.type == ActionType::Invalid) { - action.type = ActionType::TildeFont; - action.intervalStart = fragmentPosition + (ch - textStart); - action.intervalEnd = action.intervalStart + 1; - action.isTilde = tilde; - } else { - ++action.intervalEnd; - } - } else if (action.type == ActionType::TildeFont) { - break; - } - } - - if (ch + 1 < textEnd && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) { - ++ch; - ++fragmentPosition; - } - } - if (action.type != ActionType::Invalid) break; - } - if (action.type != ActionType::Invalid) break; - } - if (action.type != ActionType::Invalid) { - prepareFormattingOptimization(doc); - - QTextCursor c(doc->docHandle(), 0); - c.setPosition(action.intervalStart); - c.setPosition(action.intervalEnd, QTextCursor::KeepAnchor); - if (action.type == ActionType::InsertEmoji) { - insertEmoji(action.emoji, c); - insertPosition = action.intervalStart + 1; - } else if (action.type == ActionType::RemoveTag) { - QTextCharFormat format; - format.setAnchor(false); - format.setAnchorName(QString()); - format.setForeground(_st.textColor); - c.mergeCharFormat(format); - } else if (action.type == ActionType::TildeFont) { - QTextCharFormat format; - format.setFontFamily(action.isTilde ? semiboldFont : regularFont); - c.mergeCharFormat(format); - insertPosition = action.intervalEnd; - } - } else { - break; - } - } -} - -void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int charsAdded) { - if (_correcting) return; - - int insertPosition = (_realInsertPosition >= 0) ? _realInsertPosition : position; - int insertLength = (_realInsertPosition >= 0) ? _realCharsAdded : charsAdded; - - int removePosition = position; - int removeLength = charsRemoved; - - QTextCursor(document()->docHandle(), 0).joinPreviousEditBlock(); - - _correcting = true; - if (_maxLength >= 0) { - QTextCursor c(document()->docHandle(), 0); - c.movePosition(QTextCursor::End); - int32 fullSize = c.position(), toRemove = fullSize - _maxLength; - if (toRemove > 0) { - if (toRemove > insertLength) { - if (insertLength) { - c.setPosition(insertPosition); - c.setPosition((insertPosition + insertLength), QTextCursor::KeepAnchor); - c.removeSelectedText(); - } - c.setPosition(fullSize - (toRemove - insertLength)); - c.setPosition(fullSize, QTextCursor::KeepAnchor); - c.removeSelectedText(); - } else { - c.setPosition(insertPosition + (insertLength - toRemove)); - c.setPosition(insertPosition + insertLength, QTextCursor::KeepAnchor); - c.removeSelectedText(); - } - } - } - _correcting = false; - - if (insertPosition == removePosition) { - if (!_links.isEmpty()) { - bool changed = false; - for (auto i = _links.begin(); i != _links.end();) { - if (i->start + i->length <= insertPosition) { - ++i; - } else if (i->start >= removePosition + removeLength) { - i->start += insertLength - removeLength; - ++i; - } else { - i = _links.erase(i); - changed = true; - } - } - if (changed) emit linksChanged(); - } - } else { - parseLinks(); - } - - if (document()->availableRedoSteps() > 0) { - QTextCursor(document()->docHandle(), 0).endEditBlock(); - return; - } - - if (insertLength <= 0) { - QTextCursor(document()->docHandle(), 0).endEditBlock(); - return; - } - - _correcting = true; - auto pageSize = document()->pageSize(); - processFormatting(insertPosition, insertPosition + insertLength); - if (document()->pageSize() != pageSize) { - document()->setPageSize(pageSize); - } - _correcting = false; - - QTextCursor(document()->docHandle(), 0).endEditBlock(); -} - -void FlatTextarea::onDocumentContentsChanged() { - if (_correcting) return; - - auto tagsChanged = false; - auto curText = getTextPart(0, -1, &_lastTextWithTags.tags, &tagsChanged); - - _correcting = true; - correctValue(_lastTextWithTags.text, curText, _lastTextWithTags.tags); - _correcting = false; - - bool textOrTagsChanged = tagsChanged || (_lastTextWithTags.text != curText); - if (textOrTagsChanged) { - _lastTextWithTags.text = curText; - emit changed(); - checkContentHeight(); - } - updatePlaceholder(); - if (App::wnd()) App::wnd()->updateGlobalMenu(); -} - -void FlatTextarea::onUndoAvailable(bool avail) { - _undoAvailable = avail; - if (App::wnd()) App::wnd()->updateGlobalMenu(); -} - -void FlatTextarea::onRedoAvailable(bool avail) { - _redoAvailable = avail; - if (App::wnd()) App::wnd()->updateGlobalMenu(); -} - -void FlatTextarea::step_appearance(float64 ms, bool timer) { - float dt = ms / _st.phDuration; - if (dt >= 1) { - _a_appearance.stop(); - a_phLeft.finish(); - a_phAlpha.finish(); - a_phColorFocused.finish(); - a_phLeft = anim::ivalue(a_phLeft.current()); - a_phAlpha = anim::fvalue(a_phAlpha.current()); - a_phColorFocused = anim::fvalue(a_phColorFocused.current()); - } else { - a_phLeft.update(dt, anim::linear); - a_phAlpha.update(dt, anim::linear); - a_phColorFocused.update(dt, anim::linear); - } - if (timer) update(); -} - -void FlatTextarea::setPlaceholder(const QString &ph, int32 afterSymbols) { - _ph = ph; - if (_phAfter != afterSymbols) { - _phAfter = afterSymbols; - updatePlaceholder(); - } - int skipWidth = placeholderSkipWidth(); - _phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1 - skipWidth); - if (_phVisible) update(); -} - -void FlatTextarea::updatePlaceholder() { - bool vis = (getTextWithTags().text.size() <= _phAfter); - if (vis == _phVisible) return; - - a_phLeft.start(vis ? 0 : _st.phShift); - a_phAlpha.start(vis ? 1 : 0); - _a_appearance.start(); - - _phVisible = vis; -} - -QMimeData *FlatTextarea::createMimeDataFromSelection() const { - QMimeData *result = new QMimeData(); - QTextCursor c(textCursor()); - int32 start = c.selectionStart(), end = c.selectionEnd(); - if (end > start) { - TagList tags; - result->setText(getTextPart(start, end, &tags)); - if (!tags.isEmpty()) { - if (_tagMimeProcessor) { - for (auto &tag : tags) { - tag.id = _tagMimeProcessor->mimeTagFromTag(tag.id); - } - } - result->setData(tagsMimeType(), serializeTagsList(tags)); - } - } - return result; -} - -void FlatTextarea::setSubmitSettings(SubmitSettings settings) { - _submitSettings = settings; -} - -void FlatTextarea::keyPressEvent(QKeyEvent *e) { - bool shift = e->modifiers().testFlag(Qt::ShiftModifier); - bool macmeta = (cPlatform() == dbipMac || cPlatform() == dbipMacOld) && e->modifiers().testFlag(Qt::ControlModifier) && !e->modifiers().testFlag(Qt::MetaModifier) && !e->modifiers().testFlag(Qt::AltModifier); - bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier); - bool enterSubmit = (ctrl && shift); - if (ctrl && _submitSettings != SubmitSettings::None && _submitSettings != SubmitSettings::Enter) { - enterSubmit = true; - } - if (!ctrl && !shift && _submitSettings != SubmitSettings::None && _submitSettings != SubmitSettings::CtrlEnter) { - enterSubmit = true; - } - bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return); - - if (macmeta && e->key() == Qt::Key_Backspace) { - QTextCursor tc(textCursor()), start(tc); - start.movePosition(QTextCursor::StartOfLine); - tc.setPosition(start.position(), QTextCursor::KeepAnchor); - tc.removeSelectedText(); - } else if (enter && enterSubmit) { - emit submitted(ctrl && shift); - } else if (e->key() == Qt::Key_Escape) { - emit cancelled(); - } else if (e->key() == Qt::Key_Tab || (ctrl && e->key() == Qt::Key_Backtab)) { - if (ctrl) { - e->ignore(); - } else { - emit tabbed(); - } - } else if (e->key() == Qt::Key_Search || e == QKeySequence::Find) { - e->ignore(); -#ifdef Q_OS_MAC - } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { - auto cursor = textCursor(); - int start = cursor.selectionStart(), end = cursor.selectionEnd(); - if (end > start) { - TagList tags; - QApplication::clipboard()->setText(getTextPart(start, end, &tags), QClipboard::FindBuffer); - } -#endif // Q_OS_MAC - } else { - QTextCursor tc(textCursor()); - if (enter && ctrl) { - e->setModifiers(e->modifiers() & ~Qt::ControlModifier); - } - bool spaceOrReturn = false; - QString t(e->text()); - if (!t.isEmpty() && t.size() < 3) { - if (t.at(0) == '\n' || t.at(0) == '\r' || t.at(0).isSpace() || t.at(0) == QChar::LineSeparator) { - spaceOrReturn = true; - } - } - QTextEdit::keyPressEvent(e); - if (tc == textCursor()) { - bool check = false; - if (e->key() == Qt::Key_PageUp || e->key() == Qt::Key_Up) { - tc.movePosition(QTextCursor::Start, e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); - check = true; - } else if (e->key() == Qt::Key_PageDown || e->key() == Qt::Key_Down) { - tc.movePosition(QTextCursor::End, e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); - check = true; - } - if (check) { - if (tc == textCursor()) { - e->ignore(); - } else { - setTextCursor(tc); - } - } - } - if (spaceOrReturn) emit spacedReturnedPasted(); - } -} - -void FlatTextarea::resizeEvent(QResizeEvent *e) { - _phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1); - QTextEdit::resizeEvent(e); - checkContentHeight(); -} - -void FlatTextarea::mousePressEvent(QMouseEvent *e) { - QTextEdit::mousePressEvent(e); -} - -void FlatTextarea::dropEvent(QDropEvent *e) { - _inDrop = true; - QTextEdit::dropEvent(e); - _inDrop = false; - _insertedTags.clear(); - _realInsertPosition = -1; - - emit spacedReturnedPasted(); -} - -void FlatTextarea::contextMenuEvent(QContextMenuEvent *e) { - if (auto menu = createStandardContextMenu()) { - (new Ui::PopupMenu(menu))->popup(e->globalPos()); - } -} diff --git a/Telegram/SourceFiles/ui/flattextarea.h b/Telegram/SourceFiles/ui/flattextarea.h deleted file mode 100644 index d78b6f904..000000000 --- a/Telegram/SourceFiles/ui/flattextarea.h +++ /dev/null @@ -1,261 +0,0 @@ -/* -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 "animation.h" - -class UserData; -static UserData * const LookingUpInlineBot = SharedMemoryLocation(); - -class FlatTextarea : public QTextEdit { - Q_OBJECT - T_WIDGET - -public: - struct Tag { - int offset, length; - QString id; - }; - using TagList = QVector; - struct TextWithTags { - using Tags = FlatTextarea::TagList; - QString text; - Tags tags; - }; - - static QByteArray serializeTagsList(const TagList &tags); - static TagList deserializeTagsList(QByteArray data, int textLength); - static QString tagsMimeType(); - - FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &ph = QString(), const QString &val = QString(), const TagList &tags = TagList()); - - void setMaxLength(int32 maxLength); - void setMinHeight(int32 minHeight); - void setMaxHeight(int32 maxHeight); - - void setPlaceholder(const QString &ph, int32 afterSymbols = 0); - void updatePlaceholder(); - void finishPlaceholder(); - - QRect getTextRect() const; - int32 fakeMargin() const; - - void step_appearance(float64 ms, bool timer); - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - - EmojiPtr getSingleEmoji() const; - QString getMentionHashtagBotCommandPart(bool &start) const; - - // Get the current inline bot and request string for it. - // The *outInlineBot can be filled by LookingUpInlineBot shared ptr. - // In that case the caller should lookup the bot by *outInlineBotUsername. - QString getInlineBotQuery(UserData **outInlineBot, QString *outInlineBotUsername) const; - - void removeSingleEmoji(); - bool hasText() const; - - bool isUndoAvailable() const; - bool isRedoAvailable() const; - - void parseLinks(); - QStringList linksList() const; - - void insertFromMimeData(const QMimeData *source) override; - - QMimeData *createMimeDataFromSelection() const override; - - enum class SubmitSettings { - None, - Enter, - CtrlEnter, - Both, - }; - void setSubmitSettings(SubmitSettings settings); - - const TextWithTags &getTextWithTags() const { - return _lastTextWithTags; - } - TextWithTags getTextWithTagsPart(int start, int end = -1); - void insertTag(const QString &text, QString tagId = QString()); - - bool isEmpty() const { - return _lastTextWithTags.text.isEmpty(); - } - - enum UndoHistoryAction { - AddToUndoHistory, - MergeWithUndoHistory, - ClearUndoHistory - }; - void setTextWithTags(const TextWithTags &textWithTags, UndoHistoryAction undoHistoryAction = AddToUndoHistory); - - // If you need to make some preparations of tags before putting them to QMimeData - // (and then to clipboard or to drag-n-drop object), here is a strategy for that. - class TagMimeProcessor { - public: - virtual QString mimeTagFromTag(const QString &tagId) = 0; - virtual QString tagFromMimeTag(const QString &mimeTag) = 0; - virtual ~TagMimeProcessor() { - } - }; - void setTagMimeProcessor(std_::unique_ptr &&processor); - -public slots: - void onTouchTimer(); - - void onDocumentContentsChange(int position, int charsRemoved, int charsAdded); - void onDocumentContentsChanged(); - - void onUndoAvailable(bool avail); - void onRedoAvailable(bool avail); - -signals: - void resized(); - void changed(); - void submitted(bool ctrlShiftEnter); - void cancelled(); - void tabbed(); - void spacedReturnedPasted(); - void linksChanged(); - -protected: - void enterEventHook(QEvent *e) { - return QTextEdit::enterEvent(e); - } - void leaveEventHook(QEvent *e) { - return QTextEdit::leaveEvent(e); - } - - bool viewportEvent(QEvent *e) override; - void touchEvent(QTouchEvent *e); - void paintEvent(QPaintEvent *e) override; - void focusInEvent(QFocusEvent *e) override; - void focusOutEvent(QFocusEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void dropEvent(QDropEvent *e) override; - void contextMenuEvent(QContextMenuEvent *e) override; - - virtual void correctValue(const QString &was, QString &now, TagList &nowTags) { - } - - void insertEmoji(EmojiPtr emoji, QTextCursor c); - - QVariant loadResource(int type, const QUrl &name) override; - - void checkContentHeight(); - -private: - // "start" and "end" are in coordinates of text where emoji are replaced - // by ObjectReplacementCharacter. If "end" = -1 means get text till the end. - QString getTextPart(int start, int end, TagList *outTagsList, bool *outTagsChanged = nullptr) const; - - void getSingleEmojiFragment(QString &text, QTextFragment &fragment) const; - - // After any characters added we must postprocess them. This includes: - // 1. Replacing font family to semibold for ~ characters, if we used Open Sans 13px. - // 2. Replacing font family from semibold for all non-~ characters, if we used ... - // 3. Replacing emoji code sequences by ObjectReplacementCharacters with emoji pics. - // 4. Interrupting tags in which the text was inserted by any char except a letter. - // 5. Applying tags from "_insertedTags" in case we pasted text with tags, not just text. - // Rule 4 applies only if we inserted chars not in the middle of a tag (but at the end). - void processFormatting(int changedPosition, int changedEnd); - - bool heightAutoupdated(); - - int placeholderSkipWidth() const; - - int _minHeight = -1; // < 0 - no autosize - int _maxHeight = -1; - int _maxLength = -1; - SubmitSettings _submitSettings = SubmitSettings::Enter; - - QString _ph, _phelided; - int _phAfter = 0; - bool _phVisible; - anim::ivalue a_phLeft; - anim::fvalue a_phAlpha; - anim::fvalue a_phColorFocused; - Animation _a_appearance; - - TextWithTags _lastTextWithTags; - - // Tags list which we should apply while setText() call or insert from mime data. - TagList _insertedTags; - bool _insertedTagsAreFromMime; - - // Override insert position and charsAdded from complex text editing - // (like drag-n-drop in the same text edit field). - int _realInsertPosition = -1; - int _realCharsAdded = 0; - - std_::unique_ptr _tagMimeProcessor; - - const style::flatTextarea &_st; - - bool _undoAvailable = false; - bool _redoAvailable = false; - bool _inDrop = false; - bool _inHeightCheck = false; - - int _fakeMargin = 0; - - QTimer _touchTimer; - bool _touchPress = false; - bool _touchRightButton = false; - bool _touchMove = false; - QPoint _touchStart; - - bool _correcting = false; - - struct LinkRange { - int start; - int length; - }; - friend bool operator==(const LinkRange &a, const LinkRange &b); - friend bool operator!=(const LinkRange &a, const LinkRange &b); - using LinkRanges = QVector; - LinkRanges _links; -}; - -inline bool operator==(const FlatTextarea::Tag &a, const FlatTextarea::Tag &b) { - return (a.offset == b.offset) && (a.length == b.length) && (a.id == b.id); -} -inline bool operator!=(const FlatTextarea::Tag &a, const FlatTextarea::Tag &b) { - return !(a == b); -} - -inline bool operator==(const FlatTextarea::TextWithTags &a, const FlatTextarea::TextWithTags &b) { - return (a.text == b.text) && (a.tags == b.tags); -} -inline bool operator!=(const FlatTextarea::TextWithTags &a, const FlatTextarea::TextWithTags &b) { - return !(a == b); -} - -inline bool operator==(const FlatTextarea::LinkRange &a, const FlatTextarea::LinkRange &b) { - return (a.start == b.start) && (a.length == b.length); -} -inline bool operator!=(const FlatTextarea::LinkRange &a, const FlatTextarea::LinkRange &b) { - return !(a == b); -} diff --git a/Telegram/SourceFiles/ui/widgets/buttons.cpp b/Telegram/SourceFiles/ui/widgets/buttons.cpp index c36909300..2e8457c15 100644 --- a/Telegram/SourceFiles/ui/widgets/buttons.cpp +++ b/Telegram/SourceFiles/ui/widgets/buttons.cpp @@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "ui/widgets/buttons.h" +#include "ui/effects/ripple_animation.h" + namespace Ui { FlatButton::FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st) : AbstractButton(parent) @@ -87,6 +89,27 @@ void FlatButton::onStateChanged(int oldState, StateChangeSource source) { } else { _a_appearance.start(); } + + handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress)); +} + +void FlatButton::handleRipples(bool wasDown, bool wasPress) { + auto down = static_cast(_state & StateDown); + if (!_st.ripple.showDuration || down == wasDown) { + return; + } + + if (down && wasPress) { + // Start a ripple only from mouse press. + if (!_ripple) { + _ripple = std_::make_unique(_st.ripple, prepareRippleMask(), [this] { update(); }); + } + auto clickPosition = mapFromGlobal(QCursor::pos()); + _ripple->add(clickPosition); + } else if (!down && _ripple) { + // Finish ripple anyway. + _ripple->stopLast(); + } } void FlatButton::paintEvent(QPaintEvent *e) { @@ -97,6 +120,14 @@ void FlatButton::paintEvent(QPaintEvent *e) { p.setOpacity(_opacity); p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current())); + auto ms = getms(); + if (_ripple) { + _ripple->paint(p, 0, 0, width(), ms); + if (_ripple->empty()) { + _ripple.reset(); + } + } + p.setFont((_state & StateOver) ? _st.overFont : _st.font); p.setRenderHint(QPainter::TextAntialiasing); p.setPen(anim::pen(_st.color, _st.overColor, a_over.current())); @@ -107,6 +138,15 @@ void FlatButton::paintEvent(QPaintEvent *e) { p.drawText(r, _text, style::al_top); } +QImage FlatButton::prepareRippleMask() const { + auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(QColor(255, 255, 255)); + return std_::move(result); +} + +FlatButton::~FlatButton() = default; + LinkButton::LinkButton(QWidget *parent, const QString &text, const style::LinkButton &st) : AbstractButton(parent) , _text(text) , _textWidth(st.font->width(_text)) @@ -210,16 +250,25 @@ int RoundButton::contentWidth() const { void RoundButton::paintEvent(QPaintEvent *e) { Painter p(this); - int innerWidth = contentWidth(); - auto rounded = rtlrect(rect().marginsRemoved(_st.padding), width()); + auto innerWidth = contentWidth(); + auto rounded = rect().marginsRemoved(_st.padding); if (_fullWidthOverride < 0) { rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height()); } - App::roundRect(p, rounded, _st.textBg, ImageRoundRadius::Small); + App::roundRect(p, myrtlrect(rounded), _st.textBg, ImageRoundRadius::Small); auto over = (_state & StateOver); + auto down = (_state & StateDown); if (over) { - App::roundRect(p, rounded, _st.textBgOver, ImageRoundRadius::Small); + App::roundRect(p, myrtlrect(rounded), _st.textBgOver, ImageRoundRadius::Small); + } + + auto ms = getms(); + if (_ripple) { + _ripple->paint(p, rounded.x(), rounded.y(), width(), ms); + if (_ripple->empty()) { + _ripple.reset(); + } } p.setFont(_st.font); @@ -230,12 +279,12 @@ void RoundButton::paintEvent(QPaintEvent *e) { int textTopDelta = (_state & StateDown) ? (_st.downTextTop - _st.textTop) : 0; int textTop = _st.padding.top() + _st.textTop + textTopDelta; if (!_text.isEmpty()) { - p.setPen(over ? _st.textFgOver : _st.textFg); + p.setPen((over || down) ? _st.textFgOver : _st.textFg); p.drawTextLeft(textLeft, textTop, width(), _text); } if (!_secondaryText.isEmpty()) { textLeft += _textWidth + (_textWidth ? _st.secondarySkip : 0); - p.setPen(over ? _st.secondaryTextFgOver : _st.secondaryTextFg); + p.setPen((over || down) ? _st.secondaryTextFgOver : _st.secondaryTextFg); p.drawTextLeft(textLeft, textTop, width(), _secondaryText); } _st.icon.paint(p, QPoint(_st.padding.left(), _st.padding.right() + textTopDelta), width()); @@ -243,8 +292,50 @@ void RoundButton::paintEvent(QPaintEvent *e) { void RoundButton::onStateChanged(int oldState, StateChangeSource source) { update(); + + handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress)); } +void RoundButton::handleRipples(bool wasDown, bool wasPress) { + auto down = static_cast(_state & StateDown); + if (!_st.ripple.showDuration || down == wasDown) { + return; + } + + if (down && wasPress) { + // Start a ripple only from mouse press. + if (!_ripple) { + _ripple = std_::make_unique(_st.ripple, prepareRippleMask(), [this] { update(); }); + } + auto clickPosition = mapFromGlobal(QCursor::pos()) - QPoint(_st.padding.left(), _st.padding.top()); + _ripple->add(clickPosition); + } else if (!down && _ripple) { + // Finish ripple anyway. + _ripple->stopLast(); + } +} + +QImage RoundButton::prepareRippleMask() const { + auto innerWidth = contentWidth(); + auto rounded = rtlrect(rect().marginsRemoved(_st.padding), width()); + if (_fullWidthOverride < 0) { + rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height()); + } + + auto result = QImage(rounded.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + { + Painter p(&result); + p.setPen(Qt::NoPen); + p.setBrush(QColor(255, 255, 255)); + p.drawRoundedRect(rounded.translated(-rounded.topLeft()), st::buttonRadius, st::buttonRadius); + } + return std_::move(result); +} + +RoundButton::~RoundButton() = default; + IconButton::IconButton(QWidget *parent, const style::IconButton &st) : AbstractButton(parent) , _st(st) { resize(_st.width, _st.height); @@ -260,6 +351,15 @@ void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) { void IconButton::paintEvent(QPaintEvent *e) { Painter p(this); + auto ms = getms(); + + if (_ripple) { + _ripple->paint(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), width(), ms); + if (_ripple->empty()) { + _ripple.reset(); + } + } + auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); auto overIcon = [this] { if (_iconOverrideOver) { @@ -306,6 +406,50 @@ void IconButton::onStateChanged(int oldState, StateChangeSource source) { update(); } } + + handleRipples(oldState & StateDown, (source == StateChangeSource::ByPress)); } +void IconButton::handleRipples(bool wasDown, bool wasPress) { + auto down = static_cast(_state & StateDown); + if (!_st.ripple.showDuration || _st.rippleAreaSize <= 0 || down == wasDown) { + return; + } + + if (down && wasPress) { + // Start a ripple only from mouse press. + if (!_ripple) { + _ripple = std_::make_unique(_st.ripple, prepareRippleMask(), [this] { update(); }); + } + auto clickPosition = mapFromGlobal(QCursor::pos()); + auto rippleCenter = QRect(_st.rippleAreaPosition, QSize(_st.rippleAreaSize, _st.rippleAreaSize)).center(); + auto clickRadiusSquare = style::point::dotProduct(clickPosition - rippleCenter, clickPosition - rippleCenter); + auto startRadius = 0; + if (clickRadiusSquare * 4 > _st.rippleAreaSize * _st.rippleAreaSize) { + startRadius = sqrt(clickRadiusSquare) - (_st.rippleAreaSize / 2); + } + _ripple->add(clickPosition - _st.rippleAreaPosition, startRadius); + } else if (!down && _ripple) { + // Finish ripple anyway. + _ripple->stopLast(); + } +} + +QImage IconButton::prepareRippleMask() const { + auto size = _st.rippleAreaSize * cIntRetinaFactor(); + auto result = QImage(size, size, QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + { + Painter p(&result); + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.setPen(Qt::NoPen); + p.setBrush(QColor(255, 255, 255)); + p.drawEllipse(0, 0, _st.rippleAreaSize, _st.rippleAreaSize); + } + return std_::move(result); +} + +IconButton::~IconButton() = default; + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/buttons.h b/Telegram/SourceFiles/ui/widgets/buttons.h index f7c1011a5..c9f5ca38c 100644 --- a/Telegram/SourceFiles/ui/widgets/buttons.h +++ b/Telegram/SourceFiles/ui/widgets/buttons.h @@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { +class RippleAnimation; + class FlatButton : public AbstractButton { public: FlatButton(QWidget *parent, const QString &text, const style::FlatButton &st); @@ -38,12 +40,17 @@ public: int32 textWidth() const; + ~FlatButton(); + protected: void paintEvent(QPaintEvent *e) override; void onStateChanged(int oldState, StateChangeSource source) override; private: + QImage prepareRippleMask() const; + void handleRipples(bool wasDown, bool wasPress); + QString _text, _textForAutoSize; int _width; @@ -54,6 +61,8 @@ private: float64 _opacity = 1.; + std_::unique_ptr _ripple; + }; class LinkButton : public AbstractButton { @@ -93,12 +102,17 @@ public: }; void setTextTransform(TextTransform transform); + ~RoundButton(); + protected: void paintEvent(QPaintEvent *e) override; void onStateChanged(int oldState, StateChangeSource source) override; private: + QImage prepareRippleMask() const; + void handleRipples(bool wasDown, bool wasPress); + void updateText(); void resizeToText(); @@ -114,6 +128,8 @@ private: TextTransform _transform = TextTransform::ToUpper; + std_::unique_ptr _ripple; + }; class IconButton : public AbstractButton { @@ -123,18 +139,25 @@ public: // Pass nullptr to restore the default icon. void setIcon(const style::icon *icon, const style::icon *iconOver = nullptr); + ~IconButton(); + protected: void paintEvent(QPaintEvent *e) override; void onStateChanged(int oldState, StateChangeSource source) override; private: + QImage prepareRippleMask() const; + void handleRipples(bool wasDown, bool wasPress); + const style::IconButton &_st; const style::icon *_iconOverride = nullptr; const style::icon *_iconOverrideOver = nullptr; FloatAnimation _a_over; + std_::unique_ptr _ripple; + }; } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.h b/Telegram/SourceFiles/ui/widgets/checkbox.h index ad5b8c7cc..2b208c192 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.h +++ b/Telegram/SourceFiles/ui/widgets/checkbox.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/abstract_button.h" +#include "styles/style_widgets.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/flatinput.cpp b/Telegram/SourceFiles/ui/widgets/input_fields.cpp similarity index 63% rename from Telegram/SourceFiles/ui/flatinput.cpp rename to Telegram/SourceFiles/ui/widgets/input_fields.cpp index ec04a0da6..7086b53d9 100644 --- a/Telegram/SourceFiles/ui/flatinput.cpp +++ b/Telegram/SourceFiles/ui/widgets/input_fields.cpp @@ -19,59 +19,1462 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "ui/flatinput.h" +#include "ui/widgets/input_fields.h" #include "ui/widgets/popup_menu.h" #include "mainwindow.h" -#include "countryinput.h" +#include "ui/countryinput.h" #include "lang.h" #include "numbers.h" +namespace Ui { namespace { - template - class InputStyle : public QCommonStyle { - public: - InputStyle() { - setParent(QCoreApplication::instance()); - } - void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const { +template +class InputStyle : public QCommonStyle { +public: + InputStyle() { + setParent(QCoreApplication::instance()); + } + + void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const { + } + QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const { + switch (r) { + case SE_LineEditContents: + const InputClass *w = widget ? qobject_cast(widget) : 0; + return w ? w->getTextRect() : QCommonStyle::subElementRect(r, opt, widget); + break; } - QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const { - switch (r) { - case SE_LineEditContents: - const InputClass *w = widget ? qobject_cast(widget) : 0; - return w ? w->getTextRect() : QCommonStyle::subElementRect(r, opt, widget); - break; + return QCommonStyle::subElementRect(r, opt, widget); + } + + static InputStyle *instance() { + if (!_instance) { + if (!QGuiApplication::instance()) { + return nullptr; } - return QCommonStyle::subElementRect(r, opt, widget); + _instance = new InputStyle(); } + return _instance; + } - static InputStyle *instance() { - if (!_instance) { - if (!QGuiApplication::instance()) { - return nullptr; - } - _instance = new InputStyle(); - } - return _instance; + ~InputStyle() { + _instance = nullptr; + } + +private: + static InputStyle *_instance; + +}; + +template +InputStyle *InputStyle::_instance = nullptr; + +} // namespace + +QByteArray FlatTextarea::serializeTagsList(const TagList &tags) { + if (tags.isEmpty()) { + return QByteArray(); + } + + QByteArray tagsSerialized; + { + QBuffer buffer(&tagsSerialized); + buffer.open(QIODevice::WriteOnly); + QDataStream stream(&buffer); + stream.setVersion(QDataStream::Qt_5_1); + stream << qint32(tags.size()); + for_const (auto &tag, tags) { + stream << qint32(tag.offset) << qint32(tag.length) << tag.id; } - - ~InputStyle() { - _instance = nullptr; - } - - private: - static InputStyle *_instance; - - }; - - template - InputStyle *InputStyle::_instance = nullptr; - + } + return tagsSerialized; } -FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString &pholder, const QString &v) : QLineEdit(v, parent) +FlatTextarea::TagList FlatTextarea::deserializeTagsList(QByteArray data, int textLength) { + TagList result; + if (data.isEmpty()) { + return result; + } + + QBuffer buffer(&data); + buffer.open(QIODevice::ReadOnly); + + QDataStream stream(&buffer); + stream.setVersion(QDataStream::Qt_5_1); + + qint32 tagCount = 0; + stream >> tagCount; + if (stream.status() != QDataStream::Ok) { + return result; + } + if (tagCount <= 0 || tagCount > textLength) { + return result; + } + + for (int i = 0; i < tagCount; ++i) { + qint32 offset = 0, length = 0; + QString id; + stream >> offset >> length >> id; + if (stream.status() != QDataStream::Ok) { + return result; + } + if (offset < 0 || length <= 0 || offset + length > textLength) { + return result; + } + result.push_back({ offset, length, id }); + } + return result; +} + +QString FlatTextarea::tagsMimeType() { + return qsl("application/x-td-field-tags"); +} + +FlatTextarea::FlatTextarea(QWidget *parent, const style::FlatTextarea &st, const QString &pholder, const QString &v, const TagList &tags) : QTextEdit(parent) +, _phVisible(!v.length()) +, a_phLeft(_phVisible ? 0 : st.phShift) +, a_phAlpha(_phVisible ? 1 : 0) +, a_phColorFocused(0) +, _a_appearance(animation(this, &FlatTextarea::step_appearance)) +, _lastTextWithTags { v, tags } +, _st(st) { + setAcceptRichText(false); + resize(_st.width, _st.font->height); + + setFont(_st.font->f); + setAlignment(_st.align); + + setPlaceholder(pholder); + + QPalette p(palette()); + p.setColor(QPalette::Text, _st.textColor->c); + setPalette(p); + + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setFrameStyle(QFrame::NoFrame | QFrame::Plain); + viewport()->setAutoFillBackground(false); + + setContentsMargins(0, 0, 0, 0); + + switch (cScale()) { + case dbisOneAndQuarter: _fakeMargin = 1; break; + case dbisOneAndHalf: _fakeMargin = 2; break; + case dbisTwo: _fakeMargin = 4; break; + } + setStyleSheet(qsl("QTextEdit { margin: %1px; }").arg(_fakeMargin)); + + viewport()->setAttribute(Qt::WA_AcceptTouchEvents); + _touchTimer.setSingleShot(true); + connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); + + connect(document(), SIGNAL(contentsChange(int, int, int)), this, SLOT(onDocumentContentsChange(int, int, int))); + connect(document(), SIGNAL(contentsChanged()), this, SLOT(onDocumentContentsChanged())); + connect(this, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool))); + connect(this, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool))); + if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); + + if (!_lastTextWithTags.text.isEmpty()) { + setTextWithTags(_lastTextWithTags, ClearUndoHistory); + } +} + +TextWithTags FlatTextarea::getTextWithTagsPart(int start, int end) { + TextWithTags result; + result.text = getTextPart(start, end, &result.tags); + return result; +} + +void FlatTextarea::setTextWithTags(const TextWithTags &textWithTags, UndoHistoryAction undoHistoryAction) { + _insertedTags = textWithTags.tags; + _insertedTagsAreFromMime = false; + _realInsertPosition = 0; + _realCharsAdded = textWithTags.text.size(); + auto doc = document(); + auto cursor = QTextCursor(doc->docHandle(), 0); + if (undoHistoryAction == ClearUndoHistory) { + doc->setUndoRedoEnabled(false); + cursor.beginEditBlock(); + } else if (undoHistoryAction == MergeWithUndoHistory) { + cursor.joinPreviousEditBlock(); + } else { + cursor.beginEditBlock(); + } + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + cursor.insertText(textWithTags.text); + cursor.movePosition(QTextCursor::End); + cursor.endEditBlock(); + if (undoHistoryAction == ClearUndoHistory) { + doc->setUndoRedoEnabled(true); + } + _insertedTags.clear(); + _realInsertPosition = -1; + finishPlaceholder(); +} + +void FlatTextarea::finishPlaceholder() { + if (_a_appearance.animating()) { + a_phLeft.finish(); + a_phAlpha.finish(); + _a_appearance.stop(); + update(); + } +} + +void FlatTextarea::setMaxLength(int32 maxLength) { + _maxLength = maxLength; +} + +void FlatTextarea::setMinHeight(int32 minHeight) { + _minHeight = minHeight; + heightAutoupdated(); +} + +void FlatTextarea::setMaxHeight(int32 maxHeight) { + _maxHeight = maxHeight; + heightAutoupdated(); +} + +bool FlatTextarea::heightAutoupdated() { + if (_minHeight < 0 || _maxHeight < 0 || _inHeightCheck) return false; + _inHeightCheck = true; + + myEnsureResized(this); + + int newh = ceil(document()->size().height()) + 2 * fakeMargin(); + if (newh > _maxHeight) { + newh = _maxHeight; + } else if (newh < _minHeight) { + newh = _minHeight; + } + if (height() != newh) { + resize(width(), newh); + _inHeightCheck = false; + return true; + } + _inHeightCheck = false; + return false; +} + +void FlatTextarea::onTouchTimer() { + _touchRightButton = true; +} + +bool FlatTextarea::viewportEvent(QEvent *e) { + if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) { + QTouchEvent *ev = static_cast(e); + if (ev->device()->type() == QTouchDevice::TouchScreen) { + touchEvent(ev); + return QTextEdit::viewportEvent(e); + } + } + return QTextEdit::viewportEvent(e); +} + +void FlatTextarea::touchEvent(QTouchEvent *e) { + switch (e->type()) { + case QEvent::TouchBegin: + if (_touchPress || e->touchPoints().isEmpty()) return; + _touchTimer.start(QApplication::startDragTime()); + _touchPress = true; + _touchMove = _touchRightButton = false; + _touchStart = e->touchPoints().cbegin()->screenPos().toPoint(); + break; + + case QEvent::TouchUpdate: + if (!_touchPress || e->touchPoints().isEmpty()) return; + if (!_touchMove && (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart).manhattanLength() >= QApplication::startDragDistance()) { + _touchMove = true; + } + break; + + case QEvent::TouchEnd: + if (!_touchPress) return; + if (!_touchMove && window()) { + Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton); + QPoint mapped(mapFromGlobal(_touchStart)), winMapped(window()->mapFromGlobal(_touchStart)); + + if (_touchRightButton) { + QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, mapped, _touchStart); + contextMenuEvent(&contextEvent); + } + } + _touchTimer.stop(); + _touchPress = _touchMove = _touchRightButton = false; + break; + + case QEvent::TouchCancel: + _touchPress = false; + _touchTimer.stop(); + break; + } +} + +QRect FlatTextarea::getTextRect() const { + return rect().marginsRemoved(_st.textMrg + st::textRectMargins); +} + +int32 FlatTextarea::fakeMargin() const { + return _fakeMargin; +} + +void FlatTextarea::paintEvent(QPaintEvent *e) { + QPainter p(viewport()); + QRect r(rect().intersected(e->rect())); + p.fillRect(r, _st.bgColor->b); + bool phDraw = _phVisible; + if (_a_appearance.animating()) { + p.setOpacity(a_phAlpha.current()); + phDraw = true; + } + if (phDraw) { + p.save(); + p.setClipRect(r); + p.setFont(_st.font); + p.setPen(anim::pen(_st.phColor, _st.phFocusColor, a_phColorFocused.current())); + if (_st.phAlign == style::al_topleft && _phAfter > 0) { + int skipWidth = placeholderSkipWidth(); + p.drawText(_st.textMrg.left() - _fakeMargin + a_phLeft.current() + skipWidth, _st.textMrg.top() - _fakeMargin - st::lineWidth + _st.font->ascent, _ph); + } else { + QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom()); + p.drawText(phRect, _ph, QTextOption(_st.phAlign)); + } + p.restore(); + p.setOpacity(1); + } + QTextEdit::paintEvent(e); +} + +int FlatTextarea::placeholderSkipWidth() const { + if (!_phAfter) { + return 0; + } + auto text = getTextWithTags().text; + auto result = _st.font->width(text.mid(0, _phAfter)); + if (_phAfter > text.size()) { + result += _st.font->spacew; + } + return result; +} + +void FlatTextarea::focusInEvent(QFocusEvent *e) { + a_phColorFocused.start(1.); + _a_appearance.start(); + QTextEdit::focusInEvent(e); +} + +void FlatTextarea::focusOutEvent(QFocusEvent *e) { + a_phColorFocused.start(0.); + _a_appearance.start(); + QTextEdit::focusOutEvent(e); +} + +QSize FlatTextarea::sizeHint() const { + return geometry().size(); +} + +QSize FlatTextarea::minimumSizeHint() const { + return geometry().size(); +} + +EmojiPtr FlatTextarea::getSingleEmoji() const { + QString text; + QTextFragment fragment; + + getSingleEmojiFragment(text, fragment); + + if (!text.isEmpty()) { + QTextCharFormat format = fragment.charFormat(); + QString imageName = static_cast(&format)->name(); + if (imageName.startsWith(qstr("emoji://e."))) { + return emojiFromUrl(imageName); + } + } + return nullptr; +} + +QString FlatTextarea::getInlineBotQuery(UserData **outInlineBot, QString *outInlineBotUsername) const { + t_assert(outInlineBot != nullptr); + t_assert(outInlineBotUsername != nullptr); + + auto &text = getTextWithTags().text; + auto textLength = text.size(); + + int inlineUsernameStart = 1, inlineUsernameLength = 0; + if (textLength > 2 && text.at(0) == '@' && text.at(1).isLetter()) { + inlineUsernameLength = 1; + for (int i = inlineUsernameStart + 1; i != textLength; ++i) { + if (text.at(i).isLetterOrNumber() || text.at(i).unicode() == '_') { + ++inlineUsernameLength; + continue; + } + if (!text.at(i).isSpace()) { + inlineUsernameLength = 0; + } + break; + } + auto inlineUsernameEnd = inlineUsernameStart + inlineUsernameLength; + auto inlineUsernameEqualsText = (inlineUsernameEnd == textLength); + auto validInlineUsername = false; + if (inlineUsernameEqualsText) { + validInlineUsername = text.endsWith(qstr("bot")); + } else if (inlineUsernameEnd < textLength && inlineUsernameLength) { + validInlineUsername = text.at(inlineUsernameEnd).isSpace(); + } + if (validInlineUsername) { + auto username = text.midRef(inlineUsernameStart, inlineUsernameLength); + if (username != *outInlineBotUsername) { + *outInlineBotUsername = username.toString(); + auto peer = App::peerByName(*outInlineBotUsername); + if (peer) { + if (peer->isUser()) { + *outInlineBot = peer->asUser(); + } else { + *outInlineBot = nullptr; + } + } else { + *outInlineBot = LookingUpInlineBot; + } + } + if (*outInlineBot == LookingUpInlineBot) return QString(); + + if (*outInlineBot && (!(*outInlineBot)->botInfo || (*outInlineBot)->botInfo->inlinePlaceholder.isEmpty())) { + *outInlineBot = nullptr; + } else { + return inlineUsernameEqualsText ? QString() : text.mid(inlineUsernameEnd + 1); + } + } else { + inlineUsernameLength = 0; + } + } + if (inlineUsernameLength < 3) { + *outInlineBot = nullptr; + *outInlineBotUsername = QString(); + } + return QString(); +} + +QString FlatTextarea::getMentionHashtagBotCommandPart(bool &start) const { + start = false; + + int32 pos = textCursor().position(); + if (textCursor().anchor() != pos) return QString(); + + // check mention / hashtag / bot command + QTextDocument *doc(document()); + QTextBlock block = doc->findBlock(pos); + for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) { + QTextFragment fr(iter.fragment()); + if (!fr.isValid()) continue; + + int32 p = fr.position(), e = (p + fr.length()); + if (p >= pos || e < pos) continue; + + QTextCharFormat f = fr.charFormat(); + if (f.isImageFormat()) continue; + + bool mentionInCommand = false; + QString t(fr.text()); + for (int i = pos - p; i > 0; --i) { + if (t.at(i - 1) == '@') { + if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) { + start = (i == 1) && (p == 0); + return t.mid(i - 1, pos - p - i + 1); + } else if ((pos - p - i < 1 || t.at(i).isLetter()) && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) { + mentionInCommand = true; + --i; + continue; + } + return QString(); + } else if (t.at(i - 1) == '#') { + if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) { + start = (i == 1) && (p == 0); + return t.mid(i - 1, pos - p - i + 1); + } + return QString(); + } else if (t.at(i - 1) == '/') { + if (i < 2) { + start = (i == 1) && (p == 0); + return t.mid(i - 1, pos - p - i + 1); + } + return QString(); + } + if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break; + if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break; + } + break; + } + return QString(); +} + +void FlatTextarea::insertTag(const QString &text, QString tagId) { + auto cursor = textCursor(); + int32 pos = cursor.position(); + + auto doc = document(); + auto block = doc->findBlock(pos); + for (auto iter = block.begin(); !iter.atEnd(); ++iter) { + auto fragment = iter.fragment(); + t_assert(fragment.isValid()); + + int fragmentPosition = fragment.position(); + int fragmentEnd = (fragmentPosition + fragment.length()); + if (fragmentPosition >= pos || fragmentEnd < pos) continue; + + auto format = fragment.charFormat(); + if (format.isImageFormat()) continue; + + bool mentionInCommand = false; + auto fragmentText = fragment.text(); + for (int i = pos - fragmentPosition; i > 0; --i) { + auto previousChar = fragmentText.at(i - 1); + if (previousChar == '@' || previousChar == '#' || previousChar == '/') { + if ((i == pos - fragmentPosition || (previousChar == '/' ? fragmentText.at(i).isLetterOrNumber() : fragmentText.at(i).isLetter()) || previousChar == '#') && + (i < 2 || !(fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_'))) { + cursor.setPosition(fragmentPosition + i - 1); + int till = fragmentPosition + i; + for (; (till < fragmentEnd && till < pos); ++till) { + auto ch = fragmentText.at(till - fragmentPosition); + if (!ch.isLetterOrNumber() && ch != '_' && ch != '@') { + break; + } + } + if (till < fragmentEnd && fragmentText.at(till - fragmentPosition) == ' ') { + ++till; + } + cursor.setPosition(till, QTextCursor::KeepAnchor); + break; + } else if ((i == pos - fragmentPosition || fragmentText.at(i).isLetter()) && fragmentText.at(i - 1) == '@' && i > 2 && (fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_') && !mentionInCommand) { + mentionInCommand = true; + --i; + continue; + } + break; + } + if (pos - fragmentPosition - i > 127 || (!mentionInCommand && (pos - fragmentPosition - i > 63))) break; + if (!fragmentText.at(i - 1).isLetterOrNumber() && fragmentText.at(i - 1) != '_') break; + } + break; + } + if (tagId.isEmpty()) { + QTextCharFormat format = cursor.charFormat(); + format.setAnchor(false); + format.setAnchorName(QString()); + format.clearForeground(); + cursor.insertText(text + ' ', format); + } else { + _insertedTags.clear(); + _insertedTags.push_back({ 0, text.size(), tagId }); + _insertedTagsAreFromMime = false; + cursor.insertText(text + ' '); + _insertedTags.clear(); + } +} + +void FlatTextarea::setTagMimeProcessor(std_::unique_ptr &&processor) { + _tagMimeProcessor = std_::move(processor); +} + +void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const { + int32 end = textCursor().position(), start = end - 1; + if (textCursor().anchor() != end) return; + + if (start < 0) start = 0; + + QTextDocument *doc(document()); + QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); + if (till.isValid()) till = till.next(); + + for (QTextBlock b = from; b != till; b = b.next()) { + for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { + QTextFragment fr(iter.fragment()); + if (!fr.isValid()) continue; + + int32 p = fr.position(), e = (p + fr.length()); + if (p >= end || e <= start) { + continue; + } + + QTextCharFormat f = fr.charFormat(); + QString t(fr.text()); + if (p < start) { + t = t.mid(start - p, end - start); + } else if (e > end) { + t = t.mid(0, end - p); + } + if (f.isImageFormat() && !t.isEmpty() && t.at(0).unicode() == QChar::ObjectReplacementCharacter) { + QString imageName = static_cast(&f)->name(); + if (imageName.startsWith(qstr("emoji://e."))) { + fragment = fr; + text = t; + return; + } + } + return; + } + } + return; +} + +void FlatTextarea::removeSingleEmoji() { + QString text; + QTextFragment fragment; + + getSingleEmojiFragment(text, fragment); + + if (!text.isEmpty()) { + QTextCursor t(textCursor()); + t.setPosition(fragment.position()); + t.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor); + t.removeSelectedText(); + setTextCursor(t); + } +} + +namespace { + +class TagAccumulator { +public: + TagAccumulator(FlatTextarea::TagList *tags) : _tags(tags) { + } + + bool changed() const { + return _changed; + } + + void feed(const QString &randomTagId, int currentPosition) { + if (randomTagId == _currentTagId) return; + + if (!_currentTagId.isEmpty()) { + int randomPartPosition = _currentTagId.lastIndexOf('/'); + t_assert(randomPartPosition > 0); + + bool tagChanged = true; + if (_currentTag < _tags->size()) { + auto &alreadyTag = _tags->at(_currentTag); + if (alreadyTag.offset == _currentStart && + alreadyTag.length == currentPosition - _currentStart && + alreadyTag.id == _currentTagId.midRef(0, randomPartPosition)) { + tagChanged = false; + } + } + if (tagChanged) { + _changed = true; + TextWithTags::Tag tag = { + _currentStart, + currentPosition - _currentStart, + _currentTagId.mid(0, randomPartPosition), + }; + if (_currentTag < _tags->size()) { + (*_tags)[_currentTag] = tag; + } else { + _tags->push_back(tag); + } + } + ++_currentTag; + } + _currentTagId = randomTagId; + _currentStart = currentPosition; + }; + + void finish() { + if (_currentTag < _tags->size()) { + _tags->resize(_currentTag); + _changed = true; + } + } + +private: + FlatTextarea::TagList *_tags; + bool _changed = false; + + int _currentTag = 0; + int _currentStart = 0; + QString _currentTagId; + +}; + +} // namespace + +QString FlatTextarea::getTextPart(int start, int end, TagList *outTagsList, bool *outTagsChanged) const { + if (end >= 0 && end <= start) return QString(); + + if (start < 0) start = 0; + bool full = (start == 0) && (end < 0); + + TagAccumulator tagAccumulator(outTagsList); + + QTextDocument *doc(document()); + QTextBlock from = full ? doc->begin() : doc->findBlock(start), till = (end < 0) ? doc->end() : doc->findBlock(end); + if (till.isValid()) till = till.next(); + + int32 possibleLen = 0; + for (QTextBlock b = from; b != till; b = b.next()) { + possibleLen += b.length(); + } + QString result; + result.reserve(possibleLen + 1); + if (!full && end < 0) { + end = possibleLen; + } + + bool tillFragmentEnd = full; + for (auto b = from; b != till; b = b.next()) { + for (auto iter = b.begin(); !iter.atEnd(); ++iter) { + QTextFragment fragment(iter.fragment()); + if (!fragment.isValid()) continue; + + int32 p = full ? 0 : fragment.position(), e = full ? 0 : (p + fragment.length()); + if (!full) { + tillFragmentEnd = (e <= end); + if (p == end) { + tagAccumulator.feed(fragment.charFormat().anchorName(), result.size()); + } + if (p >= end) { + break; + } + if (e <= start) { + continue; + } + } + if (full || p >= start) { + tagAccumulator.feed(fragment.charFormat().anchorName(), result.size()); + } + + QTextCharFormat f = fragment.charFormat(); + QString emojiText; + QString t(fragment.text()); + if (!full) { + if (p < start) { + t = t.mid(start - p, end - start); + } else if (e > end) { + t = t.mid(0, end - p); + } + } + QChar *ub = t.data(), *uc = ub, *ue = uc + t.size(); + for (; uc != ue; ++uc) { + switch (uc->unicode()) { + case 0xfdd0: // QTextBeginningOfFrame + case 0xfdd1: // QTextEndOfFrame + case QChar::ParagraphSeparator: + case QChar::LineSeparator: + *uc = QLatin1Char('\n'); + break; + case QChar::Nbsp: + *uc = QLatin1Char(' '); + break; + case QChar::ObjectReplacementCharacter: + if (emojiText.isEmpty() && f.isImageFormat()) { + QString imageName = static_cast(&f)->name(); + if (imageName.startsWith(qstr("emoji://e."))) { + if (EmojiPtr emoji = emojiFromUrl(imageName)) { + emojiText = emojiString(emoji); + } + } + } + if (uc > ub) result.append(ub, uc - ub); + if (!emojiText.isEmpty()) result.append(emojiText); + ub = uc + 1; + break; + } + } + if (uc > ub) result.append(ub, uc - ub); + } + result.append('\n'); + } + result.chop(1); + + if (tillFragmentEnd) tagAccumulator.feed(QString(), result.size()); + tagAccumulator.finish(); + + if (outTagsChanged) { + *outTagsChanged = tagAccumulator.changed(); + } + return result; +} + +bool FlatTextarea::hasText() const { + QTextDocument *doc(document()); + QTextBlock from = doc->begin(), till = doc->end(); + + if (from == till) return false; + + for (QTextBlock::Iterator iter = from.begin(); !iter.atEnd(); ++iter) { + QTextFragment fragment(iter.fragment()); + if (!fragment.isValid()) continue; + if (!fragment.text().isEmpty()) return true; + } + return (from.next() != till); +} + +bool FlatTextarea::isUndoAvailable() const { + return _undoAvailable; +} + +bool FlatTextarea::isRedoAvailable() const { + return _redoAvailable; +} + +void FlatTextarea::parseLinks() { // some code is duplicated in text.cpp! + LinkRanges newLinks; + + QString text(toPlainText()); + if (text.isEmpty()) { + if (!_links.isEmpty()) { + _links.clear(); + emit linksChanged(); + } + return; + } + + initLinkSets(); + + int32 len = text.size(); + const QChar *start = text.unicode(), *end = start + text.size(); + for (int32 offset = 0, matchOffset = offset; offset < len;) { + QRegularExpressionMatch m = reDomain().match(text, matchOffset); + if (!m.hasMatch()) break; + + int32 domainOffset = m.capturedStart(); + + QString protocol = m.captured(1).toLower(); + QString topDomain = m.captured(3).toLower(); + + bool isProtocolValid = protocol.isEmpty() || validProtocols().contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); + bool isTopDomainValid = !protocol.isEmpty() || validTopDomains().contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar))); + + if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) { + QString forMailName = text.mid(offset, domainOffset - offset - 1); + QRegularExpressionMatch mMailName = reMailName().match(forMailName); + if (mMailName.hasMatch()) { + offset = matchOffset = m.capturedEnd(); + continue; + } + } + if (!isProtocolValid || !isTopDomainValid) { + offset = matchOffset = m.capturedEnd(); + continue; + } + + QStack parenth; + const QChar *domainEnd = start + m.capturedEnd(), *p = domainEnd; + for (; p < end; ++p) { + QChar ch(*p); + if (chIsLinkEnd(ch)) break; // link finished + if (chIsAlmostLinkEnd(ch)) { + const QChar *endTest = p + 1; + while (endTest < end && chIsAlmostLinkEnd(*endTest)) { + ++endTest; + } + if (endTest >= end || chIsLinkEnd(*endTest)) { + break; // link finished at p + } + p = endTest; + ch = *p; + } + if (ch == '(' || ch == '[' || ch == '{' || ch == '<') { + parenth.push(p); + } else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') { + if (parenth.isEmpty()) break; + const QChar *q = parenth.pop(), open(*q); + if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) { + p = q; + break; + } + } + } + if (p > domainEnd) { // check, that domain ended + if (domainEnd->unicode() != '/' && domainEnd->unicode() != '?') { + matchOffset = domainEnd - start; + continue; + } + } + newLinks.push_back({ domainOffset - 1, static_cast(p - start - domainOffset + 2) }); + offset = matchOffset = p - start; + } + + if (newLinks != _links) { + _links = newLinks; + emit linksChanged(); + } +} + +QStringList FlatTextarea::linksList() const { + QStringList result; + if (!_links.isEmpty()) { + QString text(toPlainText()); + for_const (auto &link, _links) { + result.push_back(text.mid(link.start + 1, link.length - 2)); + } + } + return result; +} + +void FlatTextarea::insertFromMimeData(const QMimeData *source) { + auto mime = tagsMimeType(); + auto text = source->text(); + if (source->hasFormat(mime)) { + auto tagsData = source->data(mime); + _insertedTags = deserializeTagsList(tagsData, text.size()); + _insertedTagsAreFromMime = true; + } else { + _insertedTags.clear(); + } + auto cursor = textCursor(); + _realInsertPosition = qMin(cursor.position(), cursor.anchor()); + _realCharsAdded = text.size(); + QTextEdit::insertFromMimeData(source); + if (!_inDrop) { + emit spacedReturnedPasted(); + _insertedTags.clear(); + _realInsertPosition = -1; + } +} + +void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) { + QTextImageFormat imageFormat; + int32 ew = ESize + st::emojiPadding * cIntRetinaFactor() * 2, eh = _st.font->height * cIntRetinaFactor(); + imageFormat.setWidth(ew / cIntRetinaFactor()); + imageFormat.setHeight(eh / cIntRetinaFactor()); + imageFormat.setName(qsl("emoji://e.") + QString::number(emojiKey(emoji), 16)); + imageFormat.setVerticalAlignment(QTextCharFormat::AlignBaseline); + if (c.charFormat().isAnchor()) { + imageFormat.setAnchor(true); + imageFormat.setAnchorName(c.charFormat().anchorName()); + imageFormat.setForeground(st::defaultTextStyle.linkFg); + } + static QString objectReplacement(QChar::ObjectReplacementCharacter); + c.insertText(objectReplacement, imageFormat); +} + +QVariant FlatTextarea::loadResource(int type, const QUrl &name) { + QString imageName = name.toDisplayString(); + if (imageName.startsWith(qstr("emoji://e."))) { + if (EmojiPtr emoji = emojiFromUrl(imageName)) { + return QVariant(App::emojiSingle(emoji, _st.font->height)); + } + } + return QVariant(); +} + +void FlatTextarea::checkContentHeight() { + if (heightAutoupdated()) { + emit resized(); + } +} + +namespace { + +// Optimization: with null page size document does not re-layout +// on each insertText / mergeCharFormat. +void prepareFormattingOptimization(QTextDocument *document) { + if (!document->pageSize().isNull()) { + document->setPageSize(QSizeF(0, 0)); + } +} + +void removeTags(const style::color &textFg, QTextDocument *document, int from, int end) { + QTextCursor c(document->docHandle(), 0); + c.setPosition(from); + c.setPosition(end, QTextCursor::KeepAnchor); + + QTextCharFormat format; + format.setAnchor(false); + format.setAnchorName(QString()); + format.setForeground(textFg); + c.mergeCharFormat(format); +} + +// Returns the position of the first inserted tag or "changedEnd" value if none found. +int processInsertedTags(const style::color &textFg, QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags, FlatTextarea::TagMimeProcessor *processor) { + int firstTagStart = changedEnd; + int applyNoTagFrom = changedEnd; + for_const (auto &tag, tags) { + int tagFrom = changedPosition + tag.offset; + int tagTo = tagFrom + tag.length; + accumulate_max(tagFrom, changedPosition); + accumulate_min(tagTo, changedEnd); + auto tagId = processor ? processor->tagFromMimeTag(tag.id) : tag.id; + if (tagTo > tagFrom && !tagId.isEmpty()) { + accumulate_min(firstTagStart, tagFrom); + + prepareFormattingOptimization(document); + + if (applyNoTagFrom < tagFrom) { + removeTags(textFg, document, applyNoTagFrom, tagFrom); + } + QTextCursor c(document->docHandle(), 0); + c.setPosition(tagFrom); + c.setPosition(tagTo, QTextCursor::KeepAnchor); + + QTextCharFormat format; + format.setAnchor(true); + format.setAnchorName(tagId + '/' + QString::number(rand_value())); + format.setForeground(st::defaultTextStyle.linkFg); + c.mergeCharFormat(format); + + applyNoTagFrom = tagTo; + } + } + if (applyNoTagFrom < changedEnd) { + removeTags(textFg, document, applyNoTagFrom, changedEnd); + } + + return firstTagStart; +} + +// When inserting a part of text inside a tag we need to have +// a way to know if the insertion replaced the end of the tag +// or it was strictly inside (in the middle) of the tag. +bool wasInsertTillTheEndOfTag(QTextBlock block, QTextBlock::iterator fragmentIt, int insertionEnd) { + auto insertTagName = fragmentIt.fragment().charFormat().anchorName(); + while (true) { + for (; !fragmentIt.atEnd(); ++fragmentIt) { + auto fragment = fragmentIt.fragment(); + bool fragmentOutsideInsertion = (fragment.position() >= insertionEnd); + if (fragmentOutsideInsertion) { + return (fragment.charFormat().anchorName() != insertTagName); + } + int fragmentEnd = fragment.position() + fragment.length(); + bool notFullFragmentInserted = (fragmentEnd > insertionEnd); + if (notFullFragmentInserted) { + return false; + } + } + if (block.isValid()) { + fragmentIt = block.begin(); + block = block.next(); + } else { + break; + } + } + // Insertion goes till the end of the text => not strictly inside a tag. + return true; +} + +struct FormattingAction { + enum class Type { + Invalid, + InsertEmoji, + TildeFont, + RemoveTag, + }; + Type type = Type::Invalid; + EmojiPtr emoji = nullptr; + bool isTilde = false; + int intervalStart = 0; + int intervalEnd = 0; +}; + +} // namespace + +void FlatTextarea::processFormatting(int insertPosition, int insertEnd) { + // Tilde formatting. + auto regularFont = qsl("Open Sans"), semiboldFont = qsl("Open Sans Semibold"); + bool tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == regularFont); + bool isTildeFragment = false; + + // First tag handling (the one we inserted text to). + bool startTagFound = false; + bool breakTagOnNotLetter = false; + + auto doc = document(); + + // Apply inserted tags. + auto insertedTagsProcessor = _insertedTagsAreFromMime ? _tagMimeProcessor.get() : nullptr; + int breakTagOnNotLetterTill = processInsertedTags(_st.textColor, doc, insertPosition, insertEnd, + _insertedTags, insertedTagsProcessor); + using ActionType = FormattingAction::Type; + while (true) { + FormattingAction action; + + auto fromBlock = doc->findBlock(insertPosition); + auto tillBlock = doc->findBlock(insertEnd); + if (tillBlock.isValid()) tillBlock = tillBlock.next(); + + for (auto block = fromBlock; block != tillBlock; block = block.next()) { + for (auto fragmentIt = block.begin(); !fragmentIt.atEnd(); ++fragmentIt) { + auto fragment = fragmentIt.fragment(); + t_assert(fragment.isValid()); + + int fragmentPosition = fragment.position(); + if (insertPosition >= fragmentPosition + fragment.length()) { + continue; + } + int changedPositionInFragment = insertPosition - fragmentPosition; // Can be negative. + int changedEndInFragment = insertEnd - fragmentPosition; + if (changedEndInFragment <= 0) { + break; + } + + auto charFormat = fragment.charFormat(); + if (tildeFormatting) { + isTildeFragment = (charFormat.fontFamily() == semiboldFont); + } + + auto fragmentText = fragment.text(); + auto *textStart = fragmentText.constData(); + auto *textEnd = textStart + fragmentText.size(); + + if (!startTagFound) { + startTagFound = true; + auto tagName = charFormat.anchorName(); + if (!tagName.isEmpty()) { + breakTagOnNotLetter = wasInsertTillTheEndOfTag(block, fragmentIt, insertEnd); + } + } + + auto *ch = textStart + qMax(changedPositionInFragment, 0); + for (; ch < textEnd; ++ch) { + int emojiLength = 0; + if (auto emoji = emojiFromText(ch, textEnd, &emojiLength)) { + // Replace emoji if no current action is prepared. + if (action.type == ActionType::Invalid) { + action.type = ActionType::InsertEmoji; + action.emoji = emoji; + action.intervalStart = fragmentPosition + (ch - textStart); + action.intervalEnd = action.intervalStart + emojiLength; + } + break; + } + + if (breakTagOnNotLetter && !ch->isLetter()) { + // Remove tag name till the end if no current action is prepared. + if (action.type != ActionType::Invalid) { + break; + } + breakTagOnNotLetter = false; + if (fragmentPosition + (ch - textStart) < breakTagOnNotLetterTill) { + action.type = ActionType::RemoveTag; + action.intervalStart = fragmentPosition + (ch - textStart); + action.intervalEnd = breakTagOnNotLetterTill; + break; + } + } + if (tildeFormatting) { // Tilde symbol fix in OpenSans. + bool tilde = (ch->unicode() == '~'); + if ((tilde && !isTildeFragment) || (!tilde && isTildeFragment)) { + if (action.type == ActionType::Invalid) { + action.type = ActionType::TildeFont; + action.intervalStart = fragmentPosition + (ch - textStart); + action.intervalEnd = action.intervalStart + 1; + action.isTilde = tilde; + } else { + ++action.intervalEnd; + } + } else if (action.type == ActionType::TildeFont) { + break; + } + } + + if (ch + 1 < textEnd && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) { + ++ch; + ++fragmentPosition; + } + } + if (action.type != ActionType::Invalid) break; + } + if (action.type != ActionType::Invalid) break; + } + if (action.type != ActionType::Invalid) { + prepareFormattingOptimization(doc); + + QTextCursor c(doc->docHandle(), 0); + c.setPosition(action.intervalStart); + c.setPosition(action.intervalEnd, QTextCursor::KeepAnchor); + if (action.type == ActionType::InsertEmoji) { + insertEmoji(action.emoji, c); + insertPosition = action.intervalStart + 1; + } else if (action.type == ActionType::RemoveTag) { + QTextCharFormat format; + format.setAnchor(false); + format.setAnchorName(QString()); + format.setForeground(_st.textColor); + c.mergeCharFormat(format); + } else if (action.type == ActionType::TildeFont) { + QTextCharFormat format; + format.setFontFamily(action.isTilde ? semiboldFont : regularFont); + c.mergeCharFormat(format); + insertPosition = action.intervalEnd; + } + } else { + break; + } + } +} + +void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int charsAdded) { + if (_correcting) return; + + int insertPosition = (_realInsertPosition >= 0) ? _realInsertPosition : position; + int insertLength = (_realInsertPosition >= 0) ? _realCharsAdded : charsAdded; + + int removePosition = position; + int removeLength = charsRemoved; + + QTextCursor(document()->docHandle(), 0).joinPreviousEditBlock(); + + _correcting = true; + if (_maxLength >= 0) { + QTextCursor c(document()->docHandle(), 0); + c.movePosition(QTextCursor::End); + int32 fullSize = c.position(), toRemove = fullSize - _maxLength; + if (toRemove > 0) { + if (toRemove > insertLength) { + if (insertLength) { + c.setPosition(insertPosition); + c.setPosition((insertPosition + insertLength), QTextCursor::KeepAnchor); + c.removeSelectedText(); + } + c.setPosition(fullSize - (toRemove - insertLength)); + c.setPosition(fullSize, QTextCursor::KeepAnchor); + c.removeSelectedText(); + } else { + c.setPosition(insertPosition + (insertLength - toRemove)); + c.setPosition(insertPosition + insertLength, QTextCursor::KeepAnchor); + c.removeSelectedText(); + } + } + } + _correcting = false; + + if (insertPosition == removePosition) { + if (!_links.isEmpty()) { + bool changed = false; + for (auto i = _links.begin(); i != _links.end();) { + if (i->start + i->length <= insertPosition) { + ++i; + } else if (i->start >= removePosition + removeLength) { + i->start += insertLength - removeLength; + ++i; + } else { + i = _links.erase(i); + changed = true; + } + } + if (changed) emit linksChanged(); + } + } else { + parseLinks(); + } + + if (document()->availableRedoSteps() > 0) { + QTextCursor(document()->docHandle(), 0).endEditBlock(); + return; + } + + if (insertLength <= 0) { + QTextCursor(document()->docHandle(), 0).endEditBlock(); + return; + } + + _correcting = true; + auto pageSize = document()->pageSize(); + processFormatting(insertPosition, insertPosition + insertLength); + if (document()->pageSize() != pageSize) { + document()->setPageSize(pageSize); + } + _correcting = false; + + QTextCursor(document()->docHandle(), 0).endEditBlock(); +} + +void FlatTextarea::onDocumentContentsChanged() { + if (_correcting) return; + + auto tagsChanged = false; + auto curText = getTextPart(0, -1, &_lastTextWithTags.tags, &tagsChanged); + + _correcting = true; + correctValue(_lastTextWithTags.text, curText, _lastTextWithTags.tags); + _correcting = false; + + bool textOrTagsChanged = tagsChanged || (_lastTextWithTags.text != curText); + if (textOrTagsChanged) { + _lastTextWithTags.text = curText; + emit changed(); + checkContentHeight(); + } + updatePlaceholder(); + if (App::wnd()) App::wnd()->updateGlobalMenu(); +} + +void FlatTextarea::onUndoAvailable(bool avail) { + _undoAvailable = avail; + if (App::wnd()) App::wnd()->updateGlobalMenu(); +} + +void FlatTextarea::onRedoAvailable(bool avail) { + _redoAvailable = avail; + if (App::wnd()) App::wnd()->updateGlobalMenu(); +} + +void FlatTextarea::step_appearance(float64 ms, bool timer) { + float dt = ms / _st.phDuration; + if (dt >= 1) { + _a_appearance.stop(); + a_phLeft.finish(); + a_phAlpha.finish(); + a_phColorFocused.finish(); + a_phLeft = anim::ivalue(a_phLeft.current()); + a_phAlpha = anim::fvalue(a_phAlpha.current()); + a_phColorFocused = anim::fvalue(a_phColorFocused.current()); + } else { + a_phLeft.update(dt, anim::linear); + a_phAlpha.update(dt, anim::linear); + a_phColorFocused.update(dt, anim::linear); + } + if (timer) update(); +} + +void FlatTextarea::setPlaceholder(const QString &ph, int32 afterSymbols) { + _ph = ph; + if (_phAfter != afterSymbols) { + _phAfter = afterSymbols; + updatePlaceholder(); + } + int skipWidth = placeholderSkipWidth(); + _phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1 - skipWidth); + if (_phVisible) update(); +} + +void FlatTextarea::updatePlaceholder() { + bool vis = (getTextWithTags().text.size() <= _phAfter); + if (vis == _phVisible) return; + + a_phLeft.start(vis ? 0 : _st.phShift); + a_phAlpha.start(vis ? 1 : 0); + _a_appearance.start(); + + _phVisible = vis; +} + +QMimeData *FlatTextarea::createMimeDataFromSelection() const { + QMimeData *result = new QMimeData(); + QTextCursor c(textCursor()); + int32 start = c.selectionStart(), end = c.selectionEnd(); + if (end > start) { + TagList tags; + result->setText(getTextPart(start, end, &tags)); + if (!tags.isEmpty()) { + if (_tagMimeProcessor) { + for (auto &tag : tags) { + tag.id = _tagMimeProcessor->mimeTagFromTag(tag.id); + } + } + result->setData(tagsMimeType(), serializeTagsList(tags)); + } + } + return result; +} + +void FlatTextarea::setSubmitSettings(SubmitSettings settings) { + _submitSettings = settings; +} + +void FlatTextarea::keyPressEvent(QKeyEvent *e) { + bool shift = e->modifiers().testFlag(Qt::ShiftModifier); + bool macmeta = (cPlatform() == dbipMac || cPlatform() == dbipMacOld) && e->modifiers().testFlag(Qt::ControlModifier) && !e->modifiers().testFlag(Qt::MetaModifier) && !e->modifiers().testFlag(Qt::AltModifier); + bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier); + bool enterSubmit = (ctrl && shift); + if (ctrl && _submitSettings != SubmitSettings::None && _submitSettings != SubmitSettings::Enter) { + enterSubmit = true; + } + if (!ctrl && !shift && _submitSettings != SubmitSettings::None && _submitSettings != SubmitSettings::CtrlEnter) { + enterSubmit = true; + } + bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return); + + if (macmeta && e->key() == Qt::Key_Backspace) { + QTextCursor tc(textCursor()), start(tc); + start.movePosition(QTextCursor::StartOfLine); + tc.setPosition(start.position(), QTextCursor::KeepAnchor); + tc.removeSelectedText(); + } else if (enter && enterSubmit) { + emit submitted(ctrl && shift); + } else if (e->key() == Qt::Key_Escape) { + emit cancelled(); + } else if (e->key() == Qt::Key_Tab || (ctrl && e->key() == Qt::Key_Backtab)) { + if (ctrl) { + e->ignore(); + } else { + emit tabbed(); + } + } else if (e->key() == Qt::Key_Search || e == QKeySequence::Find) { + e->ignore(); +#ifdef Q_OS_MAC + } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { + auto cursor = textCursor(); + int start = cursor.selectionStart(), end = cursor.selectionEnd(); + if (end > start) { + TagList tags; + QApplication::clipboard()->setText(getTextPart(start, end, &tags), QClipboard::FindBuffer); + } +#endif // Q_OS_MAC + } else { + QTextCursor tc(textCursor()); + if (enter && ctrl) { + e->setModifiers(e->modifiers() & ~Qt::ControlModifier); + } + bool spaceOrReturn = false; + QString t(e->text()); + if (!t.isEmpty() && t.size() < 3) { + if (t.at(0) == '\n' || t.at(0) == '\r' || t.at(0).isSpace() || t.at(0) == QChar::LineSeparator) { + spaceOrReturn = true; + } + } + QTextEdit::keyPressEvent(e); + if (tc == textCursor()) { + bool check = false; + if (e->key() == Qt::Key_PageUp || e->key() == Qt::Key_Up) { + tc.movePosition(QTextCursor::Start, e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); + check = true; + } else if (e->key() == Qt::Key_PageDown || e->key() == Qt::Key_Down) { + tc.movePosition(QTextCursor::End, e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); + check = true; + } + if (check) { + if (tc == textCursor()) { + e->ignore(); + } else { + setTextCursor(tc); + } + } + } + if (spaceOrReturn) emit spacedReturnedPasted(); + } +} + +void FlatTextarea::resizeEvent(QResizeEvent *e) { + _phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1); + QTextEdit::resizeEvent(e); + checkContentHeight(); +} + +void FlatTextarea::mousePressEvent(QMouseEvent *e) { + QTextEdit::mousePressEvent(e); +} + +void FlatTextarea::dropEvent(QDropEvent *e) { + _inDrop = true; + QTextEdit::dropEvent(e); + _inDrop = false; + _insertedTags.clear(); + _realInsertPosition = -1; + + emit spacedReturnedPasted(); +} + +void FlatTextarea::contextMenuEvent(QContextMenuEvent *e) { + if (auto menu = createStandardContextMenu()) { + (new Ui::PopupMenu(menu))->popup(e->globalPos()); + } +} + +FlatInput::FlatInput(QWidget *parent, const style::FlatInput &st, const QString &pholder, const QString &v) : QLineEdit(v, parent) , _oldtext(v) , _fullph(pholder) , _fastph(false) @@ -393,7 +1796,7 @@ void FlatInput::notaBene() { _a_appearance.start(); } -CountryCodeInput::CountryCodeInput(QWidget *parent, const style::flatInput &st) : FlatInput(parent, st) +CountryCodeInput::CountryCodeInput(QWidget *parent, const style::FlatInput &st) : FlatInput(parent, st) , _nosignal(false) { } @@ -456,7 +1859,7 @@ void CountryCodeInput::correctValue(const QString &was, QString &now) { } } -PhonePartInput::PhonePartInput(QWidget *parent, const style::flatInput &st) : FlatInput(parent, st, lang(lng_phone_ph)) { +PhonePartInput::PhonePartInput(QWidget *parent, const style::FlatInput &st) : FlatInput(parent, st, lang(lng_phone_ph)) { } void PhonePartInput::paintEvent(QPaintEvent *e) { @@ -2455,7 +3858,7 @@ void MaskedInputField::onCursorPositionChanged(int oldPosition, int position) { _oldcursor = position; } -PasswordField::PasswordField(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : MaskedInputField(parent, st, ph, val) { +PasswordInput::PasswordInput(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : MaskedInputField(parent, st, ph, val) { setEchoMode(QLineEdit::Password); } @@ -2681,3 +4084,5 @@ void PhoneInput::correctValue(const QString &was, int32 wasCursor, QString &now, setCursorPosition(newPos); } } + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/flatinput.h b/Telegram/SourceFiles/ui/widgets/input_fields.h similarity index 68% rename from Telegram/SourceFiles/ui/flatinput.h rename to Telegram/SourceFiles/ui/widgets/input_fields.h index 60d8a2e4f..908e70dbc 100644 --- a/Telegram/SourceFiles/ui/flatinput.h +++ b/Telegram/SourceFiles/ui/widgets/input_fields.h @@ -20,14 +20,232 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "animation.h" +#include "styles/style_widgets.h" + +class UserData; + +namespace Ui { + +static UserData * const LookingUpInlineBot = SharedMemoryLocation(); + +class FlatTextarea : public QTextEdit { + Q_OBJECT + T_WIDGET + +public: + using TagList = TextWithTags::Tags; + + static QByteArray serializeTagsList(const TagList &tags); + static TagList deserializeTagsList(QByteArray data, int textLength); + static QString tagsMimeType(); + + FlatTextarea(QWidget *parent, const style::FlatTextarea &st, const QString &ph = QString(), const QString &val = QString(), const TagList &tags = TagList()); + + void setMaxLength(int32 maxLength); + void setMinHeight(int32 minHeight); + void setMaxHeight(int32 maxHeight); + + void setPlaceholder(const QString &ph, int32 afterSymbols = 0); + void updatePlaceholder(); + void finishPlaceholder(); + + QRect getTextRect() const; + int32 fakeMargin() const; + + void step_appearance(float64 ms, bool timer); + + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + + EmojiPtr getSingleEmoji() const; + QString getMentionHashtagBotCommandPart(bool &start) const; + + // Get the current inline bot and request string for it. + // The *outInlineBot can be filled by LookingUpInlineBot shared ptr. + // In that case the caller should lookup the bot by *outInlineBotUsername. + QString getInlineBotQuery(UserData **outInlineBot, QString *outInlineBotUsername) const; + + void removeSingleEmoji(); + bool hasText() const; + + bool isUndoAvailable() const; + bool isRedoAvailable() const; + + void parseLinks(); + QStringList linksList() const; + + void insertFromMimeData(const QMimeData *source) override; + + QMimeData *createMimeDataFromSelection() const override; + + enum class SubmitSettings { + None, + Enter, + CtrlEnter, + Both, + }; + void setSubmitSettings(SubmitSettings settings); + + const TextWithTags &getTextWithTags() const { + return _lastTextWithTags; + } + TextWithTags getTextWithTagsPart(int start, int end = -1); + void insertTag(const QString &text, QString tagId = QString()); + + bool isEmpty() const { + return _lastTextWithTags.text.isEmpty(); + } + + enum UndoHistoryAction { + AddToUndoHistory, + MergeWithUndoHistory, + ClearUndoHistory + }; + void setTextWithTags(const TextWithTags &textWithTags, UndoHistoryAction undoHistoryAction = AddToUndoHistory); + + // If you need to make some preparations of tags before putting them to QMimeData + // (and then to clipboard or to drag-n-drop object), here is a strategy for that. + class TagMimeProcessor { + public: + virtual QString mimeTagFromTag(const QString &tagId) = 0; + virtual QString tagFromMimeTag(const QString &mimeTag) = 0; + virtual ~TagMimeProcessor() { + } + }; + void setTagMimeProcessor(std_::unique_ptr &&processor); + + public slots: + void onTouchTimer(); + + void onDocumentContentsChange(int position, int charsRemoved, int charsAdded); + void onDocumentContentsChanged(); + + void onUndoAvailable(bool avail); + void onRedoAvailable(bool avail); + +signals: + void resized(); + void changed(); + void submitted(bool ctrlShiftEnter); + void cancelled(); + void tabbed(); + void spacedReturnedPasted(); + void linksChanged(); + +protected: + void enterEventHook(QEvent *e) { + return QTextEdit::enterEvent(e); + } + void leaveEventHook(QEvent *e) { + return QTextEdit::leaveEvent(e); + } + + bool viewportEvent(QEvent *e) override; + void touchEvent(QTouchEvent *e); + void paintEvent(QPaintEvent *e) override; + void focusInEvent(QFocusEvent *e) override; + void focusOutEvent(QFocusEvent *e) override; + void keyPressEvent(QKeyEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void dropEvent(QDropEvent *e) override; + void contextMenuEvent(QContextMenuEvent *e) override; + + virtual void correctValue(const QString &was, QString &now, TagList &nowTags) { + } + + void insertEmoji(EmojiPtr emoji, QTextCursor c); + + QVariant loadResource(int type, const QUrl &name) override; + + void checkContentHeight(); + +private: + // "start" and "end" are in coordinates of text where emoji are replaced + // by ObjectReplacementCharacter. If "end" = -1 means get text till the end. + QString getTextPart(int start, int end, TagList *outTagsList, bool *outTagsChanged = nullptr) const; + + void getSingleEmojiFragment(QString &text, QTextFragment &fragment) const; + + // After any characters added we must postprocess them. This includes: + // 1. Replacing font family to semibold for ~ characters, if we used Open Sans 13px. + // 2. Replacing font family from semibold for all non-~ characters, if we used ... + // 3. Replacing emoji code sequences by ObjectReplacementCharacters with emoji pics. + // 4. Interrupting tags in which the text was inserted by any char except a letter. + // 5. Applying tags from "_insertedTags" in case we pasted text with tags, not just text. + // Rule 4 applies only if we inserted chars not in the middle of a tag (but at the end). + void processFormatting(int changedPosition, int changedEnd); + + bool heightAutoupdated(); + + int placeholderSkipWidth() const; + + int _minHeight = -1; // < 0 - no autosize + int _maxHeight = -1; + int _maxLength = -1; + SubmitSettings _submitSettings = SubmitSettings::Enter; + + QString _ph, _phelided; + int _phAfter = 0; + bool _phVisible; + anim::ivalue a_phLeft; + anim::fvalue a_phAlpha; + anim::fvalue a_phColorFocused; + Animation _a_appearance; + + TextWithTags _lastTextWithTags; + + // Tags list which we should apply while setText() call or insert from mime data. + TagList _insertedTags; + bool _insertedTagsAreFromMime; + + // Override insert position and charsAdded from complex text editing + // (like drag-n-drop in the same text edit field). + int _realInsertPosition = -1; + int _realCharsAdded = 0; + + std_::unique_ptr _tagMimeProcessor; + + const style::FlatTextarea &_st; + + bool _undoAvailable = false; + bool _redoAvailable = false; + bool _inDrop = false; + bool _inHeightCheck = false; + + int _fakeMargin = 0; + + QTimer _touchTimer; + bool _touchPress = false; + bool _touchRightButton = false; + bool _touchMove = false; + QPoint _touchStart; + + bool _correcting = false; + + struct LinkRange { + int start; + int length; + }; + friend bool operator==(const LinkRange &a, const LinkRange &b); + friend bool operator!=(const LinkRange &a, const LinkRange &b); + using LinkRanges = QVector; + LinkRanges _links; +}; + +inline bool operator==(const FlatTextarea::LinkRange &a, const FlatTextarea::LinkRange &b) { + return (a.start == b.start) && (a.length == b.length); +} +inline bool operator!=(const FlatTextarea::LinkRange &a, const FlatTextarea::LinkRange &b) { + return !(a == b); +} class FlatInput : public QLineEdit { Q_OBJECT T_WIDGET public: - FlatInput(QWidget *parent, const style::flatInput &st, const QString &ph = QString(), const QString &val = QString()); + FlatInput(QWidget *parent, const style::FlatInput &st, const QString &ph = QString(), const QString &val = QString()); void notaBene(); @@ -49,7 +267,7 @@ public: return _oldtext; } -public slots: + public slots: void onTextChange(const QString &text); void onTextEdited(); @@ -105,7 +323,7 @@ private: Animation _a_appearance; int _notingBene; - const style::flatInput &_st; + const style::FlatInput &_st; QTimer _touchTimer; bool _touchPress, _touchRightButton, _touchMove; @@ -116,9 +334,9 @@ class CountryCodeInput : public FlatInput { Q_OBJECT public: - CountryCodeInput(QWidget *parent, const style::flatInput &st); + CountryCodeInput(QWidget *parent, const style::FlatInput &st); -public slots: + public slots: void startErasing(QKeyEvent *e); void codeSelected(const QString &code); @@ -138,9 +356,9 @@ class PhonePartInput : public FlatInput { Q_OBJECT public: - PhonePartInput(QWidget *parent, const style::flatInput &st); + PhonePartInput(QWidget *parent, const style::FlatInput &st); -public slots: + public slots: void addedToNumber(const QString &added); void onChooseCode(const QString &code); @@ -221,7 +439,7 @@ public: _inner.clearFocus(); } -public slots: + public slots: void onTouchTimer(); void onDocumentContentsChange(int position, int charsRemoved, int charsAdded); @@ -392,7 +610,7 @@ public: _inner.setTextCursor(c); } -public slots: + public slots: void onTouchTimer(); void onDocumentContentsChange(int position, int charsRemoved, int charsAdded); @@ -497,7 +715,7 @@ private: class MaskedInputField : public QLineEdit { Q_OBJECT - T_WIDGET + T_WIDGET public: MaskedInputField(QWidget *parent, const style::InputField &st, const QString &placeholder = QString(), const QString &val = QString()); @@ -539,7 +757,7 @@ public: updatePlaceholder(); } -public slots: + public slots: void onTextChange(const QString &text); void onCursorPositionChanged(int oldPosition, int position); @@ -610,9 +828,9 @@ private: QPoint _touchStart; }; -class PasswordField : public MaskedInputField { +class PasswordInput : public MaskedInputField { public: - PasswordField(QWidget *parent, const style::InputField &st, const QString &ph = QString(), const QString &val = QString()); + PasswordInput(QWidget *parent, const style::InputField &st, const QString &ph = QString(), const QString &val = QString()); }; @@ -655,3 +873,5 @@ private: QVector _pattern; }; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index dfcbb1b4e..6ad1eed9e 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_widgets.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "lang.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.h b/Telegram/SourceFiles/ui/widgets/multi_select.h index 9ac568ee6..753a7b8c8 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.h +++ b/Telegram/SourceFiles/ui/widgets/multi_select.h @@ -22,10 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_widgets.h" -class InputField; - namespace Ui { +class InputField; class IconButton; class MultiSelect : public TWidget { @@ -153,7 +152,7 @@ private: int _fieldLeft = 0; int _fieldTop = 0; int _fieldWidth = 0; - ChildWidget _field; + ChildWidget _field; ChildWidget _cancel; int _newHeight = 0; diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index f3ba2c336..edadae64e 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -35,6 +35,12 @@ LinkButton { overFont: font; } +RippleAnimation { + color: color; + showDuration: int; + hideDuration: int; +} + FlatButton { color: color; overColor: color; @@ -53,6 +59,188 @@ FlatButton { overFont: font; duration: int; cursor: cursor; + + ripple: RippleAnimation; +} + +RoundButton { + textFg: color; + textFgOver: color; + textBg: color; // rect of textBg with rounded rect of textBgOver upon it + textBgOver: color; + + secondaryTextFg: color; + secondaryTextFgOver: color; + secondarySkip: pixels; + + width: pixels; + height: pixels; + padding: margins; + + textTop: pixels; + downTextTop: pixels; + + icon: icon; + + font: font; + + ripple: RippleAnimation; +} + +Checkbox { + textFg: color; + textBg: color; + + checkBg: color; + checkFg: color; + checkFgOver: color; + checkFgActive: color; + + width: pixels; + height: pixels; + + textPosition: point; + diameter: pixels; + thickness: pixels; + checkIcon: icon; + + font: font; + duration: int; +} + +Radiobutton { + textFg: color; + textBg: color; + + checkBg: color; + checkFg: color; + checkFgOver: color; + checkFgActive: color; + + width: pixels; + height: pixels; + + textPosition: point; + diameter: pixels; + thickness: pixels; + checkSkip: pixels; + + font: font; + duration: int; +} + +FlatTextarea { + textColor: color; + bgColor: color; + width: pixels; + textMrg: margins; + align: align; + font: font; + cursor: cursor; + + phColor: color; + phFocusColor: color; + phPos: point; + phAlign: align; + phShift: pixels; + phDuration: int; +} + +FlatInput { + textColor: color; + bgColor: color; + bgActive: color; + width: pixels; + height: pixels; + textMrg: margins; + align: align; + font: font; + cursor: cursor; + + icon: icon; + + borderWidth: pixels; + borderColor: color; + borderActive: color; + borderError: color; + + phColor: color; + phFocusColor: color; + phPos: point; + phAlign: align; + phShift: pixels; + phDuration: int; +} + +InputArea { + textBg: color; + textFg: color; + textMargins: margins; + + placeholderFg: color; + placeholderFgActive: color; + placeholderMargins: margins; + placeholderAlign: align; + placeholderShift: pixels; + + duration: int; + + borderFg: color; + borderFgActive: color; + borderFgError: color; + + border: pixels; + borderActive: pixels; + borderError: pixels; + + font: font; + + width: pixels; + heightMin: pixels; + heightMax: pixels; +} + +InputField { + textBg: color; + textFg: color; + textMargins: margins; + textAlign: align; + + placeholderFg: color; + placeholderFgActive: color; + placeholderMargins: margins; + placeholderAlign: align; + placeholderShift: pixels; + + duration: int; + + borderFg: color; + borderFgActive: color; + borderFgError: color; + + border: pixels; + borderActive: pixels; + borderError: pixels; + + font: font; + + width: pixels; + height: pixels; +} + +OutlineButton { + outlineWidth: pixels; + outlineFg: color; + outlineFgOver: color; + + textBg: color; + textBgOver: color; + + textFg: color; + textFgOver: color; + + font: font; + padding: margins; } IconButton { @@ -66,6 +254,10 @@ IconButton { iconPositionDown: point; duration: int; + + rippleAreaPosition: point; + rippleAreaSize: pixels; + ripple: RippleAnimation; } Shadow { @@ -232,6 +424,194 @@ defaultLinkButton: LinkButton { overFont: linkOverFont; } +defaultRippleAnimation: RippleAnimation { + color: windowOverBg; + showDuration: 200; + hideDuration: 200; +} + +emptyRippleAnimation: RippleAnimation { +} + +defaultActiveButton: RoundButton { + textFg: activeButtonFg; + textFgOver: activeButtonFgOver; + secondaryTextFg: activeButtonSecondaryFg; + secondaryTextFgOver: activeButtonSecondaryFgOver; + textBg: activeButtonBg; + textBgOver: activeButtonBgOver; + + secondarySkip: 7px; + + width: -34px; + height: 34px; + padding: margins(0px, 0px, 0px, 0px); + + textTop: 8px; + downTextTop: 8px; + + font: semiboldFont; + + ripple: RippleAnimation(defaultRippleAnimation) { + color: activeButtonBgRipple; + } +} + +defaultLightButton: RoundButton(defaultActiveButton) { + textFg: lightButtonFg; + textFgOver: lightButtonFgOver; + textBg: lightButtonBg; + textBgOver: lightButtonBgOver; + + ripple: RippleAnimation(defaultRippleAnimation) { + color: lightButtonBgRipple; + } +} + +defaultInputFont: font(17px); +defaultFlatInput: FlatInput { + textColor: #000000; + bgColor: #f2f2f2; + bgActive: #ffffff; + width: 210px; + height: 40px; + align: align(left); + textMrg: margins(5px, 5px, 5px, 5px); + font: defaultInputFont; + cursor: cursor(text); + + borderWidth: 2px; + borderColor: #f2f2f2; + borderActive: #54c3f3; + borderError: #ed8080; + + phColor: #808080; + phFocusColor: #aaaaaa; + phAlign: align(left); + phPos: point(2px, 0px); + phShift: 50px; + phDuration: 100; +} + +defaultLeftOutlineButton: OutlineButton { + outlineWidth: 3px; + outlineFg: windowBg; + outlineFgOver: windowActiveBg; + + textBg: windowBg; + textBgOver: #f2f7fa; + + textFg: windowActiveTextFg; + textFgOver: windowActiveTextFg; + + font: normalFont; + padding: margins(11px, 5px, 11px, 5px); +} +attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) { + outlineFgOver: #e43f3f; + + textBgOver: #faf2f2; + + textFg: #d15948; + textFgOver: #d15948; +} + +defaultInputArea: InputArea { + textBg: windowBg; + textFg: windowTextFg; + textMargins: margins(5px, 6px, 5px, 4px); + + placeholderFg: #999999; + placeholderFgActive: #aaaaaa; + placeholderMargins: margins(2px, 0px, 2px, 0px); + placeholderAlign: align(topleft); + placeholderShift: 50px; + duration: 120; + + borderFg: #e0e0e0; + borderFgActive: #62c0f7; + borderFgError: #e48383; + + border: 1px; + borderActive: 2px; + borderError: 2px; + + font: boxTextFont; + + heightMin: 32px; + heightMax: 128px; +} + +defaultInputField: InputField { + textBg: windowBg; + textFg: windowTextFg; + textMargins: margins(0px, 6px, 0px, 4px); + textAlign: align(topleft); + + placeholderFg: #999999; + placeholderFgActive: #aaaaaa; + placeholderMargins: margins(2px, 0px, 2px, 0px); + placeholderAlign: align(topleft); + placeholderShift: 50px; + duration: 120; + + borderFg: #e0e0e0; + borderFgActive: #62c0f7; + borderFgError: #e48383; + + border: 1px; + borderActive: 2px; + borderError: 2px; + + font: boxTextFont; + + height: 32px; +} + +defaultCheckboxIcon: icon {{ "default_checkbox_check", windowActiveFg, point(4px, 7px) }}; + +defaultCheckbox: Checkbox { + textFg: windowTextFg; + textBg: windowBg; + + checkBg: #ffffff; + checkFg: #b3b3b3; + checkFgOver: #b3b3b3; + checkFgActive: windowActiveBg; + + width: -44px; + height: 22px; + + textPosition: point(32px, 2px); + diameter: 22px; + thickness: 2px; + checkIcon: defaultCheckboxIcon; + + font: normalFont; + duration: 120; +} + +defaultRadiobutton: Radiobutton { + textFg: windowTextFg; + textBg: windowBg; + + checkBg: #ffffff; + checkFg: #b3b3b3; + checkFgOver: #bfbfbf; + checkFgActive: #4eb3ee; + + width: -46px; + height: 22px; + + textPosition: point(34px, 0px); + diameter: 22px; + thickness: 2px; + checkSkip: 65px; // * 0.1 + + font: boxTextFont; + duration: 120; +} + defaultIconButton: IconButton { iconPosition: point(-1px, -1px); iconPositionDown: point(-1px, -1px); diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 3dec77fd9..11ec3e30e 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -58,6 +58,7 @@ void MainWindow::init() { } initSize(); + updateUnreadCounter(); } HitTestResult MainWindow::hitTest(const QPoint &p) const { diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 40218c278..67307b4f9 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -26,8 +26,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "lang.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" +#include "styles/style_boxes.h" #include "styles/style_window.h" namespace Window { @@ -225,7 +227,7 @@ void Manager::moveWidgets() { } if (count > 1 || !_queuedNotifications.isEmpty()) { - auto deltaY = st::notifyHideAll.height + st::notifyDeltaY; + auto deltaY = st::notifyHideAllHeight + st::notifyDeltaY; if (!_hideAll) { _hideAll = new HideAllButton(notificationStartPosition(), lastShiftCurrent, notificationShiftDirection()); } @@ -735,17 +737,17 @@ void Notification::showReplyField() { } stopHiding(); - _background = new Background(this); + _background.create(this); _background->setGeometry(0, st::notifyMinHeight, width(), st::notifySendReply.height + st::notifyBorderWidth); _background->show(); - _replyArea = new InputArea(this, st::notifyReplyArea, lang(lng_message_ph), QString()); + _replyArea.create(this, st::notifyReplyArea, lang(lng_message_ph), QString()); _replyArea->resize(width() - st::notifySendReply.width - 2 * st::notifyBorderWidth, st::notifySendReply.height); _replyArea->moveToLeft(st::notifyBorderWidth, st::notifyMinHeight); _replyArea->show(); _replyArea->setFocus(); _replyArea->setMaxLength(MaxMessageSize); - _replyArea->setCtrlEnterSubmit(CtrlEnterSubmitBoth); + _replyArea->setCtrlEnterSubmit(Ui::CtrlEnterSubmitBoth); // Catch mouse press event to activate the window. Sandbox::installEventFilter(this); @@ -860,8 +862,8 @@ Notification::~Notification() { HideAllButton::HideAllButton(QPoint startPosition, int shift, Direction shiftDirection) : Widget(startPosition, shift, shiftDirection) { setCursor(style::cur_pointer); - auto position = computePosition(st::notifyHideAll.height); - updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyHideAll.height); + auto position = computePosition(st::notifyHideAllHeight); + updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyHideAllHeight); hide(); createWinId(); @@ -913,7 +915,7 @@ void HideAllButton::paintEvent(QPaintEvent *e) { Painter p(this); p.setClipRect(e->rect()); - p.fillRect(rect(), _mouseOver ? st::notifyHideAll.textBgOver : st::notifyHideAll.textBg); + p.fillRect(rect(), _mouseOver ? st::lightButtonBgOver : st::lightButtonBg); p.fillRect(0, 0, width(), st::notifyBorderWidth, st::notifyBorder); p.fillRect(0, height() - st::notifyBorderWidth, width(), st::notifyBorderWidth, st::notifyBorder); p.fillRect(0, st::notifyBorderWidth, st::notifyBorderWidth, height() - 2 * st::notifyBorderWidth, st::notifyBorder); diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index 67b5956e6..b6384ac62 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class IconButton; class RoundButton; +class InputArea; } // namespace Ui namespace Window { @@ -248,7 +249,7 @@ private: ChildWidget _close; ChildWidget _reply; ChildWidget _background = { nullptr }; - ChildWidget _replyArea = { nullptr }; + ChildWidget _replyArea = { nullptr }; ChildWidget _replySend = { nullptr }; bool _waitingForInput = true; diff --git a/Telegram/SourceFiles/window/slide_animation.cpp b/Telegram/SourceFiles/window/slide_animation.cpp index f8e27c174..bca18bfe7 100644 --- a/Telegram/SourceFiles/window/slide_animation.cpp +++ b/Telegram/SourceFiles/window/slide_animation.cpp @@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "window/slide_animation.h" +#include "styles/style_window.h" + namespace Window { void SlideAnimation::paintContents(Painter &p, const QRect &update) const { diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 76978560f..92133fe09 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -21,7 +21,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "window/top_bar_widget.h" -#include "styles/style_history.h" #include "styles/style_window.h" #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" @@ -41,7 +40,7 @@ TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w) , _clearSelection(this, lang(lng_selected_clear), st::topBarClearButton) , _forward(this, lang(lng_selected_forward), st::defaultActiveButton) , _delete(this, lang(lng_selected_delete), st::defaultActiveButton) -, _info(this, nullptr, st::infoButton) +, _info(this, nullptr, st::topBarInfoButton) , _mediaType(this, lang(lng_media_type), st::topBarButton) , _search(this, st::topBarSearch) , _menuToggle(this, st::topBarMenuToggle) { @@ -93,15 +92,23 @@ void TopBarWidget::onSearch() { void TopBarWidget::showMenu() { if (auto main = App::main()) { if (auto peer = main->peer()) { - _menu.create(App::main()); - App::main()->fillPeerMenu(peer, [this](const QString &text, base::lambda_unique callback) { - return _menu->addAction(text, std_::move(callback)); - }); - _menu->setHiddenCallback([this] { - _menu.destroyDelayed(); - }); - _menu->moveToRight(0, 0); - _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); + if (auto menu = _menu.ptr()) { + _menu = nullptr; + _menuToggle->removeEventFilter(menu); + menu->setHiddenCallback([menu] { menu->deleteLater(); }); + menu->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); + } else { + _menu.create(App::main()); + _menuToggle->installEventFilter(_menu); + App::main()->fillPeerMenu(peer, [this](const QString &text, base::lambda_unique callback) { + return _menu->addAction(text, std_::move(callback)); + }); + _menu->setHiddenCallback([this] { + _menu.destroyDelayed(); + }); + _menu->moveToRight(st::topBarMenuPosition.x(), st::topBarMenuPosition.y()); + _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); + } } } } diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index a6db6a26c..404a7945e 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -60,8 +60,7 @@ notifyDeltaX: 6px; notifyDeltaY: 7px; notifyActionsDuration: 200; -notifyHideAll: RoundButton(defaultBoxButton) { -} +notifyHideAllHeight: 36px; notifyReplyArea: InputArea(defaultInputArea) { font: normalFont; @@ -156,6 +155,78 @@ titleButtonClose: IconButton(titleButtonMinimize) { }; } +// Legacy top bar. +topBarHeight: 54px; +topBarMenuPosition: point(-2px, 37px); +topBarDuration: 200; +topBarBackward: icon {{ "title_back", #a3a3a3 }}; +topBarForwardAlpha: 0.6; +topBarBack: icon {{ "title_back", #259fd8 }}; +topBarBackAlpha: 0.8; +topBarBackColor: #005faf; +topBarBackFont: font(16px); +topBarArrowPadding: margins(39px, 8px, 17px, 8px); +topBarMinPadding: 5px; +topBarButton: RoundButton { + textFg: btnYesColor; + textFgOver: btnYesColor; + secondaryTextFg: btnYesColor; + secondaryTextFgOver: btnYesColor; + textBg: windowBg; + textBgOver: #edf4f7; + + width: -22px; + height: 28px; + padding: margins(0px, 14px, 12px, 12px); + + textTop: 6px; + downTextTop: 6px; + + font: font(fsize); + + ripple: RippleAnimation(defaultRippleAnimation) { + color: lightButtonBgRipple; + } +} +topBarClearButton: RoundButton(defaultLightButton) { + width: -18px; +} +topBarSearch: IconButton { + width: 44px; + height: topBarHeight; + + icon: icon {{ "title_search", menuIconFg }}; + iconOver: icon {{ "title_search", menuIconFgOver }}; + + iconPosition: point(15px, 18px); + iconPositionDown: point(15px, 18px); + + rippleAreaPosition: point(8px, 11px); + rippleAreaSize: 32px; + ripple: defaultRippleAnimation; +} +topBarMenuToggle: IconButton(topBarSearch) { + icon: icon {{ "title_menu_dots", menuIconFg }}; + iconOver: icon {{ "title_menu_dots", menuIconFgOver }}; + + iconPosition: point(15px, 17px); + iconPositionDown: point(15px, 17px); + + rippleAreaPosition: point(3px, 11px); + rippleAreaSize: 32px; + ripple: defaultRippleAnimation; +} +topBarActionSkip: 10px; + +PeerAvatarButton { + size: pixels; + photoSize: pixels; +} +topBarInfoButton: PeerAvatarButton { + size: topBarHeight; + photoSize: 42px; +} + // Mac specific macAccessoryWidth: 450.; diff --git a/Telegram/build/version b/Telegram/build/version index 10659728d..f08d2d9d9 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019006 +BetaVersion 10019007 diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index d19b437ca..43a82bef8 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -462,6 +462,8 @@ '<(src_loc)/ui/effects/radial_animation.h', '<(src_loc)/ui/effects/rect_shadow.cpp', '<(src_loc)/ui/effects/rect_shadow.h', + '<(src_loc)/ui/effects/ripple_animation.cpp', + '<(src_loc)/ui/effects/ripple_animation.h', '<(src_loc)/ui/effects/round_image_checkbox.cpp', '<(src_loc)/ui/effects/round_image_checkbox.h', '<(src_loc)/ui/effects/widget_fade_wrap.cpp', @@ -504,6 +506,8 @@ '<(src_loc)/ui/widgets/filled_slider.h', '<(src_loc)/ui/widgets/inner_dropdown.cpp', '<(src_loc)/ui/widgets/inner_dropdown.h', + '<(src_loc)/ui/widgets/input_fields.cpp', + '<(src_loc)/ui/widgets/input_fields.h', '<(src_loc)/ui/widgets/label_simple.cpp', '<(src_loc)/ui/widgets/label_simple.h', '<(src_loc)/ui/widgets/media_slider.cpp', @@ -528,12 +532,8 @@ '<(src_loc)/ui/emoji_config.h', '<(src_loc)/ui/filedialog.cpp', '<(src_loc)/ui/filedialog.h', - '<(src_loc)/ui/flatinput.cpp', - '<(src_loc)/ui/flatinput.h', '<(src_loc)/ui/flatlabel.cpp', '<(src_loc)/ui/flatlabel.h', - '<(src_loc)/ui/flattextarea.cpp', - '<(src_loc)/ui/flattextarea.h', '<(src_loc)/ui/images.cpp', '<(src_loc)/ui/images.h', '<(src_loc)/ui/scrollarea.cpp', From 7fa274a68ebe8ee8eae90c94373ea9bca3a638da Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Nov 2016 13:44:06 +0300 Subject: [PATCH 040/100] Improved ripple animation colors and styles for different buttons. Also moved FlatLabel and LabelSimple to ui/widgets/labels module. Also moved ScrollArea to ui/widgets/scroll_area module. --- Telegram/Resources/basic.style | 223 +---------- Telegram/Resources/basic_types.style | 41 +- Telegram/Resources/colors.palette | 89 ++--- Telegram/Resources/sample.tdesktop-theme | 89 ++--- Telegram/SourceFiles/app.cpp | 1 + Telegram/SourceFiles/boxes/aboutbox.cpp | 16 +- Telegram/SourceFiles/boxes/aboutbox.h | 11 +- Telegram/SourceFiles/boxes/abstractbox.cpp | 5 +- Telegram/SourceFiles/boxes/abstractbox.h | 9 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 3 +- Telegram/SourceFiles/boxes/addcontactbox.h | 7 +- Telegram/SourceFiles/boxes/boxes.style | 93 ++++- Telegram/SourceFiles/boxes/confirmbox.cpp | 7 +- Telegram/SourceFiles/boxes/confirmbox.h | 13 +- .../SourceFiles/boxes/confirmphonebox.cpp | 3 +- Telegram/SourceFiles/boxes/confirmphonebox.h | 5 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 1 + .../SourceFiles/boxes/localstoragebox.cpp | 2 +- Telegram/SourceFiles/boxes/members_box.cpp | 1 + Telegram/SourceFiles/boxes/sessionsbox.cpp | 1 + Telegram/SourceFiles/boxes/sharebox.cpp | 1 + Telegram/SourceFiles/boxes/stickers_box.cpp | 1 + Telegram/SourceFiles/boxes/stickersetbox.cpp | 1 + Telegram/SourceFiles/core/click_handler.h | 15 + Telegram/SourceFiles/dialogs/dialogs.style | 61 ++- Telegram/SourceFiles/dialogswidget.cpp | 49 +-- Telegram/SourceFiles/dialogswidget.h | 7 +- .../history/field_autocomplete.cpp | 7 +- .../SourceFiles/history/field_autocomplete.h | 6 +- Telegram/SourceFiles/history/history.style | 99 ++++- .../SourceFiles/history/history_drag_area.cpp | 1 + Telegram/SourceFiles/history/history_item.cpp | 31 +- Telegram/SourceFiles/history/history_item.h | 52 +-- .../history/history_media_types.cpp | 74 ++-- .../SourceFiles/history/history_media_types.h | 18 - .../SourceFiles/history/history_message.cpp | 28 +- .../SourceFiles/history/history_message.h | 13 - Telegram/SourceFiles/historywidget.cpp | 354 +++++++++--------- Telegram/SourceFiles/historywidget.h | 21 +- .../inline_bot_layout_internal.cpp | 1 + Telegram/SourceFiles/intro/intro.style | 8 +- Telegram/SourceFiles/intro/introphone.cpp | 24 +- Telegram/SourceFiles/intro/introphone.h | 8 +- Telegram/SourceFiles/intro/introstart.cpp | 3 +- Telegram/SourceFiles/intro/introstart.h | 5 +- Telegram/SourceFiles/layerwidget.cpp | 3 + Telegram/SourceFiles/mainwidget.cpp | 4 +- Telegram/SourceFiles/mainwindow.cpp | 2 +- .../media/player/media_player.style | 8 +- .../media/player/media_player_cover.cpp | 3 +- .../media/player/media_player_cover.h | 5 +- .../media/player/media_player_panel.cpp | 1 + .../media/player/media_player_panel.h | 5 +- .../media/player/media_player_widget.cpp | 3 +- .../media/player/media_player_widget.h | 4 +- .../media/view/media_clip_controller.cpp | 2 +- Telegram/SourceFiles/mediaview.cpp | 19 +- Telegram/SourceFiles/mediaview.h | 3 +- .../SourceFiles/overview/overview_layout.cpp | 4 +- Telegram/SourceFiles/overviewwidget.cpp | 169 ++++----- Telegram/SourceFiles/overviewwidget.h | 13 +- Telegram/SourceFiles/passcodewidget.cpp | 2 +- Telegram/SourceFiles/profile/profile.style | 18 +- .../SourceFiles/profile/profile_cover.cpp | 2 +- Telegram/SourceFiles/profile/profile_cover.h | 5 +- .../profile/profile_info_widget.cpp | 14 +- .../SourceFiles/profile/profile_info_widget.h | 22 +- .../profile/profile_invite_link_widget.cpp | 4 +- .../profile/profile_invite_link_widget.h | 4 +- .../profile/profile_members_widget.cpp | 4 +- .../profile/profile_members_widget.h | 5 +- .../profile/profile_userpic_button.cpp | 12 +- .../profile/profile_userpic_button.h | 3 +- .../SourceFiles/profile/profile_widget.cpp | 4 +- Telegram/SourceFiles/profile/profile_widget.h | 4 +- Telegram/SourceFiles/settings/settings.style | 15 +- .../settings_chat_settings_widget.cpp | 6 +- .../settings/settings_chat_settings_widget.h | 4 +- .../SourceFiles/settings/settings_cover.cpp | 2 +- .../SourceFiles/settings/settings_cover.h | 4 +- .../settings/settings_fixed_bar.cpp | 2 +- .../settings/settings_general_widget.cpp | 17 +- .../settings/settings_info_widget.cpp | 8 +- .../settings/settings_info_widget.h | 14 +- .../settings/settings_privacy_widget.cpp | 2 +- .../SourceFiles/settings/settings_widget.cpp | 4 +- .../SourceFiles/settings/settings_widget.h | 3 +- Telegram/SourceFiles/stdafx.h | 1 - Telegram/SourceFiles/stickers/emoji_pan.cpp | 252 +++++++------ Telegram/SourceFiles/stickers/emoji_pan.h | 17 +- Telegram/SourceFiles/stickers/stickers.style | 6 +- Telegram/SourceFiles/structs.cpp | 20 +- Telegram/SourceFiles/structs.h | 22 +- Telegram/SourceFiles/ui/countryinput.cpp | 4 +- Telegram/SourceFiles/ui/countryinput.h | 1 - Telegram/SourceFiles/ui/text/text.cpp | 6 +- Telegram/SourceFiles/ui/text/text.h | 4 +- Telegram/SourceFiles/ui/text/text_block.cpp | 2 +- Telegram/SourceFiles/ui/widgets/buttons.cpp | 7 +- .../SourceFiles/ui/widgets/inner_dropdown.cpp | 2 +- .../SourceFiles/ui/widgets/inner_dropdown.h | 6 +- .../SourceFiles/ui/widgets/label_simple.cpp | 64 ---- .../SourceFiles/ui/widgets/label_simple.h | 48 --- .../ui/{flatlabel.cpp => widgets/labels.cpp} | 131 ++++--- .../ui/{flatlabel.h => widgets/labels.h} | 37 +- .../SourceFiles/ui/widgets/multi_select.cpp | 1 + .../SourceFiles/ui/widgets/multi_select.h | 3 +- .../scroll_area.cpp} | 46 +-- .../{scrollarea.h => widgets/scroll_area.h} | 30 +- Telegram/SourceFiles/ui/widgets/widgets.style | 113 +++++- Telegram/SourceFiles/window/window.style | 43 ++- .../SourceFiles/window/window_main_menu.cpp | 74 ++-- .../SourceFiles/window/window_main_menu.h | 21 +- Telegram/SourceFiles/window/window_theme.cpp | 2 + Telegram/gyp/Telegram.gyp | 10 +- 115 files changed, 1435 insertions(+), 1478 deletions(-) delete mode 100644 Telegram/SourceFiles/ui/widgets/label_simple.cpp delete mode 100644 Telegram/SourceFiles/ui/widgets/label_simple.h rename Telegram/SourceFiles/ui/{flatlabel.cpp => widgets/labels.cpp} (87%) rename Telegram/SourceFiles/ui/{flatlabel.h => widgets/labels.h} (84%) rename Telegram/SourceFiles/ui/{scrollarea.cpp => widgets/scroll_area.cpp} (94%) rename Telegram/SourceFiles/ui/{scrollarea.h => widgets/scroll_area.h} (89%) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 6efd12541..6608cb298 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -27,6 +27,8 @@ semibold: "Open Sans Semibold"; fsize: 13px; normalFont: font(fsize); semiboldFont: font(fsize semibold); +boxFontSize: 14px; +boxTextFont: font(boxFontSize); emojiImgSize: 18px; // exceptional value for retina emojiSize: 18px; @@ -34,56 +36,6 @@ emojiPadding: 1px; lineWidth: 1px; -labelDefFlat: flatLabel { - font: font(fsize); - width: 0px; - maxHeight: 0px; - align: align(left); - textFg: windowTextFg; -} - -boxVerticalMargin: 10px; -boxWidth: 320px; -boxWideWidth: 364px; -boxPadding: margins(26px, 30px, 34px, 8px); -boxMaxListHeight: 600px; -boxFontSize: 14px; -boxTextFont: font(boxFontSize); -boxLittleSkip: 10px; -boxMediumSkip: 20px; - -boxTitleFont: font(boxFontSize bold); -boxTitlePosition: point(26px, 28px); -boxTitleHeight: 54px; - -boxButtonFont: font(boxFontSize semibold); -boxButtonPadding: margins(12px, 16px, 22px, 16px); -boxLabel: flatLabel(labelDefFlat) { - font: font(boxFontSize); - align: align(topleft); -} - -solidScroll: flatScroll { - barColor: #3f729734; - bgColor: #214f751a; - barOverColor: #3f729734; - bgOverColor: #214f751a; - - minHeight: 20px; - - round: 2px; - deltax: 5px; - width: 14px; - deltat: 6px; - deltab: 6px; - - topsh: 0px; - bottomsh: 0px; - shColor: #00000012; - - duration: 150; - hiding: 0; -} defaultDropdownDuration: 150; defaultDropdownPadding: margins(10px, 10px, 10px, 10px); defaultDropdownShadow: icon {{ "dropdown_shadow", windowShadowFg }}; @@ -103,14 +55,6 @@ defaultTooltip: Tooltip { linesMax: 12; } -boxScroll: flatScroll(solidScroll) { - round: 3px; - width: 18px; - deltax: 6px; -} -boxScrollSkip: 6px; -boxScrollShadowBg: #00000012; - statusFont: font(fsize); statusFg: #999999; statusFgActive: #0080c0; @@ -135,63 +79,12 @@ linkCropLimit: 360px; linkFont: normalFont; linkOverFont: font(fsize underline); -scrollDef: flatScroll { - barColor: #00000053; - bgColor: #0000001a; - barOverColor: #0000007a; - bgOverColor: #0000002c; - - round: 2px; - - width: 10px; - minHeight: 20px; - deltax: 3px; - deltat: 3px; - deltab: 3px; - - topsh: 2px; - bottomsh: 2px; - shColor: #00000012; - - duration: 150; - hiding: 1000; -} - dateRadius: 10px; buttonRadius: 3px; -scrollCountries: flatScroll(scrollDef) { - topsh: 0px; - bottomsh: -2px; -} - lnkText: #0f7dc7; -boxShadow: icon {{ "box_shadow", windowShadowFg }}; -boxShadowShift: 2px; - -btnSelectSep: #e0e0e0; - -countryRowHeight: 36px; -countryRowNameFont: semiboldFont; -countryRowNameFg: boxTextFg; -countryRowPadding: margins(22px, 9px, 8px, 0px); -countryRowCodeFont: font(fsize); -countryRowBg: windowBg; -countryRowBgOver: windowOverBg; -countryRowCodeFg: windowSubTextFg; -countryRowCodeFgOver: windowSubTextFgOver; -countriesSkip: 12px; -countriesScroll: flatScroll(boxScroll) { - deltat: 9px; - deltab: 3px; -} - setLittleSkip: 9px; -setScroll: flatScroll(scrollDef) { - bottomsh: 0px; - topsh: 0px; -} noContactsHeight: 100px; noContactsFont: font(fsize); @@ -245,20 +138,6 @@ msgOutReplyBarColor: historyOutIconFg; msgOutReplyBarSelColor: historyOutIconFgSelected; msgImgReplyBarColor: #ffffff; -msgBotKbDuration: 200; -msgBotKbFont: semiboldFont; -msgBotKbOverBg: #ffffff1a; -msgBotKbIconPadding: 2px; -msgBotKbUrlIcon: icon {{ "inline_button_url", #ffffff }}; -msgBotKbSwitchPmIcon: icon {{ "inline_button_switch", #ffffff }}; -msgBotKbButton: botKeyboardButton { - margin: 5px; - padding: 10px; - height: 36px; - textTop: 8px; - downTextTop: 9px; -} - msgServiceBg: #89a0b47f; msgServiceSelectBg: #bbc8d4a2; msgServiceColor: #ffffff; @@ -283,7 +162,7 @@ msgDateImgBgSelected: #1c4a7187; msgDateImgPadding: point(8px, 2px); msgDateImgCheckSpace: 4px; -defaultTextStyle: textStyle { +defaultTextStyle: TextStyle { linkFlags: font(fsize); linkFlagsOver: font(fsize underline); linkFg: btnYesColor; @@ -293,10 +172,7 @@ defaultTextStyle: textStyle { selectOverlay: msgSelectOverlay; lineHeight: 0px; } -boxTextStyle: textStyle(defaultTextStyle) { - lineHeight: 22px; -} -serviceTextStyle: textStyle(defaultTextStyle) { +serviceTextStyle: TextStyle(defaultTextStyle) { linkFlags: msgServiceFont; linkFlagsOver: font(fsize semibold underline); linkFg: msgServiceColor; @@ -305,35 +181,35 @@ serviceTextStyle: textStyle(defaultTextStyle) { selectBg: msgServiceSelectBg; selectOverlay: msgServiceSelectBg; } -inTextStyle: textStyle(defaultTextStyle) { +inTextStyle: TextStyle(defaultTextStyle) { monoFg: #4e7391; selectBg: msgInBgSelected; selectOverlay: msgSelectOverlay; } -outTextStyle: textStyle(defaultTextStyle) { +outTextStyle: TextStyle(defaultTextStyle) { monoFg: #469165; selectBg: msgOutBgSelected; selectOverlay: msgSelectOverlay; } -inFwdTextStyle: textStyle(defaultTextStyle) { +inFwdTextStyle: TextStyle(defaultTextStyle) { linkFlags: semiboldFont; linkFlagsOver: semiboldFont; linkFg: msgInServiceFg; linkFgDown: msgInServiceFg; } -outFwdTextStyle: textStyle(inFwdTextStyle) { +outFwdTextStyle: TextStyle(inFwdTextStyle) { linkFg: msgOutServiceFg; linkFgDown: msgOutServiceFg; } -inFwdTextStyleSelected: textStyle(inFwdTextStyle) { +inFwdTextStyleSelected: TextStyle(inFwdTextStyle) { linkFg: msgInServiceFgSelected; linkFgDown: msgInServiceFgSelected; } -outFwdTextStyleSelected: textStyle(inFwdTextStyle) { +outFwdTextStyleSelected: TextStyle(inFwdTextStyle) { linkFg: msgOutServiceFgSelected; linkFgDown: msgOutServiceFgSelected; } -medviewSaveAsTextStyle: textStyle(defaultTextStyle) { +medviewSaveAsTextStyle: TextStyle(defaultTextStyle) { linkFg: #91d9ff; linkFgDown: #91d9ff; } @@ -356,11 +232,11 @@ mediaUnreadSize: 7px; mediaUnreadSkip: 5px; mediaUnreadTop: 6px; -mediaInStyle: textStyle(defaultTextStyle) { +mediaInStyle: TextStyle(defaultTextStyle) { linkFg: mediaInFg; linkFgDown: mediaInFg; } -mediaInStyleSelected: textStyle(defaultTextStyle) { +mediaInStyleSelected: TextStyle(defaultTextStyle) { linkFg: mediaInFgSelected; linkFgDown: mediaInFgSelected; } @@ -397,7 +273,7 @@ msgFileThumbLinkOutFgSelected: #31a298; msgFileNameTop: 16px; msgFileStatusTop: 37px; msgFileMinWidth: 294px; -msgFileInBg: windowActiveBg; +msgFileInBg: windowBgActive; msgFileInBgOver: #4eade3; msgFileInBgSelected: #51a3d3; msgFileOutBg: #78c67f; @@ -413,7 +289,7 @@ msgWaveformBar: 2px; msgWaveformSkip: 1px; msgWaveformMin: 2px; msgWaveformMax: 20px; -msgWaveformInActive: windowActiveBg; +msgWaveformInActive: windowBgActive; msgWaveformInActiveSelected: #51a3d3; msgWaveformInInactive: #d4dee6; msgWaveformInInactiveSelected: #9cc1e1; @@ -422,22 +298,6 @@ msgWaveformOutActiveSelected: #6badad; msgWaveformOutInactive: #b3e2b4; msgWaveformOutInactiveSelected: #91c3c3; -historyScroll: flatScroll(scrollDef) { - barColor: #89a0b47a; - bgColor: #89a0b44c; - barOverColor: #89a0b4bc; - bgOverColor: #89a0b46b; - - round: 3px; - - width: 12px; - deltax: 3px; - deltat: 3px; - deltab: 3px; - - topsh: 0px; - bottomsh: -1px; -} textRectMargins: margins(-2px, -1px, -2px, -1px); newMsgSound: ":/gui/art/newmsg.wav"; @@ -463,13 +323,6 @@ simpleCloseIcon: icon {{ "simple_close", #c7c7c7 }}; simpleCloseIconOver: icon {{ "simple_close", #a3a3a3 }}; dialogsForwardCancelIcon: icon {{ "simple_close", dialogsForwardFg }}; -boxPhotoPadding: margins(28px, 28px, 28px, 18px); -boxPhotoCompressedPadding: margins(0px, 2px, 0px, 22px); -boxPhotoTextFg: #808080; -cropPointSize: 10px; -cropSkip: 13px; -cropMinSize: 20px; - profileMaxWidth: 410px; profilePadding: margins(28px, 30px, 28px, 0px); @@ -504,31 +357,6 @@ dragPadding: margins(20px, 10px, 20px, 10px); dragHeight: 72px; -botKbDuration: 200; -botKbBg: #edf1f5; -botKbOverBg: #d8e2ec; -botKbDownBg: #d8e2ec; -botKbColor: #4b565f; -botKbFont: font(15px semibold); -botKbButton: botKeyboardButton { - margin: 10px; - padding: 10px; - height: 38px; - textTop: 9px; - downTextTop: 9px; -} -botKbTinyButton: botKeyboardButton { - margin: 4px; - padding: 3px; - height: 25px; - textTop: 2px; - downTextTop: 2px; -} -botKbScroll: flatScroll(solidScroll) { - deltax: 3px; - width: 10px; -} - minPhotoSize: 100px; maxMediaSize: 420px; maxStickerSize: 256px; @@ -590,7 +418,7 @@ downloadPathSkip: 10px; usernamePadding: margins(23px, 22px, 21px, 12px); usernameSkip: 49px; -usernameTextStyle: textStyle(defaultTextStyle) { +usernameTextStyle: TextStyle(defaultTextStyle) { lineHeight: 20px; } usernameDefaultFg: #777777; @@ -605,23 +433,6 @@ videoIcon: icon { }; locationSize: size(320px, 240px); -mentionHeight: 40px; -mentionScroll: flatScroll(scrollDef) { - topsh: 0px; - bottomsh: 0px; -} -mentionPadding: margins(8px, 5px, 8px, 5px); -mentionTop: 11px; -mentionFont: linkFont; -mentionNameFg: windowTextFg; -mentionPhotoSize: msgPhotoSize; -mentionBg: #ffffff; -mentionBgOver: #f5f5f5; -mentionFg: #777777; -mentionFgOver: #707070; -mentionFgActive: #0080c0; -mentionFgOverActive: #0077b3; - webPageLeft: 10px; webPageBar: 2px; webPageTitleFont: semiboldFont; @@ -645,7 +456,7 @@ inlineResultsSkip: 3px; inlineMediaHeight: 96px; inlineThumbSize: 64px; inlineThumbSkip: 10px; -inlineTitleFg: windowTextFg; +inlineTitleFg: windowFg; inlineDescriptionFg: windowSubTextFg; inlineRowMargin: 6px; inlineRowBorder: 1px; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index 09967422d..07875c2a9 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ -textStyle { +TextStyle { linkFlags: font; linkFlagsOver: font; linkFg: color; @@ -30,37 +30,6 @@ textStyle { lineHeight: pixels; } -flatScroll { - barColor: color; - bgColor: color; - barOverColor: color; - bgOverColor: color; - - round: pixels; - - width: pixels; - minHeight: pixels; - deltax: pixels; - deltat: pixels; - deltab: pixels; - - topsh: pixels; - bottomsh: pixels; - shColor: color; - - duration: int; - hiding: int; -} - -flatLabel { - font: font; - margin: margins; - width: pixels; - align: align; - textFg: color; - maxHeight: pixels; -} - Tooltip { textBg: color; textFg: color; @@ -74,11 +43,3 @@ Tooltip { widthMax: pixels; linesMax: int; } - -botKeyboardButton { - margin: pixels; - padding: pixels; - height: pixels; - textTop: pixels; - downTextTop: pixels; -} diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 14b8d9d20..2547b6c57 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -21,12 +21,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org // basic windowBg: #ffffff; // white: fallback for background -windowTextFg: #000000; // black: fallback for text color +windowFg: #000000; // black: fallback for text color +windowBgOver: #f1f1f1; // light gray: fallback for over background +windowBgRipple: #e5e5e5; // darker gray: fallback for ripple effect +windowFgOver: windowFg; // black: fallback for over text color windowSubTextFg: #999999; // gray: fallback for subtext color -windowActiveBg: #40ace3; // bright blue: fallback for blue filled active areas -windowActiveFg: #ffffff; // text on bright blue: fallback for text on active areas -windowOverBg: #f0f0f0; // light gray: fallback for over background -windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over color +windowSubTextFgOver: #919191; // gray over light blue: fallback for subtext over color +windowBoldFg: #222222; // dark gray: fallback for bold text color +windowBoldFgOver: #222222; // dark gray: fallback for over bold text color +windowBgActive: #40ace3; // bright blue: fallback for blue filled active areas +windowFgActive: #ffffff; // text on bright blue: fallback for text on active areas windowActiveTextFg: #2687bf; // online blue: fallback for active color windowShadowFg: #000000; // black: fallback for shadow color windowShadowFgFallback: #cdcdcd; // gray: fallback for shadow without @@ -35,27 +39,28 @@ imageBg: #000000; imageBgTransparent: #ffffff; // widgets -activeButtonBg: windowActiveBg; -activeButtonBgOver: #46b4eb; -activeButtonBgRipple: #177eb2; -activeButtonFg: windowActiveFg; +activeButtonBg: windowBgActive; +activeButtonBgOver: #39a5db; +activeButtonBgRipple: #2095d0; +activeButtonFg: windowFgActive; activeButtonFgOver: activeButtonFg; activeButtonSecondaryFg: #cceeff; activeButtonSecondaryFgOver: activeButtonSecondaryFg; lightButtonBg: windowBg; -lightButtonBgOver: #edf7ff; -lightButtonBgRipple: #c7e1f6; +lightButtonBgOver: #e3f1fa; +lightButtonBgRipple: #c9e4f6; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; menuBg: windowBg; -menuBgOver: windowOverBg; +menuBgOver: windowBgOver; +menuBgRipple: windowBgRipple; menuIconFg: #a8a8a8; menuIconFgOver: #999999; // custom title bar for Windows and macOS -titleBg: windowOverBg; +titleBg: windowBgOver; titleShadow: #00000003; titleButtonFg: #ababab; titleButtonBgOver: #e5e5e5; @@ -80,7 +85,7 @@ cancelIconFgOver: #808080; // boxes boxBg: windowBg; -boxTextFg: windowTextFg; +boxTextFg: windowFg; boxTextFgGood: #4ab44a; boxTextFgError: #d84d4d; boxTitleFg: #404040; @@ -94,41 +99,41 @@ boxBlockTitleAdditionalFg: #808080; boxBlockTitleCloseFg: cancelIconFg; boxBlockTitleCloseFgOver: cancelIconFgOver; -attentionBoxButtonFg: #ea4b2f; -attentionBoxButtonFgOver: #ea4b2f; -attentionBoxButtonBgOver: #fff0ed; -attentionBoxButtonBgRipple: #efbcb2; +attentionBoxButtonFg: #d14e4e; +attentionBoxButtonFgOver: #d14e4e; +attentionBoxButtonBgOver: #fcdfde; +attentionBoxButtonBgRipple: #f4c3c2; -membersAboutLimitFg: windowSubTextFg; +membersAboutLimitFg: windowSubTextFgOver; contactsBg: windowBg; -contactsBgOver: windowOverBg; +contactsBgOver: windowBgOver; contactsNameFg: boxTextFg; contactsStatusFg: windowSubTextFg; -contactsStatusFgOver: contactsStatusFg; -contactsStatusFgOnline: #3b8dcc; +contactsStatusFgOver: windowSubTextFgOver; +contactsStatusFgOnline: windowActiveTextFg; photoCropFadeBg: #0000007f; photoCropPointFg: #ffffff7f; // settings -notificationsBoxMonitorFg: windowTextFg; +notificationsBoxMonitorFg: windowFg; -notificationSampleUserpicFg: windowActiveBg; +notificationSampleUserpicFg: windowBgActive; notificationSampleCloseFg: #d7d7d7 | windowSubTextFg; notificationSampleTextFg: #d7d7d7 | windowSubTextFg; notificationSampleNameFg: #939393 | windowSubTextFg; // intro -introHeaderFg: windowTextFg; -introErrorFg: windowTextFg; +introHeaderFg: windowFg; +introErrorFg: windowFg; // dialogs dialogsMenuIconFg: menuIconFg; dialogsMenuIconFgOver: menuIconFgOver; dialogsBg: windowBg; -dialogsNameFg: #373737; +dialogsNameFg: windowBoldFg; dialogsChatIconFg: dialogsNameFg; dialogsDateFg: windowSubTextFg; dialogsTextFg: windowSubTextFg; @@ -138,15 +143,15 @@ dialogsVerifiedIconBg: #4abcf1; dialogsVerifiedIconFg: #ffffff; dialogsSendingIconFg: #c1c1c1; dialogsSentIconFg: #5dc452; -dialogsUnreadBg: windowActiveBg; +dialogsUnreadBg: windowBgActive; dialogsUnreadBgMuted: #bbbbbb; dialogsUnreadFg: #ffffff; -dialogsBgOver: windowOverBg; -dialogsNameFgOver: windowTextFg; +dialogsBgOver: windowBgOver; +dialogsNameFgOver: windowBoldFgOver; dialogsChatIconFgOver: dialogsNameFgOver; -dialogsDateFgOver: #8a8a8a | dialogsDateFg; -dialogsTextFgOver: dialogsTextFg; +dialogsDateFgOver: windowSubTextFgOver; +dialogsTextFgOver: windowSubTextFgOver; dialogsTextFgServiceOver: dialogsTextFgService; dialogsDraftFgOver: dialogsDraftFg; dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; @@ -158,10 +163,10 @@ dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; dialogsUnreadFgOver: dialogsUnreadFg; dialogsBgActive: #419fd9; -dialogsNameFgActive: windowActiveFg; +dialogsNameFgActive: windowFgActive; dialogsChatIconFgActive: dialogsNameFgActive; -dialogsDateFgActive: windowActiveFg; -dialogsTextFgActive: windowActiveFg; +dialogsDateFgActive: windowFgActive; +dialogsTextFgActive: windowFgActive; dialogsTextFgServiceActive: dialogsTextFgActive; dialogsDraftFgActive: #c6e1f7; dialogsVerifiedIconBgActive: dialogsTextFgActive; @@ -186,19 +191,19 @@ emojiPanHeaderBg: #fffffff2 | emojiPanBg; historyComposeAreaBg: windowBg; historyComposeIconFg: menuIconFg; historyComposeIconFgOver: menuIconFgOver; -historySendIconFg: windowActiveBg; -historySendIconFgOver: windowActiveBg; +historySendIconFg: windowBgActive; +historySendIconFgOver: windowBgActive; historyPinnedBg: historyComposeAreaBg; historyReplyBg: historyComposeAreaBg; historyReplyCancelFg: cancelIconFg; historyReplyCancelFgOver: cancelIconFgOver; historyComposeButtonBg: historyComposeAreaBg; -historyComposeButtonBgOver: #f5f5f5; -historyComposeButtonBgRipple: #e7e7e7; +historyComposeButtonBgOver: windowBgOver; +historyComposeButtonBgRipple: windowBgRipple; -historyTextInFg: windowTextFg; -historyTextOutFg: windowTextFg; +historyTextInFg: windowFg; +historyTextOutFg: windowFg; historyCaptionInFg: historyTextInFg; historyCaptionOutFg: historyTextOutFg; historyFileNameInFg: historyTextInFg; @@ -213,7 +218,7 @@ historySystemFg: #ffffff; // mediaview mediaviewFileBg: windowBg; -mediaviewFileNameFg: windowTextFg; +mediaviewFileNameFg: windowFg; mediaviewFileSizeFg: windowSubTextFg; mediaviewFileRedCornerFg: #d55959; mediaviewFileYellowCornerFg: #e8a659; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index e1f7da4ea..33f587522 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -22,34 +22,39 @@ // windowBg: #ffffff; -windowTextFg: #000000; +windowFg: #000000; +windowBgOver: #f1f1f1; +windowBgRipple: #e5e5e5; +windowFgOver: windowFg; windowSubTextFg: #999999; -windowActiveBg: #40ace3; -windowActiveFg: #ffffff; -windowOverBg: #f0f0f0; -windowSubTextFgOver: #7c99b2; +windowSubTextFgOver: #919191; +windowBoldFg: #222222; +windowBoldFgOver: #222222; +windowBgActive: #40ace3; +windowFgActive: #ffffff; windowActiveTextFg: #2687bf; windowShadowFg: #000000; windowShadowFgFallback: #cdcdcd; imageBg: #000000; imageBgTransparent: #ffffff; -activeButtonBg: windowActiveBg; -activeButtonBgOver: #46b4eb; -activeButtonBgRipple: #177eb2; -activeButtonFg: windowActiveFg; +activeButtonBg: windowBgActive; +activeButtonBgOver: #39a5db; +activeButtonBgRipple: #2095d0; +activeButtonFg: windowFgActive; activeButtonFgOver: activeButtonFg; activeButtonSecondaryFg: #cceeff; activeButtonSecondaryFgOver: activeButtonSecondaryFg; lightButtonBg: windowBg; -lightButtonBgOver: #edf7ff; -lightButtonBgRipple: #c7e1f6; +lightButtonBgOver: #e3f1fa; +lightButtonBgRipple: #c9e4f6; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; menuBg: windowBg; -menuBgOver: windowOverBg; +menuBgOver: windowBgOver; +menuBgRipple: windowBgRipple; menuIconFg: #a8a8a8; menuIconFgOver: #999999; -titleBg: windowOverBg; +titleBg: windowBgOver; titleShadow: #00000003; titleButtonFg: #ababab; titleButtonBgOver: #e5e5e5; @@ -67,7 +72,7 @@ layerBg: #0000007f; cancelIconFg: #a2a2a2; cancelIconFgOver: #808080; boxBg: windowBg; -boxTextFg: windowTextFg; +boxTextFg: windowFg; boxTextFgGood: #4ab44a; boxTextFgError: #d84d4d; boxTitleFg: #404040; @@ -79,30 +84,30 @@ boxBlockTitleFg: boxTitleFg; boxBlockTitleAdditionalFg: #808080; boxBlockTitleCloseFg: cancelIconFg; boxBlockTitleCloseFgOver: cancelIconFgOver; -attentionBoxButtonFg: #ea4b2f; -attentionBoxButtonFgOver: #ea4b2f; -attentionBoxButtonBgOver: #fff0ed; -attentionBoxButtonBgRipple: #efbcb2; -membersAboutLimitFg: windowSubTextFg; +attentionBoxButtonFg: #d14e4e; +attentionBoxButtonFgOver: #d14e4e; +attentionBoxButtonBgOver: #fcdfde; +attentionBoxButtonBgRipple: #f4c3c2; +membersAboutLimitFg: windowSubTextFgOver; contactsBg: windowBg; -contactsBgOver: windowOverBg; +contactsBgOver: windowBgOver; contactsNameFg: boxTextFg; contactsStatusFg: windowSubTextFg; -contactsStatusFgOver: contactsStatusFg; -contactsStatusFgOnline: #3b8dcc; +contactsStatusFgOver: windowSubTextFgOver; +contactsStatusFgOnline: windowActiveTextFg; photoCropFadeBg: #0000007f; photoCropPointFg: #ffffff7f; -notificationsBoxMonitorFg: windowTextFg; -notificationSampleUserpicFg: windowActiveBg; +notificationsBoxMonitorFg: windowFg; +notificationSampleUserpicFg: windowBgActive; notificationSampleCloseFg: #d7d7d7; // windowSubTextFg; notificationSampleTextFg: #d7d7d7; // windowSubTextFg; notificationSampleNameFg: #939393; // windowSubTextFg; -introHeaderFg: windowTextFg; -introErrorFg: windowTextFg; +introHeaderFg: windowFg; +introErrorFg: windowFg; dialogsMenuIconFg: menuIconFg; dialogsMenuIconFgOver: menuIconFgOver; dialogsBg: windowBg; -dialogsNameFg: #373737; +dialogsNameFg: windowBoldFg; dialogsChatIconFg: dialogsNameFg; dialogsDateFg: windowSubTextFg; dialogsTextFg: windowSubTextFg; @@ -112,14 +117,14 @@ dialogsVerifiedIconBg: #4abcf1; dialogsVerifiedIconFg: #ffffff; dialogsSendingIconFg: #c1c1c1; dialogsSentIconFg: #5dc452; -dialogsUnreadBg: windowActiveBg; +dialogsUnreadBg: windowBgActive; dialogsUnreadBgMuted: #bbbbbb; dialogsUnreadFg: #ffffff; -dialogsBgOver: windowOverBg; -dialogsNameFgOver: windowTextFg; +dialogsBgOver: windowBgOver; +dialogsNameFgOver: windowBoldFgOver; dialogsChatIconFgOver: dialogsNameFgOver; -dialogsDateFgOver: #8a8a8a; // dialogsDateFg; -dialogsTextFgOver: dialogsTextFg; +dialogsDateFgOver: windowSubTextFgOver; +dialogsTextFgOver: windowSubTextFgOver; dialogsTextFgServiceOver: dialogsTextFgService; dialogsDraftFgOver: dialogsDraftFg; dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; @@ -130,10 +135,10 @@ dialogsUnreadBgOver: dialogsUnreadBg; dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; dialogsUnreadFgOver: dialogsUnreadFg; dialogsBgActive: #419fd9; -dialogsNameFgActive: windowActiveFg; +dialogsNameFgActive: windowFgActive; dialogsChatIconFgActive: dialogsNameFgActive; -dialogsDateFgActive: windowActiveFg; -dialogsTextFgActive: windowActiveFg; +dialogsDateFgActive: windowFgActive; +dialogsTextFgActive: windowFgActive; dialogsTextFgServiceActive: dialogsTextFgActive; dialogsDraftFgActive: #c6e1f7; dialogsVerifiedIconBgActive: dialogsTextFgActive; @@ -153,17 +158,17 @@ emojiPanHeaderBg: #fffffff2; // emojiPanBg; historyComposeAreaBg: windowBg; historyComposeIconFg: menuIconFg; historyComposeIconFgOver: menuIconFgOver; -historySendIconFg: windowActiveBg; -historySendIconFgOver: windowActiveBg; +historySendIconFg: windowBgActive; +historySendIconFgOver: windowBgActive; historyPinnedBg: historyComposeAreaBg; historyReplyBg: historyComposeAreaBg; historyReplyCancelFg: cancelIconFg; historyReplyCancelFgOver: cancelIconFgOver; historyComposeButtonBg: historyComposeAreaBg; -historyComposeButtonBgOver: #f5f5f5; -historyComposeButtonBgRipple: #e7e7e7; -historyTextInFg: windowTextFg; -historyTextOutFg: windowTextFg; +historyComposeButtonBgOver: windowBgOver; +historyComposeButtonBgRipple: windowBgRipple; +historyTextInFg: windowFg; +historyTextOutFg: windowFg; historyCaptionInFg: historyTextInFg; historyCaptionOutFg: historyTextOutFg; historyFileNameInFg: historyTextInFg; @@ -175,7 +180,7 @@ historySystemBg: #89a0b47f; historySystemBgSelected: #bbc8d4a2; historySystemFg: #ffffff; mediaviewFileBg: windowBg; -mediaviewFileNameFg: windowTextFg; +mediaviewFileNameFg: windowFg; mediaviewFileSizeFg: windowSubTextFg; mediaviewFileRedCornerFg: #d55959; mediaviewFileYellowCornerFg: #e8a659; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 982c51c90..312bc8eb3 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_overview.h" #include "styles/style_mediaview.h" #include "styles/style_stickers.h" +#include "styles/style_history.h" #include "lang.h" #include "data/data_abstract_structure.h" #include "history/history_service_layout.h" diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp index a646a91af..a6d51ee22 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.cpp +++ b/Telegram/SourceFiles/boxes/aboutbox.cpp @@ -28,12 +28,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "application.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" #include "styles/style_boxes.h" AboutBox::AboutBox() : AbstractBox(st::aboutWidth) , _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink) -, _text1(this, lang(lng_about_text_1), FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) -, _text2(this, lang(lng_about_text_2), FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) +, _text1(this, lang(lng_about_text_1), Ui::FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) +, _text2(this, lang(lng_about_text_2), Ui::FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle) , _text3(this,st::aboutLabel, st::aboutTextStyle) , _done(this, lang(lng_close), st::defaultBoxButton) { _text3->setRichText(lng_about_text_3(lt_faq_open, qsl("[a href=\"%1\"]").arg(telegramFaqLink()), lt_faq_close, qsl("[/a]"))); @@ -140,3 +141,14 @@ QString telegramFaqLink() { } return result; } + +QString currentVersionText() { + auto result = QString::fromLatin1(AppVersionStr.c_str()); + if (cAlphaVersion()) { + result += " alpha"; + } + if (cBetaVersion()) { + result += qsl(" beta %1").arg(cBetaVersion() % 1000); + } + return result; +} diff --git a/Telegram/SourceFiles/boxes/aboutbox.h b/Telegram/SourceFiles/boxes/aboutbox.h index d05cedbdb..83abd91b0 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.h +++ b/Telegram/SourceFiles/boxes/aboutbox.h @@ -20,12 +20,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" -#include "ui/flatlabel.h" +#include "boxes/abstractbox.h" namespace Ui { class RoundButton; class LinkButton; +class FlatLabel; } // namespace Ui class AboutBox : public AbstractBox { @@ -48,11 +48,12 @@ protected: private: ChildWidget _version; - ChildWidget _text1; - ChildWidget _text2; - ChildWidget _text3; + ChildWidget _text1; + ChildWidget _text2; + ChildWidget _text3; ChildWidget _done; }; QString telegramFaqLink(); +QString currentVersionText(); diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index 31c5d348d..4a65b5017 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "lang.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/scroll_area.h" #include "mainwidget.h" #include "mainwindow.h" @@ -150,7 +151,7 @@ void AbstractBox::raiseShadow() { ScrollableBoxShadow::ScrollableBoxShadow(QWidget *parent) : Ui::PlainShadow(parent, st::boxScrollShadowBg) { } -ScrollableBox::ScrollableBox(const style::flatScroll &scroll, int32 w) : AbstractBox(w) +ScrollableBox::ScrollableBox(const style::FlatScroll &scroll, int32 w) : AbstractBox(w) , _scroll(this, scroll) , _topSkip(st::boxBlockTitleHeight) , _bottomSkip(st::boxScrollSkip) { @@ -185,6 +186,6 @@ void ScrollableBox::updateScrollGeometry() { _scroll->setGeometry(0, _topSkip, width(), height() - _topSkip - _bottomSkip); } -ItemListBox::ItemListBox(const style::flatScroll &scroll, int32 w) : ScrollableBox(scroll, w) { +ItemListBox::ItemListBox(const style::FlatScroll &scroll, int32 w) : ScrollableBox(scroll, w) { setMaxHeight(st::boxMaxListHeight); } diff --git a/Telegram/SourceFiles/boxes/abstractbox.h b/Telegram/SourceFiles/boxes/abstractbox.h index 46733444f..bb7419fc9 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.h +++ b/Telegram/SourceFiles/boxes/abstractbox.h @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class IconButton; class GradientShadow; +class ScrollArea; } // namespace Ui class AbstractBox : public LayerWidget, protected base::Subscriber { @@ -81,7 +82,7 @@ public: class ScrollableBox : public AbstractBox { public: - ScrollableBox(const style::flatScroll &scroll, int w = 0); + ScrollableBox(const style::FlatScroll &scroll, int w = 0); protected: void init(TWidget *inner, int bottomSkip = -1, int topSkip = -1); @@ -89,21 +90,21 @@ protected: void resizeEvent(QResizeEvent *e) override; - ScrollArea *scrollArea() { + Ui::ScrollArea *scrollArea() { return _scroll; } private: void updateScrollGeometry(); - ChildWidget _scroll; + ChildWidget _scroll; int _topSkip, _bottomSkip; }; class ItemListBox : public ScrollableBox { public: - ItemListBox(const style::flatScroll &scroll, int32 w = 0); + ItemListBox(const style::FlatScroll &scroll, int32 w = 0); }; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 1d9dcc0ea..51c0392fc 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/labels.h" #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" @@ -1301,7 +1302,7 @@ void EditChannelBox::onSaveSignDone(const MTPUpdates &updates) { RevokePublicLinkBox::RevokePublicLinkBox(base::lambda_unique &&revokeCallback) : AbstractBox() , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _revokeWidth(st::normalFont->width(lang(lng_channels_too_much_public_revoke))) -, _aboutRevoke(this, lang(lng_channels_too_much_public_about), FlatLabel::InitType::Simple, st::aboutRevokePublicLabel) +, _aboutRevoke(this, lang(lng_channels_too_much_public_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _revokeCallback(std_::move(revokeCallback)) { setMouseTracking(true); diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index 70f77c745..0b1f9345f 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -20,14 +20,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" -#include "core/lambda_wrap.h" +#include "boxes/abstractbox.h" #include "ui/filedialog.h" -class FlatLabel; class ConfirmBox; namespace Ui { +class FlatLabel; class InputField; class PhoneInput; class InputArea; @@ -342,7 +341,7 @@ private: int _rowHeight = 0; int _revokeWidth = 0; - ChildWidget _aboutRevoke; + ChildWidget _aboutRevoke; ChildWidget _cancel; base::lambda_unique _revokeCallback; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index c8af5320a..42481fa7c 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -23,6 +23,7 @@ using "basic.style"; using "ui/widgets/widgets.style"; using "intro/intro.style"; +boxButtonFont: font(boxFontSize semibold); defaultBoxButton: RoundButton { textFg: #2f9fea; textFgOver: #2f9fea; @@ -90,14 +91,70 @@ boxLinkButton: LinkButton { boxOptionListPadding: margins(2px, 20px, 2px, 2px); -confirmInviteTitle: flatLabel(labelDefFlat) { +boxVerticalMargin: 10px; +boxWidth: 320px; +boxWideWidth: 364px; +boxPadding: margins(26px, 30px, 34px, 8px); +boxMaxListHeight: 600px; +boxLittleSkip: 10px; +boxMediumSkip: 20px; + +boxTitleFont: font(boxFontSize bold); +boxTitlePosition: point(26px, 28px); +boxTitleHeight: 54px; + +boxButtonPadding: margins(12px, 16px, 22px, 16px); +boxLabel: FlatLabel(defaultFlatLabel) { + font: font(boxFontSize); + align: align(topleft); +} + +boxScroll: FlatScroll(defaultSolidScroll) { + round: 3px; + width: 18px; + deltax: 6px; +} +boxScrollSkip: 6px; +boxScrollShadowBg: #00000012; + +boxShadow: icon {{ "box_shadow", windowShadowFg }}; +boxShadowShift: 2px; + +countryRowHeight: 36px; +countryRowNameFont: semiboldFont; +countryRowNameFg: boxTextFg; +countryRowPadding: margins(22px, 9px, 8px, 0px); +countryRowCodeFont: font(fsize); +countryRowBg: windowBg; +countryRowBgOver: windowBgOver; +countryRowCodeFg: windowSubTextFg; +countryRowCodeFgOver: windowSubTextFgOver; +countriesSkip: 12px; +countriesScroll: FlatScroll(boxScroll) { + deltat: 9px; + deltab: 3px; +} + +boxTextStyle: TextStyle(defaultTextStyle) { + lineHeight: 22px; +} + +boxPhotoPadding: margins(28px, 28px, 28px, 18px); +boxPhotoCompressedPadding: margins(0px, 2px, 0px, 22px); +boxPhotoTextFg: #808080; + +cropPointSize: 10px; +cropSkip: 13px; +cropMinSize: 20px; + +confirmInviteTitle: FlatLabel(defaultFlatLabel) { font: font(16px semibold); align: align(center); width: 320px; maxHeight: 24px; textFg: #333333; } -confirmInviteStatus: flatLabel(labelDefFlat) { +confirmInviteStatus: FlatLabel(defaultFlatLabel) { font: font(boxFontSize); align: align(center); width: 320px; @@ -111,7 +168,7 @@ confirmInviteStatusTop: 136px; confirmInviteUserHeight: 80px; confirmInviteUserPhotoSize: 56px; confirmInviteUserPhotoTop: 166px; -confirmInviteUserName: flatLabel(labelDefFlat) { +confirmInviteUserName: FlatLabel(defaultFlatLabel) { font: normalFont; align: align(center); width: 66px; @@ -119,22 +176,22 @@ confirmInviteUserName: flatLabel(labelDefFlat) { } confirmInviteUserNameTop: 227px; -confirmPhoneAboutLabel: flatLabel(labelDefFlat) { +confirmPhoneAboutLabel: FlatLabel(defaultFlatLabel) { width: 282px; } confirmPhoneCodeField: InputField(defaultInputField) { } -revokePublicLinkStatusStyle: textStyle(defaultTextStyle) { +revokePublicLinkStatusStyle: TextStyle(defaultTextStyle) { linkFg: contactsStatusFgOnline; linkFgDown: contactsStatusFgOnline; linkFlagsOver: font(fsize); } -aboutRevokePublicLabel: flatLabel(labelDefFlat) { +aboutRevokePublicLabel: FlatLabel(defaultFlatLabel) { font: normalFont; align: align(topleft); width: 320px; - textFg: windowTextFg; + textFg: windowFg; } contactUserIcon: icon {{ "add_contact_user", #999999 }}; @@ -172,7 +229,7 @@ contactsAdminCheckbox: Checkbox(defaultBoxCheckbox) { contactsAboutSkip: 53px; contactsAboutHeight: 42px; contactsAboutTop: 9px; -contactsScroll: flatScroll(boxScroll) { +contactsScroll: FlatScroll(boxScroll) { deltab: 0px; } @@ -180,7 +237,7 @@ contactsMultiSelect: MultiSelect { bg: boxSearchBg; padding: margins(8px, 8px, 8px, 8px); maxHeight: 104px; - scroll: flatScroll(solidScroll) { + scroll: FlatScroll(defaultSolidScroll) { deltat: 3px; deltab: 3px; round: 1px; @@ -195,7 +252,7 @@ contactsMultiSelect: MultiSelect { height: 32px; font: normalFont; textBg: contactsBgOver; - textFg: windowTextFg; + textFg: windowFg; textActiveBg: activeButtonBg; textActiveFg: activeButtonFg; deleteFg: activeButtonFg; @@ -242,13 +299,13 @@ contactsPhotoCheckbox: RoundImageCheckbox { imageRadius: 21px; imageSmallRadius: 18px; selectWidth: 2px; - selectFg: windowActiveBg; + selectFg: windowBgActive; selectDuration: 150; checkBorder: windowBg; - checkBg: windowActiveBg; + checkBg: windowBgActive; checkRadius: 10px; checkSmallRadius: 3px; - checkIcon: icon {{ "default_checkbox_check", windowActiveFg, point(3px, 6px) }}; + checkIcon: icon {{ "default_checkbox_check", windowFgActive, point(3px, 6px) }}; } contactsPhotoDisabledCheckFg: #bbbbbb; contactsNameCheckedFg: #2b88b8; @@ -263,7 +320,7 @@ sharePhotoCheckbox: RoundImageCheckbox(contactsPhotoCheckbox) { imageSmallRadius: 24px; } shareNameFont: font(11px); -shareNameFg: windowTextFg; +shareNameFg: windowFg; shareNameActiveFg: btnYesColor; shareNameTop: 6px; shareColumnSkip: 6px; @@ -289,7 +346,7 @@ notificationSampleSize: size(64px, 16px); membersAboutLimitPadding: margins(0px, 12px, 0px, 12px); -sessionsScroll: flatScroll(boxScroll) { +sessionsScroll: FlatScroll(boxScroll) { round: 2px; deltax: 5px; width: 14px; @@ -376,12 +433,12 @@ aboutVersionLink: LinkButton(defaultLinkButton) { } aboutTextTop: 34px; aboutSkip: 14px; -aboutLabel: flatLabel(labelDefFlat) { +aboutLabel: FlatLabel(defaultFlatLabel) { font: normalFont; width: 330px; align: align(topleft); } -aboutTextStyle: textStyle(defaultTextStyle) { +aboutTextStyle: TextStyle(defaultTextStyle) { lineHeight: 22px; } @@ -419,7 +476,7 @@ langsButton: Radiobutton(defaultRadiobutton) { backgroundPadding: 10px; backgroundSize: size(108px, 193px); -backgroundScroll: flatScroll(boxScroll) { +backgroundScroll: FlatScroll(boxScroll) { round: 2px; width: 10px; deltax: 3px; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 2e2a6bc69..9f8b9c1c1 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -29,6 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" #include "core/click_handler_types.h" #include "localstorage.h" @@ -397,7 +398,7 @@ void ConvertToSupergroupBox::resizeEvent(QResizeEvent *e) { PinMessageBox::PinMessageBox(ChannelData *channel, MsgId msgId) : AbstractBox(st::boxWidth) , _channel(channel) , _msgId(msgId) -, _text(this, lang(lng_pinned_pin_sure), FlatLabel::InitType::Simple, st::boxLabel) +, _text(this, lang(lng_pinned_pin_sure), Ui::FlatLabel::InitType::Simple, st::boxLabel) , _notify(this, lang(lng_pinned_notify), true, st::defaultBoxCheckbox) , _pin(this, lang(lng_pinned_pin), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { @@ -450,7 +451,7 @@ RichDeleteMessageBox::RichDeleteMessageBox(ChannelData *channel, UserData *from, , _channel(channel) , _from(from) , _msgId(msgId) -, _text(this, lang(lng_selected_delete_sure_this), FlatLabel::InitType::Simple, st::boxLabel) +, _text(this, lang(lng_selected_delete_sure_this), Ui::FlatLabel::InitType::Simple, st::boxLabel) , _banUser(this, lang(lng_ban_user), false, st::defaultBoxCheckbox) , _reportSpam(this, lang(lng_report_spam), false, st::defaultBoxCheckbox) , _deleteAll(this, lang(lng_delete_all_from), false, st::defaultBoxCheckbox) @@ -562,7 +563,7 @@ ConfirmInviteBox::ConfirmInviteBox(const QString &title, const MTPChatPhoto &pho int sumWidth = _participants.size() * _userWidth; int left = (width() - sumWidth) / 2; for_const (auto user, _participants) { - auto name = new FlatLabel(this, st::confirmInviteUserName); + auto name = new Ui::FlatLabel(this, st::confirmInviteUserName); name->resizeToWidth(st::confirmInviteUserPhotoSize + padding); name->setText(user->firstName.isEmpty() ? App::peerName(user) : user->firstName); name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop); diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index ab5234175..410340971 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -20,13 +20,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" -#include "ui/flatlabel.h" -#include "core/lambda_wrap.h" +#include "boxes/abstractbox.h" namespace Ui { class Checkbox; class RoundButton; +class FlatLabel; } // namespace Ui namespace st { @@ -237,7 +236,7 @@ private: ChannelData *_channel; MsgId _msgId; - ChildWidget _text; + ChildWidget _text; ChildWidget _notify; ChildWidget _pin; @@ -266,7 +265,7 @@ private: UserData *_from; MsgId _msgId; - ChildWidget _text; + ChildWidget _text; ChildWidget _banUser; ChildWidget _reportSpam; ChildWidget _deleteAll; @@ -304,8 +303,8 @@ protected: void showAll() override; private: - ChildWidget _title; - ChildWidget _status; + ChildWidget _title; + ChildWidget _status; ImagePtr _photo; QVector _participants; diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.cpp b/Telegram/SourceFiles/boxes/confirmphonebox.cpp index 1f0b6a16e..85e03a2ea 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.cpp +++ b/Telegram/SourceFiles/boxes/confirmphonebox.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/labels.h" #include "mainwidget.h" #include "lang.h" @@ -97,7 +98,7 @@ void ConfirmPhoneBox::setCallStatus(const CallStatus &status) { void ConfirmPhoneBox::launch() { setBlockTitle(true); - _about = new FlatLabel(this, st::confirmPhoneAboutLabel); + _about.create(this, st::confirmPhoneAboutLabel); TextWithEntities aboutText; auto formattedPhone = App::formatPhone(_phone); aboutText.text = lng_confirm_phone_about(lt_phone, formattedPhone); diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.h b/Telegram/SourceFiles/boxes/confirmphonebox.h index a48baf6f6..b852d1c32 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.h +++ b/Telegram/SourceFiles/boxes/confirmphonebox.h @@ -22,11 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/abstractbox.h" -class FlatLabel; - namespace Ui { class InputField; class RoundButton; +class FlatLabel; } // namespace Ui class ConfirmPhoneBox : public AbstractBox, public RPCSender { @@ -92,7 +91,7 @@ private: mtpRequestId _checkCodeRequestId = 0; - ChildWidget _about = { nullptr }; + ChildWidget _about = { nullptr }; ChildWidget _send = { nullptr }; ChildWidget _cancel = { nullptr }; ChildWidget _code = { nullptr }; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 9bd4c5119..cd40cbcb0 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -34,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/buttons.h" #include "ui/filedialog.h" #include "ui/widgets/multi_select.h" +#include "ui/widgets/scroll_area.h" #include "ui/effects/widget_slide_wrap.h" #include "boxes/photocropbox.h" #include "boxes/confirmbox.h" diff --git a/Telegram/SourceFiles/boxes/localstoragebox.cpp b/Telegram/SourceFiles/boxes/localstoragebox.cpp index f23f53b7c..afbd1eeb5 100644 --- a/Telegram/SourceFiles/boxes/localstoragebox.cpp +++ b/Telegram/SourceFiles/boxes/localstoragebox.cpp @@ -81,7 +81,7 @@ void LocalStorageBox::paintEvent(QPaintEvent *e) { paintTitle(p, lang(lng_local_storage_title)); p.setFont(st::boxTextFont); - p.setPen(st::windowTextFg); + p.setPen(st::windowFg); checkLocalStoredCounts(); int top = titleHeight() + st::localStorageBoxSkip; if (_imagesCount > 0) { diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 4f0a12f37..9120cadac 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -29,6 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/contactsbox.h" #include "boxes/confirmbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/scroll_area.h" #include "observer_peer.h" MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll) diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index fb2218d7d..6b757699c 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "countries.h" #include "boxes/confirmbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/scroll_area.h" #include "styles/style_boxes.h" SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll) diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index f0d1ee119..f8c47a29d 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -36,6 +36,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/multi_select.h" #include "history/history_media_types.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/scroll_area.h" #include "boxes/contactsbox.h" ShareBox::ShareBox(CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback) : ItemListBox(st::boxScroll) diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 9fe45983f..6b8514fa2 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_boxes.h" #include "styles/style_stickers.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/scroll_area.h" namespace { diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 1c4396e3a..8c69d9b5b 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_boxes.h" #include "styles/style_stickers.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/scroll_area.h" StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::stickersScroll) , _inner(this, set) diff --git a/Telegram/SourceFiles/core/click_handler.h b/Telegram/SourceFiles/core/click_handler.h index 7fb8796a4..467158d24 100644 --- a/Telegram/SourceFiles/core/click_handler.h +++ b/Telegram/SourceFiles/core/click_handler.h @@ -178,3 +178,18 @@ protected: virtual void onClickImpl() const = 0; }; + +class LambdaClickHandler : public ClickHandler { +public: + LambdaClickHandler(base::lambda_unique handler) : _handler(std_::move(handler)) { + } + void onClick(Qt::MouseButton button) const override final { + if (button == Qt::LeftButton && _handler) { + _handler(); + } + } + +private: + base::lambda_unique _handler; + +}; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index d4399ceed..64b0d7ec8 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -33,7 +33,8 @@ dialogsDateSkip: 5px; dialogsNameTop: 2px; dialogsRowHeight: 62px; -dialogsFilterPadding: point(11px, 11px); +dialogsFilterPadding: point(7px, 7px); +dialogsFilterSkip: 4px; dialogsPhotoSize: 46px; dialogsPhotoPadding: 12px; dialogsPadding: point(10px, 8px); @@ -45,49 +46,51 @@ dialogsSkip: 8px; dialogsWidthMin: 260px; dialogsWidthMax: 540px; dialogsTextWidthMin: 150px; -dialogsScroll: flatScroll(scrollDef) { +dialogsScroll: FlatScroll(defaultFlatScroll) { topsh: 0px; bottomsh: 0px; } -dialogsTextStyle: textStyle(defaultTextStyle) { +dialogsTextStyle: TextStyle(defaultTextStyle) { linkFg: dialogsTextFgService; linkFgDown: dialogsTextFgService; linkFlagsOver: font(fsize); } -dialogsTextStyleOver: textStyle(dialogsTextStyle) { +dialogsTextStyleOver: TextStyle(dialogsTextStyle) { linkFg: dialogsTextFgServiceOver; linkFgDown: dialogsTextFgServiceOver; } -dialogsTextStyleActive: textStyle(dialogsTextStyle) { +dialogsTextStyleActive: TextStyle(dialogsTextStyle) { linkFg: dialogsTextFgServiceActive; linkFgDown: dialogsTextFgServiceActive; } -dialogsTextStyleDraft: textStyle(dialogsTextStyle) { +dialogsTextStyleDraft: TextStyle(dialogsTextStyle) { linkFg: dialogsDraftFg; linkFgDown: dialogsDraftFg; } -dialogsTextStyleDraftOver: textStyle(dialogsTextStyle) { +dialogsTextStyleDraftOver: TextStyle(dialogsTextStyle) { linkFg: dialogsDraftFgOver; linkFgDown: dialogsDraftFgOver; } -dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) { +dialogsTextStyleDraftActive: TextStyle(dialogsTextStyle) { linkFg: dialogsDraftFgActive; linkFgDown: dialogsDraftFgActive; } dialogsMenuToggle: IconButton { - width: 32px; - height: 32px; + width: 40px; + height: 40px; icon: icon {{ "dialogs_menu", dialogsMenuIconFg }}; iconOver: icon {{ "dialogs_menu", dialogsMenuIconFgOver }}; - iconPosition: point(6px, 6px); - iconPositionDown: point(6px, 6px); + iconPosition: point(10px, 10px); + iconPositionDown: point(10px, 10px); rippleAreaPosition: point(0px, 0px); - rippleAreaSize: 32px; - ripple: defaultRippleAnimation; + rippleAreaSize: 40px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } } dialogsLock: IconButton(dialogsMenuToggle) { icon: icon {{ "dialogs_lock", dialogsMenuIconFg }}; @@ -110,33 +113,19 @@ dialogsCancelSearchInPeer: IconButton(dialogsMenuToggle) { icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg }}; iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver }}; - iconPosition: point(7px, 7px); - iconPositionDown: point(7px, 7px); + iconPosition: point(11px, 11px); + iconPositionDown: point(11px, 11px); } dialogsCancelSearch: IconButton(dialogsCancelSearchInPeer) { + width: 32px; + height: 32px; + + iconPosition: point(7px, 7px); + iconPositionDown: point(7px, 7px); + rippleAreaSize: 0px; } -dialogsMenu: Menu(defaultMenu) { - itemFont: semiboldFont; - itemIconPosition: point(28px, 11px); - itemPadding: margins(76px, 14px, 28px, 14px); -} -dialogsMenuWrap: DropdownMenu(defaultDropdownMenu) { - menu: dialogsMenu; -} -dialogsMenuPosition: point(-3px, -2px); -dialogsMenuNewGroup: icon {{ "menu_new_group", menuIconFg }}; -dialogsMenuNewGroupOver: icon {{ "menu_new_group", menuIconFgOver }}; -dialogsMenuNewChannel: icon {{ "menu_new_channel", menuIconFg }}; -dialogsMenuNewChannelOver: icon {{ "menu_new_channel", menuIconFgOver }}; -dialogsMenuContacts: icon {{ "menu_contacts", menuIconFg }}; -dialogsMenuContactsOver: icon {{ "menu_contacts", menuIconFgOver }}; -dialogsMenuSettings: icon {{ "menu_settings", menuIconFg }}; -dialogsMenuSettingsOver: icon {{ "menu_settings", menuIconFgOver }}; -dialogsMenuHelp: icon {{ "menu_help", menuIconFg }}; -dialogsMenuHelpOver: icon {{ "menu_help", menuIconFgOver }}; - dialogsChatTypeSkip: 22px; dialogsChatIcon: icon {{ "dialogs_chat", dialogsChatIconFg, point(1px, 4px) }}; dialogsChatIconOver: icon {{ "dialogs_chat", dialogsChatIconFgOver, point(1px, 4px) }}; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 182930f38..6e11b7f73 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" #include "styles/style_stickers.h" +#include "styles/style_history.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" #include "data/data_drafts.h" @@ -412,7 +413,7 @@ void DialogsInner::mousePressEvent(QMouseEvent *e) { void DialogsInner::resizeEvent(QResizeEvent *e) { _addContactLnk->move((width() - _addContactLnk->width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); - _cancelSearchInPeer->move(width() - st::dialogsPadding.x() - st::dialogsCancelSearch.width, (st::dialogsRowHeight - st::dialogsCancelSearch.height) / 2); + _cancelSearchInPeer->moveToRight(st::dialogsFilterSkip + st::dialogsFilterPadding.x() - otherWidth(), (st::dialogsRowHeight - st::dialogsCancelSearchInPeer.height) / 2); } void DialogsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) { @@ -2014,28 +2015,6 @@ void DialogsWidget::onChooseByDrag() { void DialogsWidget::showMainMenu() { App::wnd()->showMainMenu(); - return; - - if (!_mainMenu) { - _mainMenu.create(this, st::dialogsMenuWrap); - _mainMenu->addAction(lang(lng_create_group_title), [] { - App::wnd()->onShowNewGroup(); - }, &st::dialogsMenuNewGroup, &st::dialogsMenuNewGroupOver); - _mainMenu->addAction(lang(lng_create_channel_title), [] { - App::wnd()->onShowNewChannel(); - }, &st::dialogsMenuNewChannel, &st::dialogsMenuNewChannelOver); - _mainMenu->addAction(lang(lng_menu_contacts), [] { - Ui::showLayer(new ContactsBox()); - }, &st::dialogsMenuContacts, &st::dialogsMenuContactsOver); - _mainMenu->addAction(lang(lng_menu_settings), [] { - App::wnd()->showSettings(); - }, &st::dialogsMenuSettings, &st::dialogsMenuSettingsOver); - _mainMenu->addAction(lang(lng_settings_faq), [] { - QDesktopServices::openUrl(telegramFaqLink()); - }, &st::dialogsMenuHelp, &st::dialogsMenuHelpOver); - } - updateMainMenuGeometry(); - _mainMenu->showAnimated(Ui::PanelAnimation::Origin::TopLeft); } void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) { @@ -2381,7 +2360,6 @@ void DialogsWidget::onCompleteHashtag(QString tag) { void DialogsWidget::resizeEvent(QResizeEvent *e) { updateControlsGeometry(); - updateMainMenuGeometry(); } void DialogsWidget::updateLockUnlockVisibility() { @@ -2397,21 +2375,18 @@ void DialogsWidget::updateControlsGeometry() { _forwardCancel->moveToLeft(0, filterTop); filterTop += st::dialogsForwardHeight; } - auto filterLeft = st::dialogsFilterPadding.x() * 2 + _mainMenuToggle->width(); - auto filterWidth = width() - 2 * st::dialogsFilterPadding.x(); - filterWidth -= st::dialogsFilterPadding.x() + _mainMenuToggle->width(); - if (Global::LocalPasscode()) { - filterWidth -= st::dialogsFilterPadding.x() + _lockUnlock->width(); - } - filterTop += st::dialogsFilterPadding.y(); + auto filterLeft = st::dialogsFilterPadding.x() + _mainMenuToggle->width() + st::dialogsFilterPadding.x(); + auto filterRight = (Global::LocalPasscode() ? (st::dialogsFilterPadding.x() + _lockUnlock->width()) : st::dialogsFilterSkip) + st::dialogsFilterPadding.x(); + auto filterWidth = width() - filterLeft - filterRight; + auto scrollTop = st::dialogsFilterPadding.y() + _mainMenuToggle->height() + st::dialogsFilterPadding.y(); + filterTop += (scrollTop - _filter->height()) / 2; _filter->setGeometryToLeft(filterLeft, filterTop, filterWidth, _filter->height()); - _mainMenuToggle->moveToLeft(st::dialogsFilterPadding.x(), _filter->y()); - _lockUnlock->moveToRight(st::dialogsFilterPadding.x(), _filter->y()); + _mainMenuToggle->moveToLeft(st::dialogsFilterPadding.x(), st::dialogsFilterPadding.y()); + _lockUnlock->moveToRight(st::dialogsFilterPadding.x(), st::dialogsFilterPadding.y()); _cancelSearch->moveToLeft(filterLeft + filterWidth - _cancelSearch->width(), _filter->y()); auto addToScroll = App::main() ? App::main()->contentScrollAddToY() : 0; auto newScrollTop = _scroll->scrollTop() + addToScroll; - auto scrollTop = filterTop + _filter->height() + st::dialogsFilterPadding.y(); auto scrollHeight = height() - scrollTop; if (_updateTelegram) { auto updateHeight = _updateTelegram->height(); @@ -2428,12 +2403,6 @@ void DialogsWidget::updateControlsGeometry() { } } -void DialogsWidget::updateMainMenuGeometry() { - if (!_mainMenu) return; - - _mainMenu->moveToLeft(st::dialogsMenuPosition.x(), st::dialogsMenuPosition.y()); -} - void DialogsWidget::updateForwardBar() { auto selecting = App::main()->selectingPeer(); auto oneColumnSelecting = (Adaptive::OneColumn() && selecting); diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 897416fc1..917f78dfc 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "window/section_widget.h" +#include "ui/widgets/scroll_area.h" namespace Dialogs { class Row; @@ -46,7 +47,7 @@ enum DialogsSearchRequestType { DialogsSearchMigratedFromOffset, }; -class DialogsInner : public SplittedWidget, public RPCSender, private base::Subscriber { +class DialogsInner : public Ui::SplittedWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: @@ -310,7 +311,6 @@ private: void showMainMenu(); void updateLockUnlockVisibility(); void updateControlsGeometry(); - void updateMainMenuGeometry(); void updateForwardBar(); bool _dragInScroll = false; @@ -332,11 +332,10 @@ private: ChildWidget _forwardCancel = { nullptr }; ChildWidget _mainMenuToggle; - ChildWidget _mainMenu = { nullptr }; ChildWidget _filter; ChildWidget _cancelSearch; ChildWidget _lockUnlock; - ChildWidget _scroll; + ChildWidget _scroll; ChildWidget _inner; ChildWidget _updateTelegram = { nullptr }; diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index c0e21850f..3b902cb48 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "apiwrap.h" #include "localstorage.h" +#include "ui/widgets/scroll_area.h" #include "styles/style_history.h" #include "styles/style_widgets.h" #include "styles/style_stickers.h" @@ -624,11 +625,11 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { user->loadUserpic(); user->paintUserpicLeft(p, st::mentionPhotoSize, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width()); - p.setPen(st::mentionNameFg); + p.setPen(selected ? st::mentionNameFgOver : st::mentionNameFg); user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); p.setFont(st::mentionFont); - p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); + p.setPen(selected ? st::mentionFgOverActive : st::mentionFgActive); p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); if (!second.isEmpty()) { p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p); @@ -671,7 +672,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { auto commandText = '/' + toHighlight; - p.setPen(st::mentionNameFg); + p.setPen(selected ? st::mentionNameFgOver : st::mentionNameFg); p.setFont(st::semiboldFont); p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop + st::semiboldFont->ascent, commandText); diff --git a/Telegram/SourceFiles/history/field_autocomplete.h b/Telegram/SourceFiles/history/field_autocomplete.h index 8324ada7f..7f8faab8f 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.h +++ b/Telegram/SourceFiles/history/field_autocomplete.h @@ -23,6 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/twidget.h" #include "ui/effects/rect_shadow.h" +namespace Ui { +class ScrollArea; +} // namespace Ui + namespace internal { using MentionRows = QList; @@ -106,7 +110,7 @@ private: void rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const StickerPack &srows, bool resetScroll); - ChildWidget _scroll; + ChildWidget _scroll; ChildWidget _inner; ChatData *_chat = nullptr; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 32eb6c452..63a650f52 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -22,6 +22,31 @@ using "basic.style"; using "dialogs/dialogs.style"; using "ui/widgets/widgets.style"; +BotKeyboardButton { + margin: pixels; + padding: pixels; + height: pixels; + textTop: pixels; + downTextTop: pixels; +} + +historyScroll: FlatScroll(defaultFlatScroll) { + barColor: #89a0b47a; + bgColor: #89a0b44c; + barOverColor: #89a0b4bc; + bgOverColor: #89a0b46b; + + round: 3px; + + width: 12px; + deltax: 3px; + deltat: 3px; + deltab: 3px; + + topsh: 0px; + bottomsh: -1px; +} + historyPaddingBottom: 10px; historyToDownPosition: point(12px, 10px); @@ -41,7 +66,7 @@ historyEmptySize: 128px; membersInnerWidth: 310px; membersInnerHeightMax: 360px; membersInnerDropdown: InnerDropdown(defaultInnerDropdown) { - scroll: flatScroll(solidScroll) { + scroll: FlatScroll(defaultSolidScroll) { deltat: 3px; deltab: 3px; round: 1px; @@ -202,6 +227,12 @@ historyEditSaveIconOver: icon {{ "send_control_save", historySendIconFgOver, poi historyAttach: IconButton(historySend) { icon: icon {{ "send_control_attach", historyComposeIconFg }}; iconOver: icon {{ "send_control_attach", historyComposeIconFgOver }}; + + rippleAreaPosition: point(3px, 3px); + rippleAreaSize: 40px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } } historyAttachFileIcon: icon {{ "media_type_file", historyComposeIconFg }}; historyAttachFileIconOver: icon {{ "media_type_file", historyComposeIconFgOver }}; @@ -214,6 +245,8 @@ historyAttachEmoji: IconButton(historyAttach) { iconPosition: point(15px, 15px); iconPositionDown: point(15px, 15px); + + rippleAreaSize: 0px; } historyEmojiCircle: size(20px, 20px); historyEmojiCirclePeriod: 1500; @@ -289,9 +322,17 @@ historyReplyCancel: IconButton { iconPosition: point(-1px, -1px); iconPositionDown: point(-1px, -1px); + + rippleAreaPosition: point(4px, 4px); + rippleAreaSize: 40px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } } historyInlineBotCancel: IconButton(historyReplyCancel) { height: 46px; + + rippleAreaPosition: point(4px, 3px); } reportSpamHide: FlatButton { @@ -318,3 +359,59 @@ reportSpamSeparator: 30px; reportSpamBg: #fffffff0; reportSpamFg: #000000; +msgBotKbDuration: 200; +msgBotKbFont: semiboldFont; +msgBotKbOverBg: #ffffff1a; +msgBotKbIconPadding: 2px; +msgBotKbUrlIcon: icon {{ "inline_button_url", #ffffff }}; +msgBotKbSwitchPmIcon: icon {{ "inline_button_switch", #ffffff }}; +msgBotKbButton: BotKeyboardButton { + margin: 5px; + padding: 10px; + height: 36px; + textTop: 8px; + downTextTop: 9px; +} + +botKbDuration: 200; +botKbBg: #edf1f5; +botKbOverBg: #d8e2ec; +botKbDownBg: #d8e2ec; +botKbColor: #4b565f; +botKbFont: font(15px semibold); +botKbButton: BotKeyboardButton { + margin: 10px; + padding: 10px; + height: 38px; + textTop: 9px; + downTextTop: 9px; +} +botKbTinyButton: BotKeyboardButton { + margin: 4px; + padding: 3px; + height: 25px; + textTop: 2px; + downTextTop: 2px; +} +botKbScroll: FlatScroll(defaultSolidScroll) { + deltax: 3px; + width: 10px; +} + +mentionHeight: 40px; +mentionScroll: FlatScroll(defaultFlatScroll) { + topsh: 0px; + bottomsh: 0px; +} +mentionPadding: margins(8px, 5px, 8px, 5px); +mentionTop: 11px; +mentionFont: linkFont; +mentionNameFg: windowFg; +mentionNameFgOver: windowFgOver; +mentionPhotoSize: msgPhotoSize; +mentionBg: #ffffff; +mentionBgOver: #f5f5f5; +mentionFg: #777777; +mentionFgOver: #707070; +mentionFgActive: #0080c0; +mentionFgOverActive: #0077b3; diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp index 71d835eab..a5eadc849 100644 --- a/Telegram/SourceFiles/history/history_drag_area.cpp +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "history/history_drag_area.h" #include "styles/style_stickers.h" +#include "styles/style_boxes.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" #include "inline_bots/inline_bot_result.h" diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 6eccdee61..007c2cd88 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "history/history_service_layout.h" #include "media/media_clip_reader.h" #include "styles/style_dialogs.h" +#include "styles/style_history.h" #include "fileuploader.h" namespace { @@ -176,7 +177,7 @@ void ReplyKeyboard::resize(int width, int height) { } } -bool ReplyKeyboard::isEnoughSpace(int width, const style::botKeyboardButton &st) const { +bool ReplyKeyboard::isEnoughSpace(int width, const style::BotKeyboardButton &st) const { for_const (auto &row, _rows) { int s = row.size(); int widthLeft = width - ((s - 1) * st.margin + s * 2 * st.padding); @@ -317,6 +318,18 @@ void ReplyKeyboard::clearSelection() { _a_selected.stop(); } +int ReplyKeyboard::Style::buttonSkip() const { + return _st->margin; +} + +int ReplyKeyboard::Style::buttonPadding() const { + return _st->padding; +} + +int ReplyKeyboard::Style::buttonHeight() const { + return _st->height; +} + void ReplyKeyboard::Style::paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button) const { const QRect &rect = button.rect; bool pressed = ClickHandler::showAsPressed(button.link); @@ -880,12 +893,14 @@ HistoryItem::~HistoryItem() { } } -void GoToMessageClickHandler::onClickImpl() const { - if (App::main()) { - HistoryItem *current = App::mousedItem(); - if (current && current->history()->peer->id == peer()) { - App::main()->pushReplyReturn(current); +ClickHandlerPtr goToMessageClickHandler(PeerData *peer, MsgId msgId) { + return MakeShared([peer, msgId] { + if (App::main()) { + auto current = App::mousedItem(); + if (current && current->history()->peer == peer) { + App::main()->pushReplyReturn(current); + } + Ui::showPeerHistory(peer, msgId, Ui::ShowWay::Forward); } - Ui::showPeerHistory(peer(), msgid(), Ui::ShowWay::Forward); - } + }); } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 4ffdaf629..ecacda928 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/runtime_composer.h" +namespace style { +struct BotKeyboardButton; +} // namespace style + class HistoryElement { public: HistoryElement() = default; @@ -281,21 +285,15 @@ private: public: class Style { public: - Style(const style::botKeyboardButton &st) : _st(&st) { + Style(const style::BotKeyboardButton &st) : _st(&st) { } virtual void startPaint(Painter &p) const = 0; virtual style::font textFont() const = 0; - int buttonSkip() const { - return _st->margin; - } - int buttonPadding() const { - return _st->padding; - } - int buttonHeight() const { - return _st->height; - } + int buttonSkip() const; + int buttonPadding() const; + int buttonHeight() const; virtual void repaint(const HistoryItem *item) const = 0; virtual ~Style() { @@ -308,7 +306,7 @@ public: virtual int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const = 0; private: - const style::botKeyboardButton *_st; + const style::BotKeyboardButton *_st; void paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button) const; friend class ReplyKeyboard; @@ -320,7 +318,7 @@ public: ReplyKeyboard(const ReplyKeyboard &other) = delete; ReplyKeyboard &operator=(const ReplyKeyboard &other) = delete; - bool isEnoughSpace(int width, const style::botKeyboardButton &st) const; + bool isEnoughSpace(int width, const style::BotKeyboardButton &st) const; void setStyle(StylePtr &&s); void resize(int width, int height); @@ -951,30 +949,8 @@ public: } }; -class MessageClickHandler : public LeftButtonClickHandler { -public: - MessageClickHandler(PeerId peer, MsgId msgid) : _peer(peer), _msgid(msgid) { - } - MessageClickHandler(HistoryItem *item) : _peer(item->history()->peer->id), _msgid(item->id) { - } - PeerId peer() const { - return _peer; - } - MsgId msgid() const { - return _msgid; - } +ClickHandlerPtr goToMessageClickHandler(PeerData *peer, MsgId msgId); -private: - PeerId _peer; - MsgId _msgid; - -}; - -class GoToMessageClickHandler : public MessageClickHandler { -public: - using MessageClickHandler::MessageClickHandler; - -protected: - void onClickImpl() const override; - -}; +inline ClickHandlerPtr goToMessageClickHandler(HistoryItem *item) { + return goToMessageClickHandler(item->history()->peer, item->id); +} diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index c4fcc31dc..0338b2c94 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -1927,33 +1927,6 @@ bool HistoryGif::dataLoaded() const { return (!_parent || _parent->id > 0) ? _data->loaded() : false; } -namespace { - -class StickerClickHandler : public LeftButtonClickHandler { -public: - StickerClickHandler(const HistoryItem *item) : _item(item) { - } - -protected: - void onClickImpl() const override { - if (auto media = _item->getMedia()) { - if (auto document = media->getDocument()) { - if (auto sticker = document->sticker()) { - if (sticker->set.type() != mtpc_inputStickerSetEmpty && App::main()) { - App::main()->stickersBox(sticker->set); - } - } - } - } - } - -private: - const HistoryItem *_item; - -}; - -} // namespace - HistorySticker::HistorySticker(HistoryItem *parent, DocumentData *document) : HistoryMedia(parent) , _data(document) , _emoji(_data->sticker()->alt) { @@ -1963,11 +1936,21 @@ HistorySticker::HistorySticker(HistoryItem *parent, DocumentData *document) : Hi } } +class TestClickHandler : public ClickHandler { + +}; + void HistorySticker::initDimensions() { auto sticker = _data->sticker(); if (!_packLink && sticker && sticker->set.type() != mtpc_inputStickerSetEmpty) { - _packLink = ClickHandlerPtr(new StickerClickHandler(_parent)); + _packLink = MakeShared([document = _data] { + if (auto sticker = document->sticker()) { + if (sticker->set.type() != mtpc_inputStickerSetEmpty && App::main()) { + App::main()->stickersBox(sticker->set); + } + } + }); } _pixw = _data->dimensions.width(); _pixh = _data->dimensions.height(); @@ -2198,23 +2181,32 @@ int HistorySticker::additionalWidth(const HistoryMessageVia *via, const HistoryM return result; } -void SendMessageClickHandler::onClickImpl() const { - Ui::showPeerHistory(peer()->id, ShowAtUnreadMsgId, Ui::ShowWay::Forward); +namespace { + +ClickHandlerPtr sendMessageClickHandler(PeerData *peer) { + return MakeShared([peer] { + Ui::showPeerHistory(peer->id, ShowAtUnreadMsgId, Ui::ShowWay::Forward); + }); } -void AddContactClickHandler::onClickImpl() const { - if (HistoryItem *item = App::histItemById(peerToChannel(peer()), msgid())) { - if (HistoryMedia *media = item->getMedia()) { - if (media->type() == MediaTypeContact) { - QString fname = static_cast(media)->fname(); - QString lname = static_cast(media)->lname(); - QString phone = static_cast(media)->phone(); - Ui::showLayer(new AddContactBox(fname, lname, phone)); +ClickHandlerPtr addContactClickHandler(HistoryItem *item) { + return MakeShared([fullId = item->fullId()] { + if (auto item = App::histItemById(fullId)) { + if (auto media = item->getMedia()) { + if (media->type() == MediaTypeContact) { + auto contact = static_cast(media); + auto fname = contact->fname(); + auto lname = contact->lname(); + auto phone = contact->phone(); + Ui::showLayer(new AddContactBox(fname, lname, phone)); + } } } - } + }); } +} // namespace + HistoryContact::HistoryContact(HistoryItem *parent, int32 userId, const QString &first, const QString &last, const QString &phone) : HistoryMedia(parent) , _userId(userId) , _contact(0) @@ -2236,10 +2228,10 @@ void HistoryContact::initDimensions() { _contact->loadUserpic(); } if (_contact && _contact->contact > 0) { - _linkl.reset(new SendMessageClickHandler(_contact)); + _linkl = sendMessageClickHandler(_contact); _link = lang(lng_profile_send_message).toUpper(); } else if (_userId) { - _linkl.reset(new AddContactClickHandler(_parent->history()->peer->id, _parent->id)); + _linkl = addContactClickHandler(_parent); _link = lang(lng_profile_add_contact).toUpper(); } _linkw = _link.isEmpty() ? 0 : st::semiboldFont->width(_link); diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 68c913dc1..c2871553a 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -898,21 +898,3 @@ private: int32 fullHeight() const; }; - -class SendMessageClickHandler : public PeerClickHandler { -public: - using PeerClickHandler::PeerClickHandler; - -protected: - void onClickImpl() const override; - -}; - -class AddContactClickHandler : public MessageClickHandler { -public: - using MessageClickHandler::MessageClickHandler; - -protected: - void onClickImpl() const override; - -}; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 0d32d39bc..7ad1fbd54 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -83,7 +83,9 @@ void historyInitMessages() { void HistoryMessageVia::create(int32 userId) { _bot = App::user(peerFromUser(userId)); _maxWidth = st::msgServiceNameFont->width(lng_inline_bot_via(lt_inline_bot, '@' + _bot->username)); - _lnk.reset(new ViaInlineBotClickHandler(_bot)); + _lnk = MakeShared([bot = _bot] { + App::insertBotCommand('@' + bot->username); + }); } void HistoryMessageVia::resize(int32 availw) const { @@ -149,7 +151,7 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const { textstyleSet(&st::inFwdTextStyle); _text.setText(st::msgServiceNameFont, text, opts); textstyleRestore(); - _text.setLink(1, (_originalId && _authorOriginal->isChannel()) ? ClickHandlerPtr(new GoToMessageClickHandler(_authorOriginal->id, _originalId)) : _authorOriginal->openLink()); + _text.setLink(1, (_originalId && _authorOriginal->isChannel()) ? goToMessageClickHandler(_authorOriginal, _originalId) : _authorOriginal->openLink()); if (via) { _text.setLink(2, via->_lnk); } @@ -173,7 +175,7 @@ bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) { updateName(); - replyToLnk.reset(new GoToMessageClickHandler(replyToMsg->history()->peer->id, replyToMsg->id)); + replyToLnk = goToMessageClickHandler(replyToMsg); if (!replyToMsg->Has()) { if (auto bot = replyToMsg->viaBot()) { _replyToVia.reset(new HistoryMessageVia()); @@ -1760,7 +1762,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { auto from = textcmdLink(1, _from->name); Links links; - links.push_back(MakeShared(_from)); + links.push_back(peerOpenClickHandler(_from)); switch (action.type()) { case mtpc_messageActionChatAddUser: { @@ -1778,7 +1780,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { if (u == _from) { text = lng_action_user_joined(lt_from, from); } else { - links.push_back(MakeShared(u)); + links.push_back(peerOpenClickHandler(u)); text = lng_action_add_user(lt_from, from, lt_user, textcmdLink(2, u->name)); } } else if (v.isEmpty()) { @@ -1794,7 +1796,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { } else { text = lng_action_add_users_and_last(lt_accumulated, text, lt_user, linkText); } - links.push_back(MakeShared(u)); + links.push_back(peerOpenClickHandler(u)); } text = lng_action_add_users_many(lt_from, from, lt_users, text); } @@ -1811,7 +1813,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { text = lng_action_user_joined_by_link(lt_from, from); //} else { // UserData *u = App::user(App::peerFromUser(d.vinviter_id)); - // links.push_back(MakeShared(u)); + // links.push_back(peerOpenClickHandler(u)); // text = lng_action_user_joined_by_link_from(lt_from, from, lt_inviter, textcmdLink(2, u->name)); //} if (_from->isSelf() && history()->peer->isMegagroup()) { @@ -1847,7 +1849,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { text = lng_action_user_left(lt_from, from); } else { auto u = App::user(peerFromUser(d.vuser_id)); - links.push_back(MakeShared(u)); + links.push_back(peerOpenClickHandler(u)); text = lng_action_kick_user(lt_from, from, lt_user, textcmdLink(2, u->name)); } } break; @@ -1913,7 +1915,7 @@ bool HistoryService::updateDependent(bool force) { } if (!dependent->lnk) { - dependent->lnk.reset(new GoToMessageClickHandler(history()->peer->id, dependent->msgId)); + dependent->lnk = goToMessageClickHandler(history()->peer, dependent->msgId); } bool gotDependencyItem = false; if (!dependent->msg) { @@ -2312,7 +2314,7 @@ bool HistoryService::updateDependentText() { auto from = textcmdLink(1, _from->name); QString text; Links links; - links.push_back(MakeShared(_from)); + links.push_back(peerOpenClickHandler(_from)); if (Has()) { result = preparePinnedText(from, &text, &links); } else if (Has()) { @@ -2352,7 +2354,7 @@ HistoryJoined::HistoryJoined(History *history, const QDateTime &inviteDate, User if (peerToUser(inviter->id) == MTP::authedId()) { return lang(history->isMegagroup() ? lng_action_you_joined_group : lng_action_you_joined); } - links.push_back(MakeShared(inviter)); + links.push_back(peerOpenClickHandler(inviter)); if (history->isMegagroup()) { return lng_action_add_you_group(lt_from, textcmdLink(1, inviter->name)); } @@ -2360,7 +2362,3 @@ HistoryJoined::HistoryJoined(History *history, const QDateTime &inviteDate, User })(); setServiceText(text, links); } - -void ViaInlineBotClickHandler::onClickImpl() const { - App::insertBotCommand('@' + _bot->username); -} diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 500c3d957..bf8fbff54 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -366,16 +366,3 @@ protected: friend class HistoryItemInstantiated; }; - -class ViaInlineBotClickHandler : public LeftButtonClickHandler { -public: - ViaInlineBotClickHandler(UserData *bot) : _bot(bot) { - } - -protected: - void onClickImpl() const override; - -private: - UserData *_bot; - -}; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index ba8a3b016..226120e05 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -118,7 +118,7 @@ ApiWrap::RequestMessageDataCallback replyEditMessageDataCallback() { // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html -HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : TWidget(nullptr) +HistoryInner::HistoryInner(HistoryWidget *historyWidget, Ui::ScrollArea *scroll, History *history) : TWidget(nullptr) , _peer(history->peer) , _migrated(history->peer->migrateFrom() ? App::history(history->peer->migrateFrom()->id) : nullptr) , _history(history) @@ -567,16 +567,16 @@ bool HistoryInner::event(QEvent *e) { void HistoryInner::onTouchScrollTimer() { uint64 nowTime = getms(); - if (_touchScrollState == TouchScrollAcceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { - _touchScrollState = TouchScrollManual; + if (_touchScrollState == Ui::TouchScrollState::Acceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { + _touchScrollState = Ui::TouchScrollState::Manual; touchResetSpeed(); - } else if (_touchScrollState == TouchScrollAuto || _touchScrollState == TouchScrollAcceleration) { + } else if (_touchScrollState == Ui::TouchScrollState::Auto || _touchScrollState == Ui::TouchScrollState::Acceleration) { int32 elapsed = int32(nowTime - _touchTime); QPoint delta = _touchSpeed * elapsed / 1000; bool hasScrolled = _widget->touchScroll(delta); if (_touchSpeed.isNull() || !hasScrolled) { - _touchScrollState = TouchScrollManual; + _touchScrollState = Ui::TouchScrollState::Manual; _touchScroll = false; _touchScrollTimer.stop(); } else { @@ -598,7 +598,7 @@ void HistoryInner::touchUpdateSpeed() { // of a small horizontal offset when scrolling vertically const int newSpeedY = (qAbs(pixelsPerSecond.y()) > FingerAccuracyThreshold) ? pixelsPerSecond.y() : 0; const int newSpeedX = (qAbs(pixelsPerSecond.x()) > FingerAccuracyThreshold) ? pixelsPerSecond.x() : 0; - if (_touchScrollState == TouchScrollAuto) { + if (_touchScrollState == Ui::TouchScrollState::Auto) { const int oldSpeedY = _touchSpeed.y(); const int oldSpeedX = _touchSpeed.x(); if ((oldSpeedY <= 0 && newSpeedY <= 0) || ((oldSpeedY >= 0 && newSpeedY >= 0) @@ -644,7 +644,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) { _touchInProgress = false; _touchSelectTimer.stop(); _touchScroll = _touchSelect = false; - _touchScrollState = TouchScrollManual; + _touchScrollState = Ui::TouchScrollState::Manual; dragActionCancel(); return; } @@ -664,8 +664,8 @@ void HistoryInner::touchEvent(QTouchEvent *e) { if (e->touchPoints().isEmpty()) return; _touchInProgress = true; - if (_touchScrollState == TouchScrollAuto) { - _touchScrollState = TouchScrollAcceleration; + if (_touchScrollState == Ui::TouchScrollState::Auto) { + _touchScrollState = Ui::TouchScrollState::Acceleration; _touchWaitingAcceleration = true; _touchAccelerationTime = getms(); touchUpdateSpeed(); @@ -688,13 +688,13 @@ void HistoryInner::touchEvent(QTouchEvent *e) { touchUpdateSpeed(); } if (_touchScroll) { - if (_touchScrollState == TouchScrollManual) { + if (_touchScrollState == Ui::TouchScrollState::Manual) { touchScrollUpdated(_touchPos); - } else if (_touchScrollState == TouchScrollAcceleration) { + } else if (_touchScrollState == Ui::TouchScrollState::Acceleration) { touchUpdateSpeed(); _touchAccelerationTime = getms(); if (_touchSpeed.isNull()) { - _touchScrollState = TouchScrollManual; + _touchScrollState = Ui::TouchScrollState::Manual; } } } @@ -709,17 +709,17 @@ void HistoryInner::touchEvent(QTouchEvent *e) { showContextMenu(&contextMenu, true); _touchScroll = false; } else if (_touchScroll) { - if (_touchScrollState == TouchScrollManual) { - _touchScrollState = TouchScrollAuto; + if (_touchScrollState == Ui::TouchScrollState::Manual) { + _touchScrollState = Ui::TouchScrollState::Auto; _touchPrevPosValid = false; _touchScrollTimer.start(15); _touchTime = getms(); - } else if (_touchScrollState == TouchScrollAuto) { - _touchScrollState = TouchScrollManual; + } else if (_touchScrollState == Ui::TouchScrollState::Auto) { + _touchScrollState = Ui::TouchScrollState::Manual; _touchScroll = false; touchResetSpeed(); - } else if (_touchScrollState == TouchScrollAcceleration) { - _touchScrollState = TouchScrollAuto; + } else if (_touchScrollState == Ui::TouchScrollState::Acceleration) { + _touchScrollState = Ui::TouchScrollState::Auto; _touchWaitingAcceleration = false; _touchPrevPosValid = false; } @@ -2421,7 +2421,8 @@ void ReportSpamPanel::setReported(bool reported, PeerData *onPeer) { update(); } -BotKeyboard::BotKeyboard() { +BotKeyboard::BotKeyboard(QWidget *parent) : TWidget(parent) +, _st(&st::botKbButton) { setGeometry(0, 0, _st->margin, st::botKbScroll.deltat); _height = st::botKbScroll.deltat; setMouseTracking(true); @@ -3037,6 +3038,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , a_recordCancelActive(0, 0) , _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel))) , _kbScroll(this, st::botKbScroll) +, _keyboard(this) , _attachType(this, st::historyAttachDropdownMenu) , _emojiPan(this) , _attachDragDocument(this) @@ -3047,10 +3049,10 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) setAcceptDrops(true); subscribe(FileDownload::ImageLoaded(), [this] { update(); }); - connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); - connect(&_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked())); - connect(&_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide())); - connect(&_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); + connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); + connect(_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked())); + connect(_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide())); + connect(_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); connect(_historyToEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); connect(_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel())); connect(_send, SIGNAL(clicked()), this, SLOT(onSend())); @@ -3117,10 +3119,10 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _fieldBarCancel->hide(); - _scroll.hide(); + _scroll->hide(); - _kbScroll.setWidget(&_keyboard); - _kbScroll.hide(); + _kbScroll->setOwnedWidget(_keyboard); + _kbScroll->hide(); updateScrollColors(); @@ -3143,8 +3145,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _joinChannel->hide(); _muteUnmute->hide(); - _reportSpamPanel.move(0, 0); - _reportSpamPanel.hide(); + _reportSpamPanel->move(0, 0); + _reportSpamPanel->hide(); _attachToggle->hide(); _attachEmoji->hide(); @@ -3509,7 +3511,7 @@ void HistoryWidget::activate() { } void HistoryWidget::setInnerFocus() { - if (_scroll.isHidden()) { + if (_scroll->isHidden()) { setFocus(); } else if (_list) { if (_selCount || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { @@ -3588,7 +3590,7 @@ void HistoryWidget::notify_inlineBotRequesting(bool requesting) { } void HistoryWidget::notify_replyMarkupUpdated(const HistoryItem *item) { - if (_keyboard.forMsgId() == item->fullId()) { + if (_keyboard->forMsgId() == item->fullId()) { updateBotKeyboard(item->history(), true); } } @@ -4248,7 +4250,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _membersDropdownShowTimer.stop(); if (_list) _list->deleteLater(); _list = nullptr; - _scroll.takeWidget(); + _scroll->takeWidget(); clearInlineBot(); @@ -4312,10 +4314,10 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re } } - _list = new HistoryInner(this, &_scroll, _history); + _list = new HistoryInner(this, _scroll, _history); _list->hide(); - _scroll.hide(); - _scroll.setWidget(_list); + _scroll->hide(); + _scroll->setWidget(_list); _list->show(); _updateHistoryItems.stop(); @@ -4343,7 +4345,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re onPreviewParse(); } - connect(&_scroll, SIGNAL(geometryChanged()), _list, SLOT(onParentGeometryChanged())); + connect(_scroll, SIGNAL(geometryChanged()), _list, SLOT(onParentGeometryChanged())); if (startBot && _peer->isUser() && _peer->asUser()->botInfo) { if (wasHistory) _peer->asUser()->botInfo->inlineReturnPeerId = wasHistory->peer->id; @@ -4446,7 +4448,7 @@ void HistoryWidget::updateReportSpamStatus() { } else if (_reportSpamStatus == dbiprsShowButton) { requestReportSpamSetting(); } - _reportSpamPanel.setReported(_reportSpamStatus == dbiprsReportSent, _peer); + _reportSpamPanel->setReported(_reportSpamStatus == dbiprsReportSent, _peer); return; } else if (_peer->migrateFrom()) { // migrate report status i = cReportSpamStatuses().constFind(_peer->migrateFrom()->id); @@ -4463,7 +4465,7 @@ void HistoryWidget::updateReportSpamStatus() { cRefReportSpamStatuses().insert(_peer->id, _reportSpamStatus); Local::writeReportSpamStatuses(); - _reportSpamPanel.setReported(_reportSpamStatus == dbiprsReportSent, _peer); + _reportSpamPanel->setReported(_reportSpamStatus == dbiprsReportSent, _peer); return; } } @@ -4477,7 +4479,7 @@ void HistoryWidget::updateReportSpamStatus() { requestReportSpamSetting(); } if (_reportSpamStatus == dbiprsHidden) { - _reportSpamPanel.setReported(false, _peer); + _reportSpamPanel->setReported(false, _peer); cRefReportSpamStatuses().insert(_peer->id, _reportSpamStatus); Local::writeReportSpamStatuses(); } @@ -4498,7 +4500,7 @@ void HistoryWidget::reportSpamSettingDone(const MTPPeerSettings &result, mtpRequ DBIPeerReportSpamStatus status = d.is_report_spam() ? dbiprsShowButton : dbiprsHidden; if (status != _reportSpamStatus) { _reportSpamStatus = status; - _reportSpamPanel.setReported(false, _peer); + _reportSpamPanel->setReported(false, _peer); cRefReportSpamStatuses().insert(_peer->id, _reportSpamStatus); Local::writeReportSpamStatuses(); @@ -4530,9 +4532,9 @@ void HistoryWidget::updateControlsVisibility() { } updateToEndVisibility(); if (!_history || _a_show.animating()) { - _reportSpamPanel.hide(); - _scroll.hide(); - _kbScroll.hide(); + _reportSpamPanel->hide(); + _scroll->hide(); + _kbScroll->hide(); _send->hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); _unblock->hide(); @@ -4562,15 +4564,15 @@ void HistoryWidget::updateControlsVisibility() { _pinnedBar->cancel->show(); _pinnedBar->shadow->show(); } - if (_firstLoadRequest && !_scroll.isHidden()) { - _scroll.hide(); - } else if (!_firstLoadRequest && _scroll.isHidden()) { - _scroll.show(); + if (_firstLoadRequest && !_scroll->isHidden()) { + _scroll->hide(); + } else if (!_firstLoadRequest && _scroll->isHidden()) { + _scroll->show(); } if (_reportSpamStatus == dbiprsShowButton || _reportSpamStatus == dbiprsReportSent) { - _reportSpamPanel.show(); + _reportSpamPanel->show(); } else { - _reportSpamPanel.hide(); + _reportSpamPanel->hide(); } if (isBlocked() || isJoinChannel() || isMuteUnmute()) { if (isBlocked()) { @@ -4602,7 +4604,7 @@ void HistoryWidget::updateControlsVisibility() { _botStart->hide(); _attachToggle->hide(); _silent->hide(); - _kbScroll.hide(); + _kbScroll->hide(); _fieldBarCancel->hide(); _attachToggle->hide(); _attachEmoji->hide(); @@ -4636,7 +4638,7 @@ void HistoryWidget::updateControlsVisibility() { _botCommandStart->hide(); _attachToggle->hide(); _silent->hide(); - _kbScroll.hide(); + _kbScroll->hide(); _fieldBarCancel->hide(); } else { _unblock->hide(); @@ -4665,29 +4667,29 @@ void HistoryWidget::updateControlsVisibility() { _attachToggle->hide(); _silent->hide(); if (_kbShown) { - _kbScroll.show(); + _kbScroll->show(); } else { - _kbScroll.hide(); + _kbScroll->hide(); } } else { _field->show(); if (_kbShown) { - _kbScroll.show(); + _kbScroll->show(); _attachEmoji->hide(); _botKeyboardHide->show(); _botKeyboardShow->hide(); _botCommandStart->hide(); } else if (_kbReplyTo) { - _kbScroll.hide(); + _kbScroll->hide(); _attachEmoji->show(); _botKeyboardHide->hide(); _botKeyboardShow->hide(); _botCommandStart->hide(); } else { - _kbScroll.hide(); + _kbScroll->hide(); _attachEmoji->show(); _botKeyboardHide->hide(); - if (_keyboard.hasMarkup()) { + if (_keyboard->hasMarkup()) { _botKeyboardShow->show(); _botCommandStart->hide(); } else { @@ -4727,7 +4729,7 @@ void HistoryWidget::updateControlsVisibility() { _muteUnmute->hide(); _attachToggle->hide(); _silent->hide(); - _kbScroll.hide(); + _kbScroll->hide(); _fieldBarCancel->hide(); _attachToggle->hide(); _attachEmoji->hide(); @@ -4736,7 +4738,7 @@ void HistoryWidget::updateControlsVisibility() { _botCommandStart->hide(); _attachType->hide(); _emojiPan->hide(); - _kbScroll.hide(); + _kbScroll->hide(); if (!_field->isHidden()) { _field->hide(); resizeEvent(0); @@ -4758,7 +4760,7 @@ void HistoryWidget::destroyUnreadBar() { void HistoryWidget::newUnreadMsg(History *history, HistoryItem *item) { if (_history == history) { - if (_scroll.scrollTop() + 1 > _scroll.scrollTopMax()) { + if (_scroll->scrollTop() + 1 > _scroll->scrollTopMax()) { destroyUnreadBar(); } if (App::wnd()->doWeReadServerHistory()) { @@ -4776,7 +4778,7 @@ void HistoryWidget::historyToDown(History *history) { migrated->forgetScrollState(); } if (history == _history) { - _scroll.scrollToY(_scroll.scrollTopMax()); + _scroll->scrollToY(_scroll->scrollTopMax()); } } @@ -4958,12 +4960,12 @@ bool HistoryWidget::doWeReadServerHistory() const { if (!_history || !_list) return true; if (_firstLoadRequest || _a_show.animating()) return false; if (_history->loadedAtBottom()) { - int scrollTop = _scroll.scrollTop(); - if (scrollTop + 1 > _scroll.scrollTopMax()) return true; + int scrollTop = _scroll->scrollTop(); + if (scrollTop + 1 > _scroll->scrollTopMax()) return true; auto showFrom = (_migrated && _migrated->showFrom) ? _migrated->showFrom : (_history ? _history->showFrom : nullptr); if (showFrom && !showFrom->detached()) { - int scrollBottom = scrollTop + _scroll.height(); + int scrollBottom = scrollTop + _scroll->height(); if (scrollBottom > _list->itemTop(showFrom)) return true; } } @@ -5110,9 +5112,9 @@ void HistoryWidget::onScroll() { } void HistoryWidget::visibleAreaUpdated() { - if (_list && !_scroll.isHidden()) { - int scrollTop = _scroll.scrollTop(); - int scrollBottom = scrollTop + _scroll.height(); + if (_list && !_scroll->isHidden()) { + int scrollTop = _scroll->scrollTop(); + int scrollBottom = scrollTop + _scroll->height(); _list->visibleAreaUpdated(scrollTop, scrollBottom); if (_history->loadedAtBottom() && (_history->unreadCount() > 0 || (_migrated && _migrated->unreadCount() > 0))) { auto showFrom = (_migrated && _migrated->showFrom) ? _migrated->showFrom : (_history ? _history->showFrom : nullptr); @@ -5124,11 +5126,11 @@ void HistoryWidget::visibleAreaUpdated() { } void HistoryWidget::preloadHistoryIfNeeded() { - if (_firstLoadRequest || _scroll.isHidden() || !_peer) return; + if (_firstLoadRequest || _scroll->isHidden() || !_peer) return; updateToEndVisibility(); - int st = _scroll.scrollTop(), stm = _scroll.scrollTopMax(), sh = _scroll.height(); + int st = _scroll->scrollTop(), stm = _scroll->scrollTopMax(), sh = _scroll->height(); if (st + PreloadHeightsCount * sh > stm) { loadMessagesDown(); } @@ -5287,7 +5289,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { if (_previewData && _previewData->pendingTill) previewCancel(); _field->setFocus(); - if (!_keyboard.hasMarkup() && _keyboard.forceReply() && !_kbReplyTo) onKbToggle(); + if (!_keyboard->hasMarkup() && _keyboard->forceReply() && !_kbReplyTo) onKbToggle(); } void HistoryWidget::onUnblock() { @@ -5335,11 +5337,11 @@ void HistoryWidget::onBotStart() { MTP::send(MTPmessages_StartBot(_peer->asUser()->inputUser, MTP_inputPeerEmpty(), MTP_long(randomId), MTP_string(token)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _peer->asUser())); _peer->asUser()->botInfo->startToken = QString(); - if (_keyboard.hasMarkup()) { - if (_keyboard.singleUse() && _keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _history->lastKeyboardUsed) { + if (_keyboard->hasMarkup()) { + if (_keyboard->singleUse() && _keyboard->forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _history->lastKeyboardUsed) { _history->lastKeyboardHiddenId = _history->lastKeyboardId; } - if (!kbWasHidden()) _kbShown = _keyboard.hasMarkup(); + if (!kbWasHidden()) _kbShown = _keyboard->hasMarkup(); } } updateControlsVisibility(); @@ -5479,9 +5481,9 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: App::main()->topBar()->startAnim(); _topShadow->setVisible(params.withTopBarShadow ? true : false); - _scroll.hide(); - _kbScroll.hide(); - _reportSpamPanel.hide(); + _scroll->hide(); + _kbScroll->hide(); + _reportSpamPanel->hide(); _historyToEnd->hide(); _attachToggle->hide(); _attachEmoji->hide(); @@ -5684,7 +5686,7 @@ void HistoryWidget::leaveEvent(QEvent *e) { void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { QPoint pos(e ? e->pos() : mapFromGlobal(QCursor::pos())); bool inRecord = _send->geometry().contains(pos); - bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); + bool inField = pos.y() >= (_scroll->y() + _scroll->height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); bool inReplyEdit = QRect(st::historyReplySkip, _field->y() - st::historySendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId()); bool inPinnedMsg = QRect(0, 0, width(), st::historyReplyHeight).contains(pos) && _pinnedBar; if (inRecord != _inRecord) { @@ -5750,7 +5752,7 @@ void HistoryWidget::stopRecording(bool send) { void HistoryWidget::sendBotCommand(PeerData *peer, UserData *bot, const QString &cmd, MsgId replyTo) { // replyTo != 0 from ReplyKeyboardMarkup, == 0 from cmd links if (!_peer || _peer != peer) return; - bool lastKeyboardUsed = (_keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard.forMsgId() == FullMsgId(_channel, replyTo)); + bool lastKeyboardUsed = (_keyboard->forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard->forMsgId() == FullMsgId(_channel, replyTo)); QString toSend = cmd; if (bot && (!bot->isUser() || !bot->asUser()->botInfo)) { @@ -5773,7 +5775,7 @@ void HistoryWidget::sendBotCommand(PeerData *peer, UserData *bot, const QString cancelReply(); onCloudDraftSave(); } - if (_keyboard.singleUse() && _keyboard.hasMarkup() && lastKeyboardUsed) { + if (_keyboard->singleUse() && _keyboard->hasMarkup() && lastKeyboardUsed) { if (_kbShown) onKbToggle(false); _history->lastKeyboardUsed = true; } @@ -5787,7 +5789,7 @@ void HistoryWidget::app_sendBotCallback(const HistoryMessageReplyMarkup::Button return; } - bool lastKeyboardUsed = (_keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard.forMsgId() == FullMsgId(_channel, msg->id)); + bool lastKeyboardUsed = (_keyboard->forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard->forMsgId() == FullMsgId(_channel, msg->id)); auto bot = msg->getMessageBot(); @@ -5807,7 +5809,7 @@ void HistoryWidget::app_sendBotCallback(const HistoryMessageReplyMarkup::Button if (_replyToId == msg->id) { cancelReply(); } - if (_keyboard.singleUse() && _keyboard.hasMarkup() && lastKeyboardUsed) { + if (_keyboard->singleUse() && _keyboard->hasMarkup() && lastKeyboardUsed) { if (_kbShown) onKbToggle(false); _history->lastKeyboardUsed = true; } @@ -5917,7 +5919,7 @@ bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) { bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) { if (obj == _historyToEnd && e->type() == QEvent::Wheel) { - return _scroll.viewportEvent(e); + return _scroll->viewportEvent(e); } return TWidget::eventFilter(obj, e); } @@ -6073,7 +6075,7 @@ bool HistoryWidget::isMuteUnmute() const { bool HistoryWidget::updateCmdStartShown() { bool cmdStartShown = false; if (_history && _peer && ((_peer->isChat() && _peer->asChat()->botStatus > 0) || (_peer->isMegagroup() && _peer->asChannel()->mgInfo->botStatus > 0) || (_peer->isUser() && _peer->asUser()->botInfo))) { - if (!isBotStart() && !isBlocked() && !_keyboard.hasMarkup() && !_keyboard.forceReply()) { + if (!isBotStart() && !isBlocked() && !_keyboard->hasMarkup() && !_keyboard->forceReply()) { if (!_field->hasSendText()) { cmdStartShown = true; } @@ -6087,7 +6089,7 @@ bool HistoryWidget::updateCmdStartShown() { } bool HistoryWidget::kbWasHidden() const { - return _history && (_keyboard.forMsgId() == FullMsgId(_history->channelId(), _history->lastKeyboardHiddenId)); + return _history && (_keyboard->forMsgId() == FullMsgId(_history->channelId(), _history->lastKeyboardHiddenId)); } void HistoryWidget::dropEvent(QDropEvent *e) { @@ -6154,10 +6156,10 @@ void HistoryWidget::onKbToggle(bool manual) { _botKeyboardShow->show(); } if (manual && _history) { - _history->lastKeyboardHiddenId = _keyboard.forMsgId().msg; + _history->lastKeyboardHiddenId = _keyboard->forMsgId().msg; } - _kbScroll.hide(); + _kbScroll->hide(); _kbShown = false; _field->setMaxHeight(st::historyComposeFieldMaxHeight); @@ -6174,18 +6176,18 @@ void HistoryWidget::onKbToggle(bool manual) { updateBotKeyboard(); } } - } else if (!_keyboard.hasMarkup() && _keyboard.forceReply()) { + } else if (!_keyboard->hasMarkup() && _keyboard->forceReply()) { _botKeyboardHide->hide(); _botKeyboardShow->hide(); if (fieldEnabled) { _botCommandStart->show(); } - _kbScroll.hide(); + _kbScroll->hide(); _kbShown = false; _field->setMaxHeight(st::historyComposeFieldMaxHeight); - _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; + _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) { updateReplyToName(); _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); @@ -6198,13 +6200,13 @@ void HistoryWidget::onKbToggle(bool manual) { } else if (fieldEnabled) { _botKeyboardHide->show(); _botKeyboardShow->hide(); - _kbScroll.show(); + _kbScroll->show(); _kbShown = true; - int32 maxh = qMin(_keyboard.height(), st::historyComposeFieldMaxHeight - (st::historyComposeFieldMaxHeight / 2)); + int32 maxh = qMin(_keyboard->height(), st::historyComposeFieldMaxHeight - (st::historyComposeFieldMaxHeight / 2)); _field->setMaxHeight(st::historyComposeFieldMaxHeight - maxh); - _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; + _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId) { updateReplyToName(); _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); @@ -6342,7 +6344,7 @@ void HistoryWidget::onMembersDropdownShow() { } void HistoryWidget::onModerateKeyActivate(int index, bool *outHandled) { - *outHandled = _keyboard.isHidden() ? false : _keyboard.moderateKeyActivate(index); + *outHandled = _keyboard->isHidden() ? false : _keyboard->moderateKeyActivate(index); } void HistoryWidget::topBarClick() { @@ -6442,11 +6444,11 @@ void HistoryWidget::moveFieldControls() { auto keyboardHeight = 0; auto bottom = height(); auto maxKeyboardHeight = st::historyComposeFieldMaxHeight - _field->height(); - _keyboard.resizeToWidth(width(), maxKeyboardHeight); + _keyboard->resizeToWidth(width(), maxKeyboardHeight); if (_kbShown) { - keyboardHeight = qMin(_keyboard.height(), maxKeyboardHeight); + keyboardHeight = qMin(_keyboard->height(), maxKeyboardHeight); bottom -= keyboardHeight; - _kbScroll.setGeometry(0, bottom, width(), keyboardHeight); + _kbScroll->setGeometry(0, bottom, width(), keyboardHeight); } // _attachToggle --------------------------------------------------------- _emojiPan --------- _fieldBarCancel @@ -6478,7 +6480,7 @@ void HistoryWidget::moveFieldControls() { } void HistoryWidget::updateFieldSize() { - bool kbShowShown = _history && !_kbShown && _keyboard.hasMarkup(); + bool kbShowShown = _history && !_kbShown && _keyboard->hasMarkup(); int fieldWidth = width() - _attachToggle->width(); fieldWidth -= _send->width(); fieldWidth -= _attachEmoji->width(); @@ -6878,7 +6880,7 @@ void HistoryWidget::reportSpamDone(PeerData *peer, const MTPBool &result, mtpReq Local::writeReportSpamStatuses(); } _reportSpamStatus = dbiprsReportSent; - _reportSpamPanel.setReported(_reportSpamStatus == dbiprsReportSent, peer); + _reportSpamPanel->setReported(_reportSpamStatus == dbiprsReportSent, peer); } bool HistoryWidget::reportSpamFail(const RPCError &error, mtpRequestId req) { @@ -6921,7 +6923,7 @@ void HistoryWidget::peerMessagesUpdated(PeerId peer) { if (_peer && _list && peer == _peer->id) { updateListSize(); updateBotKeyboard(); - if (!_scroll.isHidden()) { + if (!_scroll->isHidden()) { bool unblock = isBlocked(), botStart = isBotStart(), joinChannel = isJoinChannel(), muteUnmute = isMuteUnmute(); bool upd = (_unblock->isHidden() == unblock); if (!upd && !unblock) upd = (_botStart->isHidden() == botStart); @@ -6954,8 +6956,8 @@ bool HistoryWidget::isItemVisible(HistoryItem *item) { if (isHidden() || _a_show.animating() || !_list) { return false; } - int32 top = _list->itemTop(item), st = _scroll.scrollTop(); - if (top < 0 || top + item->height() <= st || top >= st + _scroll.height()) { + int32 top = _list->itemTop(item), st = _scroll->scrollTop(); + if (top < 0 || top + item->height() <= st || top >= st + _scroll->height()) { return false; } return true; @@ -7021,29 +7023,29 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { } void HistoryWidget::updateControlsGeometry() { - _reportSpamPanel.resize(width(), _reportSpamPanel.height()); + _reportSpamPanel->resize(width(), _reportSpamPanel->height()); moveFieldControls(); if (_pinnedBar) { - if (_scroll.y() != st::historyReplyHeight) { - _scroll.move(0, st::historyReplyHeight); - _reportSpamPanel.move(0, st::historyReplyHeight); - _fieldAutocomplete->setBoundings(_scroll.geometry()); + if (_scroll->y() != st::historyReplyHeight) { + _scroll->move(0, st::historyReplyHeight); + _reportSpamPanel->move(0, st::historyReplyHeight); + _fieldAutocomplete->setBoundings(_scroll->geometry()); } _pinnedBar->cancel->move(width() - _pinnedBar->cancel->width(), 0); _pinnedBar->shadow->setGeometry(0, st::historyReplyHeight, width(), st::lineWidth); - } else if (_scroll.y() != 0) { - _scroll.move(0, 0); - _reportSpamPanel.move(0, 0); - _fieldAutocomplete->setBoundings(_scroll.geometry()); + } else if (_scroll->y() != 0) { + _scroll->move(0, 0); + _reportSpamPanel->move(0, 0); + _fieldAutocomplete->setBoundings(_scroll->geometry()); } updateListSize(false, false, { ScrollChangeAdd, App::main() ? App::main()->contentScrollAddToY() : 0 }); updateFieldSize(); - _historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll.y() + _scroll.height() - _historyToEnd->height() - st::historyToDownPosition.y()); + _historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll->y() + _scroll->height() - _historyToEnd->height() - st::historyToDownPosition.y()); _emojiPan->setMaxHeight(height() - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom() - _attachEmoji->height()); if (_membersDropdown) { @@ -7101,7 +7103,7 @@ void HistoryWidget::itemEdited(HistoryItem *item) { } void HistoryWidget::updateScrollColors() { - _scroll.updateBars(); + _scroll->updateBars(); } MsgId HistoryWidget::replyToId() const { @@ -7125,36 +7127,36 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh newScrollHeight -= st::historyReplyHeight; } if (_kbShown) { - newScrollHeight -= _kbScroll.height(); + newScrollHeight -= _kbScroll->height(); } } if (_pinnedBar) { newScrollHeight -= st::historyReplyHeight; } - int wasScrollTop = _scroll.scrollTop(); - bool wasAtBottom = wasScrollTop + 1 > _scroll.scrollTopMax(); - bool needResize = (_scroll.width() != width()) || (_scroll.height() != newScrollHeight); + int wasScrollTop = _scroll->scrollTop(); + bool wasAtBottom = wasScrollTop + 1 > _scroll->scrollTopMax(); + bool needResize = (_scroll->width() != width()) || (_scroll->height() != newScrollHeight); if (needResize) { - _scroll.resize(width(), newScrollHeight); - // on initial updateListSize we didn't put the _scroll.scrollTop correctly yet + _scroll->resize(width(), newScrollHeight); + // on initial updateListSize we didn't put the _scroll->scrollTop correctly yet // so visibleAreaUpdated() call will erase it with the new (undefined) value if (!initial) { visibleAreaUpdated(); } - _fieldAutocomplete->setBoundings(_scroll.geometry()); - _historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll.y() + _scroll.height() - _historyToEnd->height() - st::historyToDownPosition.y()); + _fieldAutocomplete->setBoundings(_scroll->geometry()); + _historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll->y() + _scroll->height() - _historyToEnd->height() - st::historyToDownPosition.y()); } _list->recountHeight(); - bool washidden = _scroll.isHidden(); + bool washidden = _scroll->isHidden(); if (washidden) { - _scroll.show(); + _scroll->show(); } _list->updateSize(); if (washidden) { - _scroll.hide(); + _scroll->hide(); } if ((!initial && !wasAtBottom) || (loadedDown && (!_history->showFrom || _history->unreadBar || _history->loadedAtBottom()) && (!_migrated || !_migrated->showFrom || _migrated->unreadBar || _history->loadedAtBottom()))) { @@ -7167,13 +7169,13 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh toY += _addToScroll; _addToScroll = 0; } - if (toY > _scroll.scrollTopMax()) { - toY = _scroll.scrollTopMax(); + if (toY > _scroll->scrollTopMax()) { + toY = _scroll->scrollTopMax(); } - if (_scroll.scrollTop() == toY) { + if (_scroll->scrollTop() == toY) { visibleAreaUpdated(); } else { - _scroll.scrollToY(toY); + _scroll->scrollToY(toY); } return; } @@ -7193,7 +7195,7 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh _histInited = false; return updateListSize(initial, false, change); } else { - toY = (_scroll.height() > item->height()) ? qMax(iy - (_scroll.height() - item->height()) / 2, 0) : iy; + toY = (_scroll->height() > item->height()) ? qMax(iy - (_scroll->height() - item->height()) / 2, 0) : iy; _animActiveStart = getms(); _animActiveTimer.start(AnimationTimerDelta); _activeAnimMsgId = _showAtMsgId; @@ -7206,7 +7208,7 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh _histInited = false; return updateListSize(initial, false, change); } else { - toY = (_scroll.height() > item->height()) ? qMax(iy - (_scroll.height() - item->height()) / 2, 0) : iy; + toY = (_scroll->height() > item->height()) ? qMax(iy - (_scroll->height() - item->height()) / 2, 0) : iy; _animActiveStart = getms(); _animActiveTimer.start(AnimationTimerDelta); _activeAnimMsgId = _showAtMsgId; @@ -7218,7 +7220,7 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh toY = unreadBarTop(); } else if (_migrated && _migrated->showFrom) { toY = _list->itemTop(_migrated->showFrom); - if (toY < _scroll.scrollTopMax() + HistoryMessageUnreadBar::height() - HistoryMessageUnreadBar::marginTop()) { + if (toY < _scroll->scrollTopMax() + HistoryMessageUnreadBar::height() - HistoryMessageUnreadBar::marginTop()) { _migrated->addUnreadBar(); if (_migrated->unreadBar) { setMsgId(ShowAtUnreadMsgId); @@ -7230,7 +7232,7 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh } } else if (_history->showFrom) { toY = _list->itemTop(_history->showFrom); - if (toY < _scroll.scrollTopMax() + st::unreadBarHeight) { + if (toY < _scroll->scrollTopMax() + st::unreadBarHeight) { _history->addUnreadBar(); if (_history->unreadBar) { setMsgId(ShowAtUnreadMsgId); @@ -7242,12 +7244,12 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh } } else { } - auto scrollMax = _scroll.scrollTopMax(); + auto scrollMax = _scroll->scrollTopMax(); accumulate_min(toY, scrollMax); - if (_scroll.scrollTop() == toY) { + if (_scroll->scrollTop() == toY) { visibleAreaUpdated(); } else { - _scroll.scrollToY(toY); + _scroll->scrollToY(toY); } } @@ -7311,39 +7313,39 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { bool changed = false; bool wasVisible = _kbShown || _kbReplyTo; if ((_replyToId && !_replyEditMsg) || _editMsgId || !_history) { - changed = _keyboard.updateMarkup(nullptr, force); + changed = _keyboard->updateMarkup(nullptr, force); } else if (_replyToId && _replyEditMsg) { - changed = _keyboard.updateMarkup(_replyEditMsg, force); + changed = _keyboard->updateMarkup(_replyEditMsg, force); } else { HistoryItem *keyboardItem = _history->lastKeyboardId ? App::histItemById(_channel, _history->lastKeyboardId) : nullptr; - changed = _keyboard.updateMarkup(keyboardItem, force); + changed = _keyboard->updateMarkup(keyboardItem, force); } updateCmdStartShown(); if (!changed) return; - bool hasMarkup = _keyboard.hasMarkup(), forceReply = _keyboard.forceReply() && (!_replyToId || !_replyEditMsg); + bool hasMarkup = _keyboard->hasMarkup(), forceReply = _keyboard->forceReply() && (!_replyToId || !_replyEditMsg); if (hasMarkup || forceReply) { - if (_keyboard.singleUse() && _keyboard.hasMarkup() && _keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _history->lastKeyboardUsed) { + if (_keyboard->singleUse() && _keyboard->hasMarkup() && _keyboard->forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _history->lastKeyboardUsed) { _history->lastKeyboardHiddenId = _history->lastKeyboardId; } if (!isBotStart() && !isBlocked() && _canSendMessages && (wasVisible || (_replyToId && _replyEditMsg) || (!_field->hasSendText() && !kbWasHidden()))) { if (!_a_show.animating()) { if (hasMarkup) { - _kbScroll.show(); + _kbScroll->show(); _attachEmoji->hide(); _botKeyboardHide->show(); } else { - _kbScroll.hide(); + _kbScroll->hide(); _attachEmoji->show(); _botKeyboardHide->hide(); } _botKeyboardShow->hide(); _botCommandStart->hide(); } - int32 maxh = hasMarkup ? qMin(_keyboard.height(), st::historyComposeFieldMaxHeight - (st::historyComposeFieldMaxHeight / 2)) : 0; + int32 maxh = hasMarkup ? qMin(_keyboard->height(), st::historyComposeFieldMaxHeight - (st::historyComposeFieldMaxHeight / 2)) : 0; _field->setMaxHeight(st::historyComposeFieldMaxHeight - maxh); _kbShown = hasMarkup; - _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; + _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { updateReplyToName(); _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); @@ -7352,7 +7354,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { } } else { if (!_a_show.animating()) { - _kbScroll.hide(); + _kbScroll->hide(); _attachEmoji->show(); _botKeyboardHide->hide(); _botKeyboardShow->show(); @@ -7367,8 +7369,8 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { } } } else { - if (!_scroll.isHidden()) { - _kbScroll.hide(); + if (!_scroll->isHidden()) { + _kbScroll->hide(); _attachEmoji->show(); _botKeyboardHide->hide(); _botKeyboardShow->hide(); @@ -7396,7 +7398,7 @@ void HistoryWidget::updateToEndVisibility() { if (!history->showFrom || history->showFrom->detached()) { return false; } - return (_list->itemTop(history->showFrom) >= _scroll.scrollTop() + _scroll.height()); + return (_list->itemTop(history->showFrom) >= _scroll->scrollTop() + _scroll->height()); }; auto isToEndVisible = [this, &haveUnreadBelowBottom]() { if (!_history || _firstLoadRequest) { @@ -7405,7 +7407,7 @@ void HistoryWidget::updateToEndVisibility() { if (!_history->loadedAtBottom() || _replyReturn) { return true; } - if (_scroll.scrollTop() + st::historyToDownShownAfter < _scroll.scrollTopMax()) { + if (_scroll->scrollTop() + st::historyToDownShownAfter < _scroll->scrollTopMax()) { return true; } if (haveUnreadBelowBottom(_history) || haveUnreadBelowBottom(_migrated)) { @@ -7453,12 +7455,12 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { App::main()->showBackFromStack(); emit cancelled(); } else if (e->key() == Qt::Key_PageDown) { - _scroll.keyPressEvent(e); + _scroll->keyPressEvent(e); } else if (e->key() == Qt::Key_PageUp) { - _scroll.keyPressEvent(e); + _scroll->keyPressEvent(e); } else if (e->key() == Qt::Key_Down) { if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { - _scroll.keyPressEvent(e); + _scroll->keyPressEvent(e); } } else if (e->key() == Qt::Key_Up) { if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { @@ -7469,7 +7471,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { return; } } - _scroll.keyPressEvent(e); + _scroll->keyPressEvent(e); } } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { onListEnterPressed(); @@ -7625,7 +7627,7 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { _pinnedBar->shadow->show(); } connect(_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide())); - _reportSpamPanel.raise(); + _reportSpamPanel->raise(); _topShadow->raise(); if (_membersDropdown) { _membersDropdown->raise(); @@ -7638,8 +7640,8 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { updatePinnedBar(); result = true; - if (_scroll.scrollTop() != unreadBarTop()) { - _scroll.scrollToY(_scroll.scrollTop() + st::historyReplyHeight); + if (_scroll->scrollTop() != unreadBarTop()) { + _scroll->scrollToY(_scroll->scrollTop() + st::historyReplyHeight); } } else if (_pinnedBar->msgId != pinnedMsgId) { _pinnedBar->msgId = pinnedMsgId; @@ -7653,8 +7655,8 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { } else if (_pinnedBar) { destroyPinnedBar(); result = true; - if (_scroll.scrollTop() != unreadBarTop()) { - _scroll.scrollToY(_scroll.scrollTop() - st::historyReplyHeight); + if (_scroll->scrollTop() != unreadBarTop()) { + _scroll->scrollToY(_scroll->scrollTop() - st::historyReplyHeight); } resizeEvent(0); } @@ -7958,7 +7960,7 @@ void HistoryWidget::onCopyPostLink() { bool HistoryWidget::lastForceReplyReplied(const FullMsgId &replyTo) const { if (replyTo.msg > 0 && replyTo.channel != _channel) return false; - return _keyboard.forceReply() && _keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _keyboard.forMsgId().msg == (replyTo.msg < 0 ? replyToId() : replyTo.msg); + return _keyboard->forceReply() && _keyboard->forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _keyboard->forMsgId().msg == (replyTo.msg < 0 ? replyToId() : replyTo.msg); } bool HistoryWidget::cancelReply(bool lastKeyboardUsed) { @@ -7992,7 +7994,7 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) { _saveDraftStart = getms(); onDraftSave(); } - if (!_editMsgId && _keyboard.singleUse() && _keyboard.forceReply() && lastKeyboardUsed) { + if (!_editMsgId && _keyboard->singleUse() && _keyboard->forceReply() && lastKeyboardUsed) { if (_kbReplyTo) { onKbToggle(false); } @@ -8252,7 +8254,7 @@ void HistoryWidget::onFullPeerUpdated(PeerData *data) { if (updateCmdStartShown()) { updateControlsVisibility(); updateControlsGeometry(); - } else if (!_scroll.isHidden() && _unblock->isHidden() == isBlocked()) { + } else if (!_scroll->isHidden() && _unblock->isHidden() == isBlocked()) { updateControlsVisibility(); updateControlsGeometry(); } @@ -8497,7 +8499,7 @@ void HistoryWidget::updateReplyToName() { } void HistoryWidget::updateField() { - int32 fy = _scroll.y() + _scroll.height(); + int32 fy = _scroll->y() + _scroll->height(); update(0, fy, width(), height() - fy); } @@ -8786,8 +8788,8 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { if (_pinnedBar && !_pinnedBar->cancel->isHidden()) { drawPinnedBar(p); } - if (_scroll.isHidden()) { - p.setClipRect(_scroll.geometry()); + if (_scroll->isHidden()) { + p.setClipRect(_scroll->geometry()); HistoryLayout::paintEmpty(p, width(), height() - _field->height() - 2 * st::historySendPadding); } } else { @@ -8803,7 +8805,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { } QRect HistoryWidget::historyRect() const { - return _scroll.geometry(); + return _scroll->geometry(); } void HistoryWidget::destroyData() { @@ -8849,27 +8851,27 @@ QStringList HistoryWidget::getMediasFromMime(const QMimeData *d) { QPoint HistoryWidget::clampMousePosition(QPoint point) { if (point.x() < 0) { point.setX(0); - } else if (point.x() >= _scroll.width()) { - point.setX(_scroll.width() - 1); + } else if (point.x() >= _scroll->width()) { + point.setX(_scroll->width() - 1); } - if (point.y() < _scroll.scrollTop()) { - point.setY(_scroll.scrollTop()); - } else if (point.y() >= _scroll.scrollTop() + _scroll.height()) { - point.setY(_scroll.scrollTop() + _scroll.height() - 1); + if (point.y() < _scroll->scrollTop()) { + point.setY(_scroll->scrollTop()); + } else if (point.y() >= _scroll->scrollTop() + _scroll->height()) { + point.setY(_scroll->scrollTop() + _scroll->height() - 1); } return point; } void HistoryWidget::onScrollTimer() { int32 d = (_scrollDelta > 0) ? qMin(_scrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_scrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed)); - _scroll.scrollToY(_scroll.scrollTop() + d); + _scroll->scrollToY(_scroll->scrollTop() + d); } void HistoryWidget::checkSelectingScroll(QPoint point) { - if (point.y() < _scroll.scrollTop()) { - _scrollDelta = point.y() - _scroll.scrollTop(); - } else if (point.y() >= _scroll.scrollTop() + _scroll.height()) { - _scrollDelta = point.y() - _scroll.scrollTop() - _scroll.height() + 1; + if (point.y() < _scroll->scrollTop()) { + _scrollDelta = point.y() - _scroll->scrollTop(); + } else if (point.y() >= _scroll->scrollTop() + _scroll->height()) { + _scrollDelta = point.y() - _scroll->scrollTop() - _scroll->height() + 1; } else { _scrollDelta = 0; } @@ -8885,10 +8887,10 @@ void HistoryWidget::noSelectingScroll() { } bool HistoryWidget::touchScroll(const QPoint &delta) { - int32 scTop = _scroll.scrollTop(), scMax = _scroll.scrollTopMax(), scNew = snap(scTop - delta.y(), 0, scMax); + int32 scTop = _scroll->scrollTop(), scMax = _scroll->scrollTopMax(), scNew = snap(scTop - delta.y(), 0, scMax); if (scNew == scTop) return false; - _scroll.scrollToY(scNew); + _scroll->scrollToY(scNew); return true; } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 8d689301b..1d2a2ea7e 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/effects/rect_shadow.h" #include "ui/widgets/tooltip.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/scroll_area.h" #include "history/history_common.h" #include "history/field_autocomplete.h" #include "window/section_widget.h" @@ -58,7 +59,7 @@ class HistoryInner : public TWidget, public Ui::AbstractTooltipShower, private b Q_OBJECT public: - HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history); + HistoryInner(HistoryWidget *historyWidget, Ui::ScrollArea *scroll, History *history); void messagesReceived(PeerData *peer, const QVector &messages); void messagesReceivedDown(PeerData *peer, const QVector &messages); @@ -203,7 +204,7 @@ private: std_::unique_ptr _botAbout; HistoryWidget *_widget = nullptr; - ScrollArea *_scroll = nullptr; + Ui::ScrollArea *_scroll = nullptr; mutable History *_curHistory = nullptr; mutable int _curBlock = 0; mutable int _curItem = 0; @@ -254,7 +255,7 @@ private: QPoint _touchStart, _touchPrevPos, _touchPos; QTimer _touchSelectTimer; - TouchScrollState _touchScrollState = TouchScrollManual; + Ui::TouchScrollState _touchScrollState = Ui::TouchScrollState::Manual; bool _touchPrevPosValid = false; bool _touchWaitingAcceleration = false; QPoint _touchSpeed; @@ -364,7 +365,7 @@ class BotKeyboard : public TWidget, public Ui::AbstractTooltipShower, public Cli Q_OBJECT public: - BotKeyboard(); + BotKeyboard(QWidget *parent); bool moderateKeyActivate(int index); @@ -423,7 +424,7 @@ private: class Style : public ReplyKeyboard::Style { public: - Style(BotKeyboard *parent, const style::botKeyboardButton &st) : ReplyKeyboard::Style(st), _parent(parent) { + Style(BotKeyboard *parent, const style::BotKeyboardButton &st) : ReplyKeyboard::Style(st), _parent(parent) { } void startPaint(Painter &p) const override; @@ -440,7 +441,7 @@ private: BotKeyboard *_parent; }; - const style::botKeyboardButton *_st = &st::botKbButton; + const style::BotKeyboardButton *_st = nullptr; }; @@ -1049,7 +1050,7 @@ private: MsgId _activeAnimMsgId = 0; - ScrollArea _scroll; + ChildWidget _scroll; HistoryInner *_list = nullptr; History *_migrated = nullptr; History *_history = nullptr; @@ -1077,7 +1078,7 @@ private: bool isMuteUnmute() const; bool updateCmdStartShown(); - ReportSpamPanel _reportSpamPanel; + ChildWidget _reportSpamPanel; ChildWidget _send; ChildWidget _unblock; @@ -1110,8 +1111,8 @@ private: bool _kbShown = false; HistoryItem *_kbReplyTo = nullptr; - ScrollArea _kbScroll; - BotKeyboard _keyboard; + ChildWidget _kbScroll; + ChildWidget _keyboard; ChildWidget _membersDropdown = { nullptr }; QTimer _membersDropdownShowTimer; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index d1bdca47e..6d045560d 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_overview.h" #include "styles/style_history.h" #include "styles/style_stickers.h" +#include "styles/style_widgets.h" #include "inline_bots/inline_bot_result.h" #include "media/media_audio.h" #include "media/media_clip_reader.h" diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index a39394f9c..ac5c4facc 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -63,7 +63,7 @@ introLink: LinkButton(defaultLinkButton) { font: introFont; overFont: font(16px underline); } -introLabel: flatLabel(labelDefFlat) { +introLabel: FlatLabel(defaultFlatLabel) { font: introFont; align: align(center); } @@ -124,14 +124,14 @@ introErrorTop: 15px; introErrorHeight: 40px; introErrorFont: font(16px); -introLabelTextStyle: textStyle(defaultTextStyle) { +introLabelTextStyle: TextStyle(defaultTextStyle) { lineHeight: 30px; } -introErrorLabelTextStyle: textStyle(defaultTextStyle) { +introErrorLabelTextStyle: TextStyle(defaultTextStyle) { lineHeight: 27px; } -introErrorLabel: flatLabel(labelDefFlat) { +introErrorLabel: FlatLabel(defaultFlatLabel) { font: introErrorFont; align: align(center); } diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index 1eedf740a..837781b6d 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -26,23 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/introcode.h" #include "styles/style_intro.h" #include "ui/widgets/buttons.h" - -namespace { - class SignUpClickHandler : public LeftButtonClickHandler { - public: - SignUpClickHandler(IntroPhone *widget) : _widget(widget) { - } - - protected: - void onClickImpl() const override { - _widget->toSignUp(); - } - - private: - IntroPhone *_widget; - - }; -} +#include "ui/widgets/labels.h" IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) @@ -51,7 +35,7 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) , _country(this, st::introCountry) , _phone(this, st::introPhone) , _code(this, st::introCountryCode) -, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), FlatLabel::InitType::Rich, st::introErrorLabel, st::introErrorLabelTextStyle) +, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), Ui::FlatLabel::InitType::Rich, st::introErrorLabel, st::introErrorLabelTextStyle) , _checkRequest(this) { setVisible(false); setGeometry(parent->innerRect()); @@ -68,7 +52,9 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) connect(intro(), SIGNAL(countryChanged()), this, SLOT(countryChanged())); connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); - _signup->setLink(1, MakeShared(this)); + _signup->setLink(1, MakeShared([this] { + toSignUp(); + })); _signup->hide(); _signupCache = myGrab(_signup); diff --git a/Telegram/SourceFiles/intro/introphone.h b/Telegram/SourceFiles/intro/introphone.h index f4ebedf4c..1591977df 100644 --- a/Telegram/SourceFiles/intro/introphone.h +++ b/Telegram/SourceFiles/intro/introphone.h @@ -21,13 +21,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/countryinput.h" -#include "ui/flatlabel.h" #include "intro/introwidget.h" namespace Ui { class PhonePartInput; class CountryCodeInput; class RoundButton; +class FlatLabel; } // namespace Ui class IntroPhone final : public IntroStep { @@ -52,8 +52,6 @@ public: void phoneSubmitDone(const MTPauth_SentCode &result); bool phoneSubmitFail(const RPCError &error); - void toSignUp(); - public slots: void countryChanged(); void onInputChange(); @@ -61,6 +59,8 @@ public slots: void onCheckRequest(); private: + void toSignUp(); + QString fullNumber() const; void disableAll(); void enableAll(bool failed); @@ -81,7 +81,7 @@ private: ChildWidget _phone; ChildWidget _code; - ChildWidget _signup; + ChildWidget _signup; QPixmap _signupCache; bool _showSignup = false; diff --git a/Telegram/SourceFiles/intro/introstart.cpp b/Telegram/SourceFiles/intro/introstart.cpp index 34e1d4520..e6007793a 100644 --- a/Telegram/SourceFiles/intro/introstart.cpp +++ b/Telegram/SourceFiles/intro/introstart.cpp @@ -26,9 +26,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/introphone.h" #include "langloaderplain.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent) -, _intro(this, lang(lng_intro), FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle) +, _intro(this, lang(lng_intro), Ui::FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle) , _changeLang(this, QString()) , _next(this, lang(lng_start_msgs), st::introNextButton) { _changeLang->hide(); diff --git a/Telegram/SourceFiles/intro/introstart.h b/Telegram/SourceFiles/intro/introstart.h index a88be1238..dc254535d 100644 --- a/Telegram/SourceFiles/intro/introstart.h +++ b/Telegram/SourceFiles/intro/introstart.h @@ -22,9 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/introwidget.h" -class FlatLabel; - namespace Ui { +class FlatLabel; class LinkButton; class RoundButton; } // namespace Ui @@ -39,7 +38,7 @@ public: void onSubmit() override; private: - ChildWidget _intro; + ChildWidget _intro; ChildWidget _changeLang; diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 249929550..fcdde26a2 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "mainwidget.h" #include "ui/filedialog.h" +#include "styles/style_boxes.h" #include "styles/style_stickers.h" #include "window/window_main_menu.h" @@ -366,6 +367,7 @@ void LayerStackWidget::showMainMenu() { startShow(); } else { _mainMenu->show(); + _mainMenu->showFinished(); if (App::wnd()) App::wnd()->setInnerFocus(); updateLayerBox(); } @@ -474,6 +476,7 @@ void LayerStackWidget::step_background(float64 ms, bool timer) { _background->setMainMenuRight(_mainMenu->width()); _background->setMainMenuCache(QPixmap()); _mainMenu->show(); + _mainMenu->showFinished(); } if (App::wnd()) App::wnd()->setInnerFocus(); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 17eddd37b..0c3563d38 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3541,7 +3541,9 @@ void MainWidget::start(const MTPUser &user) { Local::readSavedPeers(); cSetOtherOnline(0); - App::feedUsers(MTP_vector(1, user)); + if (auto self = App::feedUsers(MTP_vector(1, user))) { + self->loadUserpic(); + } MTP::send(MTPupdates_GetState(), rpcDone(&MainWidget::gotState)); update(); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 13b474c53..7f26e1c13 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -1503,7 +1503,7 @@ PreLaunchWindow::PreLaunchWindow(QString title) : TWidget(0) { int paddingVertical = (_size / 2); int paddingHorizontal = _size; int borderRadius = (_size / 5); - setStyleSheet(qsl("QPushButton { padding: %1px %2px; background-color: #ffffff; border-radius: %3px; }\nQPushButton#confirm:hover, QPushButton#cancel:hover { background-color: #edf7ff; color: #2f9fea; }\nQPushButton#confirm { color: #2f9fea; }\nQPushButton#cancel { color: #aeaeae; }\nQLineEdit { border: 1px solid #e0e0e0; padding: 5px; }\nQLineEdit:focus { border: 2px solid #62c0f7; padding: 4px; }").arg(paddingVertical).arg(paddingHorizontal).arg(borderRadius)); + setStyleSheet(qsl("QPushButton { padding: %1px %2px; background-color: #ffffff; border-radius: %3px; }\nQPushButton#confirm:hover, QPushButton#cancel:hover { background-color: #e3f1fa; color: #2f9fea; }\nQPushButton#confirm { color: #2f9fea; }\nQPushButton#cancel { color: #aeaeae; }\nQLineEdit { border: 1px solid #e0e0e0; padding: 5px; }\nQLineEdit:focus { border: 2px solid #62c0f7; padding: 4px; }").arg(paddingVertical).arg(paddingHorizontal).arg(borderRadius)); if (!PreLaunchWindowInstance) { PreLaunchWindowInstance = this; } diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 0adbbfed7..befe9efa4 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -34,7 +34,7 @@ MediaPlayerButton { cancelStroke: pixels; } -mediaPlayerActiveFg: windowActiveBg; +mediaPlayerActiveFg: windowBgActive; mediaPlayerInactiveFg: #dfebf2; mediaPlayerButton: MediaPlayerButton { @@ -60,9 +60,9 @@ mediaPlayerPlaySkip: 1px; mediaPlayerPlayTop: 0px; mediaPlayerCloseRight: 0px; -mediaPlayerName: flatLabel(labelDefFlat) { +mediaPlayerName: FlatLabel(defaultFlatLabel) { maxHeight: 20px; - textFg: windowTextFg; + textFg: windowFg; } mediaPlayerTime: LabelSimple(defaultLabelSimple) { textFg: windowSubTextFg; @@ -209,7 +209,7 @@ mediaPlayerPanelVolumeWidth: 64px; mediaPlayerPanelVolumeToggleSkip: 0px; mediaPlayerPanelVolumeToggleTop: 57px; -mediaPlayerScroll: flatScroll(solidScroll) { +mediaPlayerScroll: FlatScroll(defaultSolidScroll) { deltat: 10px; deltab: 0px; } diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp index 3a2eb7d4b..0af858d49 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.cpp +++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp @@ -21,8 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "media/player/media_player_cover.h" -#include "ui/flatlabel.h" -#include "ui/widgets/label_simple.h" +#include "ui/widgets/labels.h" #include "ui/widgets/media_slider.h" #include "ui/widgets/buttons.h" #include "media/media_audio.h" diff --git a/Telegram/SourceFiles/media/player/media_player_cover.h b/Telegram/SourceFiles/media/player/media_player_cover.h index bb1aa0054..0cb860ef9 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.h +++ b/Telegram/SourceFiles/media/player/media_player_cover.h @@ -22,8 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class AudioMsgId; struct AudioPlaybackState; -class FlatLabel; + namespace Ui { +class FlatLabel; class LabelSimple; class IconButton; } // namespace Ui @@ -77,7 +78,7 @@ private: QString _time; class PlayButton; - ChildWidget _nameLabel; + ChildWidget _nameLabel; ChildWidget _timeLabel; ChildWidget _close; ChildWidget _playback; diff --git a/Telegram/SourceFiles/media/player/media_player_panel.cpp b/Telegram/SourceFiles/media/player/media_player_panel.cpp index 05f3862d1..1de46609d 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.cpp +++ b/Telegram/SourceFiles/media/player/media_player_panel.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_widgets.h" #include "styles/style_media_player.h" #include "ui/widgets/shadow.h" +#include "ui/widgets/scroll_area.h" #include "mainwindow.h" namespace Media { diff --git a/Telegram/SourceFiles/media/player/media_player_panel.h b/Telegram/SourceFiles/media/player/media_player_panel.h index 03b95c52a..99326d196 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.h +++ b/Telegram/SourceFiles/media/player/media_player_panel.h @@ -22,9 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/effects/rect_shadow.h" -class ScrollArea; - namespace Ui { +class ScrollArea; class GradientShadow; } // namespace Ui @@ -110,7 +109,7 @@ private: Ui::RectShadow _shadow; ButtonCallback _pinCallback, _closeCallback; ChildWidget _cover = { nullptr }; - ChildWidget _scroll; + ChildWidget _scroll; ChildWidget _scrollShadow = { nullptr }; }; diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 4e22f965e..b44363576 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -21,8 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "media/player/media_player_widget.h" -#include "ui/flatlabel.h" -#include "ui/widgets/label_simple.h" +#include "ui/widgets/labels.h" #include "ui/widgets/filled_slider.h" #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index e1e13bde1..757fdcc2e 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -22,9 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class AudioMsgId; struct AudioPlaybackState; -class FlatLabel; namespace Ui { +class FlatLabel; class LabelSimple; class IconButton; class PlainShadow; @@ -91,7 +91,7 @@ private: QString _time; class PlayButton; - ChildWidget _nameLabel; + ChildWidget _nameLabel; ChildWidget _timeLabel; ChildWidget _previousTrack = { nullptr }; ChildWidget _playPause; diff --git a/Telegram/SourceFiles/media/view/media_clip_controller.cpp b/Telegram/SourceFiles/media/view/media_clip_controller.cpp index 9e66e6d66..f695c26c3 100644 --- a/Telegram/SourceFiles/media/view/media_clip_controller.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_controller.cpp @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/view/media_clip_playback.h" #include "media/view/media_clip_volume_controller.h" #include "styles/style_mediaview.h" -#include "ui/widgets/label_simple.h" +#include "ui/widgets/labels.h" #include "ui/widgets/media_slider.h" #include "ui/effects/widget_fade_wrap.h" #include "ui/widgets/buttons.h" diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 434c03b1b..27c1a721f 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -37,23 +37,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace { -class SaveMsgClickHandler : public ClickHandler { -public: - - SaveMsgClickHandler(MediaView *view) : _view(view) { - } - - void onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton) { - _view->showSaveMsgFile(); - } - } - -private: - - MediaView *_view; -}; - TextParseOptions _captionTextOptions = { TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags 0, // maxw @@ -95,7 +78,7 @@ MediaView::MediaView() : TWidget(App::wnd()) custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); _saveMsgText.setRichText(st::medviewSaveMsgFont, lang(lng_mediaview_saved), _textDlgOptions, custom); _saveMsg = QRect(0, 0, _saveMsgText.maxWidth() + st::medviewSaveMsgPadding.left() + st::medviewSaveMsgPadding.right(), st::medviewSaveMsgFont->height + st::medviewSaveMsgPadding.top() + st::medviewSaveMsgPadding.bottom()); - _saveMsgText.setLink(1, MakeShared(this)); + _saveMsgText.setLink(1, MakeShared([this] { showSaveMsgFile(); })); connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(onScreenResized(int))); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 1cdcdcdcd..85a360be0 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -64,7 +64,6 @@ public: void documentUpdated(DocumentData *doc); void changingMsgId(HistoryItem *row, MsgId newId); - void showSaveMsgFile(); void close(); void activateControls(); @@ -128,6 +127,8 @@ private slots: void onVideoPlayProgress(const AudioMsgId &audioId); private: + void showSaveMsgFile(); + void dropdownHidden(); void updateDocSize(); void updateControls(); diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 6de183253..c9ed59b78 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -409,7 +409,7 @@ Voice::Voice(DocumentData *voice, HistoryItem *parent) : FileBase(parent) QString d = textcmdLink(1, textRichPrepare(langDateTime(date(_data->date)))); TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto }; _details.setText(st::normalFont, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts); - _details.setLink(1, MakeShared(parent)); + _details.setLink(1, goToMessageClickHandler(parent)); } void Voice::initDimensions() { @@ -588,7 +588,7 @@ bool Voice::updateStatusText() const { Document::Document(DocumentData *document, HistoryItem *parent, const style::OverviewFileLayout &st) : FileBase(parent) , _data(document) -, _msgl(new GoToMessageClickHandler(parent)) +, _msgl(goToMessageClickHandler(parent)) , _namel(new DocumentOpenClickHandler(_data)) , _st(st) , _date(langDateTime(date(_data->date))) diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 0626583bd..201faec86 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_overview.h" #include "styles/style_dialogs.h" #include "styles/style_window.h" +#include "styles/style_settings.h" #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" #include "boxes/photocropbox.h" @@ -45,7 +46,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html -OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerData *peer, MediaOverviewType type) : TWidget(nullptr) +OverviewInner::OverviewInner(OverviewWidget *overview, Ui::ScrollArea *scroll, PeerData *peer, MediaOverviewType type) : TWidget(nullptr) , _overview(overview) , _scroll(scroll) , _peer(peer->migrateTo() ? peer->migrateTo() : peer) @@ -116,7 +117,7 @@ void OverviewInner::touchUpdateSpeed() { // of a small horizontal offset when scrolling vertically const int newSpeedY = (qAbs(pixelsPerSecond.y()) > FingerAccuracyThreshold) ? pixelsPerSecond.y() : 0; const int newSpeedX = (qAbs(pixelsPerSecond.x()) > FingerAccuracyThreshold) ? pixelsPerSecond.x() : 0; - if (_touchScrollState == TouchScrollAuto) { + if (_touchScrollState == Ui::TouchScrollState::Auto) { const int oldSpeedY = _touchSpeed.y(); const int oldSpeedX = _touchSpeed.x(); if ((oldSpeedY <= 0 && newSpeedY <= 0) || ((oldSpeedY >= 0 && newSpeedY >= 0) @@ -352,7 +353,7 @@ void OverviewInner::touchEvent(QTouchEvent *e) { _touchInProgress = false; _touchSelectTimer.stop(); _touchScroll = _touchSelect = false; - _touchScrollState = TouchScrollManual; + _touchScrollState = Ui::TouchScrollState::Manual; dragActionCancel(); return; } @@ -368,8 +369,8 @@ void OverviewInner::touchEvent(QTouchEvent *e) { if (e->touchPoints().isEmpty()) return; _touchInProgress = true; - if (_touchScrollState == TouchScrollAuto) { - _touchScrollState = TouchScrollAcceleration; + if (_touchScrollState == Ui::TouchScrollState::Auto) { + _touchScrollState = Ui::TouchScrollState::Acceleration; _touchWaitingAcceleration = true; _touchAccelerationTime = getms(); touchUpdateSpeed(); @@ -392,13 +393,13 @@ void OverviewInner::touchEvent(QTouchEvent *e) { touchUpdateSpeed(); } if (_touchScroll) { - if (_touchScrollState == TouchScrollManual) { + if (_touchScrollState == Ui::TouchScrollState::Manual) { touchScrollUpdated(_touchPos); - } else if (_touchScrollState == TouchScrollAcceleration) { + } else if (_touchScrollState == Ui::TouchScrollState::Acceleration) { touchUpdateSpeed(); _touchAccelerationTime = getms(); if (_touchSpeed.isNull()) { - _touchScrollState = TouchScrollManual; + _touchScrollState = Ui::TouchScrollState::Manual; } } } @@ -413,17 +414,17 @@ void OverviewInner::touchEvent(QTouchEvent *e) { showContextMenu(&contextMenu, true); _touchScroll = false; } else if (_touchScroll) { - if (_touchScrollState == TouchScrollManual) { - _touchScrollState = TouchScrollAuto; + if (_touchScrollState == Ui::TouchScrollState::Manual) { + _touchScrollState = Ui::TouchScrollState::Auto; _touchPrevPosValid = false; _touchScrollTimer.start(15); _touchTime = getms(); - } else if (_touchScrollState == TouchScrollAuto) { - _touchScrollState = TouchScrollManual; + } else if (_touchScrollState == Ui::TouchScrollState::Auto) { + _touchScrollState = Ui::TouchScrollState::Manual; _touchScroll = false; touchResetSpeed(); - } else if (_touchScrollState == TouchScrollAcceleration) { - _touchScrollState = TouchScrollAuto; + } else if (_touchScrollState == Ui::TouchScrollState::Acceleration) { + _touchScrollState = Ui::TouchScrollState::Auto; _touchWaitingAcceleration = false; _touchPrevPosValid = false; } @@ -1561,16 +1562,16 @@ void OverviewInner::onTouchSelect() { void OverviewInner::onTouchScrollTimer() { uint64 nowTime = getms(); - if (_touchScrollState == TouchScrollAcceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { - _touchScrollState = TouchScrollManual; + if (_touchScrollState == Ui::TouchScrollState::Acceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { + _touchScrollState = Ui::TouchScrollState::Manual; touchResetSpeed(); - } else if (_touchScrollState == TouchScrollAuto || _touchScrollState == TouchScrollAcceleration) { + } else if (_touchScrollState == Ui::TouchScrollState::Auto || _touchScrollState == Ui::TouchScrollState::Acceleration) { int32 elapsed = int32(nowTime - _touchTime); QPoint delta = _touchSpeed * elapsed / 1000; bool hasScrolled = _overview->touchScroll(delta); if (_touchSpeed.isNull() || !hasScrolled) { - _touchScrollState = TouchScrollManual; + _touchScrollState = Ui::TouchScrollState::Manual; _touchScroll = false; _touchScrollTimer.stop(); } else { @@ -1876,16 +1877,16 @@ OverviewInner::~OverviewInner() { } OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewType type) : TWidget(parent) -, _scroll(this, st::setScroll, false) -, _inner(this, &_scroll, peer, type) +, _scroll(this, st::settingsScroll, false) +, _inner(this, _scroll, peer, type) , _a_show(animation(this, &OverviewWidget::step_show)) , _topShadow(this, st::shadowColor) { - _scroll.setWidget(&_inner); - _scroll.move(0, 0); - _inner.move(0, 0); + _scroll->setOwnedWidget(_inner); + _scroll->move(0, 0); + _inner->move(0, 0); - _scroll.show(); - connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); + _scroll->show(); + connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); _scrollTimer.setSingleShot(false); @@ -1894,37 +1895,37 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp } void OverviewWidget::clear() { - _inner.clear(); + _inner->clear(); } void OverviewWidget::onScroll() { MTP::clearLoaderPriorities(); - int32 preloadThreshold = _scroll.height() * 5; + int32 preloadThreshold = _scroll->height() * 5; bool needToPreload = false; do { - needToPreload = (type() == OverviewMusicFiles || type() == OverviewVoiceFiles) ? (_scroll.scrollTop() < preloadThreshold) : (_scroll.scrollTop() + preloadThreshold > _scroll.scrollTopMax()); - if (!needToPreload || !_inner.preloadLocal()) { + needToPreload = (type() == OverviewMusicFiles || type() == OverviewVoiceFiles) ? (_scroll->scrollTop() < preloadThreshold) : (_scroll->scrollTop() + preloadThreshold > _scroll->scrollTopMax()); + if (!needToPreload || !_inner->preloadLocal()) { break; } } while (true); if (needToPreload) { - _inner.preloadMore(); + _inner->preloadMore(); } if (!_noDropResizeIndex) { - _inner.dropResizeIndex(); + _inner->dropResizeIndex(); } } void OverviewWidget::resizeEvent(QResizeEvent *e) { _noDropResizeIndex = true; - int32 st = _scroll.scrollTop(); - _scroll.resize(size()); - int32 newScrollTop = _inner.resizeToWidth(width(), st, height()); + int32 st = _scroll->scrollTop(); + _scroll->resize(size()); + int32 newScrollTop = _inner->resizeToWidth(width(), st, height()); if (int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0) { newScrollTop += addToY; } - if (newScrollTop != _scroll.scrollTop()) { - _scroll.scrollToY(newScrollTop); + if (newScrollTop != _scroll->scrollTop()) { + _scroll->scrollToY(newScrollTop); } _noDropResizeIndex = false; @@ -1955,19 +1956,19 @@ void OverviewWidget::paintEvent(QPaintEvent *e) { } void OverviewWidget::contextMenuEvent(QContextMenuEvent *e) { - return _inner.showContextMenu(e); + return _inner->showContextMenu(e); } void OverviewWidget::scrollBy(int32 add) { - if (_scroll.isHidden()) { + if (_scroll->isHidden()) { _scrollSetAfterShow += add; } else { - _scroll.scrollToY(_scroll.scrollTop() + add); + _scroll->scrollToY(_scroll->scrollTop() + add); } } void OverviewWidget::scrollReset() { - _scroll.scrollToY((type() == OverviewMusicFiles || type() == OverviewVoiceFiles) ? _scroll.scrollTopMax() : 0); + _scroll->scrollToY((type() == OverviewMusicFiles || type() == OverviewVoiceFiles) ? _scroll->scrollTopMax() : 0); } bool OverviewWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { @@ -1998,24 +1999,24 @@ void OverviewWidget::topBarClick() { } PeerData *OverviewWidget::peer() const { - return _inner.peer(); + return _inner->peer(); } PeerData *OverviewWidget::migratePeer() const { - return _inner.migratePeer(); + return _inner->migratePeer(); } MediaOverviewType OverviewWidget::type() const { - return _inner.type(); + return _inner->type(); } void OverviewWidget::switchType(MediaOverviewType type) { _selCount = 0; - disconnect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); + disconnect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); - _inner.setSelectMode(false); - _inner.switchType(type); + _inner->setSelectMode(false); + _inner->switchType(type); switch (type) { case OverviewPhotos: _header = lang(lng_profile_photos_header); break; case OverviewVideos: _header = lang(lng_profile_videos_header); break; @@ -2029,7 +2030,7 @@ void OverviewWidget::switchType(MediaOverviewType type) { updateTopBarSelection(); scrollReset(); - connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); + connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); onScroll(); activate(); @@ -2037,15 +2038,15 @@ void OverviewWidget::switchType(MediaOverviewType type) { void OverviewWidget::updateTopBarSelection() { int32 selectedForForward, selectedForDelete; - _inner.getSelectionState(selectedForForward, selectedForDelete); + _inner->getSelectionState(selectedForForward, selectedForDelete); _selCount = selectedForForward ? selectedForForward : selectedForDelete; - _inner.setSelectMode(_selCount > 0); + _inner->setSelectMode(_selCount > 0); if (App::main()) { App::main()->topBar()->showSelected(_selCount > 0 ? _selCount : 0, (selectedForDelete == selectedForForward)); App::main()->topBar()->update(); } if (App::wnd() && !Ui::isLayerShown()) { - _inner.activate(); + _inner->activate(); } update(); } @@ -2055,7 +2056,7 @@ int32 OverviewWidget::lastWidth() const { } int32 OverviewWidget::lastScrollTop() const { - return _scroll.scrollTop(); + return _scroll->scrollTop(); } int32 OverviewWidget::countBestScroll() const { @@ -2063,22 +2064,22 @@ int32 OverviewWidget::countBestScroll() const { AudioMsgId playing; audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); if (playing) { - int32 top = _inner.itemTop(playing.contextId()); + int32 top = _inner->itemTop(playing.contextId()); if (top >= 0) { - return snap(top - int(_scroll.height() - (st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom())) / 2, 0, _scroll.scrollTopMax()); + return snap(top - int(_scroll->height() - (st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom())) / 2, 0, _scroll->scrollTopMax()); } } } else if (type() == OverviewLinks || type() == OverviewFiles) { return 0; } - return _scroll.scrollTopMax(); + return _scroll->scrollTopMax(); } void OverviewWidget::fastShow(bool back, int32 lastScrollTop) { resizeEvent(0); _scrollSetAfterShow = (lastScrollTop < 0 ? countBestScroll() : lastScrollTop); show(); - _inner.activate(); + _inner->activate(); doneShow(); if (App::app()) App::app()->mtpUnpause(); @@ -2086,7 +2087,7 @@ void OverviewWidget::fastShow(bool back, int32 lastScrollTop) { void OverviewWidget::setLastScrollTop(int lastScrollTop) { resizeEvent(0); - _scroll.scrollToY(lastScrollTop < 0 ? countBestScroll() : lastScrollTop); + _scroll->scrollToY(lastScrollTop < 0 ? countBestScroll() : lastScrollTop); } void OverviewWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms) { @@ -2101,8 +2102,8 @@ void OverviewWidget::showAnimated(Window::SlideDirection direction, const Window _topShadow->setVisible(params.withTopBarShadow ? true : false); App::main()->topBar()->startAnim(); - _scrollSetAfterShow = _scroll.scrollTop(); - _scroll.hide(); + _scrollSetAfterShow = _scroll->scrollTop(); + _scroll->hide(); int delta = st::slideShift; if (direction == Window::SlideDirection::FromLeft) { @@ -2149,15 +2150,15 @@ void OverviewWidget::step_show(float64 ms, bool timer) { } void OverviewWidget::doneShow() { - _scroll.show(); - _scroll.scrollToY(_scrollSetAfterShow); + _scroll->show(); + _scroll->scrollToY(_scrollSetAfterShow); activate(); onScroll(); } void OverviewWidget::mediaOverviewUpdated(PeerData *p, MediaOverviewType t) { if ((peer() == p || migratePeer() == p) && t == type()) { - _inner.mediaOverviewUpdated(); + _inner->mediaOverviewUpdated(); onScroll(); updateTopBarSelection(); } @@ -2165,7 +2166,7 @@ void OverviewWidget::mediaOverviewUpdated(PeerData *p, MediaOverviewType t) { void OverviewWidget::changingMsgId(HistoryItem *row, MsgId newId) { if (peer() == row->history()->peer || migratePeer() == row->history()->peer) { - _inner.changingMsgId(row, newId); + _inner->changingMsgId(row, newId); } } @@ -2182,22 +2183,22 @@ void OverviewWidget::grabFinish() { void OverviewWidget::ui_repaintHistoryItem(const HistoryItem *item) { if (peer() == item->history()->peer || migratePeer() == item->history()->peer) { - _inner.repaintItem(item); + _inner->repaintItem(item); } } void OverviewWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { if (peer() == item->history()->peer || migratePeer() == item->history()->peer) { - _inner.onUpdateSelected(); + _inner->onUpdateSelected(); } } void OverviewWidget::fillSelectedItems(SelectedItemSet &sel, bool forDelete) { - _inner.fillSelectedItems(sel, forDelete); + _inner->fillSelectedItems(sel, forDelete); } void OverviewWidget::updateAfterDrag() { - _inner.dragActionUpdate(QCursor::pos()); + _inner->dragActionUpdate(QCursor::pos()); } OverviewWidget::~OverviewWidget() { @@ -2206,37 +2207,37 @@ OverviewWidget::~OverviewWidget() { } void OverviewWidget::activate() { - if (_scroll.isHidden()) { + if (_scroll->isHidden()) { setFocus(); } else { - _inner.activate(); + _inner->activate(); } } QPoint OverviewWidget::clampMousePosition(QPoint point) { if (point.x() < 0) { point.setX(0); - } else if (point.x() >= _scroll.width()) { - point.setX(_scroll.width() - 1); + } else if (point.x() >= _scroll->width()) { + point.setX(_scroll->width() - 1); } - if (point.y() < _scroll.scrollTop()) { - point.setY(_scroll.scrollTop()); - } else if (point.y() >= _scroll.scrollTop() + _scroll.height()) { - point.setY(_scroll.scrollTop() + _scroll.height() - 1); + if (point.y() < _scroll->scrollTop()) { + point.setY(_scroll->scrollTop()); + } else if (point.y() >= _scroll->scrollTop() + _scroll->height()) { + point.setY(_scroll->scrollTop() + _scroll->height() - 1); } return point; } void OverviewWidget::onScrollTimer() { int32 d = (_scrollDelta > 0) ? qMin(_scrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_scrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed)); - _scroll.scrollToY(_scroll.scrollTop() + d); + _scroll->scrollToY(_scroll->scrollTop() + d); } void OverviewWidget::checkSelectingScroll(QPoint point) { - if (point.y() < _scroll.scrollTop()) { - _scrollDelta = point.y() - _scroll.scrollTop(); - } else if (point.y() >= _scroll.scrollTop() + _scroll.height()) { - _scrollDelta = point.y() - _scroll.scrollTop() - _scroll.height() + 1; + if (point.y() < _scroll->scrollTop()) { + _scrollDelta = point.y() - _scroll->scrollTop(); + } else if (point.y() >= _scroll->scrollTop() + _scroll->height()) { + _scrollDelta = point.y() - _scroll->scrollTop() - _scroll->height() + 1; } else { _scrollDelta = 0; } @@ -2252,10 +2253,10 @@ void OverviewWidget::noSelectingScroll() { } bool OverviewWidget::touchScroll(const QPoint &delta) { - int32 scTop = _scroll.scrollTop(), scMax = _scroll.scrollTopMax(), scNew = snap(scTop - delta.y(), 0, scMax); + int32 scTop = _scroll->scrollTop(), scMax = _scroll->scrollTopMax(), scNew = snap(scTop - delta.y(), 0, scMax); if (scNew == scTop) return false; - _scroll.scrollToY(scNew); + _scroll->scrollToY(scNew); return true; } @@ -2265,7 +2266,7 @@ void OverviewWidget::onForwardSelected() { void OverviewWidget::onDeleteSelected() { SelectedItemSet sel; - _inner.fillSelectedItems(sel); + _inner->fillSelectedItems(sel); if (sel.isEmpty()) return; App::main()->deleteLayer(sel.size()); @@ -2275,7 +2276,7 @@ void OverviewWidget::onDeleteSelectedSure() { Ui::hideLayer(); SelectedItemSet sel; - _inner.fillSelectedItems(sel); + _inner->fillSelectedItems(sel); if (sel.isEmpty()) return; QMap > ids; @@ -2318,5 +2319,5 @@ void OverviewWidget::onDeleteContextSure() { } void OverviewWidget::onClearSelected() { - _inner.clearSelectedItems(); + _inner->clearSelectedItems(); } diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index b5480bf13..1f9c9d30a 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/section_widget.h" #include "ui/widgets/tooltip.h" +#include "ui/widgets/scroll_area.h" namespace Overview { namespace Layout { @@ -43,7 +44,7 @@ class OverviewInner : public TWidget, public Ui::AbstractTooltipShower, public R Q_OBJECT public: - OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerData *peer, MediaOverviewType type); + OverviewInner(OverviewWidget *overview, Ui::ScrollArea *scroll, PeerData *peer, MediaOverviewType type); void activate(); @@ -154,7 +155,7 @@ private: int32 countHeight(); OverviewWidget *_overview; - ScrollArea *_scroll; + Ui::ScrollArea *_scroll; int _resizeIndex = -1; int _resizeSkip = 0; @@ -257,7 +258,7 @@ private: QPoint _touchStart, _touchPrevPos, _touchPos; QTimer _touchSelectTimer; - TouchScrollState _touchScrollState = TouchScrollManual; + Ui::TouchScrollState _touchScrollState = Ui::TouchScrollState::Manual; bool _touchPrevPosValid = false; bool _touchWaitingAcceleration = false; QPoint _touchSpeed; @@ -325,7 +326,7 @@ public: void grapWithoutTopBarShadow(); void grabFinish() override; void rpcClear() override { - _inner.rpcClear(); + _inner->rpcClear(); RPCSender::rpcClear(); } @@ -353,8 +354,8 @@ public slots: void onClearSelected(); private: - ScrollArea _scroll; - OverviewInner _inner; + ChildWidget _scroll; + ChildWidget _inner; bool _noDropResizeIndex = false; QString _header; diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 62e89c118..2da778aa9 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -187,7 +187,7 @@ void PasscodeWidget::paintEvent(QPaintEvent *e) { p.fillRect(rect(), st::windowBg); p.setFont(st::passcodeHeaderFont); - p.setPen(st::windowTextFg); + p.setPen(st::windowFg); p.drawText(QRect(0, _passcode->y() - st::passcodeHeaderHeight, width(), st::passcodeHeaderHeight), lang(lng_passcode_enter), style::al_center); if (!_error.isEmpty()) { diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index 521453932..9fff5a617 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -39,14 +39,14 @@ profilePhotoLeftMax: 35px; profilePhotoDuration: 500; profileNameLeft: 26px; profileNameTop: 9px; -profileNameLabel: flatLabel(labelDefFlat) { +profileNameLabel: FlatLabel(defaultFlatLabel) { margin: margins(10px, 5px, 10px, 5px); font: font(16px semibold); width: 160px; maxHeight: 24px; textFg: #333333; } -profileNameTextStyle: textStyle(defaultTextStyle) { +profileNameTextStyle: TextStyle(defaultTextStyle) { } profileStatusLeft: 27px; profileStatusTop: 35px; @@ -94,14 +94,14 @@ profileBlockTitleHeight: 24px; profileBlockTitleFont: font(14px semibold); profileBlockTitleFg: #333333; profileBlockTitlePosition: point(24px, 0px); -profileBlockLabel: flatLabel(labelDefFlat) { +profileBlockLabel: FlatLabel(defaultFlatLabel) { textFg: windowSubTextFg; } -profileBlockTextPart: flatLabel(labelDefFlat) { +profileBlockTextPart: FlatLabel(defaultFlatLabel) { width: 180px; margin: margins(5px, 5px, 5px, 5px); } -profileBlockOneLineTextPart: flatLabel(profileBlockTextPart) { +profileBlockOneLineTextPart: FlatLabel(profileBlockTextPart) { width: 0px; // No need to set minWidth in one-line text. maxHeight: 20px; } @@ -111,7 +111,7 @@ profileBlockOneLineWidthMax: 240px; profileEnableNotificationsTop: 7px; profileSettingsBlockSkip: 8px; -profileInviteLinkText: flatLabel(profileBlockTextPart) { +profileInviteLinkText: FlatLabel(profileBlockTextPart) { width: 1px; // Required for BreakEverywhere } @@ -122,17 +122,17 @@ profileMemberPaddingLeft: 16px; profileMemberPhotoSize: 46px; profileMemberPhotoPosition: point(12px, 6px); profileMemberNamePosition: point(68px, 11px); -profileMemberNameFg: #222222;//windowTextFg; +profileMemberNameFg: #222222; profileMemberStatusPosition: point(68px, 31px); profileMemberStatusFg: windowSubTextFg; profileMemberStatusFgOver: windowSubTextFgOver; profileMemberStatusFgActive: windowActiveTextFg; profileMemberAdminIcon: icon {{ "profile_admin_star", #3babe7, point(4px, 3px) }}; -profileLimitReachedLabel: flatLabel(labelDefFlat) { +profileLimitReachedLabel: FlatLabel(defaultFlatLabel) { width: 180px; margin: margins(profileMemberPaddingLeft, 9px, profileMemberPaddingLeft, 6px); } -profileLimitReachedStyle: textStyle(defaultTextStyle) { +profileLimitReachedStyle: TextStyle(defaultTextStyle) { lineHeight: 19px; } diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index 122ed6f26..df1b9d1dd 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_userpic_button.h" #include "ui/widgets/buttons.h" #include "ui/filedialog.h" -#include "ui/flatlabel.h" +#include "ui/widgets/labels.h" #include "observer_peer.h" #include "boxes/confirmbox.h" #include "boxes/contactsbox.h" diff --git a/Telegram/SourceFiles/profile/profile_cover.h b/Telegram/SourceFiles/profile/profile_cover.h index 7d96fdd32..db65d2e0c 100644 --- a/Telegram/SourceFiles/profile/profile_cover.h +++ b/Telegram/SourceFiles/profile/profile_cover.h @@ -23,13 +23,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/observer.h" #include "ui/filedialog.h" -class FlatLabel; - namespace style { struct RoundButton; } // namespace style namespace Ui { +class FlatLabel; class RoundButton; class LinkButton; } // namespace Ui @@ -121,7 +120,7 @@ private: ChildWidget _userpicButton; ChildWidget _dropArea = { nullptr }; - ChildWidget _name; + ChildWidget _name; ChildWidget _cancelPhotoUpload = { nullptr }; QPoint _statusPosition; diff --git a/Telegram/SourceFiles/profile/profile_info_widget.cpp b/Telegram/SourceFiles/profile/profile_info_widget.cpp index e1d2b7247..a98e39475 100644 --- a/Telegram/SourceFiles/profile/profile_info_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_info_widget.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_info_widget.h" #include "styles/style_profile.h" -#include "ui/flatlabel.h" +#include "ui/widgets/labels.h" #include "core/click_handler_types.h" #include "observer_peer.h" #include "lang.h" @@ -80,7 +80,7 @@ int InfoWidget::resizeGetHeight(int newWidth) { newHeight += _about->height(); } - auto moveLabeledText = [&newHeight, left, newWidth, marginLeft, marginRight](FlatLabel *label, FlatLabel *text, FlatLabel *shortText) { + auto moveLabeledText = [&newHeight, left, newWidth, marginLeft, marginRight](Ui::FlatLabel *label, Ui::FlatLabel *text, Ui::FlatLabel *shortText) { if (!label) return; label->moveToLeft(left, newHeight); @@ -144,7 +144,7 @@ void InfoWidget::refreshAbout() { _about.destroy(); auto aboutText = textClean(getAboutText()); if (!aboutText.isEmpty()) { - _about = new FlatLabel(this, st::profileBlockTextPart); + _about.create(this, st::profileBlockTextPart); _about->show(); EntitiesInText aboutEntities; @@ -198,17 +198,17 @@ void InfoWidget::refreshChannelLink() { } } -void InfoWidget::setLabeledText(ChildWidget *labelWidget, const QString &label, - ChildWidget *textWidget, const TextWithEntities &textWithEntities, const QString ©Text) { +void InfoWidget::setLabeledText(ChildWidget *labelWidget, const QString &label, + ChildWidget *textWidget, const TextWithEntities &textWithEntities, const QString ©Text) { if (labelWidget) labelWidget->destroy(); textWidget->destroy(); if (textWithEntities.text.isEmpty()) return; if (labelWidget) { - *labelWidget = new FlatLabel(this, label, FlatLabel::InitType::Simple, st::profileBlockLabel); + labelWidget->create(this, label, Ui::FlatLabel::InitType::Simple, st::profileBlockLabel); (*labelWidget)->show(); } - *textWidget = new FlatLabel(this, QString(), FlatLabel::InitType::Simple, st::profileBlockOneLineTextPart); + textWidget->create(this, QString(), Ui::FlatLabel::InitType::Simple, st::profileBlockOneLineTextPart); (*textWidget)->show(); (*textWidget)->setMarkedText(textWithEntities); (*textWidget)->setContextCopyText(copyText); diff --git a/Telegram/SourceFiles/profile/profile_info_widget.h b/Telegram/SourceFiles/profile/profile_info_widget.h index 37618ab77..1abeffc95 100644 --- a/Telegram/SourceFiles/profile/profile_info_widget.h +++ b/Telegram/SourceFiles/profile/profile_info_widget.h @@ -22,7 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_block_widget.h" +namespace Ui { class FlatLabel; +} // namespace Ui namespace Notify { struct PeerUpdate; @@ -52,17 +54,17 @@ private: void refreshVisibility(); // labelWidget may be nullptr. - void setLabeledText(ChildWidget *labelWidget, const QString &label, - ChildWidget *textWidget, const TextWithEntities &textWithEntities, const QString ©Text); + void setLabeledText(ChildWidget *labelWidget, const QString &label, + ChildWidget *textWidget, const TextWithEntities &textWithEntities, const QString ©Text); - ChildWidget _about = { nullptr }; - ChildWidget _channelLinkLabel = { nullptr }; - ChildWidget _channelLink = { nullptr }; - ChildWidget _channelLinkShort = { nullptr }; - ChildWidget _mobileNumberLabel = { nullptr }; - ChildWidget _mobileNumber = { nullptr }; - ChildWidget _usernameLabel = { nullptr }; - ChildWidget _username = { nullptr }; + ChildWidget _about = { nullptr }; + ChildWidget _channelLinkLabel = { nullptr }; + ChildWidget _channelLink = { nullptr }; + ChildWidget _channelLinkShort = { nullptr }; + ChildWidget _mobileNumberLabel = { nullptr }; + ChildWidget _mobileNumber = { nullptr }; + ChildWidget _usernameLabel = { nullptr }; + ChildWidget _username = { nullptr }; }; diff --git a/Telegram/SourceFiles/profile/profile_invite_link_widget.cpp b/Telegram/SourceFiles/profile/profile_invite_link_widget.cpp index c33bad2e1..e2de75f15 100644 --- a/Telegram/SourceFiles/profile/profile_invite_link_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_invite_link_widget.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_invite_link_widget.h" #include "styles/style_profile.h" -#include "ui/flatlabel.h" +#include "ui/widgets/labels.h" #include "boxes/confirmbox.h" #include "observer_peer.h" #include "lang.h" @@ -94,7 +94,7 @@ void InviteLinkWidget::refreshLink() { if (linkData.text.isEmpty()) { _link.destroy(); } else { - _link = new FlatLabel(this, QString(), FlatLabel::InitType::Simple, st::profileInviteLinkText); + _link.create(this, QString(), Ui::FlatLabel::InitType::Simple, st::profileInviteLinkText); _link->show(); linkData.entities.push_back(EntityInText(EntityInTextUrl, 0, linkData.text.size())); diff --git a/Telegram/SourceFiles/profile/profile_invite_link_widget.h b/Telegram/SourceFiles/profile/profile_invite_link_widget.h index e01c043ff..ed9942be7 100644 --- a/Telegram/SourceFiles/profile/profile_invite_link_widget.h +++ b/Telegram/SourceFiles/profile/profile_invite_link_widget.h @@ -22,7 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_block_widget.h" +namespace Ui { class FlatLabel; +} // namespace Ui namespace Notify { struct PeerUpdate; @@ -46,7 +48,7 @@ private: void refreshLink(); void refreshVisibility(); - ChildWidget _link = { nullptr }; + ChildWidget _link = { nullptr }; }; diff --git a/Telegram/SourceFiles/profile/profile_members_widget.cpp b/Telegram/SourceFiles/profile/profile_members_widget.cpp index 2f9b1a6c8..75e99a68f 100644 --- a/Telegram/SourceFiles/profile/profile_members_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_members_widget.cpp @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_profile.h" #include "mtproto/file_download.h" #include "ui/buttons/left_outline_button.h" -#include "ui/flatlabel.h" +#include "ui/widgets/labels.h" #include "boxes/contactsbox.h" #include "boxes/confirmbox.h" #include "core/click_handler_types.h" @@ -307,7 +307,7 @@ void MembersWidget::refreshLimitReached() { bool limitReachedShown = (_list.size() >= Global::ChatSizeMax()) && chat->amCreator() && !emptyTitle(); if (limitReachedShown && !_limitReachedInfo) { - _limitReachedInfo = new FlatLabel(this, st::profileLimitReachedLabel, st::profileLimitReachedStyle); + _limitReachedInfo.create(this, st::profileLimitReachedLabel, st::profileLimitReachedStyle); QString title = textRichPrepare(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax())); QString body = textRichPrepare(lang(lng_profile_migrate_body)); QString link = textRichPrepare(lang(lng_profile_migrate_learn_more)); diff --git a/Telegram/SourceFiles/profile/profile_members_widget.h b/Telegram/SourceFiles/profile/profile_members_widget.h index 6e7cb28b3..7e8ba24eb 100644 --- a/Telegram/SourceFiles/profile/profile_members_widget.h +++ b/Telegram/SourceFiles/profile/profile_members_widget.h @@ -22,9 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_block_widget.h" -class FlatLabel; - namespace Ui { +class FlatLabel; class LeftOutlineButton; } // namespace Ui @@ -123,7 +122,7 @@ private: Member *addUser(ChannelData *megagroup, UserData *user); bool addUsersToEnd(ChannelData *megagroup); - ChildWidget _limitReachedInfo = { nullptr }; + ChildWidget _limitReachedInfo = { nullptr }; QList _list; QMap _membersByUser; diff --git a/Telegram/SourceFiles/profile/profile_userpic_button.cpp b/Telegram/SourceFiles/profile/profile_userpic_button.cpp index 267c35b0b..3d346493a 100644 --- a/Telegram/SourceFiles/profile/profile_userpic_button.cpp +++ b/Telegram/SourceFiles/profile/profile_userpic_button.cpp @@ -27,8 +27,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Profile { -UserpicButton::UserpicButton(QWidget *parent, PeerData *peer) : AbstractButton(parent), _peer(peer) { - resize(st::profilePhotoSize, st::profilePhotoSize); +UserpicButton::UserpicButton(QWidget *parent, PeerData *peer, int size) : AbstractButton(parent) +, _size(size ? size : st::profilePhotoSize) +, _peer(peer) { + resize(_size, _size); processPeerPhoto(); _notShownYet = _waiting; @@ -107,13 +109,13 @@ void UserpicButton::startNewPhotoShowing() { QPixmap UserpicButton::prepareUserpicPixmap() const { auto retina = cIntRetinaFactor(); - auto size = st::profilePhotoSize * retina; + auto size = width() * retina; QImage image(size, size, QImage::Format_ARGB32_Premultiplied); image.setDevicePixelRatio(cRetinaFactor()); + image.fill(Qt::transparent); { Painter p(&image); - p.fillRect(0, 0, st::profilePhotoSize, st::profilePhotoSize, st::profileBg); - _peer->paintUserpic(p, st::profilePhotoSize, 0, 0); + _peer->paintUserpic(p, width(), 0, 0); } return App::pixmapFromImageInPlace(std_::move(image)); } diff --git a/Telegram/SourceFiles/profile/profile_userpic_button.h b/Telegram/SourceFiles/profile/profile_userpic_button.h index ca2d7076a..f5bc9a00f 100644 --- a/Telegram/SourceFiles/profile/profile_userpic_button.h +++ b/Telegram/SourceFiles/profile/profile_userpic_button.h @@ -31,7 +31,7 @@ namespace Profile { class UserpicButton : public Ui::AbstractButton, private base::Subscriber { public: - UserpicButton(QWidget *parent, PeerData *peer); + UserpicButton(QWidget *parent, PeerData *peer, int size = 0); // If at the first moment the _userpic was not loaded, // we need to show it animated after the profile is fully shown. @@ -50,6 +50,7 @@ private: bool _notShownYet; + int _size = 0; PeerData *_peer; bool _waiting = false; QPixmap _userpic, _oldUserpic; diff --git a/Telegram/SourceFiles/profile/profile_widget.cpp b/Telegram/SourceFiles/profile/profile_widget.cpp index 9a54a2a3e..a339ca3ac 100644 --- a/Telegram/SourceFiles/profile/profile_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_widget.cpp @@ -21,16 +21,18 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "profile/profile_widget.h" +#include "styles/style_settings.h" #include "profile/profile_fixed_bar.h" #include "profile/profile_inner_widget.h" #include "profile/profile_section_memento.h" #include "mainwindow.h" #include "application.h" +#include "ui/widgets/scroll_area.h" namespace Profile { Widget::Widget(QWidget *parent, PeerData *peer) : Window::SectionWidget(parent) -, _scroll(this, st::setScroll) +, _scroll(this, st::settingsScroll) , _inner(this, peer) , _fixedBar(this, peer) , _fixedBarShadow(this, st::shadowColor) { diff --git a/Telegram/SourceFiles/profile/profile_widget.h b/Telegram/SourceFiles/profile/profile_widget.h index fa48eca67..c446de375 100644 --- a/Telegram/SourceFiles/profile/profile_widget.h +++ b/Telegram/SourceFiles/profile/profile_widget.h @@ -23,7 +23,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/section_widget.h" #include "ui/widgets/shadow.h" +namespace Ui { class ScrollArea; +} // namespace Ui namespace Profile { @@ -68,7 +70,7 @@ private: friend class SectionMemento; - ChildWidget _scroll; + ChildWidget _scroll; ChildWidget _inner; ChildWidget _fixedBar; ChildWidget _fixedBarShadow; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 071e2bb8e..2ff5d25f5 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -24,6 +24,11 @@ using "dialogs/dialogs.style"; using "ui/widgets/widgets.style"; using "boxes/boxes.style"; +settingsScroll: FlatScroll(defaultFlatScroll) { + bottomsh: 0px; + topsh: 0px; +} + settingsMaxWidth: 520px; settingsMaxPadding: 48px; settingsMinPadding: 32px; @@ -44,14 +49,14 @@ settingsPhotoLeft: -8px; settingsPhotoDuration: 500; settingsNameLeft: 26px; settingsNameTop: 9px; -settingsNameLabel: flatLabel(labelDefFlat) { +settingsNameLabel: FlatLabel(defaultFlatLabel) { margin: margins(10px, 5px, 10px, 5px); font: font(16px semibold); width: 160px; maxHeight: 24px; textFg: #333333; } -settingsNameTextStyle: textStyle(defaultTextStyle) { +settingsNameTextStyle: TextStyle(defaultTextStyle) { } settingsStatusLeft: 27px; settingsStatusTop: 35px; @@ -83,13 +88,13 @@ settingsBlockTitleHeight: 31px; settingsBlockTitleFont: font(15px semibold); settingsBlockTitleFg: #333333; settingsBlockTitleTop: 0px; -settingsPrimaryLabel: flatLabel(labelDefFlat) { +settingsPrimaryLabel: FlatLabel(defaultFlatLabel) { font: boxTextFont; } -settingsBlockLabel: flatLabel(settingsPrimaryLabel) { +settingsBlockLabel: FlatLabel(settingsPrimaryLabel) { textFg: windowSubTextFg; } -settingsBlockOneLineTextPart: flatLabel(settingsPrimaryLabel) { +settingsBlockOneLineTextPart: FlatLabel(settingsPrimaryLabel) { width: 0px; // No need to set minWidth in one-line text. margin: margins(5px, 5px, 5px, 5px); maxHeight: 20px; diff --git a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp index 6e1109246..d1546d719 100644 --- a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp @@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/effects/widget_slide_wrap.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" -#include "ui/flatlabel.h" +#include "ui/widgets/labels.h" #include "localstorage.h" #include "mainwidget.h" #include "mainwindow.h" @@ -39,7 +39,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Settings { LabeledLink::LabeledLink(QWidget *parent, const QString &label, const QString &text, Type type, const char *slot) : TWidget(parent) -, _label(this, label, FlatLabel::InitType::Simple, (type == Type::Primary) ? st::settingsPrimaryLabel : st::labelDefFlat) +, _label(this, label, Ui::FlatLabel::InitType::Simple, (type == Type::Primary) ? st::settingsPrimaryLabel : st::defaultFlatLabel) , _link(this, text, (type == Type::Primary) ? st::boxLinkButton : st::defaultLinkButton) { connect(_link, SIGNAL(clicked()), parent, slot); } @@ -97,7 +97,7 @@ void DownloadPathState::paintEvent(QPaintEvent *e) { })(); if (!text.isEmpty()) { p.setFont(st::linkFont); - p.setPen(st::windowTextFg); + p.setPen(st::windowFg); p.drawTextRight(0, 0, width(), text); } } diff --git a/Telegram/SourceFiles/settings/settings_chat_settings_widget.h b/Telegram/SourceFiles/settings/settings_chat_settings_widget.h index 71d997226..a104ac1bf 100644 --- a/Telegram/SourceFiles/settings/settings_chat_settings_widget.h +++ b/Telegram/SourceFiles/settings/settings_chat_settings_widget.h @@ -22,7 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" +namespace Ui { class FlatLabel; +} // namespace Ui namespace Settings { @@ -46,7 +48,7 @@ protected: int resizeGetHeight(int newWidth) override; private: - ChildWidget _label; + ChildWidget _label; ChildWidget _link; }; diff --git a/Telegram/SourceFiles/settings/settings_cover.cpp b/Telegram/SourceFiles/settings/settings_cover.cpp index 05390244a..e82d5f50e 100644 --- a/Telegram/SourceFiles/settings/settings_cover.cpp +++ b/Telegram/SourceFiles/settings/settings_cover.cpp @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "settings/settings_cover.h" -#include "ui/flatlabel.h" +#include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "observer_peer.h" #include "lang.h" diff --git a/Telegram/SourceFiles/settings/settings_cover.h b/Telegram/SourceFiles/settings/settings_cover.h index c086fb201..b799d7c0f 100644 --- a/Telegram/SourceFiles/settings/settings_cover.h +++ b/Telegram/SourceFiles/settings/settings_cover.h @@ -25,8 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" -class FlatLabel; namespace Ui { +class FlatLabel; class RoundButton; class IconButton; } // namespace Ui @@ -91,7 +91,7 @@ private: ChildWidget _userpicButton; ChildWidget _dropArea = { nullptr }; - ChildWidget _name; + ChildWidget _name; ChildWidget _editNameInline; ChildWidget _cancelPhotoUpload = { nullptr }; diff --git a/Telegram/SourceFiles/settings/settings_fixed_bar.cpp b/Telegram/SourceFiles/settings/settings_fixed_bar.cpp index 079c91343..a9aa941d3 100644 --- a/Telegram/SourceFiles/settings/settings_fixed_bar.cpp +++ b/Telegram/SourceFiles/settings/settings_fixed_bar.cpp @@ -51,7 +51,7 @@ void FixedBar::paintEvent(QPaintEvent *e) { p.fillRect(e->rect(), st::boxBlockTitleBg); p.setFont(st::settingsFixedBarFont); - p.setPen(st::windowTextFg); + p.setPen(st::windowFg); p.drawTextLeft(st::settingsFixedBarTextLeft, st::settingsFixedBarTextTop, width(), lang(lng_menu_settings)); } diff --git a/Telegram/SourceFiles/settings/settings_general_widget.cpp b/Telegram/SourceFiles/settings/settings_general_widget.cpp index 3225a158d..abc6ff3ce 100644 --- a/Telegram/SourceFiles/settings/settings_general_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_general_widget.cpp @@ -32,25 +32,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "boxes/languagebox.h" #include "boxes/confirmbox.h" +#include "boxes/aboutbox.h" #include "ui/filedialog.h" #include "langloaderplain.h" #include "autoupdater.h" namespace Settings { -namespace { - -QString currentVersion() { - auto result = QString::fromLatin1(AppVersionStr.c_str()); - if (cAlphaVersion()) { - result += " alpha"; - } - if (cBetaVersion()) { - result += qsl(" beta %1").arg(cBetaVersion() % 1000); - } - return result; -} - -} // namespace #ifndef TDESKTOP_DISABLE_AUTOUPDATE UpdateStateRow::UpdateStateRow(QWidget *parent) : TWidget(parent) @@ -189,7 +176,7 @@ void GeneralWidget::refreshControls() { style::margins slidedPadding(0, marginSmall.bottom() / 2, 0, marginSmall.bottom() - (marginSmall.bottom() / 2)); #ifndef TDESKTOP_DISABLE_AUTOUPDATE - addChildRow(_updateAutomatically, marginSub, lng_settings_update_automatically(lt_version, currentVersion()), SLOT(onUpdateAutomatically()), cAutoUpdate()); + addChildRow(_updateAutomatically, marginSub, lng_settings_update_automatically(lt_version, currentVersionText()), SLOT(onUpdateAutomatically()), cAutoUpdate()); style::margins marginLink(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip); addChildRow(_updateRow, marginLink, slidedPadding); connect(_updateRow->entity(), SIGNAL(restart()), this, SLOT(onRestart())); diff --git a/Telegram/SourceFiles/settings/settings_info_widget.cpp b/Telegram/SourceFiles/settings/settings_info_widget.cpp index 7deab874b..31c9a451a 100644 --- a/Telegram/SourceFiles/settings/settings_info_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_info_widget.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_settings.h" #include "lang.h" -#include "ui/flatlabel.h" +#include "ui/widgets/labels.h" #include "ui/effects/widget_slide_wrap.h" #include "boxes/usernamebox.h" #include "observer_peer.h" @@ -133,18 +133,18 @@ void InfoWidget::LabeledWidget::setLabeledText(const QString &label, const TextW _shortText.destroy(); if (textWithEntities.text.isEmpty()) return; - _label = new FlatLabel(this, label, FlatLabel::InitType::Simple, st::settingsBlockLabel); + _label.create(this, label, Ui::FlatLabel::InitType::Simple, st::settingsBlockLabel); _label->show(); setLabelText(_text, textWithEntities, copyText); setLabelText(_shortText, shortTextWithEntities, copyText); resizeToWidth(width()); } -void InfoWidget::LabeledWidget::setLabelText(ChildWidget &text, const TextWithEntities &textWithEntities, const QString ©Text) { +void InfoWidget::LabeledWidget::setLabelText(ChildWidget &text, const TextWithEntities &textWithEntities, const QString ©Text) { text.destroy(); if (textWithEntities.text.isEmpty()) return; - text = new FlatLabel(this, QString(), FlatLabel::InitType::Simple, st::settingsBlockOneLineTextPart); + text.create(this, QString(), Ui::FlatLabel::InitType::Simple, st::settingsBlockOneLineTextPart); text->show(); text->setMarkedText(textWithEntities); text->setContextCopyText(copyText); diff --git a/Telegram/SourceFiles/settings/settings_info_widget.h b/Telegram/SourceFiles/settings/settings_info_widget.h index 52a6f2b05..666350e9f 100644 --- a/Telegram/SourceFiles/settings/settings_info_widget.h +++ b/Telegram/SourceFiles/settings/settings_info_widget.h @@ -22,7 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" +namespace Ui { class FlatLabel; +} // namespace Ui namespace Notify { struct PeerUpdate; @@ -50,10 +52,10 @@ private: void setLabeledText(const QString &label, const TextWithEntities &textWithEntities, const TextWithEntities &shortTextWithEntities, const QString ©Text); - FlatLabel *textLabel() { + Ui::FlatLabel *textLabel() { return _text; } - FlatLabel *shortTextLabel() { + Ui::FlatLabel *shortTextLabel() { return _shortText; } @@ -63,11 +65,11 @@ private: int resizeGetHeight(int newWidth) override; private: - void setLabelText(ChildWidget &text, const TextWithEntities &textWithEntities, const QString ©Text); + void setLabelText(ChildWidget &text, const TextWithEntities &textWithEntities, const QString ©Text); - ChildWidget _label = { nullptr }; - ChildWidget _text = { nullptr }; - ChildWidget _shortText = { nullptr }; + ChildWidget _label = { nullptr }; + ChildWidget _text = { nullptr }; + ChildWidget _shortText = { nullptr }; }; diff --git a/Telegram/SourceFiles/settings/settings_privacy_widget.cpp b/Telegram/SourceFiles/settings/settings_privacy_widget.cpp index 20cf8ec35..673c73e72 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_widget.cpp @@ -146,7 +146,7 @@ void CloudPasswordState::paintEvent(QPaintEvent *e) { auto text = st::linkFont->elided(_waitingConfirm, width() - _turnOff->width()); if (!text.isEmpty()) { - p.setPen(st::windowTextFg); + p.setPen(st::windowFg); p.setFont(st::boxTextFont); p.drawTextLeft(0, 0, width(), text); } diff --git a/Telegram/SourceFiles/settings/settings_widget.cpp b/Telegram/SourceFiles/settings/settings_widget.cpp index 0c377d720..7a30ef9ef 100644 --- a/Telegram/SourceFiles/settings/settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_widget.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_window.h" #include "styles/style_boxes.h" #include "ui/widgets/shadow.h" -#include "ui/scrollarea.h" +#include "ui/widgets/scroll_area.h" #include "mainwindow.h" #include "mainwidget.h" #include "localstorage.h" @@ -125,7 +125,7 @@ void codesFeedString(const QString &text) { } // namespace Widget::Widget(QWidget *parent) : LayerWidget(parent) -, _scroll(this, st::setScroll) +, _scroll(this, st::settingsScroll) , _inner(this) , _fixedBar(this) , _fixedBarShadow(this, st::boxBlockTitleShadow) { diff --git a/Telegram/SourceFiles/settings/settings_widget.h b/Telegram/SourceFiles/settings/settings_widget.h index b32afaea4..0ec4dcc37 100644 --- a/Telegram/SourceFiles/settings/settings_widget.h +++ b/Telegram/SourceFiles/settings/settings_widget.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "layerwidget.h" namespace Ui { +class ScrollArea; class GradientShadow; } // namespace Ui @@ -51,7 +52,7 @@ private slots: private: void resizeUsingInnerHeight(int newWidth, int newContentLeft); - ChildWidget _scroll; + ChildWidget _scroll; ChildWidget _inner; ChildWidget _fixedBar; ChildWidget _fixedBarShadow; diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index 83ad17611..581b0d55c 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -70,7 +70,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/animation.h" #include "ui/twidget.h" -#include "ui/scrollarea.h" #include "ui/images.h" #include "ui/text/text.h" diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index faa8bf959..c13568a21 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -292,7 +292,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { 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() +EmojiPanInner::EmojiPanInner(QWidget *parent) : TWidget(parent) , _maxHeight(int(st::emojiPanMaxHeight) - st::emojiCategory.height) , _a_selected(animation(this, &EmojiPanInner::step_selected)) { resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight()); @@ -793,7 +793,7 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { update(); } -StickerPanInner::StickerPanInner() : TWidget() +StickerPanInner::StickerPanInner(QWidget *parent) : TWidget(parent) , _a_selected(animation(this, &StickerPanInner::step_selected)) , _section(cShowingSavedGifs() ? Section::Gifs : Section::Stickers) , _addText(lang(lng_stickers_featured_add).toUpper()) @@ -2813,26 +2813,26 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) , _symbols(this, st::emojiCategorySymbols) , _a_icons(animation(this, &EmojiPan::step_icons)) , e_scroll(this, st::emojiScroll) -, e_inner() -, e_switch(&e_scroll, true) +, e_inner(this) +, e_switch(e_scroll, true) , s_scroll(this, st::emojiScroll) -, s_inner() -, s_switch(&s_scroll, false) { +, s_inner(this) +, s_switch(s_scroll, false) { resize(QRect(0, 0, st::emojiPanWidth, _contentHeight).marginsAdded(innerPadding()).size()); _width = width(); _height = height(); _bottom = 0; - e_scroll.resize(st::emojiPanWidth - st::buttonRadius, _contentHeightEmoji); - s_scroll.resize(st::emojiPanWidth - st::buttonRadius, _contentHeightStickers); + e_scroll->resize(st::emojiPanWidth - st::buttonRadius, _contentHeightEmoji); + s_scroll->resize(st::emojiPanWidth - st::buttonRadius, _contentHeightStickers); - e_scroll.move(verticalRect().topLeft()); - e_scroll.setWidget(&e_inner); - s_scroll.move(verticalRect().topLeft()); - s_scroll.setWidget(&s_inner); + e_scroll->move(verticalRect().topLeft()); + e_scroll->setOwnedWidget(e_inner); + s_scroll->move(verticalRect().topLeft()); + s_scroll->setOwnedWidget(s_inner); - e_inner.moveToLeft(0, 0, e_scroll.width()); - s_inner.moveToLeft(0, 0, s_scroll.width()); + e_inner->moveToLeft(0, 0, e_scroll->width()); + s_inner->moveToLeft(0, 0, s_scroll->width()); int32 left = _iconsLeft = innerRect().x() + (st::emojiPanWidth - 8 * st::emojiCategory.width) / 2; int32 top = _iconsTop = innerRect().y() + innerRect().height() - st::emojiCategory.height; @@ -2844,7 +2844,7 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) prepareTab(left, top, _width, _travel, dbietTravel); prepareTab(left, top, _width, _objects, dbietObjects); prepareTab(left, top, _width, _symbols, dbietSymbols); - e_inner.fillPanels(e_panels); + e_inner->fillPanels(e_panels); updatePanelsPositions(e_panels, 0); setCurrentTabIcon(dbietRecent); @@ -2852,38 +2852,38 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) _hideTimer.setSingleShot(true); connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideByTimerOrLeave())); - connect(&e_inner, SIGNAL(scrollToY(int)), &e_scroll, SLOT(scrollToY(int))); - connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool))); + 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(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_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(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_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(st::buttonRadius, 0, st::emojiPanWidth); - e_switch.moveToRight(st::buttonRadius, 0, st::emojiPanWidth); + connect(s_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); + connect(e_switch, SIGNAL(clicked()), this, SLOT(onSwitch())); + s_switch->moveToRight(st::buttonRadius, 0, st::emojiPanWidth); + e_switch->moveToRight(st::buttonRadius, 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(bool)), this, SLOT(onRefreshIcons(bool))); - connect(&e_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); - connect(&s_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); + 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(bool)), this, SLOT(onRefreshIcons(bool))); + 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))); + 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); @@ -2905,7 +2905,7 @@ void EmojiPan::setMaxHeight(int32 h) { void EmojiPan::updateContentHeight() { int32 h = qMin(_contentMaxHeight, _maxHeight); int32 he = h - st::emojiCategory.height; - int32 hs = h - (s_inner.showSectionIcons() ? st::emojiCategory.height : 0); + int32 hs = h - (s_inner->showSectionIcons() ? st::emojiCategory.height : 0); if (h == _contentHeight && he == _contentHeightEmoji && hs == _contentHeightStickers) return; int32 was = _contentHeight, wase = _contentHeightEmoji, wass = _contentHeightStickers; @@ -2918,15 +2918,15 @@ void EmojiPan::updateContentHeight() { move(x(), _bottom - _height); if (was > _contentHeight || (was == _contentHeight && wass > _contentHeightStickers)) { - e_scroll.resize(e_scroll.width(), _contentHeightEmoji); - s_scroll.resize(s_scroll.width(), _contentHeightStickers); - s_inner.setMaxHeight(_contentHeightStickers); - e_inner.setMaxHeight(_contentHeightEmoji); + e_scroll->resize(e_scroll->width(), _contentHeightEmoji); + s_scroll->resize(s_scroll->width(), _contentHeightStickers); + s_inner->setMaxHeight(_contentHeightStickers); + e_inner->setMaxHeight(_contentHeightEmoji); } else { - s_inner.setMaxHeight(_contentHeightStickers); - e_inner.setMaxHeight(_contentHeightEmoji); - e_scroll.resize(e_scroll.width(), _contentHeightEmoji); - s_scroll.resize(s_scroll.width(), _contentHeightStickers); + s_inner->setMaxHeight(_contentHeightStickers); + e_inner->setMaxHeight(_contentHeightEmoji); + e_scroll->resize(e_scroll->width(), _contentHeightEmoji); + s_scroll->resize(s_scroll->width(), _contentHeightStickers); } _iconsTop = innerRect().y() + innerRect().height() - st::emojiCategory.height; @@ -3016,14 +3016,14 @@ void EmojiPan::paintContent(Painter &p) { auto inner = innerRect(); App::roundRect(p, inner, st::emojiPanBg, ImageRoundRadius::Small, App::RectPart::TopFull); - auto showSectionIcons = _emojiShown || s_inner.showSectionIcons(); + auto showSectionIcons = _emojiShown || s_inner->showSectionIcons(); auto &bottomBg = showSectionIcons ? st::emojiPanCategories : st::emojiPanBg; auto bottomParts = showSectionIcons ? (App::RectPart::NoTopBottom | App::RectPart::BottomFull) : App::RectPart::BottomFull; App::roundRect(p, inner.x(), _iconsTop - st::buttonRadius, inner.width(), st::emojiCategory.height + st::buttonRadius, bottomBg, ImageRoundRadius::Small, bottomParts); auto horizontal = horizontalRect(); auto sidesTop = horizontal.y(); - auto sidesHeight = e_scroll.y() + e_scroll.height() - sidesTop; + auto sidesHeight = e_scroll->y() + e_scroll->height() - sidesTop; p.fillRect(myrtlrect(inner.x() + inner.width() - st::emojiScroll.width, sidesTop, st::emojiScroll.width, sidesHeight), st::emojiPanBg); p.fillRect(myrtlrect(inner.x(), sidesTop, st::buttonRadius, sidesHeight), st::emojiPanBg); if (_emojiShown) { @@ -3099,7 +3099,7 @@ void EmojiPan::moveBottom(int32 bottom, bool force) { move(x(), _bottom - height()); return; } - if (!_emojiShown && s_inner.inlineResultsShown()) { + if (!_emojiShown && s_inner->inlineResultsShown()) { setOrigin(Ui::PanelAnimation::Origin::BottomLeft); moveToLeft(0, _bottom - height()); } else { @@ -3118,7 +3118,7 @@ bool EmojiPan::preventAutoHide() const { } void EmojiPan::leaveEvent(QEvent *e) { - if (preventAutoHide() || s_inner.inlineResultsShown()) return; + if (preventAutoHide() || s_inner->inlineResultsShown()) return; auto ms = getms(); if (_a_show.animating(ms) || _a_opacity.animating(ms)) { hideAnimated(); @@ -3134,7 +3134,7 @@ void EmojiPan::otherEnter() { } void EmojiPan::otherLeave() { - if (preventAutoHide() || s_inner.inlineResultsShown()) return; + if (preventAutoHide() || s_inner->inlineResultsShown()) return; auto ms = getms(); if (_a_opacity.animating(ms)) { hideByTimerOrLeave(); @@ -3200,7 +3200,7 @@ void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { if (wasDown == _iconOver && _iconOver >= 0 && _iconOver < _icons.size()) { _iconSelX = anim::ivalue(_iconOver * st::emojiCategory.width, _iconOver * st::emojiCategory.width); - s_inner.showStickerSet(_icons.at(_iconOver).setId); + s_inner->showStickerSet(_icons.at(_iconOver).setId); } } } @@ -3242,19 +3242,19 @@ void EmojiPan::hideFast() { } void EmojiPan::refreshStickers() { - s_inner.refreshStickers(); + s_inner->refreshStickers(); if (_emojiShown) { - s_inner.preloadImages(); + s_inner->preloadImages(); } update(); } void EmojiPan::refreshSavedGifs() { - e_switch.updateText(); - e_switch.moveToRight(st::buttonRadius, 0, st::emojiPanWidth); - s_inner.refreshSavedGifs(); + e_switch->updateText(); + e_switch->moveToRight(st::buttonRadius, 0, st::emojiPanWidth); + s_inner->refreshSavedGifs(); if (_emojiShown) { - s_inner.preloadImages(); + s_inner->preloadImages(); } } @@ -3262,8 +3262,8 @@ void EmojiPan::onRefreshIcons(bool scrollAnimation) { _iconOver = -1; _iconHovers.clear(); _iconAnimations.clear(); - s_inner.fillIcons(_icons); - s_inner.fillPanels(s_panels); + s_inner->fillIcons(_icons); + s_inner->fillPanels(s_panels); _iconsX.finish(); _iconSelX.finish(); _iconsStartAnim = 0; @@ -3277,7 +3277,7 @@ void EmojiPan::onRefreshIcons(bool scrollAnimation) { if (_iconsX.current() > _iconsMax) { _iconsX = anim::ivalue(_iconsMax, _iconsMax); } - updatePanelsPositions(s_panels, s_scroll.scrollTop()); + updatePanelsPositions(s_panels, s_scroll->scrollTop()); updateSelected(); if (!_emojiShown) { validateSelectedIcon(scrollAnimation ? ValidateIconAnimations::Scroll : ValidateIconAnimations::None); @@ -3287,12 +3287,12 @@ void EmojiPan::onRefreshIcons(bool scrollAnimation) { } void EmojiPan::onRefreshPanels() { - e_inner.refreshPanels(e_panels); - s_inner.refreshPanels(s_panels); + e_inner->refreshPanels(e_panels); + s_inner->refreshPanels(s_panels); if (_emojiShown) { - updatePanelsPositions(e_panels, e_scroll.scrollTop()); + updatePanelsPositions(e_panels, e_scroll->scrollTop()); } else { - updatePanelsPositions(s_panels, s_scroll.scrollTop()); + updatePanelsPositions(s_panels, s_scroll->scrollTop()); } } @@ -3346,7 +3346,7 @@ void EmojiPan::updateSelected() { } void EmojiPan::updateIcons() { - if (_emojiShown || !s_inner.showSectionIcons()) return; + if (_emojiShown || !s_inner->showSectionIcons()) return; QRect r(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top(), _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom()); update(r.left(), _iconsTop, r.width(), st::emojiCategory.height); @@ -3405,7 +3405,7 @@ void EmojiPan::opacityAnimationCallback() { } void EmojiPan::hideByTimerOrLeave() { - if (isHidden() || preventAutoHide() || s_inner.inlineResultsShown()) return; + if (isHidden() || preventAutoHide() || s_inner->inlineResultsShown()) return; hideAnimated(); } @@ -3481,8 +3481,8 @@ EmojiPan::~EmojiPan() = default; void EmojiPan::hideFinished() { hide(); - e_inner.hideFinish(); - s_inner.hideFinish(true); + e_inner->hideFinish(); + s_inner->hideFinish(true); _a_show.finish(); _showAnimation.reset(); _a_slide.finish(); @@ -3491,9 +3491,9 @@ void EmojiPan::hideFinished() { _horizontal = false; _hiding = false; - e_scroll.scrollToY(0); + e_scroll->scrollToY(0); setCurrentTabIcon(dbietRecent); - s_scroll.scrollToY(0); + s_scroll->scrollToY(0); _iconOver = _iconDown = -1; _iconSel = 0; _iconsX = anim::ivalue(0, 0); @@ -3518,32 +3518,30 @@ void EmojiPan::showAnimated(Ui::PanelAnimation::Origin origin) { void EmojiPan::showStarted() { if (isHidden()) { - e_inner.refreshRecent(); - if (s_inner.inlineResultsShown() && refreshInlineRows()) { + emit updateStickers(); + e_inner->refreshRecent(); + if (s_inner->inlineResultsShown() && refreshInlineRows()) { _emojiShown = false; _shownFromInlineQuery = true; } else { - s_inner.refreshRecent(); + s_inner->refreshRecent(); _emojiShown = true; _shownFromInlineQuery = false; _cache = QPixmap(); // clear after refreshInlineRows() } recountContentMaxHeight(); - s_inner.preloadImages(); + s_inner->preloadImages(); _a_slide.finish(); _slideAnimation.reset(); moveBottom(y() + height(), true); show(); startShowAnimation(); - return; - } else if (!_hiding) { - return; + } else if (_hiding) { + if (s_inner->inlineResultsShown() && refreshInlineRows()) { + onSwitch(); + } + startOpacityAnimation(false); } - if (s_inner.inlineResultsShown() && refreshInlineRows()) { - onSwitch(); - } - startOpacityAnimation(false); - emit updateStickers(); } bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { @@ -3578,40 +3576,40 @@ void EmojiPan::stickersInstalled(uint64 setId) { show(); } showAll(); - s_inner.showStickerSet(setId); + s_inner->showStickerSet(setId); updateContentHeight(); showAnimated(Ui::PanelAnimation::Origin::BottomRight); } void EmojiPan::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { if (!_emojiShown && !isHidden()) { - s_inner.notify_inlineItemLayoutChanged(layout); + s_inner->notify_inlineItemLayoutChanged(layout); } } void EmojiPan::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) { if (!_emojiShown && !isHidden()) { - s_inner.ui_repaintInlineItem(layout); + s_inner->ui_repaintInlineItem(layout); } } bool EmojiPan::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) { if (!_emojiShown && !isHidden()) { - return s_inner.ui_isInlineItemVisible(layout); + return s_inner->ui_isInlineItemVisible(layout); } return false; } bool EmojiPan::ui_isInlineItemBeingChosen() { if (!_emojiShown && !isHidden()) { - return s_inner.ui_isInlineItemBeingChosen(); + return s_inner->ui_isInlineItemBeingChosen(); } return false; } void EmojiPan::showAll() { if (_emojiShown) { - s_scroll.hide(); + s_scroll->hide(); _recent->show(); _people->show(); _nature->show(); @@ -3620,9 +3618,9 @@ void EmojiPan::showAll() { _travel->show(); _objects->show(); _symbols->show(); - e_scroll.show(); + e_scroll->show(); } else { - s_scroll.show(); + s_scroll->show(); _recent->hide(); _people->hide(); _nature->hide(); @@ -3631,7 +3629,7 @@ void EmojiPan::showAll() { _travel->hide(); _objects->hide(); _symbols->hide(); - e_scroll.hide(); + e_scroll->hide(); } } @@ -3644,14 +3642,14 @@ void EmojiPan::hideAll() { _travel->hide(); _objects->hide(); _symbols->hide(); - e_scroll.hide(); - s_scroll.hide(); - e_inner.clearSelection(true); - s_inner.clearSelection(true); + e_scroll->hide(); + s_scroll->hide(); + e_inner->clearSelection(true); + s_inner->clearSelection(true); } void EmojiPan::setActiveTab(DBIEmojiTab tab) { - e_inner.showEmojiPack(tab); + e_inner->showEmojiPack(tab); } void EmojiPan::updatePanelsPositions(const QVector &panels, int32 st) { @@ -3666,20 +3664,20 @@ void EmojiPan::updatePanelsPositions(const QVector &panel // 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) { + if (s_inner->currentSet(0) == Stickers::FeaturedSetId) { panels.at(i)->repaint(); } } } void EmojiPan::onScrollEmoji() { - auto st = e_scroll.scrollTop(); + auto st = e_scroll->scrollTop(); updatePanelsPositions(e_panels, st); - setCurrentTabIcon(e_inner.currentTab(st)); + setCurrentTabIcon(e_inner->currentTab(st)); - e_inner.setVisibleTopBottom(st, st + e_scroll.height()); + e_inner->setVisibleTopBottom(st, st + e_scroll->height()); } void EmojiPan::setCurrentTabIcon(DBIEmojiTab tab) { @@ -3694,20 +3692,20 @@ void EmojiPan::setCurrentTabIcon(DBIEmojiTab tab) { } void EmojiPan::onScrollStickers() { - auto st = s_scroll.scrollTop(); + auto st = s_scroll->scrollTop(); updatePanelsPositions(s_panels, st); validateSelectedIcon(ValidateIconAnimations::Full); - if (st + s_scroll.height() > s_scroll.scrollTopMax()) { + if (st + s_scroll->height() > s_scroll->scrollTopMax()) { onInlineRequest(); } - s_inner.setVisibleTopBottom(st, st + s_scroll.height()); + s_inner->setVisibleTopBottom(st, st + s_scroll->height()); } void EmojiPan::validateSelectedIcon(ValidateIconAnimations animations) { - uint64 setId = s_inner.currentSet(s_scroll.scrollTop()); + 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) { @@ -3784,9 +3782,9 @@ void EmojiPan::onSwitch() { hideAll(); if (_emojiShown) { - s_inner.hideFinish(false); + s_inner->hideFinish(false); } else { - e_inner.hideFinish(); + e_inner->hideFinish(); } _a_slide.start([this] { update(); }, 0., 1., st::emojiPanSlideDuration, anim::linear); @@ -3799,14 +3797,14 @@ void EmojiPan::performSwitch() { Notify::clipStopperHidden(ClipStopperSavedGifsPanel); } else { if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { - s_inner.showStickerSet(Stickers::DefaultSetId); + s_inner->showStickerSet(Stickers::DefaultSetId); } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && Global::StickerSetsOrder().isEmpty()) { - s_inner.showStickerSet(Stickers::NoneSetId); + s_inner->showStickerSet(Stickers::NoneSetId); } else { - s_inner.updateShowingSavedGifs(); + s_inner->updateShowingSavedGifs(); } if (cShowingSavedGifs()) { - s_inner.showFinish(); + s_inner->showFinish(); } validateSelectedIcon(ValidateIconAnimations::None); updateContentHeight(); @@ -3833,7 +3831,7 @@ void EmojiPan::onInstallSet(quint64 setId) { 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)); - s_inner.installedLocally(setId); + s_inner->installedLocally(setId); Stickers::installLocally(setId); } } @@ -3848,7 +3846,7 @@ bool EmojiPan::installSetFail(uint64 setId, const RPCError &error) { if (MTP::isDefaultHandledError(error)) { return false; } - s_inner.notInstalledLocally(setId); + s_inner->notInstalledLocally(setId); Stickers::undoInstallLocally(setId); return true; } @@ -3908,12 +3906,12 @@ void EmojiPan::onDelayedHide() { void EmojiPan::clearInlineBot() { inlineBotChanged(); - e_switch.updateText(); - e_switch.moveToRight(st::buttonRadius, 0, st::emojiPanWidth); + e_switch->updateText(); + e_switch->moveToRight(st::buttonRadius, 0, st::emojiPanWidth); } bool EmojiPan::hideOnNoInlineResults() { - return _inlineBot && !_emojiShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername()); + return _inlineBot && !_emojiShown && s_inner->inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername()); } void EmojiPan::inlineBotChanged() { @@ -3933,8 +3931,8 @@ void EmojiPan::inlineBotChanged() { delete i.value(); } _inlineCache.clear(); - s_inner.inlineBotChanged(); - s_inner.hideInlineRowsPanel(); + s_inner->inlineBotChanged(); + s_inner->hideInlineRowsPanel(); Notify::inlineBotRequesting(false); } @@ -4042,11 +4040,11 @@ void EmojiPan::onInlineRequest() { void EmojiPan::onEmptyInlineRows() { if (_shownFromInlineQuery || hideOnNoInlineResults()) { hideAnimated(); - s_inner.clearInlineRowsPanel(); + s_inner->clearInlineRowsPanel(); } else if (!_inlineBot) { - s_inner.hideInlineRowsPanel(); + s_inner->hideInlineRowsPanel(); } else { - s_inner.clearInlineRowsPanel(); + s_inner->clearInlineRowsPanel(); } } @@ -4060,7 +4058,7 @@ bool EmojiPan::refreshInlineRows(int32 *added) { _inlineNextOffset = i.value()->nextOffset; } if (!entry) prepareCache(); - int32 result = s_inner.refreshInlineRows(_inlineBot, entry, false); + int32 result = s_inner->refreshInlineRows(_inlineBot, entry, false); if (added) *added = result; return (entry != nullptr); } @@ -4068,10 +4066,10 @@ bool EmojiPan::refreshInlineRows(int32 *added) { int32 EmojiPan::showInlineRows(bool newResults) { int32 added = 0; bool clear = !refreshInlineRows(&added); - if (newResults) s_scroll.scrollToY(0); + if (newResults) s_scroll->scrollToY(0); - e_switch.updateText(s_inner.inlineResultsShown() ? _inlineBot->username : QString()); - e_switch.moveToRight(0, 0, st::emojiPanWidth); + e_switch->updateText(s_inner->inlineResultsShown() ? _inlineBot->username : QString()); + e_switch->moveToRight(0, 0, st::emojiPanWidth); bool hidden = isHidden(); if (!hidden && !clear) { @@ -4097,7 +4095,7 @@ int32 EmojiPan::showInlineRows(bool newResults) { void EmojiPan::recountContentMaxHeight() { if (_shownFromInlineQuery) { - _contentMaxHeight = qMin(s_inner.countHeight(true), int(st::emojiPanMaxHeight)); + _contentMaxHeight = qMin(s_inner->countHeight(true), int(st::emojiPanMaxHeight)); } else { _contentMaxHeight = st::emojiPanMaxHeight; } diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index be927c8cc..67dfd3c46 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -33,6 +33,7 @@ class Result; } // namespace InlineBots namespace Ui { +class ScrollArea; class IconButton; class LinkButton; class RoundButton; @@ -123,7 +124,7 @@ class EmojiPanInner : public TWidget { Q_OBJECT public: - EmojiPanInner(); + EmojiPanInner(QWidget *parent); void setMaxHeight(int32 h); void paintEvent(QPaintEvent *e) override; @@ -217,7 +218,7 @@ class StickerPanInner : public TWidget, private base::Subscriber { Q_OBJECT public: - StickerPanInner(); + StickerPanInner(QWidget *parent); void setMaxHeight(int32 h); void paintEvent(QPaintEvent *e) override; @@ -681,14 +682,14 @@ private: bool _emojiShown = true; bool _shownFromInlineQuery = false; - ScrollArea e_scroll; - internal::EmojiPanInner e_inner; + ChildWidget e_scroll; + ChildWidget e_inner; QVector e_panels; - internal::EmojiSwitchButton e_switch; - ScrollArea s_scroll; - internal::StickerPanInner s_inner; + ChildWidget e_switch; + ChildWidget s_scroll; + ChildWidget s_inner; QVector s_panels; - internal::EmojiSwitchButton s_switch; + ChildWidget s_switch; uint64 _displayingSetId = 0; uint64 _removingSetId = 0; diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index d0a6534b5..1bd70082e 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -34,7 +34,7 @@ stickersTrendingHeader: 45px; stickersTrendingSkip: 15px; stickersTrendingHeaderFont: semiboldFont; -stickersTrendingHeaderFg: windowTextFg; +stickersTrendingHeaderFg: windowFg; stickersTrendingHeaderTop: 0px; stickersTrendingSubheaderFont: normalFont; stickersTrendingSubheaderFg: #777777; @@ -68,7 +68,7 @@ stickersFeaturedInstalled: icon {{ "mediaview_save_check", lightButtonFg }}; stickersMaxHeight: 440px; stickersPadding: margins(19px, 17px, 19px, 17px); stickersSize: size(64px, 64px); -stickersScroll: flatScroll(boxScroll) { +stickersScroll: FlatScroll(boxScroll) { round: 2px; deltax: 7px; deltat: 23px; @@ -86,7 +86,7 @@ stickersTrendingActive: icon {{ "emoji_trending", #58b2ed }}; stickersSettingsUnreadSize: 17px; stickersSettingsUnreadPosition: point(4px, 5px); -emojiScroll: flatScroll(solidScroll) { +emojiScroll: FlatScroll(defaultSolidScroll) { deltat: 48px; } emojiRecent: icon {{ "emoji_recent", #b3b3b3 }}; diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 1e40b0fb0..f1c17e95c 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1678,18 +1678,20 @@ GameData::GameData(const GameId &id, const uint64 &accessHash, const QString &sh , document(document) { } -void PeerOpenClickHandler::onClickImpl() const { - if (App::main()) { - if (peer() && peer()->isChannel() && App::main()->historyPeer() != peer()) { - if (!peer()->asChannel()->isPublic() && !peer()->asChannel()->amIn()) { - Ui::showLayer(new InformBox(lang((peer()->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); +ClickHandlerPtr peerOpenClickHandler(PeerData *peer) { + return MakeShared([peer] { + if (App::main()) { + if (peer && peer->isChannel() && App::main()->historyPeer() != peer) { + if (!peer->asChannel()->isPublic() && !peer->asChannel()->amIn()) { + Ui::showLayer(new InformBox(lang((peer->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); + } else { + Ui::showPeerHistory(peer, ShowAtUnreadMsgId, Ui::ShowWay::Forward); + } } else { - Ui::showPeerHistory(peer(), ShowAtUnreadMsgId, Ui::ShowWay::Forward); + Ui::showPeerProfile(peer); } - } else { - Ui::showPeerProfile(peer()); } - } + }); } MsgId clientMsgId() { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 4cfaa501c..34e7a75c5 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -215,25 +215,7 @@ inline const QString &emptyUsername() { return empty; } -class PeerClickHandler : public LeftButtonClickHandler { -public: - PeerClickHandler(PeerData *peer) : _peer(peer) { - } - PeerData *peer() const { - return _peer; - } - -private: - PeerData *_peer; - -}; - -class PeerOpenClickHandler : public PeerClickHandler { -public: - using PeerClickHandler::PeerClickHandler; -protected: - void onClickImpl() const override; -}; +ClickHandlerPtr peerOpenClickHandler(PeerData *peer); class UserData; class ChatData; @@ -339,7 +321,7 @@ public: const ClickHandlerPtr &openLink() { if (!_openLink) { - _openLink.reset(new PeerOpenClickHandler(this)); + _openLink = peerOpenClickHandler(this); } return _openLink; } diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index 1c833447c..4a9333936 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "application.h" -#include "ui/scrollarea.h" +#include "ui/widgets/scroll_area.h" #include "ui/widgets/multi_select.h" #include "boxes/contactsbox.h" #include "countries.h" @@ -126,7 +126,7 @@ void CountryInput::paintEvent(QPaintEvent *e) { p.drawPixmap(_arrowRect.x(), _arrowRect.top(), _arrow); p.setFont(_st.font); - p.setPen(st::windowTextFg); + p.setPen(st::windowFg); p.drawText(rect().marginsRemoved(_st.textMrg), _text, QTextOption(_st.align)); } diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index a5a8d376e..eeaaceffc 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -20,7 +20,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/scrollarea.h" #include "ui/effects/rect_shadow.h" #include "boxes/abstractbox.h" #include "styles/style_intro.h" diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index 8248f5d52..a4d18eb5e 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -32,7 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace { -const style::textStyle *_textStyle = nullptr; +const style::TextStyle *_textStyle = nullptr; void initDefault() { _textStyle = &st::defaultTextStyle; @@ -44,11 +44,11 @@ inline int32 countBlockHeight(const ITextBlock *b, const style::font &font) { } // namespace -const style::textStyle *textstyleCurrent() { +const style::TextStyle *textstyleCurrent() { return _textStyle; } -void textstyleSet(const style::textStyle *style) { +void textstyleSet(const style::TextStyle *style) { _textStyle = style ? style : &st::defaultTextStyle; } diff --git a/Telegram/SourceFiles/ui/text/text.h b/Telegram/SourceFiles/ui/text/text.h index 70d31dbf9..4a86dd80c 100644 --- a/Telegram/SourceFiles/ui/text/text.h +++ b/Telegram/SourceFiles/ui/text/text.h @@ -264,8 +264,8 @@ const QRegularExpression &reHashtag(); const QRegularExpression &reBotCommand(); // text style -const style::textStyle *textstyleCurrent(); -void textstyleSet(const style::textStyle *style); +const style::TextStyle *textstyleCurrent(); +void textstyleSet(const style::TextStyle *style); inline void textstyleRestore() { textstyleSet(nullptr); } diff --git a/Telegram/SourceFiles/ui/text/text_block.cpp b/Telegram/SourceFiles/ui/text/text_block.cpp index 96a3a66b0..8befbc941 100644 --- a/Telegram/SourceFiles/ui/text/text_block.cpp +++ b/Telegram/SourceFiles/ui/text/text_block.cpp @@ -305,7 +305,7 @@ TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResi if (length) { style::font blockFont = font; if (!flags && lnkIndex) { - // should use textStyle lnkFlags somehow... not supported + // should use TextStyle lnkFlags somehow... not supported } if ((flags & TextBlockFPre) || (flags & TextBlockFCode)) { diff --git a/Telegram/SourceFiles/ui/widgets/buttons.cpp b/Telegram/SourceFiles/ui/widgets/buttons.cpp index 2e8457c15..e382bdba5 100644 --- a/Telegram/SourceFiles/ui/widgets/buttons.cpp +++ b/Telegram/SourceFiles/ui/widgets/buttons.cpp @@ -132,7 +132,7 @@ void FlatButton::paintEvent(QPaintEvent *e) { p.setRenderHint(QPainter::TextAntialiasing); p.setPen(anim::pen(_st.color, _st.overColor, a_over.current())); - int32 top = (_state & StateOver) ? ((_state & StateDown) ? _st.downTextTop : _st.overTextTop) : _st.textTop; + auto top = (_state & StateDown) ? _st.downTextTop : ((_state & StateOver) ? _st.overTextTop : _st.textTop); r.setTop(top); p.drawText(r, _text, style::al_top); @@ -259,7 +259,7 @@ void RoundButton::paintEvent(QPaintEvent *e) { auto over = (_state & StateOver); auto down = (_state & StateDown); - if (over) { + if (over || down) { App::roundRect(p, myrtlrect(rounded), _st.textBgOver, ImageRoundRadius::Small); } @@ -360,6 +360,7 @@ void IconButton::paintEvent(QPaintEvent *e) { } } + auto down = (_state & StateDown); auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); auto overIcon = [this] { if (_iconOverrideOver) { @@ -377,7 +378,7 @@ void IconButton::paintEvent(QPaintEvent *e) { } return &_st.icon; }; - auto icon = (over == 1.) ? overIcon() : justIcon(); + auto icon = (over == 1. || down) ? overIcon() : justIcon(); auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition; if (position.x() < 0) { position.setX((width() - icon->width()) / 2); diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index 7f0a4f37f..7f85bb168 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/inner_dropdown.h" #include "mainwindow.h" -#include "ui/scrollarea.h" +#include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" #include "profile/profile_members_widget.h" #include "ui/effects/panel_animation.h" diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h index 7f7dfe988..b58974c92 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h @@ -23,10 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_widgets.h" #include "ui/effects/panel_animation.h" -class ScrollArea; - namespace Ui { +class ScrollArea; + class InnerDropdown : public TWidget { Q_OBJECT @@ -112,7 +112,7 @@ private: bool _ignoreShowEvents = false; base::lambda_unique _hiddenCallback; - ChildWidget _scroll; + ChildWidget _scroll; int _maxHeight = 0; diff --git a/Telegram/SourceFiles/ui/widgets/label_simple.cpp b/Telegram/SourceFiles/ui/widgets/label_simple.cpp deleted file mode 100644 index f7c802ad2..000000000 --- a/Telegram/SourceFiles/ui/widgets/label_simple.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* -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 "ui/widgets/label_simple.h" - -namespace Ui { - -LabelSimple::LabelSimple(QWidget *parent, const style::LabelSimple &st, const QString &value) : TWidget(parent) -, _st(st) { - setText(value); -} - -void LabelSimple::setText(const QString &value, bool *outTextChanged) { - if (_fullText == value) { - if (outTextChanged) *outTextChanged = false; - return; - } - - _fullText = value; - _fullTextWidth = _st.font->width(_fullText); - if (!_st.maxWidth || _fullTextWidth <= _st.maxWidth) { - _text = _fullText; - _textWidth = _fullTextWidth; - } else { - auto newText = _st.font->elided(_fullText, _st.maxWidth); - if (newText == _text) { - if (outTextChanged) *outTextChanged = false; - return; - } - _text = newText; - _textWidth = _st.font->width(_text); - } - resize(_textWidth, _st.font->height); - update(); - if (outTextChanged) *outTextChanged = true; -} - -void LabelSimple::paintEvent(QPaintEvent *e) { - Painter p(this); - - p.setFont(_st.font); - p.setPen(_st.textFg); - p.drawTextLeft(0, 0, width(), _text, _textWidth); -} - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/label_simple.h b/Telegram/SourceFiles/ui/widgets/label_simple.h deleted file mode 100644 index a1b823705..000000000 --- a/Telegram/SourceFiles/ui/widgets/label_simple.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -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 "styles/style_widgets.h" - -namespace Ui { - -class LabelSimple : public TWidget { -public: - LabelSimple(QWidget *parent, const style::LabelSimple &st = st::defaultLabelSimple, const QString &value = QString()); - - // This method also resizes the label. - void setText(const QString &newText, bool *outTextChanged = nullptr); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - QString _fullText; - int _fullTextWidth; - - QString _text; - int _textWidth; - - const style::LabelSimple &_st; - -}; - -} // namespace Ui diff --git a/Telegram/SourceFiles/ui/flatlabel.cpp b/Telegram/SourceFiles/ui/widgets/labels.cpp similarity index 87% rename from Telegram/SourceFiles/ui/flatlabel.cpp rename to Telegram/SourceFiles/ui/widgets/labels.cpp index 6be2345ea..ae1be814e 100644 --- a/Telegram/SourceFiles/ui/flatlabel.cpp +++ b/Telegram/SourceFiles/ui/widgets/labels.cpp @@ -19,28 +19,69 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "ui/flatlabel.h" +#include "ui/widgets/labels.h" #include "ui/widgets/popup_menu.h" #include "mainwindow.h" #include "lang.h" +namespace Ui { namespace { - TextParseOptions _labelOptions = { - TextParseMultiline, // flags - 0, // maxw - 0, // maxh - Qt::LayoutDirectionAuto, // dir - }; - TextParseOptions _labelMarkedOptions = { - TextParseMultiline | TextParseRichText | TextParseLinks | TextParseHashtags | TextParseMentions | TextParseBotCommands | TextParseMono, // flags - 0, // maxw - 0, // maxh - Qt::LayoutDirectionAuto, // dir - }; + +TextParseOptions _labelOptions = { + TextParseMultiline, // flags + 0, // maxw + 0, // maxh + Qt::LayoutDirectionAuto, // dir +}; +TextParseOptions _labelMarkedOptions = { + TextParseMultiline | TextParseRichText | TextParseLinks | TextParseHashtags | TextParseMentions | TextParseBotCommands | TextParseMono, // flags + 0, // maxw + 0, // maxh + Qt::LayoutDirectionAuto, // dir +}; + +} // namespace + +LabelSimple::LabelSimple(QWidget *parent, const style::LabelSimple &st, const QString &value) : TWidget(parent) +, _st(st) { + setText(value); } -FlatLabel::FlatLabel(QWidget *parent, const style::flatLabel &st, const style::textStyle &tst) : TWidget(parent) +void LabelSimple::setText(const QString &value, bool *outTextChanged) { + if (_fullText == value) { + if (outTextChanged) *outTextChanged = false; + return; + } + + _fullText = value; + _fullTextWidth = _st.font->width(_fullText); + if (!_st.maxWidth || _fullTextWidth <= _st.maxWidth) { + _text = _fullText; + _textWidth = _fullTextWidth; + } else { + auto newText = _st.font->elided(_fullText, _st.maxWidth); + if (newText == _text) { + if (outTextChanged) *outTextChanged = false; + return; + } + _text = newText; + _textWidth = _st.font->width(_text); + } + resize(_textWidth, _st.font->height); + update(); + if (outTextChanged) *outTextChanged = true; +} + +void LabelSimple::paintEvent(QPaintEvent *e) { + Painter p(this); + + p.setFont(_st.font); + p.setPen(_st.textFg); + p.drawTextLeft(0, 0, width(), _text, _textWidth); +} + +FlatLabel::FlatLabel(QWidget *parent, const style::FlatLabel &st, const style::TextStyle &tst) : TWidget(parent) , _text(st.width ? st.width : QFIXED_MAX) , _st(st) , _tst(tst) @@ -48,7 +89,7 @@ FlatLabel::FlatLabel(QWidget *parent, const style::flatLabel &st, const style::t init(); } -FlatLabel::FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::flatLabel &st, const style::textStyle &tst) : TWidget(parent) +FlatLabel::FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::FlatLabel &st, const style::TextStyle &tst) : TWidget(parent) , _text(st.width ? st.width : QFIXED_MAX) , _st(st) , _tst(tst) @@ -356,40 +397,40 @@ void FlatLabel::touchEvent(QTouchEvent *e) { switch (e->type()) { case QEvent::TouchBegin: - if (_contextMenu) { - e->accept(); - return; // ignore mouse press, that was hiding context menu - } - if (_touchInProgress) return; - if (e->touchPoints().isEmpty()) return; + if (_contextMenu) { + e->accept(); + return; // ignore mouse press, that was hiding context menu + } + if (_touchInProgress) return; + if (e->touchPoints().isEmpty()) return; - _touchInProgress = true; - _touchSelectTimer.start(QApplication::startDragTime()); - _touchSelect = false; - _touchStart = _touchPrevPos = _touchPos; + _touchInProgress = true; + _touchSelectTimer.start(QApplication::startDragTime()); + _touchSelect = false; + _touchStart = _touchPrevPos = _touchPos; break; case QEvent::TouchUpdate: - if (!_touchInProgress) return; - if (_touchSelect) { - _lastMousePos = _touchPos; - dragActionUpdate(); - } + if (!_touchInProgress) return; + if (_touchSelect) { + _lastMousePos = _touchPos; + dragActionUpdate(); + } break; case QEvent::TouchEnd: - if (!_touchInProgress) return; - _touchInProgress = false; - if (_touchSelect) { - dragActionFinish(_touchPos, Qt::RightButton); - QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos); - showContextMenu(&contextMenu, ContextMenuReason::FromTouch); - } else { // one short tap -- like mouse click - dragActionStart(_touchPos, Qt::LeftButton); - dragActionFinish(_touchPos, Qt::LeftButton); - } - _touchSelectTimer.stop(); - _touchSelect = false; + if (!_touchInProgress) return; + _touchInProgress = false; + if (_touchSelect) { + dragActionFinish(_touchPos, Qt::RightButton); + QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos); + showContextMenu(&contextMenu, ContextMenuReason::FromTouch); + } else { // one short tap -- like mouse click + dragActionStart(_touchPos, Qt::LeftButton); + dragActionFinish(_touchPos, Qt::LeftButton); + } + _touchSelectTimer.stop(); + _touchSelect = false; break; } } @@ -558,9 +599,9 @@ void FlatLabel::updateHover(const Text::StateResult &state) { } } if (_dragAction == Selecting) { -// checkSelectingScroll(); + // checkSelectingScroll(); } else { -// noSelectingScroll(); + // noSelectingScroll(); } if (_dragAction == NoDrag && (lnkChanged || cur != _cursor)) { @@ -634,3 +675,5 @@ void FlatLabel::paintEvent(QPaintEvent *e) { } textstyleRestore(); } + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/flatlabel.h b/Telegram/SourceFiles/ui/widgets/labels.h similarity index 84% rename from Telegram/SourceFiles/ui/flatlabel.h rename to Telegram/SourceFiles/ui/widgets/labels.h index 5d8128c18..0a50f2abf 100644 --- a/Telegram/SourceFiles/ui/flatlabel.h +++ b/Telegram/SourceFiles/ui/widgets/labels.h @@ -20,21 +20,44 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include "styles/style_widgets.h" + namespace Ui { + class PopupMenu; -} // namespace Ui + +class LabelSimple : public TWidget { +public: + LabelSimple(QWidget *parent, const style::LabelSimple &st = st::defaultLabelSimple, const QString &value = QString()); + + // This method also resizes the label. + void setText(const QString &newText, bool *outTextChanged = nullptr); + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + QString _fullText; + int _fullTextWidth; + + QString _text; + int _textWidth; + + const style::LabelSimple &_st; + +}; class FlatLabel : public TWidget, public ClickHandlerHost { Q_OBJECT public: - FlatLabel(QWidget *parent, const style::flatLabel &st = st::labelDefFlat, const style::textStyle &tst = st::defaultTextStyle); + FlatLabel(QWidget *parent, const style::FlatLabel &st = st::defaultFlatLabel, const style::TextStyle &tst = st::defaultTextStyle); enum class InitType { Simple, Rich, }; - FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::flatLabel &st = st::labelDefFlat, const style::textStyle &tst = st::defaultTextStyle); + FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::FlatLabel &st = st::defaultFlatLabel, const style::TextStyle &tst = st::defaultTextStyle); void setOpacity(float64 o); @@ -51,7 +74,7 @@ public: void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk); - using ClickHandlerHook = base::lambda_unique; + using ClickHandlerHook = base::lambda_unique; void setClickHandlerHook(ClickHandlerHook &&hook); // ClickHandlerHost interface @@ -109,8 +132,8 @@ private: void showContextMenu(QContextMenuEvent *e, ContextMenuReason reason); Text _text; - const style::flatLabel &_st; - const style::textStyle &_tst; + const style::FlatLabel &_st; + const style::TextStyle &_tst; float64 _opacity = 1.; int _allowedWidth = 0; @@ -153,3 +176,5 @@ private: QTimer _touchSelectTimer; }; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index 6ad1eed9e..feb431e70 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_widgets.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/scroll_area.h" #include "lang.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.h b/Telegram/SourceFiles/ui/widgets/multi_select.h index 753a7b8c8..9f60fc9bc 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.h +++ b/Telegram/SourceFiles/ui/widgets/multi_select.h @@ -26,6 +26,7 @@ namespace Ui { class InputField; class IconButton; +class ScrollArea; class MultiSelect : public TWidget { public: @@ -59,7 +60,7 @@ private: const style::MultiSelect &_st; - ChildWidget _scroll; + ChildWidget _scroll; class Inner; ChildWidget _inner; diff --git a/Telegram/SourceFiles/ui/scrollarea.cpp b/Telegram/SourceFiles/ui/widgets/scroll_area.cpp similarity index 94% rename from Telegram/SourceFiles/ui/scrollarea.cpp rename to Telegram/SourceFiles/ui/widgets/scroll_area.cpp index 19c32037d..b884b24fb 100644 --- a/Telegram/SourceFiles/ui/scrollarea.cpp +++ b/Telegram/SourceFiles/ui/widgets/scroll_area.cpp @@ -19,11 +19,13 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "ui/scrollarea.h" +#include "ui/widgets/scroll_area.h" + +namespace Ui { // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html -ScrollShadow::ScrollShadow(ScrollArea *parent, const style::flatScroll *st) : QWidget(parent), _st(st) { +ScrollShadow::ScrollShadow(ScrollArea *parent, const style::FlatScroll *st) : QWidget(parent), _st(st) { setVisible(false); } @@ -36,7 +38,7 @@ void ScrollShadow::changeVisibility(bool shown) { setVisible(shown); } -ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st) : QWidget(parent) +ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::FlatScroll *st) : QWidget(parent) , _st(st) , _vertical(vert) , _over(false) @@ -338,7 +340,7 @@ void SplittedWidgetOther::paintEvent(QPaintEvent *e) { } } -ScrollArea::ScrollArea(QWidget *parent, const style::flatScroll &st, bool handleTouch) : QScrollArea(parent) +ScrollArea::ScrollArea(QWidget *parent, const style::FlatScroll &st, bool handleTouch) : QScrollArea(parent) , _st(st) , _horizontalBar(this, false, &_st) , _verticalBar(this, true, &_st) @@ -444,16 +446,16 @@ void ScrollArea::onTouchTimer() { void ScrollArea::onTouchScrollTimer() { uint64 nowTime = getms(); - if (_touchScrollState == TouchScrollAcceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { - _touchScrollState = TouchScrollManual; + if (_touchScrollState == TouchScrollState::Acceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) { + _touchScrollState = TouchScrollState::Manual; touchResetSpeed(); - } else if (_touchScrollState == TouchScrollAuto || _touchScrollState == TouchScrollAcceleration) { + } else if (_touchScrollState == TouchScrollState::Auto || _touchScrollState == TouchScrollState::Acceleration) { int32 elapsed = int32(nowTime - _touchTime); QPoint delta = _touchSpeed * elapsed / 1000; bool hasScrolled = touchScroll(delta); if (_touchSpeed.isNull() || !hasScrolled) { - _touchScrollState = TouchScrollManual; + _touchScrollState = TouchScrollState::Manual; _touchScroll = false; _touchScrollTimer.stop(); } else { @@ -475,7 +477,7 @@ void ScrollArea::touchUpdateSpeed() { // of a small horizontal offset when scrolling vertically const int newSpeedY = (qAbs(pixelsPerSecond.y()) > FingerAccuracyThreshold) ? pixelsPerSecond.y() : 0; const int newSpeedX = (qAbs(pixelsPerSecond.x()) > FingerAccuracyThreshold) ? pixelsPerSecond.x() : 0; - if (_touchScrollState == TouchScrollAuto) { + if (_touchScrollState == TouchScrollState::Auto) { const int oldSpeedY = _touchSpeed.y(); const int oldSpeedX = _touchSpeed.x(); if ((oldSpeedY <= 0 && newSpeedY <= 0) || ((oldSpeedY >= 0 && newSpeedY >= 0) @@ -542,8 +544,8 @@ void ScrollArea::touchEvent(QTouchEvent *e) { case QEvent::TouchBegin: if (_touchPress || e->touchPoints().isEmpty()) return; _touchPress = true; - if (_touchScrollState == TouchScrollAuto) { - _touchScrollState = TouchScrollAcceleration; + if (_touchScrollState == TouchScrollState::Auto) { + _touchScrollState = TouchScrollState::Acceleration; _touchWaitingAcceleration = true; _touchAccelerationTime = getms(); touchUpdateSpeed(); @@ -564,13 +566,13 @@ void ScrollArea::touchEvent(QTouchEvent *e) { touchUpdateSpeed(); } if (_touchScroll) { - if (_touchScrollState == TouchScrollManual) { + if (_touchScrollState == TouchScrollState::Manual) { touchScrollUpdated(_touchPos); - } else if (_touchScrollState == TouchScrollAcceleration) { + } else if (_touchScrollState == TouchScrollState::Acceleration) { touchUpdateSpeed(); _touchAccelerationTime = getms(); if (_touchSpeed.isNull()) { - _touchScrollState = TouchScrollManual; + _touchScrollState = TouchScrollState::Manual; } } } @@ -580,17 +582,17 @@ void ScrollArea::touchEvent(QTouchEvent *e) { if (!_touchPress) return; _touchPress = false; if (_touchScroll) { - if (_touchScrollState == TouchScrollManual) { - _touchScrollState = TouchScrollAuto; + if (_touchScrollState == TouchScrollState::Manual) { + _touchScrollState = TouchScrollState::Auto; _touchPrevPosValid = false; _touchScrollTimer.start(15); _touchTime = getms(); - } else if (_touchScrollState == TouchScrollAuto) { - _touchScrollState = TouchScrollManual; + } else if (_touchScrollState == TouchScrollState::Auto) { + _touchScrollState = TouchScrollState::Manual; _touchScroll = false; touchResetSpeed(); - } else if (_touchScrollState == TouchScrollAcceleration) { - _touchScrollState = TouchScrollAuto; + } else if (_touchScrollState == TouchScrollState::Acceleration) { + _touchScrollState = TouchScrollState::Auto; _touchWaitingAcceleration = false; _touchPrevPosValid = false; } @@ -616,7 +618,7 @@ void ScrollArea::touchEvent(QTouchEvent *e) { case QEvent::TouchCancel: _touchPress = false; _touchScroll = false; - _touchScrollState = TouchScrollManual; + _touchScrollState = TouchScrollState::Manual; _touchTimer.stop(); break; } @@ -822,3 +824,5 @@ ScrollArea::~ScrollArea() { takeWidget(); } } + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/scrollarea.h b/Telegram/SourceFiles/ui/widgets/scroll_area.h similarity index 89% rename from Telegram/SourceFiles/ui/scrollarea.h rename to Telegram/SourceFiles/ui/widgets/scroll_area.h index 306b36562..8a5479acb 100644 --- a/Telegram/SourceFiles/ui/scrollarea.h +++ b/Telegram/SourceFiles/ui/widgets/scroll_area.h @@ -20,10 +20,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -enum TouchScrollState { - TouchScrollManual, // Scrolling manually with the finger on the screen - TouchScrollAuto, // Scrolling automatically - TouchScrollAcceleration // Scrolling automatically but a finger is on the screen +#include "styles/style_widgets.h" + +namespace Ui { + +enum class TouchScrollState { + Manual, // Scrolling manually with the finger on the screen + Auto, // Scrolling automatically + Acceleration // Scrolling automatically but a finger is on the screen }; class ScrollArea; @@ -33,7 +37,7 @@ class ScrollShadow : public QWidget { public: - ScrollShadow(ScrollArea *parent, const style::flatScroll *st); + ScrollShadow(ScrollArea *parent, const style::FlatScroll *st); void paintEvent(QPaintEvent *e); @@ -43,7 +47,7 @@ public slots: private: - const style::flatScroll *_st; + const style::FlatScroll *_st; }; @@ -52,7 +56,7 @@ class ScrollBar : public QWidget { public: - ScrollBar(ScrollArea *parent, bool vertical, const style::flatScroll *st); + ScrollBar(ScrollArea *parent, bool vertical, const style::FlatScroll *st); void recountSize(); @@ -82,7 +86,7 @@ signals: private: ScrollArea *area(); - const style::flatScroll *_st; + const style::FlatScroll *_st; bool _vertical; bool _over, _overbar, _moving; @@ -156,10 +160,10 @@ private: class SplittedWidgetOther; class ScrollArea : public QScrollArea { Q_OBJECT - T_WIDGET + T_WIDGET public: - ScrollArea(QWidget *parent, const style::flatScroll &st = st::scrollDef, bool handleTouch = true); + ScrollArea(QWidget *parent, const style::FlatScroll &st = st::defaultFlatScroll, bool handleTouch = true); int scrollWidth() const; int scrollHeight() const; @@ -229,7 +233,7 @@ private: bool _ownsWidget = false; // if true, the widget is deleted in destructor. bool _movingByScrollBar = false; - const style::flatScroll &_st; + const style::FlatScroll &_st; ChildWidget _horizontalBar, _verticalBar; ChildWidget _topShadow, _bottomShadow; int _horizontalValue, _verticalValue; @@ -241,7 +245,7 @@ private: bool _touchRightButton = false; QPoint _touchStart, _touchPrevPos, _touchPos; - TouchScrollState _touchScrollState = TouchScrollManual; + TouchScrollState _touchScrollState = TouchScrollState::Manual; bool _touchPrevPosValid = false; bool _touchWaitingAcceleration = false; QPoint _touchSpeed; @@ -266,3 +270,5 @@ protected: void paintEvent(QPaintEvent *e) override; }; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index edadae64e..769052e05 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -27,6 +27,15 @@ LabelSimple { textFg: color; } +FlatLabel { + font: font; + margin: margins; + width: pixels; + align: align; + textFg: color; + maxHeight: pixels; +} + LinkButton { color: color; overColor: color; @@ -129,6 +138,28 @@ Radiobutton { duration: int; } +FlatScroll { + barColor: color; + bgColor: color; + barOverColor: color; + bgOverColor: color; + + round: pixels; + + width: pixels; + minHeight: pixels; + deltax: pixels; + deltat: pixels; + deltab: pixels; + + topsh: pixels; + bottomsh: pixels; + shColor: color; + + duration: int; + hiding: int; +} + FlatTextarea { textColor: color; bgColor: color; @@ -327,7 +358,7 @@ MultiSelect { bg: color; padding: margins; maxHeight: pixels; - scroll: flatScroll; + scroll: FlatScroll; item: MultiSelectItem; itemSkip: pixels; @@ -400,7 +431,7 @@ InnerDropdown { width: pixels; bg: color; - scroll: flatScroll; + scroll: FlatScroll; scrollMargin: margins; scrollPadding: margins; } @@ -413,7 +444,15 @@ DropdownMenu { defaultLabelSimple: LabelSimple { font: normalFont; maxWidth: 0px; - textFg: windowTextFg; + textFg: windowFg; +} + +defaultFlatLabel: FlatLabel { + font: font(fsize); + width: 0px; + maxHeight: 0px; + align: align(left); + textFg: windowFg; } defaultLinkButton: LinkButton { @@ -425,7 +464,7 @@ defaultLinkButton: LinkButton { } defaultRippleAnimation: RippleAnimation { - color: windowOverBg; + color: windowBgRipple; showDuration: 200; hideDuration: 200; } @@ -468,6 +507,50 @@ defaultLightButton: RoundButton(defaultActiveButton) { } } +defaultFlatScroll: FlatScroll { + barColor: #00000053; + bgColor: #0000001a; + barOverColor: #0000007a; + bgOverColor: #0000002c; + + round: 2px; + + width: 10px; + minHeight: 20px; + deltax: 3px; + deltat: 3px; + deltab: 3px; + + topsh: 2px; + bottomsh: 2px; + shColor: #00000012; + + duration: 150; + hiding: 1000; +} + +defaultSolidScroll: FlatScroll { + barColor: #3f729734; + bgColor: #214f751a; + barOverColor: #3f729734; + bgOverColor: #214f751a; + + minHeight: 20px; + + round: 2px; + deltax: 5px; + width: 14px; + deltat: 6px; + deltab: 6px; + + topsh: 0px; + bottomsh: 0px; + shColor: #00000012; + + duration: 150; + hiding: 0; +} + defaultInputFont: font(17px); defaultFlatInput: FlatInput { textColor: #000000; @@ -496,7 +579,7 @@ defaultFlatInput: FlatInput { defaultLeftOutlineButton: OutlineButton { outlineWidth: 3px; outlineFg: windowBg; - outlineFgOver: windowActiveBg; + outlineFgOver: windowBgActive; textBg: windowBg; textBgOver: #f2f7fa; @@ -518,7 +601,7 @@ attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) { defaultInputArea: InputArea { textBg: windowBg; - textFg: windowTextFg; + textFg: windowFg; textMargins: margins(5px, 6px, 5px, 4px); placeholderFg: #999999; @@ -544,7 +627,7 @@ defaultInputArea: InputArea { defaultInputField: InputField { textBg: windowBg; - textFg: windowTextFg; + textFg: windowFg; textMargins: margins(0px, 6px, 0px, 4px); textAlign: align(topleft); @@ -568,16 +651,16 @@ defaultInputField: InputField { height: 32px; } -defaultCheckboxIcon: icon {{ "default_checkbox_check", windowActiveFg, point(4px, 7px) }}; +defaultCheckboxIcon: icon {{ "default_checkbox_check", windowFgActive, point(4px, 7px) }}; defaultCheckbox: Checkbox { - textFg: windowTextFg; + textFg: windowFg; textBg: windowBg; checkBg: #ffffff; checkFg: #b3b3b3; checkFgOver: #b3b3b3; - checkFgActive: windowActiveBg; + checkFgActive: windowBgActive; width: -44px; height: 22px; @@ -592,7 +675,7 @@ defaultCheckbox: Checkbox { } defaultRadiobutton: Radiobutton { - textFg: windowTextFg; + textFg: windowFg; textBg: windowBg; checkBg: #ffffff; @@ -669,9 +752,9 @@ defaultMenu: Menu { skip: 0px; itemBg: windowBg; - itemBgOver: windowOverBg; - itemFg: windowTextFg; - itemFgOver: windowTextFg; + itemBgOver: windowBgOver; + itemFg: windowFg; + itemFgOver: windowFgOver; itemFgDisabled: #cccccc; itemFgShortcut: #999999; itemFgShortcutOver: #7c99b2; @@ -709,7 +792,7 @@ defaultInnerDropdown: InnerDropdown { showDuration: 200; bg: menuBg; - scroll: solidScroll; + scroll: defaultSolidScroll; } defaultDropdownMenu: DropdownMenu { wrap: InnerDropdown(defaultInnerDropdown) { diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 404a7945e..5b27d9a8b 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -86,7 +86,7 @@ titleUnreadCounterRight: 35px; mainMenuBg: #ffffff; mainMenuWidth: 274px; mainMenuCoverBg: #419fd9; -mainMenuCoverFg: windowActiveFg; +mainMenuCoverFg: windowFgActive; mainMenuCoverHeight: 140px; mainMenuUserpicLeft: 24px; mainMenuUserpicTop: 22px; @@ -95,23 +95,38 @@ mainMenuCoverTextLeft: 30px; mainMenuCoverNameTop: 88px; mainMenuCoverStatusTop: 106px; mainMenuSkip: 13px; +mainMenu: Menu(defaultMenu) { + itemFont: semiboldFont; + itemIconPosition: point(28px, 11px); + itemPadding: margins(76px, 14px, 28px, 14px); +} +mainMenuNewGroup: icon {{ "menu_new_group", menuIconFg }}; +mainMenuNewGroupOver: icon {{ "menu_new_group", menuIconFgOver }}; +mainMenuNewChannel: icon {{ "menu_new_channel", menuIconFg }}; +mainMenuNewChannelOver: icon {{ "menu_new_channel", menuIconFgOver }}; +mainMenuContacts: icon {{ "menu_contacts", menuIconFg }}; +mainMenuContactsOver: icon {{ "menu_contacts", menuIconFgOver }}; +mainMenuSettings: icon {{ "menu_settings", menuIconFg }}; +mainMenuSettingsOver: icon {{ "menu_settings", menuIconFgOver }}; +mainMenuHelp: icon {{ "menu_help", menuIconFg }}; +mainMenuHelpOver: icon {{ "menu_help", menuIconFgOver }}; mainMenuFooterLeft: 30px; -mainMenuTelegramLabel: flatLabel(labelDefFlat) { +mainMenuTelegramLabel: FlatLabel(defaultFlatLabel) { font: semiboldFont; align: align(left); textFg: windowSubTextFg; } -mainMenuTelegramStyle: textStyle(defaultTextStyle) { +mainMenuTelegramStyle: TextStyle(defaultTextStyle) { linkFlags: semiboldFont; linkFlagsOver: font(fsize semibold underline); linkFg: windowSubTextFg; linkFgDown: windowSubTextFg; } mainMenuTelegramBottom: 43px; -mainMenuVersionLabel: flatLabel(mainMenuTelegramLabel) { +mainMenuVersionLabel: FlatLabel(mainMenuTelegramLabel) { font: normalFont; } -mainMenuVersionStyle: textStyle(mainMenuTelegramStyle) { +mainMenuVersionStyle: TextStyle(mainMenuTelegramStyle) { linkFlags: normalFont; linkFlagsOver: font(fsize underline); } @@ -192,7 +207,7 @@ topBarClearButton: RoundButton(defaultLightButton) { width: -18px; } topBarSearch: IconButton { - width: 44px; + width: 45px; height: topBarHeight; icon: icon {{ "title_search", menuIconFg }}; @@ -201,20 +216,20 @@ topBarSearch: IconButton { iconPosition: point(15px, 18px); iconPositionDown: point(15px, 18px); - rippleAreaPosition: point(8px, 11px); - rippleAreaSize: 32px; - ripple: defaultRippleAnimation; + rippleAreaPosition: point(4px, 7px); + rippleAreaSize: 40px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } } topBarMenuToggle: IconButton(topBarSearch) { icon: icon {{ "title_menu_dots", menuIconFg }}; iconOver: icon {{ "title_menu_dots", menuIconFgOver }}; - iconPosition: point(15px, 17px); - iconPositionDown: point(15px, 17px); + iconPosition: point(16px, 17px); + iconPositionDown: point(16px, 17px); - rippleAreaPosition: point(3px, 11px); - rippleAreaSize: 32px; - ripple: defaultRippleAnimation; + rippleAreaPosition: point(0px, 7px); } topBarActionSkip: 10px; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 1e2cc1e90..905f8dc9e 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -22,61 +22,88 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/window_main_menu.h" #include "styles/style_window.h" -#include "ui/flatlabel.h" +#include "styles/style_dialogs.h" +#include "profile/profile_userpic_button.h" +#include "ui/widgets/labels.h" #include "ui/widgets/menu.h" #include "mainwindow.h" #include "boxes/contactsbox.h" #include "boxes/aboutbox.h" #include "lang.h" #include "core/click_handler_types.h" -#include "styles/style_dialogs.h" namespace Window { -namespace { - -class AboutClickHandler : public ClickHandler { -public: - void onClick(Qt::MouseButton) const override { - Ui::showLayer(new AboutBox()); - } - -}; - -} // namespace MainMenu::MainMenu(QWidget *parent) : TWidget(parent) -, _menu(this, st::dialogsMenu) +, _menu(this, st::mainMenu) , _telegram(this, st::mainMenuTelegramLabel, st::mainMenuTelegramStyle) , _version(this, st::mainMenuVersionLabel, st::mainMenuVersionStyle) { setAttribute(Qt::WA_OpaquePaintEvent); + + subscribe(Global::RefSelfChanged(), [this] { + checkSelf(); + }); + checkSelf(); + resize(st::mainMenuWidth, parentWidget()->height()); _menu->setTriggeredCallback([](QAction *action, int actionTop, Ui::Menu::TriggeredSource source) { emit action->triggered(); }); _menu->addAction(lang(lng_create_group_title), [] { App::wnd()->onShowNewGroup(); - }, &st::dialogsMenuNewGroup, &st::dialogsMenuNewGroupOver); + }, &st::mainMenuNewGroup, &st::mainMenuNewGroupOver); _menu->addAction(lang(lng_create_channel_title), [] { App::wnd()->onShowNewChannel(); - }, &st::dialogsMenuNewChannel, &st::dialogsMenuNewChannelOver); + }, &st::mainMenuNewChannel, &st::mainMenuNewChannelOver); _menu->addAction(lang(lng_menu_contacts), [] { Ui::showLayer(new ContactsBox()); - }, &st::dialogsMenuContacts, &st::dialogsMenuContactsOver); + }, &st::mainMenuContacts, &st::mainMenuContactsOver); _menu->addAction(lang(lng_menu_settings), [] { App::wnd()->showSettings(); - }, &st::dialogsMenuSettings, &st::dialogsMenuSettingsOver); + }, &st::mainMenuSettings, &st::mainMenuSettingsOver); _menu->addAction(lang(lng_settings_faq), [] { QDesktopServices::openUrl(telegramFaqLink()); - }, &st::dialogsMenuHelp, &st::dialogsMenuHelpOver); + }, &st::mainMenuHelp, &st::mainMenuHelpOver); _telegram->setRichText(textcmdLink(1, qsl("Telegram Desktop"))); - _telegram->setLink(1, ClickHandlerPtr(new UrlClickHandler(qsl("https://desktop.telegram.org")))); - _version->setRichText(textcmdLink(1, qsl("Version 1.2.3")) + QChar(' ') + QChar(8211) + QChar(' ') + textcmdLink(2, qsl("About"))); - _version->setLink(1, ClickHandlerPtr(new UrlClickHandler(qsl("https://desktop.telegram.org/?_hash=changelog")))); - _version->setLink(2, ClickHandlerPtr(new AboutClickHandler())); + _telegram->setLink(1, MakeShared(qsl("https://desktop.telegram.org"))); + _version->setRichText(textcmdLink(1, lng_settings_current_version(lt_version, currentVersionText())) + QChar(' ') + QChar(8211) + QChar(' ') + textcmdLink(2, lang(lng_menu_about))); + _version->setLink(1, MakeShared(qsl("https://desktop.telegram.org/?_hash=changelog"))); + _version->setLink(2, MakeShared([] { Ui::showLayer(new AboutBox()); })); + + subscribe(FileDownload::ImageLoaded(), [this] { update(); }); +} + +void MainMenu::checkSelf() { + if (auto self = App::self()) { + _userpicButton.create(this, self, st::mainMenuUserpicSize); + _userpicButton->setClickedCallback([] { + App::wnd()->showSettings(); + }); + updateControlsGeometry(); + if (_showFinished) { + _userpicButton->showFinished(); + } + } else { + _userpicButton.destroy(); + } +} + +void MainMenu::showFinished() { + _showFinished = true; + if (_userpicButton) { + _userpicButton->showFinished(); + } } void MainMenu::resizeEvent(QResizeEvent *e) { + updateControlsGeometry(); +} + +void MainMenu::updateControlsGeometry() { + if (_userpicButton) { + _userpicButton->moveToLeft(st::mainMenuUserpicLeft, st::mainMenuUserpicTop); + } _menu->setGeometry(0, st::mainMenuCoverHeight + st::mainMenuSkip, width(), _menu->height()); _telegram->moveToLeft(st::mainMenuFooterLeft, height() - st::mainMenuTelegramBottom - _telegram->height()); _version->moveToLeft(st::mainMenuFooterLeft, height() - st::mainMenuVersionBottom - _version->height()); @@ -90,7 +117,6 @@ void MainMenu::paintEvent(QPaintEvent *e) { p.setPen(st::mainMenuCoverFg); p.setFont(st::semiboldFont); if (auto self = App::self()) { - self->paintUserpicLeft(p, st::mainMenuUserpicSize, st::mainMenuUserpicLeft, st::mainMenuUserpicTop, width()); self->nameText.drawLeftElided(p, st::mainMenuCoverTextLeft, st::mainMenuCoverNameTop, width() - 2 * st::mainMenuCoverTextLeft, width()); p.setFont(st::normalFont); p.drawTextLeft(st::mainMenuCoverTextLeft, st::mainMenuCoverStatusTop, width(), qsl("online")); diff --git a/Telegram/SourceFiles/window/window_main_menu.h b/Telegram/SourceFiles/window/window_main_menu.h index 62f68b4a4..6861d3e5a 100644 --- a/Telegram/SourceFiles/window/window_main_menu.h +++ b/Telegram/SourceFiles/window/window_main_menu.h @@ -22,26 +22,37 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/effects/rect_shadow.h" -class FlatLabel; - namespace Ui { +class FlatLabel; class Menu; } // namespace Ui +namespace Profile { +class UserpicButton; +} // namespace Profile + namespace Window { -class MainMenu : public TWidget { +class MainMenu : public TWidget, private base::Subscriber { public: MainMenu(QWidget *parent); + void showFinished(); + protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; private: + void checkSelf(); + void updateControlsGeometry(); + + ChildWidget _userpicButton = { nullptr }; ChildWidget _menu; - ChildWidget _telegram; - ChildWidget _version; + ChildWidget _telegram; + ChildWidget _version; + + bool _showFinished = false; }; diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp index 9865d38f1..c5b845b72 100644 --- a/Telegram/SourceFiles/window/window_theme.cpp +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "core/parse_helper.h" #include "core/zlib_help.h" +#include "styles/style_widgets.h" +#include "styles/style_history.h" namespace Window { namespace Theme { diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 43a82bef8..c75433c4b 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -508,8 +508,8 @@ '<(src_loc)/ui/widgets/inner_dropdown.h', '<(src_loc)/ui/widgets/input_fields.cpp', '<(src_loc)/ui/widgets/input_fields.h', - '<(src_loc)/ui/widgets/label_simple.cpp', - '<(src_loc)/ui/widgets/label_simple.h', + '<(src_loc)/ui/widgets/labels.cpp', + '<(src_loc)/ui/widgets/labels.h', '<(src_loc)/ui/widgets/media_slider.cpp', '<(src_loc)/ui/widgets/media_slider.h', '<(src_loc)/ui/widgets/menu.cpp', @@ -518,6 +518,8 @@ '<(src_loc)/ui/widgets/multi_select.h', '<(src_loc)/ui/widgets/popup_menu.cpp', '<(src_loc)/ui/widgets/popup_menu.h', + '<(src_loc)/ui/widgets/scroll_area.cpp', + '<(src_loc)/ui/widgets/scroll_area.h', '<(src_loc)/ui/widgets/shadow.cpp', '<(src_loc)/ui/widgets/shadow.h', '<(src_loc)/ui/widgets/tooltip.cpp', @@ -532,12 +534,8 @@ '<(src_loc)/ui/emoji_config.h', '<(src_loc)/ui/filedialog.cpp', '<(src_loc)/ui/filedialog.h', - '<(src_loc)/ui/flatlabel.cpp', - '<(src_loc)/ui/flatlabel.h', '<(src_loc)/ui/images.cpp', '<(src_loc)/ui/images.h', - '<(src_loc)/ui/scrollarea.cpp', - '<(src_loc)/ui/scrollarea.h', '<(src_loc)/ui/twidget.cpp', '<(src_loc)/ui/twidget.h', '<(src_loc)/window/main_window.cpp', From cdef9fa14ff5f1e2ac59b1ec83710f929b41521a Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Nov 2016 15:06:02 +0300 Subject: [PATCH 041/100] Active state for IconButton added, when ripple is not hidden. --- Telegram/SourceFiles/dialogswidget.cpp | 28 +++++----- Telegram/SourceFiles/dialogswidget.h | 1 + Telegram/SourceFiles/historywidget.cpp | 2 +- Telegram/SourceFiles/mainwidget.h | 10 ++++ .../ui/effects/ripple_animation.cpp | 51 ++++++++++++++++++- .../SourceFiles/ui/effects/ripple_animation.h | 5 +- Telegram/SourceFiles/ui/widgets/buttons.cpp | 48 ++++++++++++----- Telegram/SourceFiles/ui/widgets/buttons.h | 9 ++++ .../SourceFiles/ui/widgets/inner_dropdown.cpp | 13 ++++- .../SourceFiles/ui/widgets/inner_dropdown.h | 8 +++ Telegram/SourceFiles/ui/widgets/widgets.style | 8 +-- .../SourceFiles/window/top_bar_widget.cpp | 51 ++++++++++++++----- Telegram/SourceFiles/window/top_bar_widget.h | 1 + Telegram/SourceFiles/window/window.style | 2 +- 14 files changed, 192 insertions(+), 45 deletions(-) diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 6e11b7f73..12965fc44 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1154,8 +1154,8 @@ bool DialogsInner::hasFilteredResults() const { } void DialogsInner::searchInPeer(PeerData *peer) { - _searchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : 0; - _searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0; + _searchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : nullptr; + _searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : nullptr; if (_searchInPeer) { onHashtagFilterUpdate(QStringRef()); _cancelSearchInPeer->show(); @@ -2021,9 +2021,7 @@ void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) { if ((_filter->getLastText() != query) || (inPeer && inPeer != _searchInPeer && inPeer->migrateTo() != _searchInPeer)) { if (inPeer) { onCancelSearch(); - _searchInPeer = inPeer->migrateTo() ? inPeer->migrateTo() : inPeer; - _searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0; - _inner->searchInPeer(_searchInPeer); + setSearchInPeer(inPeer); } _filter->setText(query); _filter->updatePlaceholder(); @@ -2309,12 +2307,20 @@ void DialogsWidget::onFilterUpdate(bool force) { void DialogsWidget::searchInPeer(PeerData *peer) { onCancelSearch(); - _searchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : 0; - _searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0; - _inner->searchInPeer(_searchInPeer); + setSearchInPeer(peer); onFilterUpdate(true); } +void DialogsWidget::setSearchInPeer(PeerData *peer) { + auto newSearchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : nullptr; + _searchInMigrated = newSearchInPeer ? newSearchInPeer->migrateFrom() : nullptr; + if (newSearchInPeer != _searchInPeer) { + _searchInPeer = newSearchInPeer; + App::main()->searchInPeerChanged().notify(_searchInPeer, true); + } + _inner->searchInPeer(_searchInPeer); +} + void DialogsWidget::onFilterCursorMoved(int from, int to) { if (to < 0) to = _filter->cursorPosition(); QString t = _filter->getLastText(); @@ -2527,8 +2533,7 @@ bool DialogsWidget::onCancelSearch() { if (Adaptive::OneColumn()) { Ui::showPeerHistory(_searchInPeer, ShowAtUnreadMsgId); } - _searchInPeer = _searchInMigrated = 0; - _inner->searchInPeer(0); + setSearchInPeer(nullptr); clearing = true; } _inner->clearFilter(); @@ -2547,8 +2552,7 @@ void DialogsWidget::onCancelSearchInPeer() { if (Adaptive::OneColumn() && !App::main()->selectingPeer()) { Ui::showPeerHistory(_searchInPeer, ShowAtUnreadMsgId); } - _searchInPeer = _searchInMigrated = 0; - _inner->searchInPeer(0); + setSearchInPeer(nullptr); } _inner->clearFilter(); _filter->clear(); diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 917f78dfc..11155fbf1 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -308,6 +308,7 @@ private slots: #endif // TDESKTOP_DISABLE_AUTOUPDATE private: + void setSearchInPeer(PeerData *peer); void showMainMenu(); void updateLockUnlockVisibility(); void updateControlsGeometry(); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 226120e05..2ae434f17 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -4362,7 +4362,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re App::main()->dlgUpdated(wasHistory, wasMsgId); emit historyShown(_history, _showAtMsgId); - App::main()->topBar()->update(); + App::main()->historyPeerChanged().notify(_peer, true); update(); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 1c8a279a5..947f229da 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -371,6 +371,13 @@ public: bool contentOverlapped(const QRect &globalRect); + base::Observable &searchInPeerChanged() { + return _searchInPeerChanged; + } + base::Observable &historyPeerChanged() { + return _historyPeerChanged; + } + void rpcClear() override; bool isItemVisible(HistoryItem *item); @@ -579,6 +586,9 @@ private: void clearCachedBackground(); + base::Observable _searchInPeerChanged; + base::Observable _historyPeerChanged; + Animation _a_show; QPixmap _cacheUnder, _cacheOver; anim::ivalue a_coordUnder, a_coordOver; diff --git a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp index 71823d0d9..c9c377e54 100644 --- a/Telegram/SourceFiles/ui/effects/ripple_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/ripple_animation.cpp @@ -26,10 +26,13 @@ namespace Ui { class RippleAnimation::Ripple { public: Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, UpdateCallback update); + Ripple(const style::RippleAnimation &st, const QPixmap &mask, UpdateCallback update); void paint(QPainter &p, const QPixmap &mask, uint64 ms); void stop(); + void unstop(); + void finish(); bool finished() const { return _hiding && !_hide.animating(); } @@ -72,6 +75,17 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, _show.start(UpdateCallback(_update), 0., 1., _st.showDuration); } +RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap &mask, UpdateCallback update) +: _st(st) +, _update(update) +, _origin(mask.width() / (2 * cIntRetinaFactor()), mask.height() / (2 * cIntRetinaFactor())) +, _radiusFrom(mask.width() + mask.height()) +, _frame(mask.size(), QImage::Format_ARGB32_Premultiplied) { + _frame.setDevicePixelRatio(mask.devicePixelRatio()); + _radiusTo = _radiusFrom; + _hide.start(UpdateCallback(_update), 0., 1., _st.hideDuration); +} + void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, uint64 ms) { auto opacity = _hide.current(ms, _hiding ? 0. : 1.); if (opacity == 0.) { @@ -110,6 +124,23 @@ void RippleAnimation::Ripple::stop() { _hide.start(UpdateCallback(_update), 1., 0., _st.hideDuration); } +void RippleAnimation::Ripple::unstop() { + if (_hiding) { + if (_hide.animating()) { + _hide.start(UpdateCallback(_update), 0., 1., _st.hideDuration); + } + _hiding = false; + } +} + +void RippleAnimation::Ripple::finish() { + if (_update) { + _update(); + } + _show.finish(); + _hide.finish(); +} + RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, UpdateCallback callback) : _st(st) , _mask(App::pixmapFromImageInPlace(std_::move(mask))) @@ -118,15 +149,33 @@ RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, void RippleAnimation::add(QPoint origin, int startRadius) { + lastStop(); _ripples.push_back(new Ripple(_st, origin, startRadius, _mask, _update)); } -void RippleAnimation::stopLast() { +void RippleAnimation::addFading() { + lastStop(); + _ripples.push_back(new Ripple(_st, _mask, _update)); +} + +void RippleAnimation::lastStop() { if (!_ripples.isEmpty()) { _ripples.back()->stop(); } } +void RippleAnimation::lastUnstop() { + if (!_ripples.isEmpty()) { + _ripples.back()->unstop(); + } +} + +void RippleAnimation::lastFinish() { + if (!_ripples.isEmpty()) { + _ripples.back()->finish(); + } +} + void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, uint64 ms) { if (_ripples.isEmpty()) { return; diff --git a/Telegram/SourceFiles/ui/effects/ripple_animation.h b/Telegram/SourceFiles/ui/effects/ripple_animation.h index ed531a3ff..2c95d1a3c 100644 --- a/Telegram/SourceFiles/ui/effects/ripple_animation.h +++ b/Telegram/SourceFiles/ui/effects/ripple_animation.h @@ -34,7 +34,10 @@ public: void setMask(QImage &&mask); void add(QPoint origin, int startRadius = 0); - void stopLast(); + void addFading(); + void lastStop(); + void lastUnstop(); + void lastFinish(); void paint(QPainter &p, int x, int y, int outerWidth, uint64 ms); diff --git a/Telegram/SourceFiles/ui/widgets/buttons.cpp b/Telegram/SourceFiles/ui/widgets/buttons.cpp index e382bdba5..99ba4c568 100644 --- a/Telegram/SourceFiles/ui/widgets/buttons.cpp +++ b/Telegram/SourceFiles/ui/widgets/buttons.cpp @@ -108,7 +108,7 @@ void FlatButton::handleRipples(bool wasDown, bool wasPress) { _ripple->add(clickPosition); } else if (!down && _ripple) { // Finish ripple anyway. - _ripple->stopLast(); + _ripple->lastStop(); } } @@ -311,7 +311,7 @@ void RoundButton::handleRipples(bool wasDown, bool wasPress) { _ripple->add(clickPosition); } else if (!down && _ripple) { // Finish ripple anyway. - _ripple->stopLast(); + _ripple->lastStop(); } } @@ -348,6 +348,26 @@ void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) { update(); } +void IconButton::setActiveState(bool activeState, SetStateWay way) { + if (_activeState != activeState) { + _activeState = activeState; + if (_activeState) { + ensureRipple(); + if (_ripple->empty()) { + _ripple->addFading(); + } else { + _ripple->lastUnstop(); + } + } else if (_ripple) { + _ripple->lastStop(); + } + } + if (way == SetStateWay::SkipAnimation && _ripple) { + _ripple->lastFinish(); + } + update(); +} + void IconButton::paintEvent(QPaintEvent *e) { Painter p(this); @@ -361,7 +381,7 @@ void IconButton::paintEvent(QPaintEvent *e) { } auto down = (_state & StateDown); - auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); + auto overIconOpacity = (down || _activeState) ? 1. : _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); auto overIcon = [this] { if (_iconOverrideOver) { return _iconOverrideOver; @@ -378,7 +398,7 @@ void IconButton::paintEvent(QPaintEvent *e) { } return &_st.icon; }; - auto icon = (over == 1. || down) ? overIcon() : justIcon(); + auto icon = (overIconOpacity == 1.) ? overIcon() : justIcon(); auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition; if (position.x() < 0) { position.setX((width() - icon->width()) / 2); @@ -387,10 +407,10 @@ void IconButton::paintEvent(QPaintEvent *e) { position.setY((height() - icon->height()) / 2); } icon->paint(p, position, width()); - if (over > 0. && over < 1.) { + if (overIconOpacity > 0. && overIconOpacity < 1.) { auto iconOver = overIcon(); if (iconOver != icon) { - p.setOpacity(over); + p.setOpacity(overIconOpacity); iconOver->paint(p, position, width()); } } @@ -417,11 +437,9 @@ void IconButton::handleRipples(bool wasDown, bool wasPress) { return; } - if (down && wasPress) { + if (down && wasPress && !_activeState) { // Start a ripple only from mouse press. - if (!_ripple) { - _ripple = std_::make_unique(_st.ripple, prepareRippleMask(), [this] { update(); }); - } + ensureRipple(); auto clickPosition = mapFromGlobal(QCursor::pos()); auto rippleCenter = QRect(_st.rippleAreaPosition, QSize(_st.rippleAreaSize, _st.rippleAreaSize)).center(); auto clickRadiusSquare = style::point::dotProduct(clickPosition - rippleCenter, clickPosition - rippleCenter); @@ -430,9 +448,15 @@ void IconButton::handleRipples(bool wasDown, bool wasPress) { startRadius = sqrt(clickRadiusSquare) - (_st.rippleAreaSize / 2); } _ripple->add(clickPosition - _st.rippleAreaPosition, startRadius); - } else if (!down && _ripple) { + } else if (!down && _ripple && !_activeState) { // Finish ripple anyway. - _ripple->stopLast(); + _ripple->lastStop(); + } +} + +void IconButton::ensureRipple() { + if (!_ripple) { + _ripple = std_::make_unique(_st.ripple, prepareRippleMask(), [this] { update(); }); } } diff --git a/Telegram/SourceFiles/ui/widgets/buttons.h b/Telegram/SourceFiles/ui/widgets/buttons.h index c9f5ca38c..a6bdffd30 100644 --- a/Telegram/SourceFiles/ui/widgets/buttons.h +++ b/Telegram/SourceFiles/ui/widgets/buttons.h @@ -139,6 +139,13 @@ public: // Pass nullptr to restore the default icon. void setIcon(const style::icon *icon, const style::icon *iconOver = nullptr); + // Displays full ripple circle constantly. + enum class SetStateWay { + Default, + SkipAnimation, + }; + void setActiveState(bool activeState, SetStateWay way = SetStateWay::Default); + ~IconButton(); protected: @@ -147,6 +154,7 @@ protected: void onStateChanged(int oldState, StateChangeSource source) override; private: + void ensureRipple(); QImage prepareRippleMask() const; void handleRipples(bool wasDown, bool wasPress); @@ -157,6 +165,7 @@ private: FloatAnimation _a_over; std_::unique_ptr _ripple; + bool _activeState = false; }; diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index 7f85bb168..5fb4abb3b 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -214,6 +214,14 @@ void InnerDropdown::prepareCache() { } void InnerDropdown::startOpacityAnimation(bool hiding) { + if (hiding) { + if (_hideStartCallback) { + _hideStartCallback(); + } + } else if (_showStartCallback) { + _showStartCallback(); + } + _hiding = false; prepareCache(); _hiding = hiding; @@ -234,6 +242,9 @@ void InnerDropdown::showStarted() { } void InnerDropdown::startShowAnimation() { + if (_showStartCallback) { + _showStartCallback(); + } if (!_a_show.animating()) { auto opacityAnimation = base::take(_a_opacity); showChildren(); @@ -289,7 +300,7 @@ bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) { otherEnter(); } else if (e->type() == QEvent::Leave) { otherLeave(); - } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton) { + } else if (e->type() == QEvent::MouseButtonRelease && static_cast(e)->button() == Qt::LeftButton) { if (isHidden() || _hiding) { otherEnter(); } else { diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h index b58974c92..ebb93fe59 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h @@ -48,6 +48,12 @@ public: void otherLeave(); void hideFast(); + void setShowStartCallback(base::lambda_unique callback) { + _showStartCallback = std_::move(callback); + } + void setHideStartCallback(base::lambda_unique callback) { + _hideStartCallback = std_::move(callback); + } void setHiddenCallback(base::lambda_unique callback) { _hiddenCallback = std_::move(callback); } @@ -110,6 +116,8 @@ private: QTimer _hideTimer; bool _ignoreShowEvents = false; + base::lambda_unique _showStartCallback; + base::lambda_unique _hideStartCallback; base::lambda_unique _hiddenCallback; ChildWidget _scroll; diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 769052e05..a15816d86 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -591,12 +591,12 @@ defaultLeftOutlineButton: OutlineButton { padding: margins(11px, 5px, 11px, 5px); } attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) { - outlineFgOver: #e43f3f; + outlineFgOver: attentionBoxButtonFg; - textBgOver: #faf2f2; + textBgOver: attentionBoxButtonBgOver; - textFg: #d15948; - textFgOver: #d15948; + textFg: attentionBoxButtonFg; + textFgOver: attentionBoxButtonFg; } defaultInputArea: InputArea { diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 92133fe09..bd5f8ba0b 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -53,6 +53,16 @@ TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w) _search->setClickedCallback([this] { onSearch(); }); _menuToggle->setClickedCallback([this] { showMenu(); }); + subscribe(w->searchInPeerChanged(), [this](PeerData *peer) { + _searchInPeer = peer; + auto historyPeer = App::main() ? App::main()->historyPeer() : nullptr; + _search->setActiveState(historyPeer && historyPeer == _searchInPeer); + }); + subscribe(w->historyPeerChanged(), [this](PeerData *peer) { + _search->setActiveState(peer && peer == _searchInPeer, Ui::IconButton::SetStateWay::SkipAnimation); + update(); + }); + subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); }); if (Adaptive::OneColumn()) { _unreadCounterSubscription = subscribe(Global::RefUnreadCounterUpdate(), [this] { @@ -92,20 +102,36 @@ void TopBarWidget::onSearch() { void TopBarWidget::showMenu() { if (auto main = App::main()) { if (auto peer = main->peer()) { - if (auto menu = _menu.ptr()) { - _menu = nullptr; - _menuToggle->removeEventFilter(menu); - menu->setHiddenCallback([menu] { menu->deleteLater(); }); - menu->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); - } else { + if (!_menu) { _menu.create(App::main()); + struct Data { + Ui::DropdownMenu *menu = nullptr; + QPointer that; + }; + auto data = MakeShared(); + data->that = weakThis(); + data->menu = _menu.ptr(); + _menu->setHiddenCallback([this, data] { + data->menu->deleteLater(); + if (data->that && _menu == data->menu) { + _menu = nullptr; + _menuToggle->setActiveState(false); + } + }); + _menu->setShowStartCallback([this, data] { + if (data->that && _menu == data->menu) { + _menuToggle->setActiveState(true); + } + }); + _menu->setHideStartCallback([this, data] { + if (data->that && _menu == data->menu) { + _menuToggle->setActiveState(false); + } + }); _menuToggle->installEventFilter(_menu); App::main()->fillPeerMenu(peer, [this](const QString &text, base::lambda_unique callback) { return _menu->addAction(text, std_::move(callback)); }); - _menu->setHiddenCallback([this] { - _menu.destroyDelayed(); - }); _menu->moveToRight(st::topBarMenuPosition.x(), st::topBarMenuPosition.y()); _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); } @@ -261,7 +287,8 @@ void TopBarWidget::showAll() { resizeEvent(nullptr); return; } - PeerData *h = App::main() ? App::main()->historyPeer() : 0, *o = App::main() ? App::main()->overviewPeer() : 0; + auto historyPeer = App::main() ? App::main()->historyPeer() : nullptr; + auto overviewPeer = App::main() ? App::main()->overviewPeer() : nullptr; if (_selCount) { _clearSelection->show(); if (_canDelete) { @@ -281,9 +308,9 @@ void TopBarWidget::showAll() { _mediaType->hide(); } } - if (h && !o && _clearSelection->isHidden()) { + if (historyPeer && !overviewPeer && _clearSelection->isHidden()) { if (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) { - _info->setPeer(h); + _info->setPeer(historyPeer); _info->show(); _menuToggle->hide(); _menu.destroy(); diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index 5fdd48fef..1e06e8bf6 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -78,6 +78,7 @@ private: anim::fvalue a_over = { 0. }; Animation _a_appearance; + PeerData *_searchInPeer = nullptr; PeerData *_selPeer = nullptr; int _selCount = 0; bool _canDelete = false; diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 5b27d9a8b..cc2623baa 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -172,7 +172,7 @@ titleButtonClose: IconButton(titleButtonMinimize) { // Legacy top bar. topBarHeight: 54px; -topBarMenuPosition: point(-2px, 37px); +topBarMenuPosition: point(-2px, 35px); topBarDuration: 200; topBarBackward: icon {{ "title_back", #a3a3a3 }}; topBarForwardAlpha: 0.6; From 07689476a6d7f94e8034cc9061de4ebea7261bd9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Nov 2016 19:04:25 +0300 Subject: [PATCH 042/100] Closed beta 10019008: Some more ripple animations added. --- Telegram/Resources/colors.palette | 11 +- Telegram/Resources/sample.tdesktop-theme | 9 +- Telegram/Resources/winrc/Telegram.rc | 8 +- Telegram/Resources/winrc/Updater.rc | 8 +- Telegram/SourceFiles/boxes/autolockbox.h | 2 +- Telegram/SourceFiles/boxes/backgroundbox.h | 2 +- Telegram/SourceFiles/boxes/boxes.style | 28 +- Telegram/SourceFiles/boxes/connectionbox.h | 2 +- Telegram/SourceFiles/boxes/contactsbox.h | 4 +- Telegram/SourceFiles/boxes/downloadpathbox.h | 2 +- Telegram/SourceFiles/boxes/emojibox.h | 2 +- Telegram/SourceFiles/boxes/languagebox.h | 2 +- Telegram/SourceFiles/boxes/localstoragebox.h | 2 +- Telegram/SourceFiles/boxes/members_box.cpp | 28 ++ Telegram/SourceFiles/boxes/members_box.h | 20 +- .../SourceFiles/boxes/notifications_box.h | 2 +- Telegram/SourceFiles/boxes/passcodebox.h | 2 +- Telegram/SourceFiles/boxes/photocropbox.h | 2 +- Telegram/SourceFiles/boxes/photosendbox.h | 2 +- Telegram/SourceFiles/boxes/report_box.h | 2 +- Telegram/SourceFiles/boxes/sessionsbox.h | 2 +- Telegram/SourceFiles/boxes/sharebox.h | 2 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 79 +++- Telegram/SourceFiles/boxes/stickers_box.h | 11 +- Telegram/SourceFiles/boxes/stickersetbox.h | 2 +- Telegram/SourceFiles/boxes/usernamebox.h | 2 +- Telegram/SourceFiles/core/version.h | 2 +- Telegram/SourceFiles/dialogs/dialogs.style | 11 +- Telegram/SourceFiles/history/history.style | 63 ++- Telegram/SourceFiles/history/history_item.cpp | 88 +++-- Telegram/SourceFiles/history/history_item.h | 37 +- .../SourceFiles/history/history_message.cpp | 11 +- .../SourceFiles/history/history_message.h | 4 +- Telegram/SourceFiles/historywidget.cpp | 21 +- Telegram/SourceFiles/historywidget.h | 4 +- Telegram/SourceFiles/intro/intro.style | 3 - .../media/player/media_player.style | 2 - .../SourceFiles/media/view/mediaview.style | 7 +- .../profile/profile_actions_widget.cpp | 2 +- .../profile/profile_members_widget.cpp | 2 +- .../profile/profile_settings_widget.cpp | 2 +- .../profile/profile_shared_media_widget.cpp | 2 +- Telegram/SourceFiles/settings/settings.style | 1 - Telegram/SourceFiles/stickers/emoji_pan.cpp | 63 ++- Telegram/SourceFiles/stickers/emoji_pan.h | 4 + Telegram/SourceFiles/stickers/stickers.style | 5 - .../ui/buttons/history_down_button.cpp | 61 ++- .../ui/buttons/history_down_button.h | 12 +- .../ui/buttons/left_outline_button.cpp | 71 ---- .../ui/buttons/left_outline_button.h | 49 --- .../ui/effects/ripple_animation.cpp | 29 ++ .../SourceFiles/ui/effects/ripple_animation.h | 5 + Telegram/SourceFiles/ui/widgets/buttons.cpp | 361 ++++++++---------- Telegram/SourceFiles/ui/widgets/buttons.h | 154 +++++--- .../SourceFiles/ui/widgets/dropdown_menu.cpp | 9 + .../SourceFiles/ui/widgets/dropdown_menu.h | 4 + Telegram/SourceFiles/ui/widgets/menu.cpp | 65 +++- Telegram/SourceFiles/ui/widgets/menu.h | 13 + .../SourceFiles/ui/widgets/popup_menu.cpp | 9 + Telegram/SourceFiles/ui/widgets/popup_menu.h | 4 + Telegram/SourceFiles/ui/widgets/widgets.style | 81 ++-- .../SourceFiles/window/top_bar_widget.cpp | 10 +- Telegram/SourceFiles/window/window.style | 11 +- .../SourceFiles/window/window_main_menu.cpp | 1 + Telegram/build/version | 2 +- Telegram/gyp/Telegram.gyp | 2 - 66 files changed, 845 insertions(+), 680 deletions(-) delete mode 100644 Telegram/SourceFiles/ui/buttons/left_outline_button.cpp delete mode 100644 Telegram/SourceFiles/ui/buttons/left_outline_button.h diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 2547b6c57..033868bc1 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -53,6 +53,11 @@ lightButtonBgRipple: #c9e4f6; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; +attentionButtonFg: #d14e4e; +attentionButtonFgOver: #d14e4e; +attentionButtonBgOver: #fcdfde; +attentionButtonBgRipple: #f4c3c2; + menuBg: windowBg; menuBgOver: windowBgOver; menuBgRipple: windowBgRipple; @@ -99,11 +104,6 @@ boxBlockTitleAdditionalFg: #808080; boxBlockTitleCloseFg: cancelIconFg; boxBlockTitleCloseFgOver: cancelIconFgOver; -attentionBoxButtonFg: #d14e4e; -attentionBoxButtonFgOver: #d14e4e; -attentionBoxButtonBgOver: #fcdfde; -attentionBoxButtonBgRipple: #f4c3c2; - membersAboutLimitFg: windowSubTextFgOver; contactsBg: windowBg; @@ -228,6 +228,7 @@ mediaviewFileExtFg: activeButtonFg; mediaviewMenuBg: #383838; mediaviewMenuBgOver: #505050; +mediaviewMenuBgRipple: #676767; mediaviewMenuFg: #ffffff; mediaviewBg: #222222eb; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index 33f587522..fdcb3084b 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -49,6 +49,10 @@ lightButtonBgOver: #e3f1fa; lightButtonBgRipple: #c9e4f6; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; +attentionButtonFg: #d14e4e; +attentionButtonFgOver: #d14e4e; +attentionButtonBgOver: #fcdfde; +attentionButtonBgRipple: #f4c3c2; menuBg: windowBg; menuBgOver: windowBgOver; menuBgRipple: windowBgRipple; @@ -84,10 +88,6 @@ boxBlockTitleFg: boxTitleFg; boxBlockTitleAdditionalFg: #808080; boxBlockTitleCloseFg: cancelIconFg; boxBlockTitleCloseFgOver: cancelIconFgOver; -attentionBoxButtonFg: #d14e4e; -attentionBoxButtonFgOver: #d14e4e; -attentionBoxButtonBgOver: #fcdfde; -attentionBoxButtonBgRipple: #f4c3c2; membersAboutLimitFg: windowSubTextFgOver; contactsBg: windowBg; contactsBgOver: windowBgOver; @@ -189,6 +189,7 @@ mediaviewFileBlueCornerFg: #599dcf; mediaviewFileExtFg: activeButtonFg; mediaviewMenuBg: #383838; mediaviewMenuBgOver: #505050; +mediaviewMenuBgRipple: #676767; mediaviewMenuFg: #ffffff; mediaviewBg: #222222eb; mediaviewVideoBg: #000000; diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index f0e42625a..6f364b393 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,7 - PRODUCTVERSION 0,10,19,7 + FILEVERSION 0,10,19,8 + PRODUCTVERSION 0,10,19,8 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.19.7" + VALUE "FileVersion", "0.10.19.8" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.7" + VALUE "ProductVersion", "0.10.19.8" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 16592aafb..79ff23db8 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,7 - PRODUCTVERSION 0,10,19,7 + FILEVERSION 0,10,19,8 + PRODUCTVERSION 0,10,19,8 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.19.7" + VALUE "FileVersion", "0.10.19.8" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.7" + VALUE "ProductVersion", "0.10.19.8" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/boxes/autolockbox.h b/Telegram/SourceFiles/boxes/autolockbox.h index e5b6e6c92..9ef8c3132 100644 --- a/Telegram/SourceFiles/boxes/autolockbox.h +++ b/Telegram/SourceFiles/boxes/autolockbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class Radiobutton; diff --git a/Telegram/SourceFiles/boxes/backgroundbox.h b/Telegram/SourceFiles/boxes/backgroundbox.h index 500ad61d8..b495780a3 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.h +++ b/Telegram/SourceFiles/boxes/backgroundbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" #include "core/lambda_wrap.h" class BackgroundBox : public ItemListBox { diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 42481fa7c..138f20a75 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -37,7 +37,6 @@ defaultBoxButton: RoundButton { padding: margins(0px, 0px, 0px, 0px); textTop: 8px; - downTextTop: 8px; font: boxButtonFont; @@ -51,12 +50,12 @@ cancelBoxButton: RoundButton(defaultBoxButton) { } attentionBoxButton: RoundButton(defaultBoxButton) { - textFg: attentionBoxButtonFg; - textFgOver: attentionBoxButtonFgOver; - textBgOver: attentionBoxButtonBgOver; + textFg: attentionButtonFg; + textFgOver: attentionButtonFgOver; + textBgOver: attentionButtonBgOver; ripple: RippleAnimation(defaultRippleAnimation) { - color: attentionBoxButtonBgRipple; + color: attentionButtonBgRipple; } } @@ -198,12 +197,21 @@ contactUserIcon: icon {{ "add_contact_user", #999999 }}; contactPhoneIcon: icon {{ "add_contact_phone", #999999 }}; contactIconTop: 10px; -contactsAdd: IconButton { +contactsAddIconAbove: icon {{ "contacts_add", activeButtonFg, point(18px, 18px) }}; +contactsAdd: TwoIconButton { width: 52px; height: 52px; - icon: contactsAddIcon; - iconOver: contactsAddIconOver; + iconBelow: contactsAddIconBelow; + iconBelowOver: contactsAddIconBelowOver; + iconAbove: contactsAddIconAbove; + iconAboveOver: contactsAddIconAbove; + + rippleAreaPosition: point(5px, 5px); + rippleAreaSize: 42px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: activeButtonBgRipple; + } } contactsAddPosition: point(14px, 8px); @@ -289,9 +297,7 @@ contactsMultiSelect: MultiSelect { icon: boxSearchCancelIcon; iconOver: boxSearchCancelIconOver; - iconPosition: point(8px, 18px); - iconPositionDown: point(8px, 18px); } fieldCancelSkip: 34px; } @@ -370,9 +376,7 @@ sessionTerminate: IconButton { icon: simpleCloseIcon; iconOver: simpleCloseIconOver; - iconPosition: point(3px, 3px); - iconPositionDown: point(3px, 4px); } sessionTerminateAllButton: LinkButton(boxLinkButton) { color: #d15948; diff --git a/Telegram/SourceFiles/boxes/connectionbox.h b/Telegram/SourceFiles/boxes/connectionbox.h index 65ae47359..346fcdde4 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.h +++ b/Telegram/SourceFiles/boxes/connectionbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class InputField; diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 41d9520d9..2dd846199 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" #include "core/single_timer.h" #include "ui/effects/round_image_checkbox.h" #include "boxes/members_box.h" @@ -93,7 +93,7 @@ private: class Inner; ChildWidget _inner; ChildWidget> _select; - ChildWidget _add = { nullptr }; + ChildWidget _add = { nullptr }; ChildWidget _next; ChildWidget _cancel; diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.h b/Telegram/SourceFiles/boxes/downloadpathbox.h index 070e8889b..4d543da4b 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.h +++ b/Telegram/SourceFiles/boxes/downloadpathbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" #include "core/observer.h" namespace Ui { diff --git a/Telegram/SourceFiles/boxes/emojibox.h b/Telegram/SourceFiles/boxes/emojibox.h index a04b49378..4dad5370f 100644 --- a/Telegram/SourceFiles/boxes/emojibox.h +++ b/Telegram/SourceFiles/boxes/emojibox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" class EmojiBox : public AbstractBox { Q_OBJECT diff --git a/Telegram/SourceFiles/boxes/languagebox.h b/Telegram/SourceFiles/boxes/languagebox.h index 53a5852e0..860fc5ac9 100644 --- a/Telegram/SourceFiles/boxes/languagebox.h +++ b/Telegram/SourceFiles/boxes/languagebox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class Radiobutton; diff --git a/Telegram/SourceFiles/boxes/localstoragebox.h b/Telegram/SourceFiles/boxes/localstoragebox.h index 178135d9e..9bb5120e8 100644 --- a/Telegram/SourceFiles/boxes/localstoragebox.h +++ b/Telegram/SourceFiles/boxes/localstoragebox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class RoundButton; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 9120cadac..737818fe5 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -30,8 +30,36 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" +#include "ui/effects/ripple_animation.h" #include "observer_peer.h" + +MembersAddButton::MembersAddButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple) +, _st(st) { + resize(_st.width, _st.height); + setCursor(style::cur_pointer); +} + +void MembersAddButton::paintEvent(QPaintEvent *e) { + Painter p(this); + + auto ms = getms(); + auto over = (_state & StateOver); + auto down = (_state & StateDown); + + ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width()); + paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms); + ((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width()); +} + +QImage MembersAddButton::prepareRippleMask() const { + return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize)); +} + +QPoint MembersAddButton::prepareRippleStartPosition() const { + return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition; +} + MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll) , _inner(this, channel, filter) { ItemListBox::init(_inner); diff --git a/Telegram/SourceFiles/boxes/members_box.h b/Telegram/SourceFiles/boxes/members_box.h index b2bdffbdd..809f8408e 100644 --- a/Telegram/SourceFiles/boxes/members_box.h +++ b/Telegram/SourceFiles/boxes/members_box.h @@ -20,9 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" #include "core/single_timer.h" #include "ui/effects/round_image_checkbox.h" +#include "ui/widgets/buttons.h" class ContactsBox; class ConfirmBox; @@ -33,6 +34,21 @@ enum class MembersFilter { }; using MembersAlreadyIn = OrderedSet; +class MembersAddButton : public Ui::RippleButton { +public: + MembersAddButton(QWidget *parent, const style::TwoIconButton &st); + +protected: + void paintEvent(QPaintEvent *e) override; + + QImage prepareRippleMask() const override; + QPoint prepareRippleStartPosition() const override; + +private: + const style::TwoIconButton &_st; + +}; + class MembersBox : public ItemListBox { Q_OBJECT @@ -54,7 +70,7 @@ private: class Inner; ChildWidget _inner; - ChildWidget _add = { nullptr }; + ChildWidget _add = { nullptr }; ContactsBox *_addBox = nullptr; diff --git a/Telegram/SourceFiles/boxes/notifications_box.h b/Telegram/SourceFiles/boxes/notifications_box.h index cd9114483..a6c948819 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.h +++ b/Telegram/SourceFiles/boxes/notifications_box.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class RoundButton; diff --git a/Telegram/SourceFiles/boxes/passcodebox.h b/Telegram/SourceFiles/boxes/passcodebox.h index 9c15dbb94..251da9c91 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.h +++ b/Telegram/SourceFiles/boxes/passcodebox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class InputField; diff --git a/Telegram/SourceFiles/boxes/photocropbox.h b/Telegram/SourceFiles/boxes/photocropbox.h index 470344acd..65671f206 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.h +++ b/Telegram/SourceFiles/boxes/photocropbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class RoundButton; diff --git a/Telegram/SourceFiles/boxes/photosendbox.h b/Telegram/SourceFiles/boxes/photosendbox.h index 64dd01048..0d1588430 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.h +++ b/Telegram/SourceFiles/boxes/photosendbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" #include "localimageloader.h" namespace Ui { diff --git a/Telegram/SourceFiles/boxes/report_box.h b/Telegram/SourceFiles/boxes/report_box.h index b02bee021..28520ee20 100644 --- a/Telegram/SourceFiles/boxes/report_box.h +++ b/Telegram/SourceFiles/boxes/report_box.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class Radiobutton; diff --git a/Telegram/SourceFiles/boxes/sessionsbox.h b/Telegram/SourceFiles/boxes/sessionsbox.h index b3d8c96df..141a4adda 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.h +++ b/Telegram/SourceFiles/boxes/sessionsbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" #include "core/single_timer.h" class ConfirmBox; diff --git a/Telegram/SourceFiles/boxes/sharebox.h b/Telegram/SourceFiles/boxes/sharebox.h index f986e14b6..c24e93e26 100644 --- a/Telegram/SourceFiles/boxes/sharebox.h +++ b/Telegram/SourceFiles/boxes/sharebox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" #include "core/lambda_wrap.h" #include "core/observer.h" #include "core/vector_of_moveable.h" diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 6b8514fa2..d883fb7cc 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -33,6 +33,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_stickers.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" +#include "ui/effects/ripple_animation.h" namespace { @@ -563,6 +564,7 @@ void StickersBox::Inner::paintEvent(QPaintEvent *e) { _a_shifting.step(); + auto ms = getms(); p.fillRect(r, st::boxBg); p.setClipRect(r); @@ -591,19 +593,26 @@ void StickersBox::Inner::paintEvent(QPaintEvent *e) { p.translate(0, from * _rowHeight); for (int32 i = from; i < to; ++i) { if (i != _above) { - paintRow(p, i); + paintRow(p, i, ms); } p.translate(0, _rowHeight); } if (from <= _above && _above < to) { p.translate(0, (_above - to) * _rowHeight); - paintRow(p, _above); + paintRow(p, _above, ms); } } } -void StickersBox::Inner::paintRow(Painter &p, int32 index) { - const StickerSetRow *s(_rows.at(index)); +QRect StickersBox::Inner::relativeAddButtonRect() const { + int addw = st::stickersAddSize.width(); + int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; + int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; + return QRect(addx, addy, addw, st::stickersAddSize.height()); +} + +void StickersBox::Inner::paintRow(Painter &p, int32 index, uint64 ms) { + auto s = _rows.at(index); int32 xadd = 0, yadd = s->yadd.current(); if (xadd || yadd) p.translate(xadd, yadd); @@ -639,17 +648,23 @@ void StickersBox::Inner::paintRow(Painter &p, int32 index) { int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (addw + st::stickersFeaturedInstalled.width()) / 2); int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2; st::stickersFeaturedInstalled.paint(p, QPoint(checkx, checky), width()); + if (s->ripple) { + s->ripple.reset(); + } } else { - int addw = st::stickersAddSize.width(); - int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; - int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; - QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height())); + auto relativeAdd = relativeAddButtonRect(); + auto add = myrtlrect(relativeAdd); - auto &textBg = (_actionSel == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg; + auto &textBg = (_actionSel == index || _actionDown == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg; App::roundRect(p, add, textBg, ImageRoundRadius::Small); - int iconx = addx + (st::stickersAddSize.width() - st::stickersAddIcon.width()) / 2; - int icony = addy + (st::stickersAddSize.height() - st::stickersAddIcon.height()) / 2; - icony += (_actionSel == index && _actionDown == index) ? (st::defaultActiveButton.downTextTop - st::defaultActiveButton.textTop) : 0; + if (s->ripple) { + s->ripple->paint(p, relativeAdd.x(), relativeAdd.y(), width(), ms); + if (s->ripple->empty()) { + s->ripple.reset(); + } + } + int iconx = relativeAdd.x() + (st::stickersAddSize.width() - st::stickersAddIcon.width()) / 2; + int icony = relativeAdd.y() + (st::stickersAddSize.height() - st::stickersAddIcon.height()) / 2; st::stickersAddIcon.paint(p, QPoint(iconx, icony), width()); } @@ -696,7 +711,7 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) { _pressed = _selected; if (_actionSel >= 0) { - _actionDown = _actionSel; + setActionDown(_actionSel); update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight); } else if (_selected >= 0 && _section == Section::Installed && !_rows.at(_selected)->recent) { _above = _dragging = _started = _selected; @@ -704,6 +719,33 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) { } } +void StickersBox::Inner::setActionDown(int newActionDown) { + if (_actionDown == newActionDown) { + return; + } + if (_actionDown >= 0 && _actionDown < _rows.size()) { + update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight); + auto set = _rows[_actionDown]; + if (set->ripple) { + set->ripple->lastStop(); + } + } + _actionDown = newActionDown; + if (_actionDown >= 0 && _actionDown < _rows.size()) { + update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight); + auto set = _rows[_actionDown]; + if (!set->ripple) { + auto mask = Ui::RippleAnimation::roundRectMask(st::stickersAddSize, st::buttonRadius); + set->ripple = MakeShared(st::defaultActiveButton.ripple, std_::move(mask), [this, index = _actionDown] { + update(0, _itemsTop + index * _rowHeight, width(), _rowHeight); + }); + } + auto relativeAdd = relativeAddButtonRect(); + auto add = myrtlrect(relativeAdd); + set->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(add.x(), _itemsTop + _actionDown * _rowHeight + add.y())); + } +} + void StickersBox::Inner::mouseMoveEvent(QMouseEvent *e) { if (_saving) return; _mouse = e->globalPos(); @@ -762,10 +804,8 @@ void StickersBox::Inner::onUpdateSelected() { } else if (_rows.at(selected)->installed && !_rows.at(selected)->disabled) { actionSel = -1; } else { - int addw = st::stickersAddSize.width(); - int addx = width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - addw; - int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; - QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height())); + auto relativeAdd = relativeAddButtonRect(); + auto add = myrtlrect(relativeAdd); actionSel = add.contains(local.x(), local.y() - _itemsTop - selected * _rowHeight) ? selected : -1; } } else if (_hasFeaturedButton && QRect(0, st::membersPadding.top(), width(), _buttonHeight).contains(local)) { @@ -878,10 +918,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { } } } - if (_actionDown >= 0) { - update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight); - _actionDown = -1; - } + setActionDown(-1); } void StickersBox::Inner::leaveEvent(QEvent *e) { diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h index aed6e99f6..562f30780 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.h +++ b/Telegram/SourceFiles/boxes/stickers_box.h @@ -20,13 +20,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" class ConfirmBox; namespace Ui { class PlainShadow; class RoundButton; +class RippleAnimation; } // namespace Ui class StickersBox : public ItemListBox, public RPCSender { @@ -150,11 +151,13 @@ private slots: void onImageLoaded(); private: + void setActionDown(int newActionDown); void setup(); + QRect relativeAddButtonRect() const; void paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const; void step_shifting(uint64 ms, bool timer); - void paintRow(Painter &p, int32 index); + void paintRow(Painter &p, int32 index, uint64 ms); void clear(); void setActionSel(int32 actionSel); float64 aboveShadowOpacity() const; @@ -192,6 +195,7 @@ private: bool installed, official, unread, disabled, recent; int32 pixw, pixh; anim::ivalue yadd; + QSharedPointer ripple; }; using StickerSetRows = QList; @@ -236,5 +240,6 @@ private: Ui::RectShadow _aboveShadow; - int32 _scrollbar = 0; + int _scrollbar = 0; + }; diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h index 7c052f622..52ebf0e47 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.h +++ b/Telegram/SourceFiles/boxes/stickersetbox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" #include "core/vector_of_moveable.h" class ConfirmBox; diff --git a/Telegram/SourceFiles/boxes/usernamebox.h b/Telegram/SourceFiles/boxes/usernamebox.h index d26a62fc2..afe476fd0 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.h +++ b/Telegram/SourceFiles/boxes/usernamebox.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "abstractbox.h" +#include "boxes/abstractbox.h" namespace Ui { class UsernameInput; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index fb6796711..99f826d4a 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019007ULL) +#define BETA_VERSION_MACRO (10019008ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 64b0d7ec8..004741066 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -84,7 +84,6 @@ dialogsMenuToggle: IconButton { icon: icon {{ "dialogs_menu", dialogsMenuIconFg }}; iconOver: icon {{ "dialogs_menu", dialogsMenuIconFgOver }}; iconPosition: point(10px, 10px); - iconPositionDown: point(10px, 10px); rippleAreaPosition: point(0px, 0px); rippleAreaSize: 40px; @@ -112,18 +111,14 @@ dialogsFilter: FlatInput(defaultFlatInput) { dialogsCancelSearchInPeer: IconButton(dialogsMenuToggle) { icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg }}; iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver }}; - iconPosition: point(11px, 11px); - iconPositionDown: point(11px, 11px); } dialogsCancelSearch: IconButton(dialogsCancelSearchInPeer) { width: 32px; height: 32px; - iconPosition: point(7px, 7px); - iconPositionDown: point(7px, 7px); - rippleAreaSize: 0px; + ripple: emptyRippleAnimation; } dialogsChatTypeSkip: 22px; @@ -177,8 +172,6 @@ dialogsUpdateButton: FlatButton { height: 46px; textTop: 14px; - overTextTop: 14px; - downTextTop: 15px; font: semiboldFont; overFont: semiboldFont; @@ -197,8 +190,6 @@ dialogsForwardCancel: IconButton { icon: dialogsForwardCancelIcon; iconOver: dialogsForwardCancelIcon; - iconPosition: point(12px, 11px); - iconPositionDown: point(12px, 11px); } dialogsForwardFont: semiboldFont; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 63a650f52..a05310bc4 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -22,14 +22,6 @@ using "basic.style"; using "dialogs/dialogs.style"; using "ui/widgets/widgets.style"; -BotKeyboardButton { - margin: pixels; - padding: pixels; - height: pixels; - textTop: pixels; - downTextTop: pixels; -} - historyScroll: FlatScroll(defaultFlatScroll) { barColor: #89a0b47a; bgColor: #89a0b44c; @@ -47,13 +39,28 @@ historyScroll: FlatScroll(defaultFlatScroll) { bottomsh: -1px; } -historyPaddingBottom: 10px; +historyPaddingBottom: 8px; historyToDownPosition: point(12px, 10px); -historyToDownArrow: icon { - { "history_down_arrow", #b9b9b9, point(17px, 23px) }, -}; +historyToDownAbove: icon {{ "history_down_arrow", #b9b9b9, point(17px, 23px) }}; +historyToDownAboveOver: icon {{ "history_down_arrow", #a3a3a3, point(17px, 23px) }}; historyToDownPaddingTop: 10px; +historyToDown: TwoIconButton { + width: 52px; + height: 62px; + + iconBelow: historyToDownBelow; + iconBelowOver: historyToDownBelowOver; + iconAbove: historyToDownAbove; + iconAboveOver: historyToDownAboveOver; + iconPosition: point(0px, historyToDownPaddingTop); + + rippleAreaPosition: point(5px, 15px); + rippleAreaSize: 42px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgRipple; + } +} historyToDownBadgeFont: semiboldFont; historyToDownBadgeSize: 22px; @@ -196,8 +203,6 @@ historyComposeButton: FlatButton { height: 46px; textTop: 14px; - overTextTop: 14px; - downTextTop: 14px; font: semiboldFont; overFont: semiboldFont; @@ -217,9 +222,7 @@ historySend: IconButton { icon: icon {{ "send_control_send", historySendIconFg }}; iconOver: icon {{ "send_control_send", historySendIconFgOver }}; - iconPosition: point(11px, 11px); - iconPositionDown: point(11px, 11px); } historyEditSaveIcon: icon {{ "send_control_save", historySendIconFg, point(3px, 7px) }}; historyEditSaveIconOver: icon {{ "send_control_save", historySendIconFgOver, point(3px, 7px) }}; @@ -242,11 +245,9 @@ historyAttachPhotoIconOver: icon {{ "media_type_photo", historyComposeIconFgOver historyAttachEmoji: IconButton(historyAttach) { icon: icon {{ "send_control_emoji", historyComposeIconFg }}; iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }}; - iconPosition: point(15px, 15px); - iconPositionDown: point(15px, 15px); - rippleAreaSize: 0px; + ripple: emptyRippleAnimation; } historyEmojiCircle: size(20px, 20px); historyEmojiCirclePeriod: 1500; @@ -263,9 +264,7 @@ historyBotKeyboardShow: IconButton(historySend) { historyBotKeyboardHide: IconButton(historySend) { icon: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFg }}; iconOver: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFgOver }}; - iconPosition: point(11px, 16px); - iconPositionDown: point(11px, 16px); } historyBotCommandStart: IconButton(historySend) { icon: icon {{ "send_control_bot_command", historyComposeIconFg }}; @@ -319,9 +318,7 @@ historyReplyCancel: IconButton { icon: historyReplyCancelIcon; iconOver: historyReplyCancelIconOver; - iconPosition: point(-1px, -1px); - iconPositionDown: point(-1px, -1px); rippleAreaPosition: point(4px, 4px); rippleAreaSize: 40px; @@ -349,8 +346,6 @@ reportSpamHide: FlatButton { height: 46px; textTop: 15px; - overTextTop: 15px; - downTextTop: 16px; font: font(fsize); overFont: font(fsize underline); @@ -361,7 +356,7 @@ reportSpamFg: #000000; msgBotKbDuration: 200; msgBotKbFont: semiboldFont; -msgBotKbOverBg: #ffffff1a; +msgBotKbOverBg: #ffffff20; msgBotKbIconPadding: 2px; msgBotKbUrlIcon: icon {{ "inline_button_url", #ffffff }}; msgBotKbSwitchPmIcon: icon {{ "inline_button_switch", #ffffff }}; @@ -370,28 +365,30 @@ msgBotKbButton: BotKeyboardButton { padding: 10px; height: 36px; textTop: 8px; - downTextTop: 9px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: #00000020; + } } botKbDuration: 200; -botKbBg: #edf1f5; -botKbOverBg: #d8e2ec; -botKbDownBg: #d8e2ec; -botKbColor: #4b565f; +botKbBg: menuBgOver; +botKbOverBg: menuBgOver; +botKbDownBg: menuBgRipple; +botKbColor: windowBoldFgOver; botKbFont: font(15px semibold); botKbButton: BotKeyboardButton { margin: 10px; padding: 10px; height: 38px; textTop: 9px; - downTextTop: 9px; + ripple: defaultRippleAnimation; } botKbTinyButton: BotKeyboardButton { margin: 4px; padding: 3px; height: 25px; textTop: 2px; - downTextTop: 2px; + ripple: defaultRippleAnimation; } botKbScroll: FlatScroll(defaultSolidScroll) { deltax: 3px; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 007c2cd88..342958c57 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/media_clip_reader.h" #include "styles/style_dialogs.h" #include "styles/style_history.h" +#include "ui/effects/ripple_animation.h" #include "fileuploader.h" namespace { @@ -221,7 +222,7 @@ int ReplyKeyboard::naturalHeight() const { return (_rows.size() - 1) * _st->buttonSkip() + _rows.size() * _st->buttonHeight(); } -void ReplyKeyboard::paint(Painter &p, int outerWidth, const QRect &clip) const { +void ReplyKeyboard::paint(Painter &p, int outerWidth, const QRect &clip, uint64 ms) const { t_assert(_st != nullptr); t_assert(_width > 0); @@ -235,7 +236,7 @@ void ReplyKeyboard::paint(Painter &p, int outerWidth, const QRect &clip) const { // just ignore the buttons that didn't layout well if (rect.x() + rect.width() > _width) break; - _st->paintButton(p, outerWidth, button); + _st->paintButton(p, outerWidth, button, ms); } } } @@ -251,6 +252,7 @@ ClickHandlerPtr ReplyKeyboard::getState(int x, int y) const { if (rect.x() + rect.width() > _width) break; if (rect.contains(x, y)) { + _savedCoords = QPoint(x, y); return button.link; } } @@ -261,34 +263,63 @@ ClickHandlerPtr ReplyKeyboard::getState(int x, int y) const { void ReplyKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { if (!p) return; - bool startAnimation = false; + _savedActive = active ? p : ClickHandlerPtr(); + auto coords = findButtonCoordsByClickHandler(p); + if (coords.i >= 0 && _savedPressed != p) { + startAnimation(coords.i, coords.j, active ? 1 : -1); + } +} + + +ReplyKeyboard::ButtonCoords ReplyKeyboard::findButtonCoordsByClickHandler(const ClickHandlerPtr &p) { for (int i = 0, rows = _rows.size(); i != rows; ++i) { - auto &row = _rows.at(i); + auto &row = _rows[i]; for (int j = 0, cols = row.size(); j != cols; ++j) { - if (row.at(j).link == p) { - bool startAnimation = _animations.isEmpty(); + if (row[j].link == p) { + return { i, j }; + } + } + } + return { -1, -1 }; +} - int indexForAnimation = i * MatrixRowShift + j + 1; - if (!active) { - indexForAnimation = -indexForAnimation; - } +void ReplyKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + if (!p) return; - _animations.remove(-indexForAnimation); - if (!_animations.contains(indexForAnimation)) { - _animations.insert(indexForAnimation, getms()); - } - - if (startAnimation && !_a_selected.animating()) { - _a_selected.start(); - } - return; + _savedPressed = pressed ? p : ClickHandlerPtr(); + auto coords = findButtonCoordsByClickHandler(p); + if (coords.i >= 0) { + auto &button = _rows[coords.i][coords.j]; + if (pressed) { + if (!button.ripple) { + auto mask = Ui::RippleAnimation::roundRectMask(button.rect.size(), _st->buttonRadius()); + button.ripple = MakeShared(_st->_st->ripple, std_::move(mask), [this] { _st->repaint(_item); }); + } + button.ripple->add(_savedCoords - button.rect.topLeft()); + } else { + if (button.ripple) { + button.ripple->lastStop(); + } + if (_savedActive != p) { + startAnimation(coords.i, coords.j, -1); } } } } -void ReplyKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { - _st->repaint(_item); +void ReplyKeyboard::startAnimation(int i, int j, int direction) { + auto notStarted = _animations.isEmpty(); + + int indexForAnimation = (i * MatrixRowShift + j + 1) * direction; + + _animations.remove(-indexForAnimation); + if (!_animations.contains(indexForAnimation)) { + _animations.insert(indexForAnimation, getms()); + } + + if (notStarted && !_a_selected.animating()) { + _a_selected.start(); + } } void ReplyKeyboard::step_selected(uint64 ms, bool timer) { @@ -330,11 +361,15 @@ int ReplyKeyboard::Style::buttonHeight() const { return _st->height; } -void ReplyKeyboard::Style::paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button) const { +void ReplyKeyboard::Style::paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button, uint64 ms) const { const QRect &rect = button.rect; - bool pressed = ClickHandler::showAsPressed(button.link); - - paintButtonBg(p, rect, pressed, button.howMuchOver); + paintButtonBg(p, rect, button.howMuchOver); + if (button.ripple) { + button.ripple->paint(p, rect.x(), rect.y(), outerWidth, ms); + if (button.ripple->empty()) { + button.ripple.reset(); + } + } paintButtonIcon(p, rect, outerWidth, button.type); if (button.type == HistoryMessageReplyMarkup::Button::Type::Callback || button.type == HistoryMessageReplyMarkup::Button::Type::Game) { @@ -353,8 +388,7 @@ void ReplyKeyboard::Style::paintButton(Painter &p, int outerWidth, const ReplyKe tx += (tw - st::botKbFont->elidew) / 2; tw = st::botKbFont->elidew; } - int textTop = rect.y() + (pressed ? _st->downTextTop : _st->textTop); - button.text.drawElided(p, tx, textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top); + button.text.drawElided(p, tx, rect.y() + _st->textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top); } void HistoryMessageReplyMarkup::createFromButtonRows(const QVector &v) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index ecacda928..fc12f2673 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -22,8 +22,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/runtime_composer.h" +namespace Ui { +class RippleAnimation; +} // namespace Ui + namespace style { struct BotKeyboardButton; +struct RippleAnimation; } // namespace style class HistoryElement { @@ -294,13 +299,14 @@ public: int buttonSkip() const; int buttonPadding() const; int buttonHeight() const; + virtual int buttonRadius() const = 0; virtual void repaint(const HistoryItem *item) const = 0; virtual ~Style() { } protected: - virtual void paintButtonBg(Painter &p, const QRect &rect, bool pressed, float64 howMuchOver) const = 0; + virtual void paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const = 0; virtual void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const = 0; virtual void paintButtonLoading(Painter &p, const QRect &rect) const = 0; virtual int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const = 0; @@ -308,7 +314,7 @@ public: private: const style::BotKeyboardButton *_st; - void paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button) const; + void paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button, uint64 ms) const; friend class ReplyKeyboard; }; @@ -326,7 +332,7 @@ public: int naturalWidth() const; int naturalHeight() const; - void paint(Painter &p, int outerWidth, const QRect &clip) const; + void paint(Painter &p, int outerWidth, const QRect &clip, uint64 ms) const; ClickHandlerPtr getState(int x, int y) const; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active); @@ -336,8 +342,7 @@ public: void updateMessageId(); private: - const HistoryItem *_item; - int _width = 0; + void startAnimation(int i, int j, int direction); friend class Style; using ReplyMarkupClickHandlerPtr = QSharedPointer; @@ -348,17 +353,33 @@ private: float64 howMuchOver = 0.; HistoryMessageReplyMarkup::Button::Type type; ReplyMarkupClickHandlerPtr link; + mutable QSharedPointer ripple; }; using ButtonRow = QVector

^5x zQCn_51Wu=V{-OkpT~qD7uYAZEk)dCgzf63ZIU1yvOTn}K=cIlfW?Y%eOmLQSvBqUs z`93k}#6%mL3^K#{tD)86>h%k=t*2#H?wLE9#>@fYIdO__D7b7Zr1ujQOLLdbB>>t%-?-p#f; z1ez;35rDi28dw9Eu<}XZl=y`jTAaM*Ri#rNz@y&H2Vid2i{Ty~Ig?=^iJgv)jzra9 zSC^re*EoJKYoH2h2&ni%%VA75`yiHH<}RgnJ!7||d2V_61K>MKl{UqG9H9S0;j;Al zF>qo%EQ)!Ykue6+fOw7;cdU)BPbL zQ=l$h@vpqR*s!%l=@2~?6~@i+Ok+pK!BTe+b>+vZxI5pjm;L^|K1k@FSX7`s-*&_oOw6cXUD)ZH5-JbVAfYP{%;&*RNkSP8}EBmgJAA zsZ8st5e5|o0(WvP-WRO7e!Al_$NVPY~k%y3s%%Da%Ii#_Ck0A(pvKlK6Z|J+<) zZgReT5Jey|2|v3+bMvf%NF3@4KxfBt*{Y?gw+h_vd0LsUiJBD@05 z$vh?Aq8QuSaNlUCzX{#?H5Ms37y<8ne;~1$KmhOlQSBwaJ}kr(6l$?&yvj^`d;_)iJ;k&p>#j!C3y3Ob;Z>fUgm=cT{3W5;> zjF&GvO~7drNw60B*4$!#oX51{F=q9y>K6-d`Km?pbA6Yr8O5>4+ZM#gZI#%CV+OMi zGvugJ&DoZF{80pdp>OmR-IKh!rcSh31GWWq81|{i@R2gCF9uoJzPUXNiWk5-P0^yJ zQx`Q!dcvWHqB+ZLQ9N1r?1;mK3onPbUWUcaN^)v2(GgE{UAM(}-^wgbnXgKXe6%Z< zq9=m+0sDO`x&_~LjXl4)36tUEJ!HuV!NnNlk#Q{zX0>p&z%mQV z(h&~TjV8<#z}dTbCUr+6+741fjYkny6R8n=2ZS)|g$?1lh%XVz)<-A+)q23`T!=1D zq0_ovXJ|XsBD94<4@Y;{J;%|Y*`k5z-j*C%9bIcI0hN+ z)MB*FUCZBSJ6K=4=DH+@#25mrrLT7M7ED*tfLz)svTecVSB?_S{#YYXW+0>(r1LAo z;dZEui{G8?x$<%RZD(IoGbvydpSijLT}g{CZB*SJ!?boIh8ZS$rnj_Ru!R(#mY=(8 z+d_n17sb?GXxg0qBf{#>3fAqyDBgnPj$^(;Hk^Cj#QpaF{uW=?+kE~GQ=d^-1_3X; Mk@=Zd2Ck9+0rfcDwEzGB diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 9040e248e..9b919578a 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -272,36 +272,10 @@ solidScroll: flatScroll { duration: 150; hiding: 0; } +defaultDropdownDuration: 150; +defaultDropdownPadding: margins(10px, 10px, 10px, 10px); defaultDropdownShadow: icon {{ "dropdown_shadow", windowShadowFg }}; -defaultPopupMenu: PopupMenu { - skip: 5px; - - shadow: defaultDropdownShadow; - shadowShift: 1px; - - itemBg: white; - itemBgOver: overBg; - itemFg: black; - itemFgOver: black; - itemFgDisabled: #ccc; - itemFgShortcut: #999; - itemFgShortcutOver: #7c99b2; - itemFgShortcutDisabled: #ccc; - - itemPadding: margins(17px, 8px, 17px, 7px); - itemFont: normalFont; - - separatorPadding: margins(0px, 5px, 0px, 5px); - separatorWidth: 1px; - separatorFg: #f1f1f1; - - arrow: icon {{ "dropdown_submenu_arrow", #373737 }}; - - duration: 120; - - widthMin: 180px; - widthMax: 300px; -} +defaultDropdownShadowShift: 1px; defaultTooltip: Tooltip { textBg: #eef2f5; @@ -1088,54 +1062,6 @@ btnUnblock: flatButton(btnSend) { downColor: #db6352; } -btnAttachDocument: iconedButton(btnDefIconed) { - icon: sprite(218px, 68px, 24px, 24px); - iconPos: point(11px, 11px); - downIcon: sprite(218px, 68px, 24px, 24px); - downIconPos: point(11px, 12px); - - overBgColor: btnWhiteHover; - width: 46px; - height: 46px; -} -btnAttachPhoto: iconedButton(btnAttachDocument) { - icon: sprite(118px, 0px, 24px, 24px); - downIcon: sprite(118px, 0px, 24px, 24px); -} -btnAttachEmoji: iconedButton(btnAttachDocument) { - overBgColor: white; - icon: sprite(374px, 344px, 21px, 22px); - iconPos: point(6px, 12px); - downIcon: sprite(374px, 344px, 21px, 22px); - downIconPos: point(6px, 12px); - - width: 33px; -} -emojiCircle: size(19px, 19px); -emojiCirclePeriod: 1500; -emojiCircleDuration: 500; -emojiCircleTop: 13px; -emojiCircleLine: 2px; -emojiCircleFg: #b9b9b9; -emojiCirclePart: 3.5; -btnBotKbShow: iconedButton(btnAttachEmoji) { - icon: sprite(375px, 74px, 21px, 21px); - iconPos: point(6px, 12px); - downIcon: sprite(375px, 74px, 21px, 21px); - downIconPos: point(6px, 12px); -} -btnBotCmdStart: iconedButton(btnAttachEmoji) { - icon: sprite(354px, 74px, 21px, 21px); - iconPos: point(6px, 12px); - downIcon: sprite(354px, 74px, 21px, 21px); - downIconPos: point(6px, 12px); -} -btnBotKbHide: iconedButton(btnAttachEmoji) { - icon: sprite(373px, 95px, 23px, 14px); - iconPos: point(5px, 17px); - downIcon: sprite(373px, 95px, 23px, 14px); - downIconPos: point(5px, 17px); -} silentToggle: flatCheckbox { textColor: black; bgColor: white; @@ -1158,15 +1084,6 @@ silentToggle: flatCheckbox { imagePos: point(6px, 12px); } -btnRecordAudio: sprite(379px, 390px, 16px, 24px); -btnRecordAudioActive: sprite(379px, 366px, 16px, 24px); -recordSignalColor: #f17077; -recordSignalMin: 5px; -recordSignalMax: 12px; -recordCancel: #aaa; -recordCancelActive: #ec6466; -recordFont: font(13px); -recordTextTop: 14px; replySkip: 51px; replyColor: #377aae; @@ -1418,68 +1335,6 @@ connectingBG: #fffe; connectingColor: #777; connectingPadding: margins(5px, 5px, 5px, 5px); -dropdownDef: dropdown { - border: 1px; - borderColor: #ebebeb; - - padding: margins(10px, 10px, 10px, 10px); - shadow: defaultDropdownShadow; - shadowShift: 1px; - - duration: 150; - width: 0px; -} -defaultInnerDropdown: InnerDropdown { - padding: margins(10px, 10px, 10px, 10px); - shadow: defaultDropdownShadow; - shadowShift: 1px; - - duration: 150; -} - -dropdownAttachDocument: iconedButton(btnAttachDocument) { - iconPos: point(14px, 13px); - downIconPos: point(14px, 14px); - - width: 172px; - height: 49px; - - color: black; - - font: font(16px); - - textPos: point(50px, 13px); - downTextPos: point(50px, 14px); -} -dropdownAttachPhoto: iconedButton(dropdownAttachDocument) { - icon: sprite(118px, 0px, 24px, 24px); - downIcon: sprite(118px, 0px, 24px, 24px); -} -dropdownMediaPhotos: iconedButton(dropdownAttachPhoto) { - width: 200px; -} -dropdownMediaVideos: iconedButton(dropdownMediaPhotos) { - icon: sprite(92px, 348px, 24px, 24px); - downIcon: sprite(92px, 348px, 24px, 24px); -} -dropdownMediaSongs: iconedButton(dropdownMediaPhotos) { - icon: sprite(60px, 374px, 24px, 26px); - downIcon: sprite(60px, 374px, 24px, 26px); - iconPos: point(12px, 12px); - downIconPos: point(12px, 13px); -} -dropdownMediaDocuments: iconedButton(dropdownAttachDocument) { - width: 200px; -} -dropdownMediaAudios: iconedButton(dropdownMediaDocuments) { - icon: sprite(62px, 348px, 24px, 24px); - downIcon: sprite(62px, 348px, 24px, 24px); -} -dropdownMediaLinks: iconedButton(dropdownMediaDocuments) { - icon: sprite(372px, 414px, 24px, 24px); - downIcon: sprite(372px, 414px, 24px, 24px); -} - dragFont: font(28px semibold); dragSubfont: font(20px semibold); dragColor: #777; @@ -1720,49 +1575,6 @@ mvControlMargin: 0px; mvControlSize: 90px; mvIconSize: size(60px, 56px); -mvDropdown: dropdown(dropdownDef) { - shadow: icon {}; - padding: margins(11px, 12px, 11px, 12px); - - border: 0px; - width: 182px; -} -mvButton: iconedButton(btnDefIconed) { - bgColor: #383838; - overBgColor: #505050; - font: font(fsize); - - opacity: 1.; - overOpacity: 1.; - - width: -32px; - height: 36px; - - color: white; - - textPos: point(16px, 9px); - downTextPos: point(16px, 10px); - - duration: 0; -} -mvPopupMenu: PopupMenu(defaultPopupMenu) { - shadow: icon {}; - - itemBg: #383838; - itemBgOver: #505050; - itemFg: white; - itemFgOver: white; - itemFgDisabled: #999; - itemFgShortcut: #eee; - itemFgShortcutOver: #fff; - itemFgShortcutDisabled: #999; - - separatorFg: #484848; -} -mvContextButton: iconedButton(mvButton) { - bgColor: #383838E6; - overBgColor: #505050E7; -} mvWaitHide: 2000; mvHideDuration: 1000; mvShowDuration: 200; @@ -1818,23 +1630,6 @@ macAlwaysThisAppTop: 4; macAppHintTop: 8; macCautionIconSize: 16; -btnContext: iconedButton(btnDefIconed) { - bgColor: white; - overBgColor: btnWhiteHover; - font: font(14px); - - opacity: 1.; - overOpacity: 1.; - - width: -32px; - height: 36px; - - color: black; - - textPos: point(16px, 7px); - downTextPos: point(16px, 8px); -} - radialSize: size(50px, 50px); radialLine: 3px; radialDuration: 350; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index f3baf7e69..3ade742e2 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -227,59 +227,6 @@ switcher { duration: int; } -dropdown { - border: pixels; - borderColor: color; - - padding: margins; - shadow: icon; - shadowShift: pixels; - - duration: int; - width: pixels; -} - -InnerDropdown { - padding: margins; - shadow: icon; - shadowShift: pixels; - - duration: int; - width: pixels; - - scrollMargin: margins; - scrollPadding: margins; -} - -PopupMenu { - skip: pixels; - - shadow: icon; - shadowShift: pixels; - - itemBg: color; - itemBgOver: color; - itemFg: color; - itemFgOver: color; - itemFgDisabled: color; - itemFgShortcut: color; - itemFgShortcutOver: color; - itemFgShortcutDisabled: color; - itemPadding: margins; - itemFont: font; - - separatorPadding: margins; - separatorWidth: pixels; - separatorFg: color; - - arrow: icon; - - duration: int; - - widthMin: pixels; - widthMax: pixels; -} - Tooltip { textBg: color; textFg: color; diff --git a/Telegram/Resources/icons/media_type_file.png b/Telegram/Resources/icons/media_type_file.png new file mode 100644 index 0000000000000000000000000000000000000000..dcc7996f18dc53f58420a1d783a6fa4e66cf5d75 GIT binary patch literal 417 zcmV;S0bc%zP)X01J_Tg^0*KB}0MkwY^zp`p_1>Klw@W0sw_C zuAe7|LI{v^1`z>O1!jgABOu~*zutM?@3-0#5oKn7ueIv3Ed815@RX8+LJ0mH5fA?m z@xH*eZ60+87_y5o-VptRz3cM}fZfd$`#*3*wT1foz!l6Fv&`m0)AxA^a#X>r7^LkK0SW2%l%rb|NAU z@UD^W3D`)mN;xaf#{xv;uv+V3G(UjP`+$f%6SK@l!rkX(D&JbarBAJL68)Oi00000 LNkvXXu0mjfhn~B( literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_type_file@2x.png b/Telegram/Resources/icons/media_type_file@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7df85a0b287bf745d68e7bf673d8fd103a47fa6c GIT binary patch literal 829 zcmV-D1H$}?P){gw`j4{+&TLZfLH$`B`)(6Kq zw|qtFscHDB;%p7PFG|U(Q3^aII`2xuNdXjiYU%7QNFTh;~CRhdu^5!XFi*^2pHGnzL$fg#Tn(Xr9mKq|Nbow4TB_=Wx5-tS58V zCPgDfAXF6&7Y}!RP^!;FohBgmNb4@fR4>%41-KXVq9`UEW`>Uo001iurk=`w0e)J5 z`^l$mTko;&gAcv+WEBALTGMN-QA&wI2rSDIrIarVcI~%;qymbfAb;b{3{MjpCkUT5D|EHdSpitA7nDKDB-PuT{PQ{DIq;-;Lzk00000NkvXX Hu0mjfXgh*5 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_type_link.png b/Telegram/Resources/icons/media_type_link.png new file mode 100644 index 0000000000000000000000000000000000000000..02b47d0e929ba9d5a1c580deb45ace071d8d7240 GIT binary patch literal 450 zcmV;z0X_bSP)0h_?j&wTh=lu)-0ISssilRgZ0D#?Y2VK`6eA0~)q)aA5rBaD>U8h_wN1CQlu~_tY z4u=C(DivBR7U5=~sw%qOE;gGD!s>iJqgt){KaniU7z_qr=FpAZx?ZnnHk;1@*=!cu z?e?w4ZZR_&jfNK>%Q9@+{-8G#b{j&7+jZ|Hgz$Ko8Ld|9dC=UUVP=d*BbcV?8#Kc( z?q?jwLA_poo#bAx2WG~2JVrjB50&H_;B-2{vMm2yG?`3LE|>qbDh$KGe!qthBFIbb siH9ylQJ|`7pfB_;dRO23Wqu9d7yh^SlD{7Q)c^nh07*qoM6N<$f}^g=9{>OV literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_type_link@2x.png b/Telegram/Resources/icons/media_type_link@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d10338d6efad695176ca3a6dcb121f6e5b3cc9a4 GIT binary patch literal 884 zcmV-)1B?8LP)7{=f8-B0jBK~Z5Pcnc${_L{YTNujT+FBO`Qpct|ZREtdFk`3VgV57X)CDM^xK+KbyB=<*40~{|LQPFgw7(9j0K92oVSzR_Hb@WzP51w;W*Ej8b$54DS63HFl0+RH9hx@m z^z<}291aQwgQTiz`8CMGKQz6)z2x!68a$#j<1*uf3Dxp8?{{9{wkHFG(gPb3lnW2{n-djBvpGox#PLZN{3^K&dMEn$3o9B#K8Hk<9o z#Y9oW{QP`H-(s4!~P=0N$zt@YX-s1w;Ekf^p~o0000< KMNUMnLSTX%4xb7D literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_type_photo.png b/Telegram/Resources/icons/media_type_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..520c9b5d861ae86af97ebaebf144f0681a59f352 GIT binary patch literal 451 zcmV;!0X+VRP)#DiDZ@3Iq>;LySc707wLHQduz)*O#b6P8}pgQA0{HV#Haq zA$zmX>E+&fgeh5Gf_K)_)!7lGd71N}`YO?+|p^SXC9b+b!qwIio0|l#*GNb%Hnn zd7g7L8W8}4AmD5^d-Qj?T+$fxKA@^920=jI_jx!R9_!cZm8;c?0Jz)j-Un@Koms`!|mM^F3`1u)Ne)PM`fx6n3yPvZUDt@?7)g>q2;n@} t8St(?%ChV&yev!fKCTD&)t~1`8cJ zR)VogM1s?`LtG+w-r=>^=Cwj{@9i5ZJ`gye_vAdkCcXFER005mA=XIJ26*)gkhHh~ z6h#3^l0p-_nJq34K~kjZ4w^E@u&{Zy@1QIe#= z16*8O2shtVrBWFzz~0{8z|12e20_5`I7J({ySvN%%tw*yy12Qy86O~@&!c5o12@R4 zTCIk;TrLbyEEX{cf~37gmhbzR&1OFVhT)t4SEgwq0H6c_==(m5jg7%D>F(|hW@ctU z1_0saA0Hp#=H><-9v%PyV0LyE78e&GolXm@F*5SmLCd5yuAGFj8QBWg{>p) zcKh=;z$@2vadmYy^3=Dzz0Ex(3j?ri8z(0x`x;HB)2Jv)-_JBn<7!ES01!fOZf-7Q zY+_;p4Z}d!b?G?Aaj;sgVk(sijV%-k==*+h0Q>vp_#PMB(Q1Guh> zvMf_0rBW%Waqe>+7JZYUJFdr6tfbjhgJoVT|(RvYX9qUReM8>}_}fN>fngoN4zP17RvH=gH(S`SrKnKn~GR^p?h zqc8nUv)TN1yk%Las%j|5G)<=3LI8pw_|h84vW&W}qiLGhZnx34Z8Qu6%jNQyhOS&L zGuIZ@jS@mIpU=mA6@oiEJIsxoPyocg{f(~cq2B1bV{~*BE0qd&O`!l?*|v?kuJ_#^ zsH%zw2M3`xn@3#$=>vdk8tbQ*mltpx2LJ#xO%uNFzQ4bR>FH@07{H*_1^5|%aJ;_0 zCha4(-rwH=-~*u1Xe4TeMa^cD#+c1!hw9xZAp|p-O!)0|E|1r;P)35QMwu_WS*9fYa$j zp-@0H8bv4+f+R_gKJH3nw35jry{qYTN;hAy-|wHgz!Mrbyh==FLSjYgQy=h*FbIG@jkeyjC6 z_8~wjmBMT`d&GJr%Oj>|`|&h@G4^i(@900kP?yW)Yya{z{VBWM4p9_wI2<5~;_m?j zK|rZgLN1qsEX!B*n}Fl-Xd2G*JO+aSJRZ+Y&tCwJ;~+^APNx$Bfq*HX(P-Qaw^%+~ zOpGzg=kwI>_YKFc%jLQn=Yc@LbX<44T>=2A)oOQ*MgUosO#$(EoRH0CO^sf!*YHy+ zm&-TL6pO`A-(s;a1<0~&2w;p+CX=C7t3~Venx@k!RjXABhr{2#$z*ajyi%zchW`W! zf6G%=#k7TLLr=w>(V=e*=}!3Vw@Ip=#mkIbCGIsO7b@Q0maSVYAub zcDupzJpR)Cb#rBb-Py+xr=_%JS;&6b^CQ-NeMiSzSw9334&6h$Nw38d5MPhrbZY18;Nfn+j? zv$HdZqKIfTidZa$TrQ{j2B4b2)zuYtcXzSBzmJQH3nY`tn(S7p2?T>dY;0_()vjt( z+eTkrUeszOiGSufP>H&L*3<>GrY@j0bpfrZ3usMUpjJFRJ(c}j^PiM|^Y(_%Y&Ju$ z*Q24K0oiO8cDudo_#Y#{vMk2N#xOZK2|*C5o~OA0MNycTn83oq0yvJ7JYRExm6a6) z0s-0cCEvuAUNFf)X?Q^9F7*8TtY}dH7#<$R^z^jiK3`r!EX#u9IJC61pja$|Wm%-t zX~m0D6os|5HN|~snl3Nk@pv#kK8~TGAyrRPO8b`{*2(4m{{EwY(P+f{{5<@AzvAX! zF*rD=N}s*Gy>|hg=W%j!^1mm8YEbDtr&Lx~S6^>pgTa7AB2km@0IB#lx*e;V>B(82H>LlgX$eV6|E$3%r%hW+O{WOC%bN zl1wH;9v&V@DwQG!2M1(kW`=Zjc7FWUODi6aD-OT4wIw@zY0)%ISe7MLtCcVeQ#o{L z&Cbp$7BCu(stCv`gTbIEd~a{BcHv(O#Ow7+4xY>9h#&}Bhkq3SKzu$Qspfvj=ksK7 zaglHwr*&`wfPDC?fljAGe}6wlMn>RpIAF0@001{PH;6`~*xA{ED2hm>QurzS1`6oC U<~Pwf_W%F@07*qoM6N<$f+No4z5oCK literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_type_video.png b/Telegram/Resources/icons/media_type_video.png new file mode 100644 index 0000000000000000000000000000000000000000..48a8321e1d0aa758513fb7613f6c2f572948bc32 GIT binary patch literal 302 zcmV+}0nz@6P)Nkla)73B+@F0Bb?pcmokHV`Jd~#3rc}HSnicc39bAm?0zVF7wLw zlK0>zFEIcRPQ=HV=I?-qU^2##Wf?rrdwTOUO~~^cobvB01wlYf)BMnc5M=5eqGeg=x(=p!+e$mY zw(Yz6-|xZi50FwqDRo}Jx~_<#2yNTmUhvAIm1TM0rIfE}^7p4ANfO$&?W!4!F-p@E zj^jK%o_U_ZIY(7h2R;l#aLzFd0}Q;s%zqzn0iYY=%GmNK8vp4HM%7gw79xWwMsHR(Q@@s(MpV+M<(AkVbYgBup)W^B08+RFK8zTwl#!`#mw zSiURW*Zz1j^Lf^`R_^!Q?+$M6Wp?<`UAfryKzSnL7C3sy=nyD!CCjv9QAdErmr3^9 za;4i67u?NrzHD+nZuw}uP2TXj0c-Ugw!$n%(eE+>$@7(jx-N*l)s@t#L>GJ5^hqYm^=gZaG&5z&qJT>QZsr)s_5VNp)zv{kz+q#A%gT@{kYWXn4i_bfUbhJCZo9_Qvr~ORfBN>Zy^4YvZ!a7vFzB lIIu71QY-6)HW+%w|4)6LJKNKD{J>|N{7SsR$ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_type_voice.png b/Telegram/Resources/icons/media_type_voice.png new file mode 100644 index 0000000000000000000000000000000000000000..66deec6dd67f4055196bcbbfdd554693ed143e99 GIT binary patch literal 421 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 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/send_control_bot_keyboard_hide.png b/Telegram/Resources/icons/send_control_bot_keyboard_hide.png new file mode 100644 index 0000000000000000000000000000000000000000..6ada764b54ed16d0f0bd9044ad91600d2a1688b9 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^;y}#D!3HFcd~W{?q|!ZI978O6mrnNPI$*%z@;vX} ze-*hydQB6ywfwT;R{T-f?54U>CDbKkzif5EG-c!5M)Bpcx%I4kbCM6R>hDng5NPhV zujP)tvOhimo&CE%kY2*=qr3Tez5m)zz%dC?v0=91rPK< z*WqmZVm7@2E7PqJI?J~qa02{h3;f9WJfDR3WNOe5g!E;a9@qsUeomJLS7GEI*m6J- zNJL}cy?F3C2NqPt6BcLCuq>Xm9EJy8@T@ibQwWt(%I+iUkk7Q=tzOsy9cBv=3~2xW N002ovPDHLkV1k)_aB2Vm literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/send_control_emoji.png b/Telegram/Resources/icons/send_control_emoji.png new file mode 100644 index 0000000000000000000000000000000000000000..65c52d50c90461d2aa9f1601a5c790d4d3a593df GIT binary patch literal 227 zcmV<90382`P)oU5n%E|Son zT>ll$mtu^nwJOH=CBFE+?*P`o70|k0iYJbX~``ZIn{bFbo{YIWx~QIp;SU#}P>)gg{COGXucPNGTCQ dKoX~hKLM~mfGHVAAJzZ>002ovPDHLkV1nRvT7m!o literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/send_control_emoji@2x.png b/Telegram/Resources/icons/send_control_emoji@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..773350b0034ed0900fb2bf76bb3ddbc886f43b11 GIT binary patch literal 360 zcmV-u0hj)XP);C5!aOHZNO*XVd4Q!xYI%_ZssaPEEjYDR&gbia z=uk&8@s3BkJL3cZVF9d`5cvrGvk(J-G)=MF?EnB>*Wq%x{QBreeo++UoTCSvb5s-s z;VWOkiSmsx^c}{Ss8E#ey$=fB`>0TizdE1K!4Af8jP57OFUvBRSqKsBD8^3!&=RgC z`27Aq2`Qy;yWKDh1B4K7ALV%ttu?H*P)a@T$yz%Lmy|MUzm#%jzqR&J12s((r1f+< z(cy5QEXyd%GTQI=bUYpdUR~EOAL(B!lQy6&%6LTZM#8B`66AMBc_XOVtv%2FrvwtjX zvJWcgs#mWoc~3-y`_fvI=Q(}f)Av1jo};zi@ZM_O2!eod9J$9hjs!vQxj`5?UgmXO1CS)i)s0=(0g$HY%4uO35{BWbm!>HIUDv(a*%-6@R+c5c z?{94D`#xn^E<9t*IuDF748s6`bB?xc$+CX0000yrrIjhI^c94;#XX*z zUH@W|bIvXNU?KZ6-+b;EoEZQBTrzZB$0&-BMHEG->-r+D&+*tnQ4}#v)1G4%X_}%a zitiO5iXxU}IqSSbS(Zyg%pW?R&ksBAa6X^kBS6!%#m+lunl>w73o|$#k8?*}ww+X1 zRn^lsP*v6J4QxJtjH|=R?OAL#o5}sz5PUaQ0jq#jz$#!BunPEn0iNdpA_5|U=Xu}m zkoC7g=7Z0Ezu%n?uzbHC0(ODtd1`*U-JT|AZCP#rfNj$>)WXNd2Q|N5uTKkLZCUQ7 zX<(b@IkmXk?Wp?}uE=XP1i`p-`uJ>Y&INCkwzhq;7xw@{=wrwPYpe)N@HnJ=uAp~vP9(3F} zA9+dpPauloU^t#d6h%`loAIl(EbC?GmSxR_Y&xE@EKgqT5V}y^hiPogHYn~v2mw`9 zVZYyls;bmp9LJC(3B+*>m&@f*N?0V|9>`=ibDfYe{C^qIDqt1x)A$235i<#ck(@LD O0000updateIsActive((state == Qt::ApplicationActive) ? Global::OnlineFocusTimeout() : Global::OfflineBlurTimeout()); } if (state != Qt::ApplicationActive) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); } } diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index 6df51ba70..106b79d70 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -201,7 +201,7 @@ void ScrollableBox::resizeEvent(QResizeEvent *e) { AbstractBox::resizeEvent(e); } -void ScrollableBox::init(ScrolledWidget *inner, int bottomSkip, int topSkip) { +void ScrollableBox::init(TWidget *inner, int bottomSkip, int topSkip) { _bottomSkip = bottomSkip; _topSkip = topSkip; _scroll->setOwnedWidget(inner); diff --git a/Telegram/SourceFiles/boxes/abstractbox.h b/Telegram/SourceFiles/boxes/abstractbox.h index 4c6e28b1a..2f80e07ec 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.h +++ b/Telegram/SourceFiles/boxes/abstractbox.h @@ -105,7 +105,7 @@ public: ScrollableBox(const style::flatScroll &scroll, int w = st::boxWideWidth); protected: - void init(ScrolledWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); + void init(TWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); void setScrollSkips(int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight); void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index 181ff3d49..ae9d61c94 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -54,7 +54,7 @@ void BackgroundBox::onBackgroundChosen(int index) { onClose(); } -BackgroundBox::Inner::Inner(QWidget *parent) : ScrolledWidget(parent) +BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent) , _bgCount(0) , _rows(0) , _over(-1) diff --git a/Telegram/SourceFiles/boxes/backgroundbox.h b/Telegram/SourceFiles/boxes/backgroundbox.h index 798593658..500ad61d8 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.h +++ b/Telegram/SourceFiles/boxes/backgroundbox.h @@ -42,7 +42,7 @@ private: }; // This class is hold in header because it requires Qt preprocessing. -class BackgroundBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { +class BackgroundBox::Inner : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 8a5e09674..879935f68 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -555,7 +555,7 @@ ContactsBox::Inner::ContactData::ContactData(PeerData *peer, base::lambda_wrap(st::contactsPhotoCheckbox, std_::move(updateCallback), PaintUserpicCallback(peer))) { } -ContactsBox::Inner::Inner(QWidget *parent, CreatingGroupType creating) : ScrolledWidget(parent) +ContactsBox::Inner::Inner(QWidget *parent, CreatingGroupType creating) : TWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _newItemHeight(creating == CreatingGroupNone ? st::contactsNewItemHeight : 0) , _creating(creating) @@ -565,7 +565,7 @@ ContactsBox::Inner::Inner(QWidget *parent, CreatingGroupType creating) : Scrolle init(); } -ContactsBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already) : ScrolledWidget(parent) +ContactsBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter membersFilter, const MembersAlreadyIn &already) : TWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _channel(channel) , _membersFilter(membersFilter) @@ -583,7 +583,7 @@ namespace { } } -ContactsBox::Inner::Inner(QWidget *parent, ChatData *chat, MembersFilter membersFilter) : ScrolledWidget(parent) +ContactsBox::Inner::Inner(QWidget *parent, ChatData *chat, MembersFilter membersFilter) : TWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _chat(chat) , _membersFilter(membersFilter) @@ -615,7 +615,7 @@ void ContactsBox::Inner::addDialogsToList(FilterCallback callback) { } } -ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : ScrolledWidget(parent) +ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _bot(bot) , _allAdmins(this, lang(lng_chat_all_members_admins), false, st::contactsAdminCheckbox) diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 0db72c0dd..c56f97b50 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -132,7 +132,7 @@ private: }; // This class is hold in header because it requires Qt preprocessing. -class ContactsBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { +class ContactsBox::Inner : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 8290a5bec..c5647ef29 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -97,7 +97,7 @@ void MembersBox::onAdminAdded() { _loadTimer.start(ReloadChannelMembersTimeout); } -MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter filter) : ScrolledWidget(parent) +MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter filter) : TWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _newItemHeight((channel->amCreator() && (channel->membersCount() < (channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!channel->isMegagroup() && !channel->isPublic()) || filter == MembersFilter::Admins)) ? st::contactsNewItemHeight : 0) , _newItemSel(false) diff --git a/Telegram/SourceFiles/boxes/members_box.h b/Telegram/SourceFiles/boxes/members_box.h index f93f7a7b2..a9639610f 100644 --- a/Telegram/SourceFiles/boxes/members_box.h +++ b/Telegram/SourceFiles/boxes/members_box.h @@ -61,7 +61,7 @@ private: }; // This class is hold in header because it requires Qt preprocessing. -class MembersBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { +class MembersBox::Inner : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index 52003f6ff..7cd8d1e25 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -242,7 +242,7 @@ void SessionsBox::onTerminateAll() { } } -SessionsBox::Inner::Inner(QWidget *parent, SessionsBox::List *list, SessionsBox::Data *current) : ScrolledWidget(parent) +SessionsBox::Inner::Inner(QWidget *parent, SessionsBox::List *list, SessionsBox::Data *current) : TWidget(parent) , _list(list) , _current(current) , _terminating(0) diff --git a/Telegram/SourceFiles/boxes/sessionsbox.h b/Telegram/SourceFiles/boxes/sessionsbox.h index f591464f4..0021162cf 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.h +++ b/Telegram/SourceFiles/boxes/sessionsbox.h @@ -72,7 +72,7 @@ private: }; // This class is hold in header because it requires Qt preprocessing. -class SessionsBox::Inner : public ScrolledWidget, public RPCSender { +class SessionsBox::Inner : public TWidget, public RPCSender { Q_OBJECT public: diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index a0054c2d2..aa65bdc6c 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -289,7 +289,7 @@ void ShareBox::onScroll() { _inner->setVisibleTopBottom(scrollTop, scrollTop + scroll->height()); } -ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : ScrolledWidget(parent) +ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : TWidget(parent) , _filterCallback(std_::move(filterCallback)) , _chatsIndexed(std_::make_unique(Dialogs::SortMode::Add)) { _rowsTop = st::shareRowsTop; diff --git a/Telegram/SourceFiles/boxes/sharebox.h b/Telegram/SourceFiles/boxes/sharebox.h index 449c56242..413344ca5 100644 --- a/Telegram/SourceFiles/boxes/sharebox.h +++ b/Telegram/SourceFiles/boxes/sharebox.h @@ -113,7 +113,7 @@ private: }; // This class is hold in header because it requires Qt preprocessing. -class ShareBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { +class ShareBox::Inner : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 907a5f9d8..03c13475e 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -498,7 +498,7 @@ void StickersBox::showAll() { ItemListBox::showAll(); } -StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : ScrolledWidget(parent) +StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : TWidget(parent) , _section(section) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _a_shifting(animation(this, &Inner::step_shifting)) @@ -511,7 +511,7 @@ StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : Scrol setup(); } -StickersBox::Inner::Inner(QWidget *parent, const Stickers::Order &archivedIds) : ScrolledWidget(parent) +StickersBox::Inner::Inner(QWidget *parent, const Stickers::Order &archivedIds) : TWidget(parent) , _section(StickersBox::Section::ArchivedPart) , _archivedIds(archivedIds) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h index 3524a7db8..31bc703b1 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.h +++ b/Telegram/SourceFiles/boxes/stickers_box.h @@ -103,7 +103,7 @@ private: int32 stickerPacksCount(bool includeDisabledOfficial = false); // This class is hold in header because it requires Qt preprocessing. -class StickersBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { +class StickersBox::Inner : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 3bf665a98..cc71ef5a8 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -147,7 +147,7 @@ void StickerSetBox::resizeEvent(QResizeEvent *e) { } } -StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : ScrolledWidget(parent) +StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : TWidget(parent) , _input(set) { switch (set.type()) { case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break; diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h index 8252ab4ab..53932eff3 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.h +++ b/Telegram/SourceFiles/boxes/stickersetbox.h @@ -65,7 +65,7 @@ private: }; // This class is hold in header because it requires Qt preprocessing. -class StickerSetBox::Inner : public ScrolledWidget, public RPCSender, private base::Subscriber { +class StickerSetBox::Inner : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 53ea0b47d..c43a4b373 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/qthelp_regex.h" #include "core/qthelp_url.h" #include "localstorage.h" -#include "ui/popupmenu.h" +#include "ui/widgets/tooltip.h" QString UrlClickHandler::copyToClipboardContextItemText() const { return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link); @@ -64,7 +64,7 @@ QString tryConvertUrlToLocal(QString url) { } // namespace void UrlClickHandler::doOpen(QString url) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); if (isEmail(url)) { QUrl u(qstr("mailto:") + url); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 55d84da20..eb9007f36 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" #include "ui/buttons/round_button.h" -#include "ui/popupmenu.h" +#include "ui/widgets/popup_menu.h" #include "data/data_drafts.h" #include "lang.h" #include "application.h" @@ -639,7 +639,7 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) { if (!history) return; _menuPeer = history->peer; - _menu = new PopupMenu(); + _menu = new Ui::PopupMenu(); _menu->addAction(lang((_menuPeer->isChat() || _menuPeer->isMegagroup()) ? lng_context_view_group : (_menuPeer->isUser() ? lng_context_view_profile : lng_context_view_channel)), this, SLOT(onContextProfile()))->setEnabled(true); _menu->addAction(lang(menuPeerMuted() ? lng_enable_notifications_from_tray : lng_disable_notifications_from_tray), this, SLOT(onContextToggleNotifications()))->setEnabled(true); _menu->addAction(lang(lng_profile_search_messages), this, SLOT(onContextSearch()))->setEnabled(true); diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 7a6631c4f..930349072 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -30,10 +30,10 @@ class IndexedList; namespace Ui { class RoundButton; +class PopupMenu; } // namespace Ui class MainWidget; -class PopupMenu; enum DialogsSearchRequestType { DialogsSearchFromStart, @@ -230,7 +230,7 @@ private: PeerData *_menuPeer = nullptr; PeerData *_menuActionPeer = nullptr; - PopupMenu *_menu = nullptr; + Ui::PopupMenu *_menu = nullptr; }; diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp deleted file mode 100644 index 5876eeff9..000000000 --- a/Telegram/SourceFiles/dropdown.cpp +++ /dev/null @@ -1,456 +0,0 @@ -/* -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 "dropdown.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 "historywidget.h" -#include "localstorage.h" -#include "lang.h" -#include "mainwindow.h" -#include "apiwrap.h" -#include "mainwidget.h" - -Dropdown::Dropdown(QWidget *parent, const style::dropdown &st) : TWidget(parent) -, _st(st) -, _width(_st.width) -, a_opacity(0) -, _a_appearance(animation(this, &Dropdown::step_appearance)) -, _shadow(_st.shadow) { - resetButtons(); - - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); - - if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { - connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); - } -} - -void Dropdown::ignoreShow(bool ignore) { - _ignore = ignore; -} - -void Dropdown::onWndActiveChanged() { - if (!App::wnd()->windowHandle()->isActive() && !isHidden()) { - leaveEvent(0); - } -} - -IconedButton *Dropdown::addButton(IconedButton *button) { - button->setParent(this); - - int32 nw = _st.padding.left() + _st.padding.right() + button->width(); - if (nw > _width) { - _width = nw; - for (int32 i = 0, l = _buttons.size(); i < l; ++i) _buttons[i]->resize(_width - _st.padding.left() - _st.padding.right(), _buttons[i]->height()); - } else { - button->resize(_width - _st.padding.left() - _st.padding.right(), button->height()); - } - if (!button->isHidden()) { - if (_height > _st.padding.top() + _st.padding.bottom()) { - _height += _st.border; - } - _height += button->height(); - } - _buttons.push_back(button); - connect(button, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(buttonStateChanged(int, ButtonStateChangeSource))); - - resize(_width, _height); - - return button; -} - -void Dropdown::resetButtons() { - _width = qMax(_st.padding.left() + _st.padding.right(), int(_st.width)); - _height = _st.padding.top() + _st.padding.bottom(); - for (int32 i = 0, l = _buttons.size(); i < l; ++i) { - delete _buttons[i]; - } - _buttons.clear(); - resize(_width, _height); - - _selected = -1; -} - -void Dropdown::updateButtons() { - int32 top = _st.padding.top(), starttop = top; - for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) { - if (!(*i)->isHidden()) { - (*i)->move(_st.padding.left(), top); - if ((*i)->width() != _width - _st.padding.left() - _st.padding.right()) { - (*i)->resize(_width - _st.padding.left() - _st.padding.right(), (*i)->height()); - } - top += (*i)->height() + _st.border; - } - } - _height = top + _st.padding.bottom() - (top > starttop ? _st.border : 0); - resize(_width, _height); -} - -void Dropdown::resizeEvent(QResizeEvent *e) { - int32 top = _st.padding.top(); - for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) { - if (!(*i)->isHidden()) { - (*i)->move(_st.padding.left(), top); - top += (*i)->height() + _st.border; - } - } -} - -void Dropdown::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (_a_appearance.animating()) { - p.setOpacity(a_opacity.current()); - } - - // draw shadow - QRect r(_st.padding.left(), _st.padding.top(), _width - _st.padding.left() - _st.padding.right(), _height - _st.padding.top() - _st.padding.bottom()); - _shadow.paint(p, r, _st.shadowShift); - - if (!_buttons.isEmpty() && _st.border > 0) { // paint separators - p.setPen(_st.borderColor->p); - int32 top = _st.padding.top(), i = 0, l = _buttons.size(); - for (; i < l; ++i) { - if (!_buttons.at(i)->isHidden()) break; - } - if (i < l) { - top += _buttons.at(i)->height(); - for (++i; i < l; ++i) { - if (!_buttons.at(i)->isHidden()) { - p.fillRect(_st.padding.left(), top, _width - _st.padding.left() - _st.padding.right(), _st.border, _st.borderColor->b); - top += _st.border + _buttons.at(i)->height(); - } - } - } - } -} - -void Dropdown::enterEvent(QEvent *e) { - _hideTimer.stop(); - if (_hiding) showStart(); - return TWidget::enterEvent(e); -} - -void Dropdown::leaveEvent(QEvent *e) { - if (_a_appearance.animating()) { - hideStart(); - } else { - _hideTimer.start(300); - } - return TWidget::leaveEvent(e); -} - -void Dropdown::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - if (_selected >= 0 && _selected < _buttons.size()) { - emit _buttons[_selected]->clicked(); - return; - } - } else if (e->key() == Qt::Key_Escape) { - hideStart(); - return; - } - if ((e->key() != Qt::Key_Up && e->key() != Qt::Key_Down) || _buttons.size() < 1) return; - - bool none = (_selected < 0 || _selected >= _buttons.size()); - int32 delta = (e->key() == Qt::Key_Down ? 1 : -1); - int32 newSelected = none ? (e->key() == Qt::Key_Down ? 0 : _buttons.size() - 1) : (_selected + delta); - if (newSelected < 0) { - newSelected = _buttons.size() - 1; - } else if (newSelected >= _buttons.size()) { - newSelected = 0; - } - int32 startFrom = newSelected; - while (_buttons.at(newSelected)->isHidden()) { - newSelected += delta; - if (newSelected < 0) { - newSelected = _buttons.size() - 1; - } else if (newSelected >= _buttons.size()) { - newSelected = 0; - } - if (newSelected == startFrom) return; - } - if (!none) { - _buttons[_selected]->setOver(false); - } - _selected = newSelected; - _buttons[_selected]->setOver(true); -} - -void Dropdown::buttonStateChanged(int oldState, ButtonStateChangeSource source) { - if (source == ButtonByUser) { - for (int32 i = 0, l = _buttons.size(); i < l; ++i) { - if (_buttons[i]->getState() & Button::StateOver) { - if (i != _selected) { - _buttons[i]->setOver(false); - } - } - } - } else if (source == ButtonByHover) { - bool found = false; - for (int32 i = 0, l = _buttons.size(); i < l; ++i) { - if (_buttons[i]->getState() & Button::StateOver) { - found = true; - if (i != _selected) { - int32 sel = _selected; - _selected = i; - if (sel >= 0 && sel < _buttons.size()) { - _buttons[sel]->setOver(false); - } - } - } - } - if (!found) { - _selected = -1; - } - } -} - -void Dropdown::otherEnter() { - _hideTimer.stop(); - showStart(); -} - -void Dropdown::otherLeave() { - if (_a_appearance.animating()) { - hideStart(); - } else { - _hideTimer.start(0); - } -} - -void Dropdown::fastHide() { - if (_a_appearance.animating()) { - _a_appearance.stop(); - } - a_opacity = anim::fvalue(0, 0); - _hideTimer.stop(); - hide(); -} - -void Dropdown::adjustButtons() { - for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) { - (*i)->setOpacity(a_opacity.current()); - } -} - -void Dropdown::hideStart() { - _hiding = true; - a_opacity.start(0); - _a_appearance.start(); -} - -void Dropdown::hideFinish() { - emit hiding(); - hide(); - for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) { - (*i)->clearState(); - } - _selected = -1; -} - -void Dropdown::showStart() { - if (!isHidden() && a_opacity.current() == 1) { - return; - } - _selected = -1; - _hiding = false; - show(); - a_opacity.start(1); - _a_appearance.start(); -} - -void Dropdown::step_appearance(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_appearance.stop(); - a_opacity.finish(); - if (_hiding) { - hideFinish(); - } - } else { - a_opacity.update(dt, anim::linear); - } - adjustButtons(); - if (timer) update(); -} - -bool Dropdown::eventFilter(QObject *obj, QEvent *e) { - if (e->type() == QEvent::Enter) { - otherEnter(); - } else if (e->type() == QEvent::Leave) { - otherLeave(); - } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton) { - if (isHidden() || _hiding) { - otherEnter(); - } else { - otherLeave(); - } - } - return false; -} - -DragArea::DragArea(QWidget *parent) : TWidget(parent) -, _hiding(false) -, _in(false) -, a_opacity(0) -, a_color(st::dragColor->c) -, _a_appearance(animation(this, &DragArea::step_appearance)) -, _shadow(st::boxShadow) { - setMouseTracking(true); - setAcceptDrops(true); -} - -void DragArea::mouseMoveEvent(QMouseEvent *e) { - if (_hiding) return; - - bool newIn = QRect(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()).contains(e->pos()); - if (newIn != _in) { - _in = newIn; - a_opacity.start(1); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); - _a_appearance.start(); - } -} - -void DragArea::dragMoveEvent(QDragMoveEvent *e) { - QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()); - bool newIn = r.contains(e->pos()); - if (newIn != _in) { - _in = newIn; - a_opacity.start(1); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); - _a_appearance.start(); - } - e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction); - e->accept(); -} - -void DragArea::setText(const QString &text, const QString &subtext) { - _text = text; - _subtext = subtext; - update(); -} - -void DragArea::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (_a_appearance.animating()) { - p.setOpacity(a_opacity.current()); - } - - QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()); - - // draw shadow - _shadow.paint(p, r, st::boxShadowShift); - - p.fillRect(r, st::white->b); - - p.setPen(a_color.current()); - - p.setFont(st::dragFont->f); - p.drawText(QRect(0, (height() - st::dragHeight) / 2, width(), st::dragFont->height), _text, QTextOption(style::al_top)); - - p.setFont(st::dragSubfont->f); - p.drawText(QRect(0, (height() + st::dragHeight) / 2 - st::dragSubfont->height, width(), st::dragSubfont->height * 2), _subtext, QTextOption(style::al_top)); -} - -void DragArea::dragEnterEvent(QDragEnterEvent *e) { - static_cast(parentWidget())->dragEnterEvent(e); - e->setDropAction(Qt::IgnoreAction); - e->accept(); -} - -void DragArea::dragLeaveEvent(QDragLeaveEvent *e) { - static_cast(parentWidget())->dragLeaveEvent(e); - _in = false; - a_opacity.start(_hiding ? 0 : 1); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); - _a_appearance.start(); -} - -void DragArea::dropEvent(QDropEvent *e) { - static_cast(parentWidget())->dropEvent(e); - if (e->isAccepted()) { - emit dropped(e->mimeData()); - } -} - -void DragArea::otherEnter() { - showStart(); -} - -void DragArea::otherLeave() { - hideStart(); -} - -void DragArea::fastHide() { - if (_a_appearance.animating()) { - _a_appearance.stop(); - } - a_opacity = anim::fvalue(0, 0); - hide(); -} - -void DragArea::hideStart() { - _hiding = true; - _in = false; - a_opacity.start(0); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); - _a_appearance.start(); -} - -void DragArea::hideFinish() { - hide(); - _in = false; - a_color = anim::cvalue(st::dragColor->c); -} - -void DragArea::showStart() { - _hiding = false; - show(); - a_opacity.start(1); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); - _a_appearance.start(); -} - -void DragArea::step_appearance(float64 ms, bool timer) { - float64 dt = ms / st::dropdownDef.duration; - if (dt >= 1) { - a_opacity.finish(); - a_color.finish(); - if (_hiding) { - hideFinish(); - } - _a_appearance.stop(); - } else { - a_opacity.update(dt, anim::linear); - a_color.update(dt, anim::linear); - } - if (timer) update(); -} diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index 5ef830b47..bcae2de31 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "apiwrap.h" #include "localstorage.h" +#include "styles/style_history.h" FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent) , _scroll(this, st::mentionScroll) @@ -333,7 +334,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { void FieldAutocomplete::rowsUpdated(const internal::MentionRows &mrows, const internal::HashtagRows &hrows, const internal::BotCommandRows &brows, const StickerPack &srows, bool resetScroll) { if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.isEmpty()) { if (!isHidden()) { - hideStart(); + hideAnimated(); } _mrows.clear(); _hrows.clear(); @@ -354,7 +355,7 @@ void FieldAutocomplete::rowsUpdated(const internal::MentionRows &mrows, const in update(); if (hidden) { hide(); - showStart(); + showAnimated(); } } } @@ -394,7 +395,7 @@ void FieldAutocomplete::recount(bool resetScroll) { if (resetScroll) _inner->clearSel(); } -void FieldAutocomplete::fastHide() { +void FieldAutocomplete::hideFast() { if (_a_appearance.animating()) { _a_appearance.stop(); } @@ -403,18 +404,20 @@ void FieldAutocomplete::fastHide() { hideFinish(); } -void FieldAutocomplete::hideStart() { - if (!_hiding) { - if (_cache.isNull()) { - _scroll->show(); - _cache = myGrab(this); - } - _scroll->hide(); - _hiding = true; - a_opacity.start(0); - setAttribute(Qt::WA_OpaquePaintEvent, false); - _a_appearance.start(); +void FieldAutocomplete::hideAnimated() { + if (isHidden() || _hiding) { + return; } + + if (_cache.isNull()) { + _scroll->show(); + _cache = myGrab(this); + } + _scroll->hide(); + _hiding = true; + a_opacity.start(0); + setAttribute(Qt::WA_OpaquePaintEvent, false); + _a_appearance.start(); } void FieldAutocomplete::hideFinish() { @@ -424,7 +427,7 @@ void FieldAutocomplete::hideFinish() { _inner->clearSel(true); } -void FieldAutocomplete::showStart() { +void FieldAutocomplete::showAnimated() { if (!isHidden() && a_opacity.current() == 1 && !_hiding) { return; } @@ -441,7 +444,7 @@ void FieldAutocomplete::showStart() { } void FieldAutocomplete::step_appearance(float64 ms, bool timer) { - float64 dt = ms / st::dropdownDef.duration; + float64 dt = ms / st::defaultDropdownDuration; if (dt >= 1) { _a_appearance.stop(); a_opacity.finish(); @@ -544,7 +547,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#'); int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize; int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right(); - int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; + int32 htagleft = st::historyAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; if (!_srows->isEmpty()) { int32 rows = rowscount(_srows->size(), _stickersPerRow); @@ -650,7 +653,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { } } - p.setFont(st::mentionFont->f); + p.setFont(st::mentionFont); if (!first.isEmpty()) { p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); diff --git a/Telegram/SourceFiles/history/field_autocomplete.h b/Telegram/SourceFiles/history/field_autocomplete.h index d0579b16b..751035ffc 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.h +++ b/Telegram/SourceFiles/history/field_autocomplete.h @@ -39,8 +39,6 @@ class FieldAutocomplete final : public TWidget { public: FieldAutocomplete(QWidget *parent); - void fastHide(); - bool clearFilteredBotCommands(); void showFiltered(PeerData *peer, QString query, bool addInlineBots); void showStickers(EmojiPtr emoji); @@ -75,6 +73,8 @@ public: return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); } + void hideFast(); + ~FieldAutocomplete(); signals: @@ -86,15 +86,15 @@ signals: void moderateKeyActivate(int key, bool *outHandled) const; public slots: - void hideStart(); - void hideFinish(); - - void showStart(); + void showAnimated(); + void hideAnimated(); protected: void paintEvent(QPaintEvent *e) override; private: + void hideFinish(); + void updateFiltered(bool resetScroll = false); void recount(bool resetScroll = false); diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index ae7d0e116..469119da6 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -20,6 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "basic.style"; using "dialogs/dialogs.style"; +using "ui/widgets/widgets.style"; historyPaddingBottom: 10px; @@ -38,16 +39,16 @@ historyToDownBadgeSize: 22px; historyEmptyDog: icon {{ "history_empty_dog", #ffffff }}; historyEmptySize: 128px; -membersInnerScroll: flatScroll(solidScroll) { - deltat: 3px; - deltab: 3px; - round: 1px; - width: 8px; - deltax: 3px; -} membersInnerWidth: 310px; membersInnerHeightMax: 360px; membersInnerDropdown: InnerDropdown(defaultInnerDropdown) { + scroll: flatScroll(solidScroll) { + deltat: 3px; + deltab: 3px; + round: 1px; + width: 8px; + deltax: 3px; + } scrollMargin: margins(0px, 5px, 0px, 5px); scrollPadding: margins(0px, 3px, 8px, 3px); } @@ -138,3 +139,74 @@ historyPeer8NameFg: #ce671b; // orange historyPeer8UserpicBg: #f7b37c; historyPeer8UserpicFg: #de8d62; historyPeer8UserpicPerson: icon {{ size(120px, 120px), historyPeer8UserpicBg }, { "userpic_person", historyPeer8UserpicFg }}; + +historyMediaTypeFile: icon {{ "media_type_file", #b3b3b3, point(2px, 2px) }}; +historyMediaTypePhoto: icon {{ "media_type_photo", #bebebe, point(2px, 2px) }}; +historyMediaTypeVideo: icon {{ "media_type_video", #bebebe, point(2px, 2px) }}; +historyMediaTypeSong: icon {{ "media_type_song", #bebebe, point(0px, 0px) }}; +historyMediaTypeVoice: icon {{ "media_type_voice", #bebebe, point(2px, 2px) }}; +historyMediaTypeLink: icon {{ "media_type_link", #bebebe, point(2px, 2px) }}; + +historyAttachDocument: IconButton { + width: 46px; + height: 46px; + + opacity: 0.78; + overOpacity: 1.; + + icon: historyMediaTypeFile; + iconPosition: point(9px, 9px); + downIconPosition: point(9px, 10px); + + duration: 150; +} +historyAttachPhoto: IconButton(historyAttachDocument) { + icon: historyMediaTypePhoto; +} +historyAttachEmoji: IconButton(historyAttachDocument) { + width: 33px; + icon: icon {{ "send_control_emoji", #b9b9b9 }}; + iconPosition: point(12px, 16px); + downIconPosition: point(12px, 16px); +} +historyEmojiCircle: size(19px, 19px); +historyEmojiCirclePeriod: 1500; +historyEmojiCircleDuration: 500; +historyEmojiCircleTop: 13px; +historyEmojiCircleLine: 2px; +historyEmojiCircleFg: #b9b9b9; +historyEmojiCirclePart: 3.5; +historyBotKeyboardShow: IconButton(historyAttachEmoji) { + icon: icon {{ "send_control_bot_keyboard", #b3b3b3 }}; + iconPosition: point(6px, 12px); + downIconPosition: point(6px, 12px); +} +historyBotKeyboardHide: IconButton(historyAttachEmoji) { + icon: icon {{ "send_control_bot_keyboard_hide", #b3b3b3 }}; + iconPosition: point(5px, 17px); + downIconPosition: point(5px, 17px); +} +historyBotCommandStart: IconButton(historyBotKeyboardShow) { + icon: icon {{ "send_control_bot_command", #b3b3b3 }}; +} +historyRecordVoice: icon {{ "send_control_record", #b9b9b9 }}; +historyRecordVoiceActive: icon {{ "send_control_record", #58b2ed }}; +historyRecordSignalColor: #f17077; +historyRecordSignalMin: 5px; +historyRecordSignalMax: 12px; +historyRecordCancel: #aaa; +historyRecordCancelActive: #ec6466; +historyRecordFont: font(13px); +historyRecordTextTop: 14px; + +historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { + menu: Menu(defaultMenu) { + skip: 5px; + + itemBgOver: btnWhiteHover; + itemIconPosition: point(12px, 6px); + itemIconOpacity: 0.78; + itemIconOverOpacity: 1.; + itemPadding: margins(48px, 11px, 48px, 11px); + } +} diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp new file mode 100644 index 000000000..d3aa25800 --- /dev/null +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -0,0 +1,175 @@ +/* +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 "history/history_drag_area.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 "historywidget.h" +#include "localstorage.h" +#include "lang.h" +#include "mainwindow.h" +#include "apiwrap.h" +#include "mainwidget.h" + +DragArea::DragArea(QWidget *parent) : TWidget(parent) +, _hiding(false) +, _in(false) +, a_opacity(0) +, a_color(st::dragColor->c) +, _a_appearance(animation(this, &DragArea::step_appearance)) +, _shadow(st::boxShadow) { + setMouseTracking(true); + setAcceptDrops(true); +} + +void DragArea::mouseMoveEvent(QMouseEvent *e) { + if (_hiding) return; + + bool newIn = QRect(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()).contains(e->pos()); + if (newIn != _in) { + _in = newIn; + a_opacity.start(1); + a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + _a_appearance.start(); + } +} + +void DragArea::dragMoveEvent(QDragMoveEvent *e) { + QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()); + bool newIn = r.contains(e->pos()); + if (newIn != _in) { + _in = newIn; + a_opacity.start(1); + a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + _a_appearance.start(); + } + e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction); + e->accept(); +} + +void DragArea::setText(const QString &text, const QString &subtext) { + _text = text; + _subtext = subtext; + update(); +} + +void DragArea::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (_a_appearance.animating()) { + p.setOpacity(a_opacity.current()); + } + + QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom()); + + // draw shadow + _shadow.paint(p, r, st::boxShadowShift); + + p.fillRect(r, st::white->b); + + p.setPen(a_color.current()); + + p.setFont(st::dragFont->f); + p.drawText(QRect(0, (height() - st::dragHeight) / 2, width(), st::dragFont->height), _text, QTextOption(style::al_top)); + + p.setFont(st::dragSubfont->f); + p.drawText(QRect(0, (height() + st::dragHeight) / 2 - st::dragSubfont->height, width(), st::dragSubfont->height * 2), _subtext, QTextOption(style::al_top)); +} + +void DragArea::dragEnterEvent(QDragEnterEvent *e) { + static_cast(parentWidget())->dragEnterEvent(e); + e->setDropAction(Qt::IgnoreAction); + e->accept(); +} + +void DragArea::dragLeaveEvent(QDragLeaveEvent *e) { + static_cast(parentWidget())->dragLeaveEvent(e); + _in = false; + a_opacity.start(_hiding ? 0 : 1); + a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + _a_appearance.start(); +} + +void DragArea::dropEvent(QDropEvent *e) { + static_cast(parentWidget())->dropEvent(e); + if (e->isAccepted()) { + emit dropped(e->mimeData()); + } +} + +void DragArea::otherEnter() { + showStart(); +} + +void DragArea::otherLeave() { + hideStart(); +} + +void DragArea::hideFast() { + if (_a_appearance.animating()) { + _a_appearance.stop(); + } + a_opacity = anim::fvalue(0, 0); + hide(); +} + +void DragArea::hideStart() { + _hiding = true; + _in = false; + a_opacity.start(0); + a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + _a_appearance.start(); +} + +void DragArea::hideFinish() { + hide(); + _in = false; + a_color = anim::cvalue(st::dragColor->c); +} + +void DragArea::showStart() { + _hiding = false; + show(); + a_opacity.start(1); + a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + _a_appearance.start(); +} + +void DragArea::step_appearance(float64 ms, bool timer) { + float64 dt = ms / st::defaultDropdownDuration; + if (dt >= 1) { + a_opacity.finish(); + a_color.finish(); + if (_hiding) { + hideFinish(); + } + _a_appearance.stop(); + } else { + a_opacity.update(dt, anim::linear); + a_color.update(dt, anim::linear); + } + if (timer) update(); +} diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/history/history_drag_area.h similarity index 59% rename from Telegram/SourceFiles/dropdown.h rename to Telegram/SourceFiles/history/history_drag_area.h index a9ed3f699..003bd9548 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/history/history_drag_area.h @@ -23,78 +23,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/twidget.h" #include "ui/effects/rect_shadow.h" -class Dropdown : public TWidget { - Q_OBJECT - -public: - Dropdown(QWidget *parent, const style::dropdown &st = st::dropdownDef); - - IconedButton *addButton(IconedButton *button); - void resetButtons(); - void updateButtons(); - - void resizeEvent(QResizeEvent *e); - void paintEvent(QPaintEvent *e); - - void enterEvent(QEvent *e); - void leaveEvent(QEvent *e); - void keyPressEvent(QKeyEvent *e); - void otherEnter(); - void otherLeave(); - - void fastHide(); - void ignoreShow(bool ignore = true); - - void step_appearance(float64 ms, bool timer); - - bool eventFilter(QObject *obj, QEvent *e); - - bool overlaps(const QRect &globalRect) { - if (isHidden() || _a_appearance.animating()) return false; - - return QRect(_st.padding.left(), - _st.padding.top(), - _width - _st.padding.left() - _st.padding.right(), - _height - _st.padding.top() - _st.padding.bottom() - ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); - } - -signals: - void hiding(); - -public slots: - void hideStart(); - void hideFinish(); - - void showStart(); - void onWndActiveChanged(); - - void buttonStateChanged(int oldState, ButtonStateChangeSource source); - -private: - void adjustButtons(); - - bool _ignore = false; - - typedef QVector Buttons; - Buttons _buttons; - - int32 _selected = -1; - - const style::dropdown &_st; - - int32 _width, _height; - bool _hiding = false; - - anim::fvalue a_opacity; - Animation _a_appearance; - - QTimer _hideTimer; - - Ui::RectShadow _shadow; - -}; - class DragArea : public TWidget { Q_OBJECT @@ -106,8 +34,6 @@ public: void otherEnter(); void otherLeave(); - void fastHide(); - void step_appearance(float64 ms, bool timer); bool overlaps(const QRect &globalRect) { @@ -120,6 +46,8 @@ public: ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); } + void hideFast(); + protected: void paintEvent(QPaintEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 0473b6fcf..e48517114 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -29,17 +29,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" #include "ui/toast/toast.h" #include "ui/buttons/history_down_button.h" -#include "ui/inner_dropdown.h" +#include "ui/buttons/icon_button.h" +#include "ui/widgets/inner_dropdown.h" +#include "ui/widgets/dropdown_menu.h" #include "inline_bots/inline_bot_result.h" #include "data/data_drafts.h" #include "history/history_service_layout.h" #include "history/history_media_types.h" +#include "history/history_drag_area.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" @@ -52,6 +54,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/chat_background.h" #include "observer_peer.h" #include "core/qthelp_regex.h" +#include "ui/widgets/popup_menu.h" namespace { @@ -1154,7 +1157,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { isUponSelected = hasSelected; } - _menu = new PopupMenu(); + _menu = new Ui::PopupMenu(); _contextMenuLnk = ClickHandler::getActive(); HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem(); @@ -1698,7 +1701,7 @@ void HistoryInner::toggleScrollDateShown() { _scrollDateShown = !_scrollDateShown; auto from = _scrollDateShown ? 0. : 1.; auto to = _scrollDateShown ? 1. : 0.; - _scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::btnAttachEmoji.duration); + _scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::historyAttachPhoto.duration); } void HistoryInner::repaintScrollDateCallback() { @@ -1749,7 +1752,7 @@ void HistoryInner::leaveEvent(QEvent *e) { App::hoveredItem(nullptr); } ClickHandler::clearActive(); - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); if (!ClickHandler::getPressed() && _cursor != style::cur_default) { _cursor = style::cur_default; setCursor(_cursor); @@ -1970,7 +1973,7 @@ void HistoryInner::onUpdateSelected() { dragState = item->getState(m.x(), m.y(), request); lnkhost = item; if (!dragState.link && m.x() >= st::msgMargin.left() && m.x() < st::msgMargin.left() + st::msgPhotoSize) { - if (HistoryMessage *msg = item->toHistoryMessage()) { + if (auto msg = item->toHistoryMessage()) { if (msg->hasFromPhoto()) { enumerateUserpics([&dragState, &lnkhost, &point](HistoryMessage *message, int userpicTop) -> bool { // stop enumeration if the userpic is above our point @@ -1992,10 +1995,10 @@ void HistoryInner::onUpdateSelected() { } bool lnkChanged = ClickHandler::setActive(dragState.link, lnkhost); if (lnkChanged || dragState.cursor != _dragCursorState) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); } if (dragState.link || dragState.cursor == HistoryInDateCursorState || dragState.cursor == HistoryInForwardedCursorState) { - PopupTooltip::Show(1000, this); + Ui::Tooltip::Show(1000, this); } Qt::CursorShape cur = style::cur_default; @@ -2611,7 +2614,7 @@ void BotKeyboard::updateStyle(int newWidth) { void BotKeyboard::clearSelection() { if (_impl) { if (ClickHandler::setActive(ClickHandlerPtr(), this)) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); setCursor(style::cur_default); } } @@ -2629,7 +2632,7 @@ QString BotKeyboard::tooltipText() const { } void BotKeyboard::updateSelected() { - PopupTooltip::Show(1000, this); + Ui::Tooltip::Show(1000, this); if (!_impl) return; @@ -2638,7 +2641,7 @@ void BotKeyboard::updateSelected() { auto link = _impl->getState(p.x() - x, p.y() - _st->margin); if (ClickHandler::setActive(link, this)) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); setCursor(link ? style::cur_pointer : style::cur_default); } } @@ -2917,19 +2920,19 @@ SilentToggle::SilentToggle(QWidget *parent) : FlatCheckbox(parent, QString(), fa void SilentToggle::mouseMoveEvent(QMouseEvent *e) { FlatCheckbox::mouseMoveEvent(e); if (rect().contains(e->pos())) { - PopupTooltip::Show(1000, this); + Ui::Tooltip::Show(1000, this); } else { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); } } void SilentToggle::leaveEvent(QEvent *e) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); } void SilentToggle::mouseReleaseEvent(QMouseEvent *e) { FlatCheckbox::mouseReleaseEvent(e); - PopupTooltip::Show(0, this); + Ui::Tooltip::Show(0, this); PeerData *p = App::main() ? App::main()->peer() : nullptr; if (p && p->isChannel() && p->notify != UnknownNotifySettings) { App::main()->updateNotifySetting(p, NotifySettingDontChange, checked() ? SilentNotifiesSetSilent : SilentNotifiesSetNotify); @@ -2991,20 +2994,20 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _botStart(this, lang(lng_bot_start), st::btnSend) , _joinChannel(this, lang(lng_channel_join), st::btnSend) , _muteUnmute(this, lang(lng_channel_mute), st::btnSend) -, _attachDocument(this, st::btnAttachDocument) -, _attachPhoto(this, st::btnAttachPhoto) -, _attachEmoji(this, st::btnAttachEmoji) -, _kbShow(this, st::btnBotKbShow) -, _kbHide(this, st::btnBotKbHide) -, _cmdStart(this, st::btnBotCmdStart) +, _attachDocument(this, st::historyAttachDocument) +, _attachPhoto(this, st::historyAttachPhoto) +, _attachEmoji(this, st::historyAttachEmoji) +, _botKeyboardShow(this, st::historyBotKeyboardShow) +, _botKeyboardHide(this, st::historyBotKeyboardHide) +, _botCommandStart(this, st::historyBotCommandStart) , _silent(this) , _field(this, st::taMsgField, lang(lng_message_ph)) , _a_record(animation(this, &HistoryWidget::step_record)) , _a_recording(animation(this, &HistoryWidget::step_recording)) -, a_recordCancel(st::recordCancel->c, st::recordCancel->c) -, _recordCancelWidth(st::recordFont->width(lang(lng_record_cancel))) +, a_recordCancel(st::historyRecordCancel->c, st::historyRecordCancel->c) +, _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel))) , _kbScroll(this, st::botKbScroll) -, _attachType(this) +, _attachType(this, st::historyAttachDropdownMenu) , _emojiPan(this) , _attachDragDocument(this) , _attachDragPhoto(this) @@ -3028,8 +3031,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_joinChannel, SIGNAL(clicked()), this, SLOT(onJoinChannel())); connect(&_muteUnmute, SIGNAL(clicked()), this, SLOT(onMuteUnmute())); connect(&_silent, SIGNAL(clicked()), this, SLOT(onBroadcastSilentChange())); - connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect())); - connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect())); + connect(_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect())); + connect(_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect())); connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); connect(&_field, SIGNAL(cancelled()), this, SLOT(onCancel())); connect(&_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed())); @@ -3107,24 +3110,24 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _reportSpamPanel.move(0, 0); _reportSpamPanel.hide(); - _attachDocument.hide(); - _attachPhoto.hide(); - _attachEmoji.hide(); - _kbShow.hide(); - _kbHide.hide(); + _attachDocument->hide(); + _attachPhoto->hide(); + _attachEmoji->hide(); + _botKeyboardShow->hide(); + _botKeyboardHide->hide(); _silent.hide(); - _cmdStart.hide(); + _botCommandStart->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(_botKeyboardShow, SIGNAL(clicked()), this, SLOT(onKbToggle())); + connect(_botKeyboardHide, SIGNAL(clicked()), this, SLOT(onKbToggle())); + connect(_botCommandStart, 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->addAction(lang(lng_attach_file), this, SLOT(onDocumentSelect()), &st::historyMediaTypeFile); + _attachType->addAction(lang(lng_attach_photo), this, SLOT(onPhotoSelect()), &st::historyMediaTypePhoto); _attachType->hide(); _emojiPan->hide(); _attachDragDocument->hide(); @@ -3221,7 +3224,7 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) { _emojiPan->queryInlineBot(_inlineBot, _peer, query); } if (!_fieldAutocomplete->isHidden()) { - _fieldAutocomplete->hideStart(); + _fieldAutocomplete->hideAnimated(); } } else { clearInlineBot(); @@ -3267,7 +3270,7 @@ void HistoryWidget::onTextChange() { _a_record.stop(); _inRecord = _inField = false; a_recordOver = a_recordDown = anim::fvalue(0, 0); - a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c); + a_recordCancel = anim::cvalue(st::historyRecordCancel->c, st::historyRecordCancel->c); } } if (updateCmdStartShown()) { @@ -3541,7 +3544,7 @@ void HistoryWidget::notify_botCommandsChanged(UserData *user) { } void HistoryWidget::notify_inlineBotRequesting(bool requesting) { - _attachEmoji.setLoading(requesting); + _attachEmoji->setLoading(requesting); } void HistoryWidget::notify_replyMarkupUpdated(const HistoryItem *item) { @@ -4497,14 +4500,14 @@ void HistoryWidget::updateControlsVisibility() { _fieldAutocomplete->hide(); _field.hide(); _fieldBarCancel.hide(); - _attachDocument.hide(); - _attachPhoto.hide(); - _attachEmoji.hide(); + _attachDocument->hide(); + _attachPhoto->hide(); + _attachEmoji->hide(); _silent.hide(); _historyToEnd->hide(); - _kbShow.hide(); - _kbHide.hide(); - _cmdStart.hide(); + _botKeyboardShow->hide(); + _botKeyboardHide->hide(); + _botCommandStart->hide(); _attachType->hide(); _emojiPan->hide(); if (_pinnedBar) { @@ -4556,17 +4559,17 @@ void HistoryWidget::updateControlsVisibility() { _send.hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); _botStart.hide(); - _attachDocument.hide(); - _attachPhoto.hide(); + _attachDocument->hide(); + _attachPhoto->hide(); _silent.hide(); _kbScroll.hide(); _fieldBarCancel.hide(); - _attachDocument.hide(); - _attachPhoto.hide(); - _attachEmoji.hide(); - _kbShow.hide(); - _kbHide.hide(); - _cmdStart.hide(); + _attachDocument->hide(); + _attachPhoto->hide(); + _attachEmoji->hide(); + _botKeyboardShow->hide(); + _botKeyboardHide->hide(); + _botCommandStart->hide(); _attachType->hide(); _emojiPan->hide(); if (!_field.isHidden()) { @@ -4588,12 +4591,12 @@ void HistoryWidget::updateControlsVisibility() { _send.hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); _field.hide(); - _attachEmoji.hide(); - _kbShow.hide(); - _kbHide.hide(); - _cmdStart.hide(); - _attachDocument.hide(); - _attachPhoto.hide(); + _attachEmoji->hide(); + _botKeyboardShow->hide(); + _botKeyboardHide->hide(); + _botCommandStart->hide(); + _attachDocument->hide(); + _attachPhoto->hide(); _silent.hide(); _kbScroll.hide(); _fieldBarCancel.hide(); @@ -4618,12 +4621,12 @@ void HistoryWidget::updateControlsVisibility() { } if (_recording) { _field.hide(); - _attachEmoji.hide(); - _kbShow.hide(); - _kbHide.hide(); - _cmdStart.hide(); - _attachDocument.hide(); - _attachPhoto.hide(); + _attachEmoji->hide(); + _botKeyboardShow->hide(); + _botKeyboardHide->hide(); + _botCommandStart->hide(); + _attachDocument->hide(); + _attachPhoto->hide(); _silent.hide(); if (_kbShown) { _kbScroll.show(); @@ -4634,38 +4637,38 @@ void HistoryWidget::updateControlsVisibility() { _field.show(); if (_kbShown) { _kbScroll.show(); - _attachEmoji.hide(); - _kbHide.show(); - _kbShow.hide(); - _cmdStart.hide(); + _attachEmoji->hide(); + _botKeyboardHide->show(); + _botKeyboardShow->hide(); + _botCommandStart->hide(); } else if (_kbReplyTo) { _kbScroll.hide(); - _attachEmoji.show(); - _kbHide.hide(); - _kbShow.hide(); - _cmdStart.hide(); + _attachEmoji->show(); + _botKeyboardHide->hide(); + _botKeyboardShow->hide(); + _botCommandStart->hide(); } else { _kbScroll.hide(); - _attachEmoji.show(); - _kbHide.hide(); + _attachEmoji->show(); + _botKeyboardHide->hide(); if (_keyboard.hasMarkup()) { - _kbShow.show(); - _cmdStart.hide(); + _botKeyboardShow->show(); + _botCommandStart->hide(); } else { - _kbShow.hide(); + _botKeyboardShow->hide(); if (_cmdStartShown) { - _cmdStart.show(); + _botCommandStart->show(); } else { - _cmdStart.hide(); + _botCommandStart->hide(); } } } if (cDefaultAttach() == dbidaPhoto) { - _attachDocument.hide(); - _attachPhoto.show(); + _attachDocument->hide(); + _attachPhoto->show(); } else { - _attachDocument.show(); - _attachPhoto.hide(); + _attachDocument->show(); + _attachPhoto->hide(); } if (hasSilentToggle()) { _silent.show(); @@ -4692,17 +4695,17 @@ void HistoryWidget::updateControlsVisibility() { _botStart.hide(); _joinChannel.hide(); _muteUnmute.hide(); - _attachDocument.hide(); - _attachPhoto.hide(); + _attachDocument->hide(); + _attachPhoto->hide(); _silent.hide(); _kbScroll.hide(); _fieldBarCancel.hide(); - _attachDocument.hide(); - _attachPhoto.hide(); - _attachEmoji.hide(); - _kbShow.hide(); - _kbHide.hide(); - _cmdStart.hide(); + _attachDocument->hide(); + _attachPhoto->hide(); + _attachEmoji->hide(); + _botKeyboardShow->hide(); + _botKeyboardHide->hide(); + _botCommandStart->hide(); _attachType->hide(); _emojiPan->hide(); _kbScroll.hide(); @@ -5219,6 +5222,12 @@ bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtp return true; } +void HistoryWidget::hideSelectorControlsAnimated() { + _fieldAutocomplete->hideAnimated(); + _attachType->hideAnimated(); + _emojiPan->hideAnimated(); +} + void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { if (!_history) return; @@ -5244,9 +5253,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { _saveDraftStart = getms(); onDraftSave(); - if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType->isHidden()) _attachType->hideStart(); - if (!_emojiPan->isHidden()) _emojiPan->hideStart(); + hideSelectorControlsAnimated(); if (replyTo < 0) cancelReply(lastKeyboardUsed); if (_previewData && _previewData->pendingTill) previewCancel(); @@ -5448,14 +5455,14 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: _kbScroll.hide(); _reportSpamPanel.hide(); _historyToEnd->hide(); - _attachDocument.hide(); - _attachPhoto.hide(); - _attachEmoji.hide(); + _attachDocument->hide(); + _attachPhoto->hide(); + _attachEmoji->hide(); _fieldAutocomplete->hide(); _silent.hide(); - _kbShow.hide(); - _kbHide.hide(); - _cmdStart.hide(); + _botKeyboardShow->hide(); + _botKeyboardHide->hide(); + _botCommandStart->hide(); _field.hide(); _fieldBarCancel.hide(); _send.hide(); @@ -5565,16 +5572,16 @@ void HistoryWidget::step_recording(float64 ms, bool timer) { } else { a_recordingLevel.update(dt, anim::linear); } - if (timer) update(_attachDocument.geometry()); + if (timer) update(_attachDocument->geometry()); } void HistoryWidget::onPhotoSelect() { if (!_history) return; - _attachDocument.clearState(); - _attachDocument.hide(); - _attachPhoto.show(); - _attachType->fastHide(); + _attachDocument->clearState(); + _attachDocument->hide(); + _attachPhoto->show(); + _attachType->hideFast(); if (cDefaultAttach() != dbidaPhoto) { cSetDefaultAttach(dbidaPhoto); @@ -5599,10 +5606,10 @@ void HistoryWidget::onPhotoSelect() { void HistoryWidget::onDocumentSelect() { if (!_history) return; - _attachPhoto.clearState(); - _attachPhoto.hide(); - _attachDocument.show(); - _attachType->fastHide(); + _attachPhoto->clearState(); + _attachPhoto->hide(); + _attachDocument->show(); + _attachType->hideFast(); if (cDefaultAttach() != dbidaDocument) { cSetDefaultAttach(dbidaDocument); @@ -5671,7 +5678,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { _inField = inField; a_recordOver.restart(); a_recordDown.start(_inField ? 1 : 0); - a_recordCancel.start(_inField ? st::recordCancel->c : st::recordCancelActive->c); + a_recordCancel.start(_inField ? st::historyRecordCancel->c : st::historyRecordCancelActive->c); startAnim = true; } if (inReplyEdit != _inReplyEdit) { @@ -5722,7 +5729,7 @@ void HistoryWidget::stopRecording(bool send) { a_recordDown.start(0); a_recordOver.restart(); - a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c); + a_recordCancel = anim::cvalue(st::historyRecordCancel->c, st::historyRecordCancel->c); _a_record.start(); } @@ -5960,7 +5967,7 @@ void HistoryWidget::updateDragAreas() { case DragStateFiles: _attachDragDocument->otherEnter(); _attachDragDocument->setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files)); - _attachDragPhoto->fastHide(); + _attachDragPhoto->hideFast(); break; case DragStatePhotoFiles: _attachDragDocument->otherEnter(); @@ -5969,7 +5976,7 @@ void HistoryWidget::updateDragAreas() { _attachDragPhoto->setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick)); break; case DragStateImage: - _attachDragDocument->fastHide(); + _attachDragDocument->hideFast(); _attachDragPhoto->otherEnter(); _attachDragPhoto->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick)); break; @@ -6127,10 +6134,10 @@ void HistoryWidget::onFilesDrop(const QMimeData *data) { void HistoryWidget::onKbToggle(bool manual) { auto fieldEnabled = canWriteMessage(); if (_kbShown || _kbReplyTo) { - _kbHide.hide(); + _botKeyboardHide->hide(); if (_kbShown) { if (fieldEnabled) { - _kbShow.show(); + _botKeyboardShow->show(); } if (manual && _history) { _history->lastKeyboardHiddenId = _keyboard.forMsgId().msg; @@ -6154,10 +6161,10 @@ void HistoryWidget::onKbToggle(bool manual) { } } } else if (!_keyboard.hasMarkup() && _keyboard.forceReply()) { - _kbHide.hide(); - _kbShow.hide(); + _botKeyboardHide->hide(); + _botKeyboardShow->hide(); if (fieldEnabled) { - _cmdStart.show(); + _botCommandStart->show(); } _kbScroll.hide(); _kbShown = false; @@ -6175,8 +6182,8 @@ void HistoryWidget::onKbToggle(bool manual) { _history->lastKeyboardHiddenId = 0; } } else if (fieldEnabled) { - _kbHide.show(); - _kbShow.hide(); + _botKeyboardHide->show(); + _botKeyboardShow->hide(); _kbScroll.show(); _kbShown = true; @@ -6195,10 +6202,10 @@ void HistoryWidget::onKbToggle(bool manual) { } } resizeEvent(0); - if (_kbHide.isHidden() && canWriteMessage()) { - _attachEmoji.show(); + if (_botKeyboardHide->isHidden() && canWriteMessage()) { + _attachEmoji->show(); } else { - _attachEmoji.hide(); + _attachEmoji->hide(); } updateField(); } @@ -6310,13 +6317,13 @@ void HistoryWidget::setMembersShowAreaActive(bool active) { void HistoryWidget::onMembersDropdownShow() { if (!_membersDropdown) { - _membersDropdown.create(this, st::membersInnerDropdown, st::membersInnerScroll); + _membersDropdown.create(this, st::membersInnerDropdown); _membersDropdown->setOwnedWidget(new Profile::MembersWidget(_membersDropdown, _peer, Profile::MembersWidget::TitleVisibility::Hidden)); - _membersDropdown->resize(st::membersInnerWidth, _membersDropdown->height()); + _membersDropdown->resizeToWidth(st::membersInnerWidth); _membersDropdown->setMaxHeight(countMembersDropdownHeightMax()); _membersDropdown->moveToLeft(0, 0); - connect(_membersDropdown, SIGNAL(hidden()), this, SLOT(onMembersDropdownHidden())); + connect(_membersDropdown, SIGNAL(beforeHidden()), this, SLOT(onMembersDropdownHidden())); } _membersDropdown->otherEnter(); } @@ -6436,24 +6443,24 @@ void HistoryWidget::moveFieldControls() { // (_attachDocument|_attachPhoto) _field (_silent|_cmdStart|_kbShow) (_kbHide|_attachEmoji) [_broadcast] _send // (_botStart|_unblock|_joinChannel|_muteUnmute) - int buttonsBottom = bottom - _attachDocument.height(); - _attachDocument.move(0, buttonsBottom); - _attachPhoto.move(0, buttonsBottom); - _field.move(_attachDocument.width(), bottom - _field.height() - st::sendPadding); + int buttonsBottom = bottom - _attachDocument->height(); + _attachDocument->move(0, buttonsBottom); + _attachPhoto->move(0, buttonsBottom); + _field.move(_attachDocument->width(), bottom - _field.height() - st::sendPadding); _send.move(right - _send.width(), buttonsBottom); if (_inlineBotCancel) _inlineBotCancel->move(_send.pos()); right -= _send.width(); - _attachEmoji.move(right - _attachEmoji.width(), buttonsBottom); - _kbHide.move(right - _kbHide.width(), buttonsBottom); - right -= _attachEmoji.width(); - _kbShow.move(right - _kbShow.width(), buttonsBottom); - _cmdStart.move(right - _cmdStart.width(), buttonsBottom); + _attachEmoji->move(right - _attachEmoji->width(), buttonsBottom); + _botKeyboardHide->move(right - _botKeyboardHide->width(), buttonsBottom); + right -= _attachEmoji->width(); + _botKeyboardShow->move(right - _botKeyboardShow->width(), buttonsBottom); + _botCommandStart->move(right - _botCommandStart->width(), buttonsBottom); _silent.move(right - _silent.width(), buttonsBottom); 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()); @@ -6463,11 +6470,11 @@ void HistoryWidget::moveFieldControls() { void HistoryWidget::updateFieldSize() { bool kbShowShown = _history && !_kbShown && _keyboard.hasMarkup(); - int fieldWidth = width() - _attachDocument.width(); + int fieldWidth = width() - _attachDocument->width(); fieldWidth -= _send.width(); - fieldWidth -= _attachEmoji.width(); - if (kbShowShown) fieldWidth -= _kbShow.width(); - if (_cmdStartShown) fieldWidth -= _cmdStart.width(); + fieldWidth -= _attachEmoji->width(); + if (kbShowShown) fieldWidth -= _botKeyboardShow->width(); + if (_cmdStartShown) fieldWidth -= _botCommandStart->width(); if (hasSilentToggle()) fieldWidth -= _silent.width(); if (_field.width() != fieldWidth) { @@ -6493,7 +6500,7 @@ void HistoryWidget::inlineBotChanged() { _inlineBotCancel = std_::make_unique(this, st::inlineBotCancel); connect(_inlineBotCancel.get(), SIGNAL(clicked()), this, SLOT(onInlineBotCancel())); _inlineBotCancel->setGeometry(_send.geometry()); - _attachEmoji.raise(); + _attachEmoji->raise(); updateFieldSubmitSettings(); updateControlsVisibility(); } else if (!isInlineBot && _inlineBotCancel) { @@ -7029,7 +7036,7 @@ void HistoryWidget::updateControlsGeometry() { _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::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom() - _attachEmoji->height()); if (_membersDropdown) { _membersDropdown->setMaxHeight(countMembersDropdownHeightMax()); } @@ -7315,15 +7322,15 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { if (!_a_show.animating()) { if (hasMarkup) { _kbScroll.show(); - _attachEmoji.hide(); - _kbHide.show(); + _attachEmoji->hide(); + _botKeyboardHide->show(); } else { _kbScroll.hide(); - _attachEmoji.show(); - _kbHide.hide(); + _attachEmoji->show(); + _botKeyboardHide->hide(); } - _kbShow.hide(); - _cmdStart.hide(); + _botKeyboardShow->hide(); + _botCommandStart->hide(); } int32 maxh = hasMarkup ? qMin(_keyboard.height(), int(st::maxFieldHeight) - (int(st::maxFieldHeight) / 2)) : 0; _field.setMaxHeight(st::maxFieldHeight - maxh); @@ -7338,10 +7345,10 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { } else { if (!_a_show.animating()) { _kbScroll.hide(); - _attachEmoji.show(); - _kbHide.hide(); - _kbShow.show(); - _cmdStart.hide(); + _attachEmoji->show(); + _botKeyboardHide->hide(); + _botKeyboardShow->show(); + _botCommandStart->hide(); } _field.setMaxHeight(st::maxFieldHeight); _kbShown = false; @@ -7354,10 +7361,10 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { } else { if (!_scroll.isHidden()) { _kbScroll.hide(); - _attachEmoji.show(); - _kbHide.hide(); - _kbShow.hide(); - _cmdStart.show(); + _attachEmoji->show(); + _botKeyboardHide->hide(); + _botKeyboardShow->hide(); + _botCommandStart->show(); } _field.setMaxHeight(st::maxFieldHeight); _kbShown = false; @@ -7543,9 +7550,7 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot Local::writeRecentHashtagsAndBots(); } - if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType->isHidden()) _attachType->hideStart(); - if (!_emojiPan->isHidden()) _emojiPan->hideStart(); + hideSelectorControlsAnimated(); _field.setFocus(); } @@ -7711,9 +7716,7 @@ bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti onCloudDraftSave(); // won't be needed if SendInlineBotResult will clear the cloud draft } - if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart(); - if (!_attachType->isHidden()) _attachType->hideStart(); - if (!_emojiPan->isHidden()) _emojiPan->hideStart(); + hideSelectorControlsAnimated(); _field.setFocus(); return true; @@ -7758,9 +7761,7 @@ 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(); + hideSelectorControlsAnimated(); _field.setFocus(); } @@ -8001,7 +8002,7 @@ void HistoryWidget::cancelReplyAfterMediaSend(bool lastKeyboardUsed) { int HistoryWidget::countMembersDropdownHeightMax() const { int result = height() - st::membersInnerDropdown.padding.top() - st::membersInnerDropdown.padding.bottom(); - result -= _attachEmoji.height(); + result -= _attachEmoji->height(); accumulate_min(result, st::membersInnerHeightMax); return result; } @@ -8221,7 +8222,7 @@ void HistoryWidget::onCancel() { onFieldBarCancel(); } } else if (!_fieldAutocomplete->isHidden()) { - _fieldAutocomplete->hideStart(); + _fieldAutocomplete->hideAnimated(); } else { App::main()->showBackFromStack(); emit cancelled(); @@ -8637,36 +8638,36 @@ void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int void HistoryWidget::drawRecordButton(Painter &p) { if (a_recordDown.current() < 1) { - p.setOpacity(st::btnAttachEmoji.opacity * (1 - a_recordOver.current()) + st::btnAttachEmoji.overOpacity * a_recordOver.current()); - p.drawSprite(_send.x() + (_send.width() - st::btnRecordAudio.pxWidth()) / 2, _send.y() + (_send.height() - st::btnRecordAudio.pxHeight()) / 2, st::btnRecordAudio); + p.setOpacity(st::historyAttachEmoji.opacity * (1 - a_recordOver.current()) + st::historyAttachEmoji.overOpacity * a_recordOver.current()); + st::historyRecordVoice.paint(p, _send.x() + (_send.width() - st::historyRecordVoice.width()) / 2, _send.y() + (_send.height() - st::historyRecordVoice.height()) / 2, width()); } if (a_recordDown.current() > 0) { p.setOpacity(a_recordDown.current()); - p.drawSprite(_send.x() + (_send.width() - st::btnRecordAudioActive.pxWidth()) / 2, _send.y() + (_send.height() - st::btnRecordAudioActive.pxHeight()) / 2, st::btnRecordAudioActive); + st::historyRecordVoiceActive.paint(p, _send.x() + (_send.width() - st::historyRecordVoiceActive.width()) / 2, _send.y() + (_send.height() - st::historyRecordVoiceActive.height()) / 2, width()); } p.setOpacity(1); } void HistoryWidget::drawRecording(Painter &p) { p.setPen(Qt::NoPen); - p.setBrush(st::recordSignalColor->b); + p.setBrush(st::historyRecordSignalColor); p.setRenderHint(QPainter::HighQualityAntialiasing); float64 delta = qMin(float64(a_recordingLevel.current()) / 0x4000, 1.); - int32 d = 2 * qRound(st::recordSignalMin + (delta * (st::recordSignalMax - st::recordSignalMin))); - p.drawEllipse(_attachPhoto.x() + (_attachEmoji.width() - d) / 2, _attachPhoto.y() + (_attachPhoto.height() - d) / 2, d, d); + int32 d = 2 * qRound(st::historyRecordSignalMin + (delta * (st::historyRecordSignalMax - st::historyRecordSignalMin))); + p.drawEllipse(_attachPhoto->x() + (_attachEmoji->width() - d) / 2, _attachPhoto->y() + (_attachPhoto->height() - d) / 2, d, d); p.setRenderHint(QPainter::HighQualityAntialiasing, false); QString duration = formatDurationText(_recordingSamples / AudioVoiceMsgFrequency); - p.setFont(st::recordFont->f); + p.setFont(st::historyRecordFont); - p.setPen(st::black->p); - p.drawText(_attachPhoto.x() + _attachEmoji.width(), _attachPhoto.y() + st::recordTextTop + st::recordFont->ascent, duration); + p.setPen(st::black); + p.drawText(_attachPhoto->x() + _attachEmoji->width(), _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration); - int32 left = _attachPhoto.x() + _attachEmoji.width() + st::recordFont->width(duration) + ((_send.width() - st::btnRecordAudio.pxWidth()) / 2); + int32 left = _attachPhoto->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send.width() - st::historyRecordVoice.width()) / 2); int32 right = width() - _send.width(); p.setPen(a_recordCancel.current()); - p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto.y() + st::recordTextTop + st::recordFont->ascent, lang(lng_record_cancel)); + p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, lang(lng_record_cancel)); } void HistoryWidget::drawPinnedBar(Painter &p) { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index f9906c92d..d5a81ba86 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localimageloader.h" #include "ui/effects/rect_shadow.h" -#include "ui/popupmenu.h" +#include "ui/widgets/tooltip.h" #include "history/history_common.h" #include "history/field_autocomplete.h" #include "window/section_widget.h" @@ -36,17 +36,20 @@ class Result; } // namespace InlineBots namespace Ui { -class HistoryDownButton; class InnerDropdown; +class DropdownMenu; class PlainShadow; +class PopupMenu; +class IconButton; +class HistoryDownButton; +class EmojiButton; } // namespace Ui -class Dropdown; class DragArea; class EmojiPan; class HistoryWidget; -class HistoryInner : public TWidget, public AbstractTooltipShower, private base::Subscriber { +class HistoryInner : public TWidget, public Ui::AbstractTooltipShower, private base::Subscriber { Q_OBJECT public: @@ -256,7 +259,7 @@ private: QTimer _touchScrollTimer; // context menu - PopupMenu *_menu = nullptr; + Ui::PopupMenu *_menu = nullptr; // save visible area coords for painting / pressing userpics int _visibleAreaTop = 0; @@ -351,7 +354,7 @@ private: }; -class BotKeyboard : public TWidget, public AbstractTooltipShower, public ClickHandlerHost { +class BotKeyboard : public TWidget, public Ui::AbstractTooltipShower, public ClickHandlerHost { Q_OBJECT public: @@ -505,7 +508,7 @@ private: }; -class SilentToggle : public FlatCheckbox, public AbstractTooltipShower { +class SilentToggle : public FlatCheckbox, public Ui::AbstractTooltipShower { public: SilentToggle(QWidget *parent); @@ -868,6 +871,7 @@ private: void cancelReplyAfterMediaSend(bool lastKeyboardUsed); + void hideSelectorControlsAnimated(); int countMembersDropdownHeightMax() const; MsgId _replyToId = 0; @@ -1086,9 +1090,12 @@ private: FlatButton _send, _unblock, _botStart, _joinChannel, _muteUnmute; mtpRequestId _unblockRequest = 0; mtpRequestId _reportSpamRequest = 0; - IconedButton _attachDocument, _attachPhoto; - EmojiButton _attachEmoji; - IconedButton _kbShow, _kbHide, _cmdStart; + ChildWidget _attachDocument; + ChildWidget _attachPhoto; + ChildWidget _attachEmoji; + ChildWidget _botKeyboardShow; + ChildWidget _botKeyboardHide; + ChildWidget _botCommandStart; SilentToggle _silent; bool _cmdStartShown = false; MessageField _field; @@ -1115,7 +1122,7 @@ private: ChildWidget _membersDropdown = { nullptr }; QTimer _membersDropdownShowTimer; - ChildWidget _attachType; + ChildWidget _attachType; ChildWidget _emojiPan; DragState _attachDrag = DragStateNone; ChildWidget _attachDragDocument, _attachDragPhoto; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 550216a01..83e0ac535 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "styles/style_dialogs.h" +#include "styles/style_history.h" #include "ui/buttons/peer_avatar_button.h" #include "ui/buttons/round_button.h" #include "ui/widgets/shadow.h" @@ -29,7 +30,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 "ui/widgets/dropdown_menu.h" #include "observer_peer.h" #include "apiwrap.h" #include "dialogswidget.h" @@ -75,7 +76,7 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window) , _topBar(this) , _playerPlaylist(this, Media::Player::Panel::Layout::OnlyPlaylist) , _playerPanel(this, Media::Player::Panel::Layout::Full) -, _mediaType(this) +, _mediaType(this, st::historyAttachDropdownMenu) , _api(new ApiWrap(this)) { setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); @@ -1382,16 +1383,16 @@ void MainWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { } } if (mask != _mediaTypeMask) { - _mediaType->resetButtons(); + _mediaType->clearActions(); for (int32 i = 0; i < OverviewCount; ++i) { if (mask & (1 << i)) { switch (i) { - case OverviewPhotos: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaPhotos, lang(lng_media_type_photos))), SIGNAL(clicked()), this, SLOT(onPhotosSelect())); break; - case OverviewVideos: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaVideos, lang(lng_media_type_videos))), SIGNAL(clicked()), this, SLOT(onVideosSelect())); break; - case OverviewMusicFiles: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaSongs, lang(lng_media_type_songs))), SIGNAL(clicked()), this, SLOT(onSongsSelect())); break; - case OverviewFiles: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaDocuments, lang(lng_media_type_files))), SIGNAL(clicked()), this, SLOT(onDocumentsSelect())); break; - case OverviewVoiceFiles: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaAudios, lang(lng_media_type_audios))), SIGNAL(clicked()), this, SLOT(onAudiosSelect())); break; - case OverviewLinks: connect(_mediaType->addButton(new IconedButton(this, st::dropdownMediaLinks, lang(lng_media_type_links))), SIGNAL(clicked()), this, SLOT(onLinksSelect())); break; + case OverviewPhotos: _mediaType->addAction(lang(lng_media_type_photos), this, SLOT(onPhotosSelect()), &st::historyMediaTypePhoto); break; + case OverviewVideos: _mediaType->addAction(lang(lng_media_type_videos), this, SLOT(onVideosSelect()), &st::historyMediaTypeVideo); break; + case OverviewMusicFiles: _mediaType->addAction(lang(lng_media_type_songs), this, SLOT(onSongsSelect()), &st::historyMediaTypeSong); break; + case OverviewFiles: _mediaType->addAction(lang(lng_media_type_files), this, SLOT(onDocumentsSelect()), &st::historyMediaTypeFile); break; + case OverviewVoiceFiles: _mediaType->addAction(lang(lng_media_type_audios), this, SLOT(onAudiosSelect()), &st::historyMediaTypeVoice); break; + case OverviewLinks: _mediaType->addAction(lang(lng_media_type_links), this, SLOT(onLinksSelect()), &st::historyMediaTypeLink); break; } } } @@ -2912,32 +2913,32 @@ void MainWidget::setMembersShowAreaActive(bool active) { void MainWidget::onPhotosSelect() { if (_overview) _overview->switchType(OverviewPhotos); - _mediaType->hideStart(); + _mediaType->hideAnimated(); } void MainWidget::onVideosSelect() { if (_overview) _overview->switchType(OverviewVideos); - _mediaType->hideStart(); + _mediaType->hideAnimated(); } void MainWidget::onSongsSelect() { if (_overview) _overview->switchType(OverviewMusicFiles); - _mediaType->hideStart(); + _mediaType->hideAnimated(); } void MainWidget::onDocumentsSelect() { if (_overview) _overview->switchType(OverviewFiles); - _mediaType->hideStart(); + _mediaType->hideAnimated(); } void MainWidget::onAudiosSelect() { if (_overview) _overview->switchType(OverviewVoiceFiles); - _mediaType->hideStart(); + _mediaType->hideAnimated(); } void MainWidget::onLinksSelect() { if (_overview) _overview->switchType(OverviewLinks); - _mediaType->hideStart(); + _mediaType->hideAnimated(); } Window::TopBarWidget *MainWidget::topBar() { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 69c8b4ddd..60f7b6f6c 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -39,6 +39,7 @@ class Panel; namespace Ui { class PeerAvatarButton; class PlainShadow; +class DropdownMenu; } // namespace Ui namespace Window { @@ -56,7 +57,6 @@ class DialogsWidget; class HistoryWidget; class OverviewWidget; class HistoryHider; -class Dropdown; enum StackItemType { HistoryStackItem, @@ -609,7 +609,7 @@ private: int _playerHeight = 0; int _contentScrollAddToY = 0; - ChildWidget _mediaType; + ChildWidget _mediaType; int32 _mediaTypeMask = 0; int32 updDate = 0; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 54f4ace9c..2472c2b32 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" -#include "ui/popupmenu.h" +#include "ui/widgets/popup_menu.h" #include "zip.h" #include "lang.h" #include "shortcuts.h" @@ -185,7 +185,7 @@ void MainWindow::onWindowActiveChanged() { void MainWindow::firstShow() { #ifdef Q_OS_WIN - trayIconMenu = new PopupMenu(); + trayIconMenu = new Ui::PopupMenu(); trayIconMenu->deleteOnHide(false); #else // Q_OS_WIN trayIconMenu = new QMenu(this); diff --git a/Telegram/SourceFiles/media/player/media_player_list.h b/Telegram/SourceFiles/media/player/media_player_list.h index bead9a398..b6aa5f1da 100644 --- a/Telegram/SourceFiles/media/player/media_player_list.h +++ b/Telegram/SourceFiles/media/player/media_player_list.h @@ -29,7 +29,7 @@ class Document; namespace Media { namespace Player { -class ListWidget : public ScrolledWidget, private base::Subscriber { +class ListWidget : public TWidget, private base::Subscriber { public: ListWidget(); diff --git a/Telegram/SourceFiles/media/player/media_player_panel.cpp b/Telegram/SourceFiles/media/player/media_player_panel.cpp index 19bd4fc44..2ec37694d 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.cpp +++ b/Telegram/SourceFiles/media/player/media_player_panel.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/player/media_player_list.h" #include "media/player/media_player_instance.h" #include "styles/style_overview.h" +#include "styles/style_widgets.h" #include "styles/style_media_player.h" #include "ui/widgets/shadow.h" #include "mainwindow.h" @@ -34,7 +35,7 @@ namespace Player { Panel::Panel(QWidget *parent, Layout layout) : TWidget(parent) , _layout(layout) -, _shadow(st::defaultInnerDropdown.shadow) +, _shadow(st::defaultDropdownShadow) , _scroll(this, st::mediaPlayerScroll) { _hideTimer.setSingleShot(true); connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart())); @@ -90,7 +91,7 @@ void Panel::updateControlsGeometry() { if (scrollHeight > 0) { _scroll->setGeometryToRight(contentRight(), scrollTop, width, scrollHeight); } - if (auto widget = static_cast(_scroll->widget())) { + if (auto widget = static_cast(_scroll->widget())) { widget->resizeToWidth(width); onScroll(); } @@ -118,7 +119,7 @@ void Panel::scrollPlaylistToCurrentTrack() { } void Panel::onScroll() { - if (auto widget = static_cast(_scroll->widget())) { + if (auto widget = static_cast(_scroll->widget())) { int visibleTop = _scroll->scrollTop(); int visibleBottom = visibleTop + _scroll->height(); widget->setVisibleTopBottom(visibleTop, visibleBottom); @@ -153,7 +154,7 @@ void Panel::paintEvent(QPaintEvent *e) { if (animating) { p.setOpacity(_a_appearance.current(_hiding ? 0. : 1.)); } else if (_hiding || isHidden()) { - hidingFinished(); + hideFinished(); return; } p.drawPixmap(0, 0, _cache); @@ -290,7 +291,7 @@ void Panel::onShowStart() { void Panel::hideIgnoringEnterEvents() { _ignoringEnterEvents = true; if (isHidden()) { - hidingFinished(); + hideFinished(); } else { onHideStart(); } @@ -317,13 +318,13 @@ void Panel::startAnimation() { void Panel::appearanceCallback() { if (!_a_appearance.animating() && _hiding) { _hiding = false; - hidingFinished(); + hideFinished(); } else { update(); } } -void Panel::hidingFinished() { +void Panel::hideFinished() { hide(); _cache = QPixmap(); performDestroy(); diff --git a/Telegram/SourceFiles/media/player/media_player_panel.h b/Telegram/SourceFiles/media/player/media_player_panel.h index aa87c5981..03b95c52a 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.h +++ b/Telegram/SourceFiles/media/player/media_player_panel.h @@ -81,7 +81,7 @@ private: void updateSize(); void appearanceCallback(); - void hidingFinished(); + void hideFinished(); int contentLeft() const; int contentTop() const; int contentRight() const; diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.h b/Telegram/SourceFiles/media/player/media_player_volume_controller.h index 980028ca2..305654995 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.h +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.h @@ -56,9 +56,6 @@ public: bool overlaps(const QRect &globalRect); - void otherEnter(); - void otherLeave(); - QMargins getMargin() const; protected: @@ -75,6 +72,9 @@ private slots: void onWindowActiveChanged(); private: + void otherEnter(); + void otherLeave(); + void appearanceCallback(); void hidingFinished(); void startAnimation(); diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 7f43918ef..7171d2b12 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -110,3 +110,50 @@ mediaviewFileBlue: icon { mediaviewTransparentBg: #ffffff; mediaviewTransparentFg: #cccccc; mediaviewTransparentSize: 4px; + +mediaviewMenu: Menu(defaultMenu) { + itemBg: #383838; + itemBgOver: #505050; + itemFg: white; + itemFgOver: white; + itemFgDisabled: #999; + itemFgShortcut: #eee; + itemFgShortcutOver: #fff; + itemFgShortcutDisabled: #999; + + separatorFg: #484848; +} +mediaviewPopupMenu: PopupMenu(defaultPopupMenu) { + shadow: icon {}; + menu: mediaviewMenu; +} +mediaviewDropdownMenu: DropdownMenu(defaultDropdownMenu) { + menu: mediaviewMenu; +} +/* +mvDropdown: dropdown(dropdownDef) { + shadow: icon {}; + padding: margins(11px, 12px, 11px, 12px); + + border: 0px; + width: 182px; +} +mvButton: iconedButton(btnDefIconed) { + bgColor: #383838; + overBgColor: #505050; + font: font(fsize); + + opacity: 1.; + overOpacity: 1.; + + width: -32px; + height: 36px; + + color: white; + + textPos: point(16px, 9px); + downTextPos: point(16px, 10px); + + duration: 0; +} +*/ \ No newline at end of file diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index eb3c30909..6b497473f 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "application.h" #include "ui/filedialog.h" -#include "ui/popupmenu.h" +#include "ui/widgets/popup_menu.h" #include "media/media_clip_reader.h" #include "media/view/media_clip_controller.h" #include "styles/style_mediaview.h" @@ -88,7 +88,7 @@ MediaView::MediaView() : TWidget(App::wnd()) , _radial(animation(this, &MediaView::step_radial)) , _lastAction(-st::mvDeltaFromLastAction, -st::mvDeltaFromLastAction) , _a_state(animation(this, &MediaView::step_state)) -, _dropdown(this, st::mvDropdown) { +, _dropdown(this, st::mediaviewDropdownMenu) { TextCustomTagsMap custom; custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); _saveMsgText.setRichText(st::medviewSaveMsgFont, lang(lng_mediaview_saved), _textDlgOptions, custom); @@ -126,32 +126,14 @@ MediaView::MediaView() : TWidget(App::wnd()) _touchTimer.setSingleShot(true); connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); - _btns.push_back(_btnSaveCancel = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_cancel)))); - connect(_btnSaveCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel())); - _btns.push_back(_btnToMessage = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_context_to_msg)))); - connect(_btnToMessage, SIGNAL(clicked()), this, SLOT(onToMessage())); - _btns.push_back(_btnShowInFolder = _dropdown.addButton(new IconedButton(this, st::mvButton, lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder)))); - connect(_btnShowInFolder, SIGNAL(clicked()), this, SLOT(onShowInFolder())); - _btns.push_back(_btnCopy = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_copy)))); - connect(_btnCopy, SIGNAL(clicked()), this, SLOT(onCopy())); - _btns.push_back(_btnForward = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_forward)))); - connect(_btnForward, SIGNAL(clicked()), this, SLOT(onForward())); - _btns.push_back(_btnDelete = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_delete)))); - connect(_btnDelete, SIGNAL(clicked()), this, SLOT(onDelete())); - _btns.push_back(_btnSaveAs = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_save_as)))); - connect(_btnSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs())); - _btns.push_back(_btnViewAll = _dropdown.addButton(new IconedButton(this, st::mvButton, lang(lng_mediaview_photos_all)))); - connect(_btnViewAll, SIGNAL(clicked()), this, SLOT(onOverview())); - - _dropdown.hide(); - connect(&_dropdown, SIGNAL(hiding()), this, SLOT(onDropdownHiding())); - _controlsHideTimer.setSingleShot(true); connect(&_controlsHideTimer, SIGNAL(timeout()), this, SLOT(onHideControls())); connect(&_docDownload, SIGNAL(clicked()), this, SLOT(onDownload())); connect(&_docSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs())); connect(&_docCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel())); + + connect(_dropdown, SIGNAL(beforeHidden()), this, SLOT(onDropdownHidden())); } void MediaView::moveToScreen() { @@ -400,18 +382,31 @@ void MediaView::updateControls() { update(); } -void MediaView::updateDropdown() { - _btnSaveCancel->setVisible(_doc && _doc->loading()); - _btnToMessage->setVisible(_msgid > 0); - _btnShowInFolder->setVisible(_doc && !_doc->filepath(DocumentData::FilePathResolveChecked).isEmpty()); - _btnSaveAs->setVisible(true); - _btnCopy->setVisible((_doc && fileShown()) || (_photo && _photo->loaded())); - _btnForward->setVisible(_canForward); - _btnDelete->setVisible(_canDelete || (_photo && App::self() && _user == App::self()) || (_photo && _photo->peer && _photo->peer->photoId == _photo->id && (_photo->peer->isChat() || (_photo->peer->isChannel() && _photo->peer->asChannel()->amCreator())))); - _btnViewAll->setVisible(_history && typeHasMediaOverview(_overview)); - _btnViewAll->setText(lang(_doc ? lng_mediaview_files_all : lng_mediaview_photos_all)); - _dropdown.updateButtons(); - _dropdown.moveToRight(0, height() - _dropdown.height()); +void MediaView::updateActions() { + _actions.clear(); + + if (_doc && _doc->loading()) { + _actions.push_back({ lang(lng_cancel), SLOT(onSaveCancel()) }); + } + if (_msgid > 0) { + _actions.push_back({ lang(lng_context_to_msg), SLOT(onToMessage()) }); + } + if (_doc && !_doc->filepath(DocumentData::FilePathResolveChecked).isEmpty()) { + _actions.push_back({ lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), SLOT(onShowInFolder()) }); + } + if ((_doc && fileShown()) || (_photo && _photo->loaded())) { + _actions.push_back({ lang(lng_mediaview_copy), SLOT(onCopy()) }); + } + if (_canForward) { + _actions.push_back({ lang(lng_mediaview_forward), SLOT(onForward()) }); + } + if (_canDelete || (_photo && App::self() && _user == App::self()) || (_photo && _photo->peer && _photo->peer->photoId == _photo->id && (_photo->peer->isChat() || (_photo->peer->isChannel() && _photo->peer->asChannel()->amCreator())))) { + _actions.push_back({ lang(lng_mediaview_delete), SLOT(onDelete()) }); + } + _actions.push_back({ lang(lng_mediaview_save_as), SLOT(onSaveAs()) }); + if (_history && typeHasMediaOverview(_overview)) { + _actions.push_back({ lang(_doc ? lng_mediaview_files_all : lng_mediaview_photos_all), SLOT(onOverview()) }); + } } void MediaView::step_state(uint64 ms, bool timer) { @@ -662,7 +657,7 @@ void MediaView::activateControls() { void MediaView::onHideControls(bool force) { if (!force) { - if (!_dropdown.isHidden() + if (!_dropdown->isHidden() || _menu || _mousePressed || (_fullScreenVideo && _clipController && _clipController->geometry().contains(_lastMouseMovePos))) { @@ -682,7 +677,7 @@ void MediaView::onHideControls(bool force) { if (!_a_state.animating()) _a_state.start(); } -void MediaView::onDropdownHiding() { +void MediaView::onDropdownHidden() { setFocus(); _ignoringDropdown = true; _lastMouseMovePos = mapFromGlobal(QCursor::pos()); @@ -961,10 +956,7 @@ void MediaView::onOverview() { } void MediaView::onCopy() { - if (!_dropdown.isHidden()) { - _dropdown.ignoreShow(); - _dropdown.hideStart(); - } + _dropdown->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); if (_doc) { if (!_current.isNull()) { QApplication::clipboard()->setPixmap(_current); @@ -2395,10 +2387,10 @@ void MediaView::contextMenuEvent(QContextMenuEvent *e) { _menu->deleteLater(); _menu = 0; } - _menu = new PopupMenu(st::mvPopupMenu); - updateDropdown(); - for (int32 i = 0, l = _btns.size(); i < l; ++i) { - if (!_btns.at(i)->isHidden()) _menu->addAction(_btns.at(i)->getText(), _btns.at(i), SIGNAL(clicked()))->setEnabled(true); + _menu = new Ui::PopupMenu(st::mediaviewPopupMenu); + updateActions(); + for_const (auto &action, _actions) { + _menu->addAction(action.text, this, action.member)->setEnabled(true); } connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); _menu->popup(e->globalPos()); @@ -2541,10 +2533,14 @@ void MediaView::receiveMouse() { } void MediaView::onDropdown() { - updateDropdown(); - _dropdown.ignoreShow(false); - _dropdown.showStart(); - _dropdown.setFocus(); + updateActions(); + _dropdown->clearActions(); + for_const (auto &action, _actions) { + _dropdown->addAction(action.text, this, action.member); + } + _dropdown->moveToRight(0, height() - _dropdown->height()); + _dropdown->showAnimated(); + _dropdown->setFocus(); } void MediaView::onCheckActive() { diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index b86383165..2f4600851 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "dropdown.h" +#include "ui/widgets/dropdown_menu.h" #include "ui/effects/radial_animation.h" namespace Media { @@ -29,7 +29,9 @@ class Controller; } // namespace Clip } // namespace Media +namespace Ui { class PopupMenu; +} // namespace Ui struct AudioPlaybackState; @@ -60,9 +62,6 @@ public: void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type); void documentUpdated(DocumentData *doc); void changingMsgId(HistoryItem *row, MsgId newId); - void updateDocSize(); - void updateControls(); - void updateDropdown(); void showSaveMsgFile(); void close(); @@ -81,9 +80,9 @@ public: void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; -public slots: +private slots: void onHideControls(bool force = false); - void onDropdownHiding(); + void onDropdownHidden(); void onScreenResized(int screen); @@ -130,6 +129,10 @@ private slots: void onVideoPlayProgress(const AudioMsgId &audioId); private: + void updateDocSize(); + void updateControls(); + void updateActions(); + void displayPhoto(PhotoData *photo, HistoryItem *item); void displayDocument(DocumentData *doc, HistoryItem *item); void displayFinished(); @@ -304,10 +307,14 @@ private: anim::fvalue a_cOpacity; bool _mousePressed = false; - PopupMenu *_menu = nullptr; - Dropdown _dropdown; - IconedButton *_btnSaveCancel, *_btnToMessage, *_btnShowInFolder, *_btnSaveAs, *_btnCopy, *_btnForward, *_btnDelete, *_btnViewAll; - QList _btns; + Ui::PopupMenu *_menu = nullptr; + ChildWidget _dropdown; + + struct ActionData { + QString text; + const char *member; + }; + QList _actions; bool _receiveMouse = true; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index c1a2ba9b6..e75fd1d1d 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "boxes/photocropbox.h" #include "ui/filedialog.h" +#include "ui/widgets/popup_menu.h" +#include "ui/widgets/tooltip.h" #include "window/top_bar_widget.h" #include "window/chat_background.h" #include "lang.h" @@ -931,22 +933,22 @@ void OverviewInner::onUpdateSelected() { Qt::CursorShape cur = style::cur_default; bool lnkChanged = ClickHandler::setActive(lnk, lnkhost); if (lnkChanged) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); } App::mousedItem(item); if (_mousedItem != oldMousedItem) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); if (oldMousedItem) repaintItem(oldMousedItem, oldMousedItemIndex); if (item) repaintItem(item); } if (_cursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) { - PopupTooltip::Hide(); + Ui::Tooltip::Hide(); } if (cursorState != _cursorState) { _cursorState = cursorState; } if (lnk || cursorState == HistoryInDateCursorState) { - PopupTooltip::Show(1000, this); + Ui::Tooltip::Show(1000, this); } fixItemIndex(_dragItemIndex, _dragItem); @@ -1183,7 +1185,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { bool lnkIsAudio = lnkDocument ? (lnkDocument->document()->voice() != nullptr) : false; bool lnkIsSong = lnkDocument ? (lnkDocument->document()->song() != nullptr) : false; if (lnkPhoto || lnkDocument) { - _menu = new PopupMenu(); + _menu = new Ui::PopupMenu(); if (App::hoveredLinkItem()) { _menu->addAction(lang(lng_context_to_msg), this, SLOT(goToMessage()))->setEnabled(true); } @@ -1221,7 +1223,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { repaintItem(App::contextItem()); if (_selectedMsgId) repaintItem(_selectedMsgId, -1); } else if (!ignoreMousedItem && App::mousedItem() && App::mousedItem()->channelId() == itemChannel(_mousedItem) && App::mousedItem()->id == itemMsgId(_mousedItem)) { - _menu = new PopupMenu(); + _menu = new Ui::PopupMenu(); QString linkCopyToClipboardText = _contextMenuLnk ? _contextMenuLnk->copyToClipboardContextItemText() : QString(); if (!linkCopyToClipboardText.isEmpty()) { _menu->addAction(linkCopyToClipboardText, this, SLOT(copyContextUrl()))->setEnabled(true); diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index f43728545..abd092a8d 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "window/section_widget.h" -#include "ui/popupmenu.h" +#include "ui/widgets/tooltip.h" namespace Overview { namespace Layout { @@ -33,10 +33,11 @@ class Date; namespace Ui { class PlainShadow; +class PopupMenu; } // namespace Ui class OverviewWidget; -class OverviewInner : public TWidget, public AbstractTooltipShower, public RPCSender, private base::Subscriber { +class OverviewInner : public TWidget, public Ui::AbstractTooltipShower, public RPCSender, private base::Subscriber { Q_OBJECT public: @@ -263,7 +264,7 @@ private: uint64 _touchTime = 0; QTimer _touchScrollTimer; - PopupMenu *_menu = nullptr; + Ui::PopupMenu *_menu = nullptr; }; class OverviewWidget : public TWidget, public RPCSender { diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index fe5365f38..dfb2b1198 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -28,7 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "lang.h" #include "localstorage.h" -#include "ui/popupmenu.h" +#include "ui/widgets/popup_menu.h" #include diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 20713abe4..aca3eda28 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -23,7 +23,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/main_window.h" #include +namespace Ui { class PopupMenu; +} // namespace Ui namespace Platform { @@ -109,7 +111,7 @@ protected: bool posInited = false; QSystemTrayIcon *trayIcon = nullptr; - PopupMenu *trayIconMenu = nullptr; + Ui::PopupMenu *trayIconMenu = nullptr; QImage icon256, iconbig256; QIcon wndIcon; diff --git a/Telegram/SourceFiles/profile/profile_block_widget.cpp b/Telegram/SourceFiles/profile/profile_block_widget.cpp index 93007eefc..1b287cc6e 100644 --- a/Telegram/SourceFiles/profile/profile_block_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_block_widget.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Profile { -BlockWidget::BlockWidget(QWidget *parent, PeerData *peer, const QString &title) : ScrolledWidget(parent) +BlockWidget::BlockWidget(QWidget *parent, PeerData *peer, const QString &title) : TWidget(parent) , _peer(peer) , _title(title) { } diff --git a/Telegram/SourceFiles/profile/profile_block_widget.h b/Telegram/SourceFiles/profile/profile_block_widget.h index d185f9f04..a580803df 100644 --- a/Telegram/SourceFiles/profile/profile_block_widget.h +++ b/Telegram/SourceFiles/profile/profile_block_widget.h @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Profile { -class BlockWidget : public ScrolledWidget, protected base::Subscriber { +class BlockWidget : public TWidget, protected base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/settings/settings_block_widget.cpp b/Telegram/SourceFiles/settings/settings_block_widget.cpp index 2d143ac84..8a188de02 100644 --- a/Telegram/SourceFiles/settings/settings_block_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_block_widget.cpp @@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Settings { -BlockWidget::BlockWidget(QWidget *parent, UserData *self, const QString &title) : ScrolledWidget(parent) +BlockWidget::BlockWidget(QWidget *parent, UserData *self, const QString &title) : TWidget(parent) , _self(self) , _title(title) { } diff --git a/Telegram/SourceFiles/settings/settings_block_widget.h b/Telegram/SourceFiles/settings/settings_block_widget.h index 6f34ae152..059973d04 100644 --- a/Telegram/SourceFiles/settings/settings_block_widget.h +++ b/Telegram/SourceFiles/settings/settings_block_widget.h @@ -32,7 +32,7 @@ class WidgetSlideWrap; namespace Settings { -class BlockWidget : public ScrolledWidget, protected base::Subscriber { +class BlockWidget : public TWidget, protected base::Subscriber { Q_OBJECT public: diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 74dca4181..fc0dd4799 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -42,19 +42,19 @@ EmojiColorPicker::EmojiColorPicker() : TWidget() , _a_selected(animation(this, &EmojiColorPicker::step_selected)) , a_opacity(0) , _a_appearance(animation(this, &EmojiColorPicker::step_appearance)) -, _shadow(st::dropdownDef.shadow) { +, _shadow(st::defaultDropdownShadow) { 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.width() * 2; - int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::dropdownDef.shadow.height() * 2; + int32 w = st::emojiPanSize.width() * (EmojiColorsCount + 1) + 4 * st::emojiColorsPadding + st::emojiColorsSep + st::defaultDropdownShadow.width() * 2; + int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::defaultDropdownShadow.height() * 2; resize(w, h); _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideAnimated())); } void EmojiColorPicker::showEmoji(uint32 code) { @@ -72,7 +72,7 @@ void EmojiColorPicker::showEmoji(uint32 code) { _variants[5] = emojiGet(e, 0xD83CDFFF); if (!_cache.isNull()) _cache = QPixmap(); - showStart(); + showAnimated(); } void EmojiColorPicker::paintEvent(QPaintEvent *e) { @@ -85,12 +85,12 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) { p.setClipRect(e->rect()); } - int32 w = st::dropdownDef.shadow.width(), h = st::dropdownDef.shadow.height(); + int32 w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height(); QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h); - _shadow.paint(p, r, st::dropdownDef.shadowShift); + _shadow.paint(p, r, st::defaultDropdownShadowShift); if (_cache.isNull()) { - p.fillRect(e->rect().intersected(r), st::white->b); + p.fillRect(e->rect().intersected(r), st::white); int32 x = w + 2 * st::emojiColorsPadding + st::emojiPanSize.width(); if (rtl()) x = width() - x - st::emojiColorsSep; @@ -108,7 +108,7 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) { void EmojiColorPicker::enterEvent(QEvent *e) { _hideTimer.stop(); - if (_hiding) showStart(); + if (_hiding) showAnimated(); TWidget::enterEvent(e); } @@ -135,7 +135,7 @@ void EmojiColorPicker::mouseReleaseEvent(QMouseEvent *e) { emit emojiSelected(_variants[_selected]); } _ignoreShow = true; - hideStart(); + hideAnimated(); } void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) { @@ -148,7 +148,7 @@ void EmojiColorPicker::step_appearance(float64 ms, bool timer) { _a_appearance.stop(); return; } - float64 dt = ms / st::dropdownDef.duration; + float64 dt = ms / st::defaultDropdownDuration; if (dt >= 1) { a_opacity.finish(); _cache = QPixmap(); @@ -178,34 +178,34 @@ void EmojiColorPicker::step_selected(uint64 ms, bool timer) { _hovers[index] = (i.key() > 0) ? dt : (1 - dt); ++i; } - toUpdate += QRect(st::dropdownDef.shadow.width() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.height() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height()); + toUpdate += QRect(st::defaultDropdownShadow.width() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::defaultDropdownShadow.height() + 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.width(), h = st::dropdownDef.shadow.height(); - _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::hideFast() { + 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(); } -void EmojiColorPicker::showStart() { +void EmojiColorPicker::hideAnimated() { + if (_cache.isNull()) { + int32 w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height(); + _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::showAnimated() { if (_ignoreShow) return; _hiding = false; @@ -217,7 +217,7 @@ void EmojiColorPicker::showStart() { return; } if (_cache.isNull()) { - int32 w = st::dropdownDef.shadow.width(), h = st::dropdownDef.shadow.height(); + int32 w = st::defaultDropdownShadow.width(), h = st::defaultDropdownShadow.height(); _cache = myGrab(this, QRect(w, h, width() - 2 * w, height() - 2 * h)); clearSelection(true); } @@ -241,9 +241,9 @@ void EmojiColorPicker::clearSelection(bool fast) { void EmojiColorPicker::updateSelected() { int32 selIndex = -1; QPoint p(mapFromGlobal(_lastMousePos)); - int32 sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::dropdownDef.shadow.height() - st::emojiColorsPadding; + int32 sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::defaultDropdownShadow.height() - st::emojiColorsPadding; if (y >= 0 && y < st::emojiPanSize.height()) { - int32 x = sx - st::dropdownDef.shadow.width() - st::emojiColorsPadding; + int32 x = sx - st::defaultDropdownShadow.width() - st::emojiColorsPadding; if (x >= 0 && x < st::emojiPanSize.width()) { selIndex = 0; } else { @@ -279,7 +279,7 @@ void EmojiColorPicker::updateSelected() { void EmojiColorPicker::drawVariant(Painter &p, int variant) { float64 hover = _hovers[variant]; - QPoint w(st::dropdownDef.shadow.width() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.height() + st::emojiColorsPadding); + QPoint w(st::defaultDropdownShadow.width() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::defaultDropdownShadow.height() + st::emojiColorsPadding); if (hover > 0) { p.setOpacity(hover); QPoint tl(w); @@ -291,7 +291,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { 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() : ScrolledWidget() +EmojiPanInner::EmojiPanInner() : TWidget() , _maxHeight(int(st::emojiPanMaxHeight) - st::rbEmoji.height) , _a_selected(animation(this, &EmojiPanInner::step_selected)) { resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); @@ -404,7 +404,7 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { bool EmojiPanInner::checkPickerHide() { if (!_picker.isHidden() && _selected == _pickerSel) { - _picker.hideStart(); + _picker.hideAnimated(); _pickerSel = -1; updateSelected(); return true; @@ -446,7 +446,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { 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(); + _picker.hideAnimated(); _pickerSel = -1; } } @@ -576,7 +576,7 @@ void EmojiPanInner::onColorSelected(EmojiPtr emoji) { } } selectEmoji(emoji); - _picker.hideStart(); + _picker.hideAnimated(); } void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) { @@ -642,7 +642,7 @@ DBIEmojiTab EmojiPanInner::currentTab(int yOffset) const { void EmojiPanInner::hideFinish() { if (!_picker.isHidden()) { - _picker.hideStart(true); + _picker.hideFast(); _pickerSel = -1; clearSelection(true); } @@ -738,9 +738,9 @@ void EmojiPanInner::updateSelected() { setCursor((newSel >= 0) ? style::cur_pointer : style::cur_default); if (newSel >= 0 && !_picker.isHidden()) { if (newSel != _pickerSel) { - _picker.hideStart(); + _picker.hideAnimated(); } else { - _picker.showStart(); + _picker.showAnimated(); } } } @@ -793,7 +793,7 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { update(); } -StickerPanInner::StickerPanInner() : ScrolledWidget() +StickerPanInner::StickerPanInner() : TWidget() , _a_selected(animation(this, &StickerPanInner::step_selected)) , _section(cShowingSavedGifs() ? Section::Gifs : Section::Stickers) , _addText(lang(lng_stickers_featured_add).toUpper()) @@ -2550,7 +2550,7 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) , _contentHeightEmoji(_contentHeight - st::rbEmoji.height) , _contentHeightStickers(_contentHeight - st::rbEmoji.height) , _a_appearance(animation(this, &EmojiPan::step_appearance)) -, _shadow(st::dropdownDef.shadow) +, _shadow(st::defaultDropdownShadow) , _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) @@ -2573,24 +2573,24 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) 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(); + _width = st::defaultDropdownPadding.left() + st::emojiPanWidth + st::defaultDropdownPadding.right(); + _height = st::defaultDropdownPadding.top() + _contentHeight + st::defaultDropdownPadding.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.move(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top()); e_scroll.setWidget(&e_inner); - s_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); + s_scroll.move(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.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; + int32 left = _iconsLeft = st::defaultDropdownPadding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2; + int32 top = _iconsTop = st::defaultDropdownPadding.top() + _contentHeight - st::rbEmoji.height; prepareTab(left, top, _width, _recent); prepareTab(left, top, _width, _people); prepareTab(left, top, _width, _nature); @@ -2603,7 +2603,7 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) updatePanelsPositions(e_panels, 0); _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideAnimated())); connect(&e_inner, SIGNAL(scrollToY(int)), &e_scroll, SLOT(scrollToY(int))); connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool))); @@ -2666,7 +2666,7 @@ void EmojiPan::updateContentHeight() { _contentHeightEmoji = he; _contentHeightStickers = hs; - _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom(); + _height = st::defaultDropdownPadding.top() + _contentHeight + st::defaultDropdownPadding.bottom(); resize(_width, _height); move(x(), _bottom - height()); @@ -2683,7 +2683,7 @@ void EmojiPan::updateContentHeight() { s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); } - _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height; + _iconsTop = st::defaultDropdownPadding.top() + _contentHeight - st::rbEmoji.height; _recent.move(_recent.x(), _iconsTop); _people.move(_people.x(), _iconsTop); _nature.move(_nature.x(), _iconsTop); @@ -2742,9 +2742,9 @@ void EmojiPan::paintEvent(QPaintEvent *e) { 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()); + QRect r(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top(), _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom()); - _shadow.paint(p, r, st::dropdownDef.shadowShift); + _shadow.paint(p, r, st::defaultDropdownShadowShift); if (_toCache.isNull()) { if (_cache.isNull()) { @@ -2871,7 +2871,7 @@ void EmojiPan::moveBottom(int32 bottom, bool force) { void EmojiPan::enterEvent(QEvent *e) { _hideTimer.stop(); - if (_hiding) showStart(); + if (_hiding) showAnimated(); } bool EmojiPan::preventAutoHide() const { @@ -2881,7 +2881,7 @@ bool EmojiPan::preventAutoHide() const { void EmojiPan::leaveEvent(QEvent *e) { if (preventAutoHide() || s_inner.inlineResultsShown()) return; if (_a_appearance.animating()) { - hideStart(); + hideAnimated(); } else { _hideTimer.start(300); } @@ -2889,13 +2889,13 @@ void EmojiPan::leaveEvent(QEvent *e) { void EmojiPan::otherEnter() { _hideTimer.stop(); - showStart(); + showAnimated(); } void EmojiPan::otherLeave() { if (preventAutoHide() || s_inner.inlineResultsShown()) return; if (_a_appearance.animating()) { - hideStart(); + hideAnimated(); } else { _hideTimer.start(0); } @@ -2990,7 +2990,7 @@ bool EmojiPan::event(QEvent *e) { return TWidget::event(e); } -void EmojiPan::fastHide() { +void EmojiPan::hideFast() { if (_a_appearance.animating()) { _a_appearance.stop(); } @@ -3107,7 +3107,7 @@ void EmojiPan::updateSelected() { 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()); + QRect r(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top(), _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom()); update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); } @@ -3177,7 +3177,7 @@ void EmojiPan::step_appearance(float64 ms, bool timer) { return; } - float64 dt = ms / st::dropdownDef.duration; + float64 dt = ms / st::defaultDropdownDuration; if (dt >= 1) { _a_appearance.stop(); a_opacity.finish(); @@ -3193,10 +3193,10 @@ void EmojiPan::step_appearance(float64 ms, bool timer) { if (timer) update(); } -void EmojiPan::hideStart() { - if (preventAutoHide() || s_inner.inlineResultsShown()) return; +void EmojiPan::hideAnimated() { + if (isHidden() || preventAutoHide() || s_inner.inlineResultsShown()) return; - hideAnimated(); + startHideAnimated(); } void EmojiPan::prepareShowHideCache() { @@ -3204,12 +3204,12 @@ void EmojiPan::prepareShowHideCache() { QPixmap from = _fromCache, to = _toCache; _fromCache = _toCache = QPixmap(); showAll(); - _cache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _cache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding)); _fromCache = from; _toCache = to; } } -void EmojiPan::hideAnimated() { +void EmojiPan::startHideAnimated() { if (_hiding) return; prepareShowHideCache(); @@ -3247,7 +3247,7 @@ void EmojiPan::hideFinish() { Notify::clipStopperHidden(ClipStopperSavedGifsPanel); } -void EmojiPan::showStart() { +void EmojiPan::showAnimated() { if (!isHidden() && !_hiding) { return; } @@ -3297,7 +3297,7 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) { if (isHidden() || _hiding) { _hideTimer.stop(); - showStart(); + showAnimated(); } else { hideAnimated(); } @@ -3317,7 +3317,7 @@ void EmojiPan::stickersInstalled(uint64 setId) { showAll(); s_inner.showStickerSet(setId); updateContentHeight(); - showStart(); + showAnimated(); } void EmojiPan::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) { @@ -3490,7 +3490,7 @@ void EmojiPan::validateSelectedIcon(ValidateIconAnimations animations) { void EmojiPan::onSwitch() { QPixmap cache = _cache; - _fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _fromCache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding)); _stickersShown = !_stickersShown; if (!_stickersShown) { Notify::clipStopperHidden(ClipStopperSavedGifsPanel); @@ -3515,7 +3515,7 @@ void EmojiPan::onSwitch() { _cache = QPixmap(); showAll(); - _toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); + _toCache = myGrab(this, rect().marginsRemoved(st::defaultDropdownPadding)); _cache = cache; hideAll(); @@ -3804,7 +3804,7 @@ int32 EmojiPan::showInlineRows(bool newResults) { } else { _hideTimer.stop(); if (hidden || _hiding) { - showStart(); + showAnimated(); } else if (!_stickersShown) { onSwitch(); } diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index 488f5c86f..a90ae4d1e 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -67,21 +67,20 @@ public: void step_appearance(float64 ms, bool timer); void step_selected(uint64 ms, bool timer); - void showStart(); void clearSelection(bool fast = false); -public slots: + void hideFast(); - void hideStart(bool fast = false); +public slots: + void showAnimated(); + void hideAnimated(); signals: - void emojiSelected(EmojiPtr emoji); void hidden(); private: - void drawVariant(Painter &p, int variant); void updateSelected(); @@ -113,7 +112,7 @@ private: }; class EmojiPanel; -class EmojiPanInner : public ScrolledWidget { +class EmojiPanInner : public TWidget { Q_OBJECT public: @@ -207,7 +206,7 @@ struct StickerIcon { int pixh = 0; }; -class StickerPanInner : public ScrolledWidget, private base::Subscriber { +class StickerPanInner : public TWidget, private base::Subscriber { Q_OBJECT public: @@ -499,7 +498,7 @@ public: bool event(QEvent *e); - void fastHide(); + void hideFast(); bool hiding() const { return _hiding || _hideTimer.isActive(); } @@ -517,10 +516,10 @@ public: 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() + return QRect(st::defaultDropdownPadding.left(), + st::defaultDropdownPadding.top(), + _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), + _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom() ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); } @@ -534,7 +533,9 @@ public: } public slots: - void hideStart(); + void showAnimated(); + void hideAnimated(); + void refreshStickers(); private slots: @@ -542,7 +543,6 @@ private slots: void hideFinish(); - void showStart(); void onWndActiveChanged(); void onTabChange(); @@ -590,7 +590,7 @@ private: void updateContentHeight(); void leaveToChildEvent(QEvent *e, QWidget *child); - void hideAnimated(); + void startHideAnimated(); void prepareShowHideCache(); void updateSelected(); diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp index 773742426..f994f7f27 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { HistoryDownButton::HistoryDownButton(QWidget *parent) : Button(parent) -, a_arrowOpacity(st::btnAttachEmoji.opacity, st::btnAttachEmoji.opacity) +, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity) , _a_arrowOver(animation(this, &HistoryDownButton::step_arrowOver)) { setCursor(style::cur_pointer); @@ -83,7 +83,7 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) { } void HistoryDownButton::onStateChanged(int oldState, ButtonStateChangeSource source) { - a_arrowOpacity.start((_state & (StateOver | StateDown)) ? st::btnAttachEmoji.overOpacity : st::btnAttachEmoji.opacity); + a_arrowOpacity.start((_state & (StateOver | StateDown)) ? st::historyAttachEmoji.overOpacity : st::historyAttachEmoji.opacity); if (source == ButtonByUser || source == ButtonByPress) { _a_arrowOver.stop(); @@ -118,7 +118,7 @@ void HistoryDownButton::hideAnimated() { void HistoryDownButton::toggleAnimated() { _shown = !_shown; float64 from = _shown ? 0. : 1., to = _shown ? 1. : 0.; - _a_show.start([this] { update(); }, from, to, st::btnAttachEmoji.duration); + _a_show.start([this] { update(); }, from, to, st::historyAttachEmoji.duration); } void HistoryDownButton::finishAnimation() { @@ -127,7 +127,7 @@ void HistoryDownButton::finishAnimation() { } void HistoryDownButton::step_arrowOver(float64 ms, bool timer) { - float64 dt = ms / st::btnAttachEmoji.duration; + float64 dt = ms / st::historyAttachEmoji.duration; if (dt >= 1) { _a_arrowOver.stop(); a_arrowOpacity.finish(); @@ -137,4 +137,64 @@ void HistoryDownButton::step_arrowOver(float64 ms, bool timer) { if (timer) update(); } +EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : Button(parent) +, _st(st) +, _a_loading(animation(this, &EmojiButton::step_loading)) { + resize(_st.width, _st.height); + setCursor(style::cur_pointer); +} + +void EmojiButton::paintEvent(QPaintEvent *e) { + Painter p(this); + + uint64 ms = getms(); + + p.fillRect(e->rect(), st::white); + + auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); + auto opacity = over * _st.overOpacity + (1. - over) * _st.opacity; + + auto loading = a_loading.current(ms, _loading ? 1 : 0); + p.setOpacity(opacity * (1 - loading)); + + _st.icon.paint(p, (_state & StateDown) ? _st.downIconPosition : _st.iconPosition, width()); + + p.setOpacity(opacity); + p.setPen(QPen(st::historyEmojiCircleFg, st::historyEmojiCircleLine)); + p.setBrush(Qt::NoBrush); + + p.setRenderHint(QPainter::HighQualityAntialiasing); + QRect inner(QPoint((width() - st::historyEmojiCircle.width()) / 2, st::historyEmojiCircleTop), st::historyEmojiCircle); + if (loading > 0) { + int32 full = 5760; + int32 start = qRound(full * float64(ms % uint64(st::historyEmojiCirclePeriod)) / st::historyEmojiCirclePeriod), part = qRound(loading * full / st::historyEmojiCirclePart); + p.drawArc(inner, start, full - part); + } else { + p.drawEllipse(inner); + } + p.setRenderHint(QPainter::HighQualityAntialiasing, false); +} + +void EmojiButton::setLoading(bool loading) { + if (_loading != loading) { + _loading = loading; + auto from = loading ? 0. : 1., to = loading ? 1. : 0.; + a_loading.start([this] { update(); }, from, to, st::historyEmojiCircleDuration); + if (loading) { + _a_loading.start(); + } else { + _a_loading.stop(); + } + } +} + +void EmojiButton::onStateChanged(int oldState, ButtonStateChangeSource source) { + auto over = (_state & StateOver); + if (over != (oldState & StateOver)) { + auto from = over ? 0. : 1.; + auto to = over ? 1. : 0.; + _a_over.start([this] { update(); }, from, to, _st.duration); + } +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.h b/Telegram/SourceFiles/ui/buttons/history_down_button.h index 8030a905e..0ab821ee5 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.h +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.h @@ -61,4 +61,31 @@ private: }; +class EmojiButton : public Button { +public: + EmojiButton(QWidget *parent, const style::IconButton &st); + + void setLoading(bool loading); + +protected: + void paintEvent(QPaintEvent *e) override; + void onStateChanged(int oldState, ButtonStateChangeSource source) override; + +private: + const style::IconButton &_st; + + FloatAnimation _a_over; + + bool _loading = false; + FloatAnimation a_loading; + Animation _a_loading; + + void step_loading(uint64 ms, bool timer) { + if (timer) { + update(); + } + } + +}; + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index c4a390e9c..ff146b3bc 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -261,10 +261,8 @@ void CountrySelectBox::doSetInnerFocus() { _select->setInnerFocus(); } -CountrySelectBox::Inner::Inner(QWidget *parent) : ScrolledWidget(parent) -, _rowHeight(st::countryRowHeight) -, _sel(0) -, _mouseSel(false) { +CountrySelectBox::Inner::Inner(QWidget *parent) : TWidget(parent) +, _rowHeight(st::countryRowHeight) { setAttribute(Qt::WA_OpaquePaintEvent); CountriesByISO2::const_iterator l = _countriesByISO2.constFind(lastValidISO); diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index fe3b217a9..e9a72d257 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -103,7 +103,7 @@ private: }; // This class is hold in header because it requires Qt preprocessing. -class CountrySelectBox::Inner : public ScrolledWidget { +class CountrySelectBox::Inner : public TWidget { Q_OBJECT public: @@ -135,11 +135,11 @@ protected: private: void updateSelectedRow(); - int32 _rowHeight; + int _rowHeight; - int32 _sel; + int _sel = 0; QString _filter; - bool _mouseSel; + bool _mouseSel = false; QPoint _lastMousePos; diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 167d30f86..7fcc8d8b3 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "ui/flatbutton.h" +#include "styles/style_history.h" + FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatButton &st) : Button(parent) , _text(text) , _st(st) @@ -264,83 +266,6 @@ void IconedButton::paintEvent(QPaintEvent *e) { } } -MaskedButton::MaskedButton(QWidget *parent, const style::iconedButton &st, const QString &text) : IconedButton(parent, st, text) { -} - -void MaskedButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - p.setOpacity(_opacity); - - p.setOpacity(a_opacity.current() * _opacity); - - if (!_text.isEmpty()) { - p.setFont(_st.font->f); - p.setRenderHint(QPainter::TextAntialiasing); - p.setPen(a_bg.current()); - const QPoint &t((_state & StateDown) ? _st.downTextPos : _st.textPos); - p.drawText(t.x(), t.y() + _st.font->ascent, _text); - } - - const style::sprite &i((_state & StateDown) ? _st.downIcon : _st.icon); - if (i.pxWidth()) { - const QPoint &t((_state & StateDown) ? _st.downIconPos : _st.iconPos); - p.fillRect(QRect(t, QSize(i.pxWidth(), i.pxHeight())), a_bg.current()); - p.drawSprite(t, i); - } -} - -EmojiButton::EmojiButton(QWidget *parent, const style::iconedButton &st) : IconedButton(parent, st) -, _loading(false) -, _a_loading(animation(this, &EmojiButton::step_loading)) { -} - -void EmojiButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - uint64 ms = getms(); - float64 loading = a_loading.current(ms, _loading ? 1 : 0); - p.setOpacity(_opacity * (1 - loading)); - - p.fillRect(e->rect(), a_bg.current()); - - p.setOpacity(a_opacity.current() * _opacity * (1 - loading)); - - const style::sprite &i((_state & StateDown) ? _st.downIcon : _st.icon); - if (!i.isEmpty()) { - const QPoint &t((_state & StateDown) ? _st.downIconPos : _st.iconPos); - p.drawSprite(t, i); - } - - p.setOpacity(a_opacity.current() * _opacity); - p.setPen(QPen(st::emojiCircleFg, st::emojiCircleLine)); - p.setBrush(Qt::NoBrush); - - p.setRenderHint(QPainter::HighQualityAntialiasing); - QRect inner(QPoint((width() - st::emojiCircle.width()) / 2, st::emojiCircleTop), st::emojiCircle); - if (loading > 0) { - int32 full = 5760; - int32 start = qRound(full * float64(ms % uint64(st::emojiCirclePeriod)) / st::emojiCirclePeriod), part = qRound(loading * full / st::emojiCirclePart); - p.drawArc(inner, start, full - part); - } else { - p.drawEllipse(inner); - } - p.setRenderHint(QPainter::HighQualityAntialiasing, false); -} - -void EmojiButton::setLoading(bool loading) { - if (_loading != loading) { - _loading = loading; - auto from = loading ? 0. : 1., to = loading ? 1. : 0.; - a_loading.start([this] { update(); }, from, to, st::emojiCircleDuration); - if (loading) { - _a_loading.start(); - } else { - _a_loading.stop(); - } - } -} - BoxButton::BoxButton(QWidget *parent, const QString &text, const style::RoundButton &st) : Button(parent) , _text(text.toUpper()) , _fullText(text.toUpper()) diff --git a/Telegram/SourceFiles/ui/flatbutton.h b/Telegram/SourceFiles/ui/flatbutton.h index 090b3232f..643836c66 100644 --- a/Telegram/SourceFiles/ui/flatbutton.h +++ b/Telegram/SourceFiles/ui/flatbutton.h @@ -28,7 +28,6 @@ class FlatButton : public Button { Q_OBJECT public: - FlatButton(QWidget *parent, const QString &text, const style::flatButton &st); void step_appearance(float64 ms, bool timer); @@ -45,11 +44,9 @@ public: } public slots: - void onStateChange(int oldState, ButtonStateChangeSource source); private: - QString _text, _textForAutoSize; int32 _textWidth; @@ -91,7 +88,6 @@ class IconedButton : public Button { Q_OBJECT public: - IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text = QString()); void step_appearance(float64 ms, bool timer); @@ -103,11 +99,9 @@ public: QString getText() const; public slots: - void onStateChange(int oldState, ButtonStateChangeSource source); protected: - QString _text; style::iconedButton _st; @@ -120,39 +114,6 @@ protected: float64 _opacity; }; -class MaskedButton : public IconedButton { - Q_OBJECT - -public: - - MaskedButton(QWidget *parent, const style::iconedButton &st, const QString &text = QString()); - - void paintEvent(QPaintEvent *e); - -}; - -class EmojiButton : public IconedButton { - Q_OBJECT - -public: - EmojiButton(QWidget *parent, const style::iconedButton &st); - - void paintEvent(QPaintEvent *e); - void setLoading(bool loading); - -private: - bool _loading; - FloatAnimation a_loading; - Animation _a_loading; - - void step_loading(uint64 ms, bool timer) { - if (timer) { - update(); - } - } - -}; - class BoxButton : public Button { Q_OBJECT diff --git a/Telegram/SourceFiles/ui/flatinput.cpp b/Telegram/SourceFiles/ui/flatinput.cpp index d950fc5fd..2829086ca 100644 --- a/Telegram/SourceFiles/ui/flatinput.cpp +++ b/Telegram/SourceFiles/ui/flatinput.cpp @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "ui/flatinput.h" -#include "ui/popupmenu.h" +#include "ui/widgets/popup_menu.h" #include "mainwindow.h" #include "countryinput.h" #include "lang.h" @@ -244,7 +244,12 @@ void FlatInput::updatePlaceholderText() { void FlatInput::contextMenuEvent(QContextMenuEvent *e) { if (auto menu = createStandardContextMenu()) { - (new PopupMenu(menu))->popup(e->globalPos()); + menu->addSeparator(); + auto action = menu->addAction(QString("test")); + action->setMenu(new QMenu(this)); + action->menu()->addAction(QString("test123")); + action->menu()->addAction(QString("test456")); + (new Ui::PopupMenu(menu))->popup(e->globalPos()); } } @@ -1282,7 +1287,7 @@ void InputArea::Inner::paintEvent(QPaintEvent *e) { void InputArea::Inner::contextMenuEvent(QContextMenuEvent *e) { if (auto menu = createStandardContextMenu()) { - (new PopupMenu(menu))->popup(e->globalPos()); + (new Ui::PopupMenu(menu))->popup(e->globalPos()); } } @@ -2029,7 +2034,7 @@ void InputField::Inner::paintEvent(QPaintEvent *e) { void InputField::Inner::contextMenuEvent(QContextMenuEvent *e) { if (auto menu = createStandardContextMenu()) { - (new PopupMenu(menu))->popup(e->globalPos()); + (new Ui::PopupMenu(menu))->popup(e->globalPos()); } } @@ -2237,7 +2242,7 @@ void MaskedInputField::updatePlaceholderText() { void MaskedInputField::contextMenuEvent(QContextMenuEvent *e) { if (auto menu = createStandardContextMenu()) { - (new PopupMenu(menu))->popup(e->globalPos()); + (new Ui::PopupMenu(menu))->popup(e->globalPos()); } } diff --git a/Telegram/SourceFiles/ui/flatlabel.cpp b/Telegram/SourceFiles/ui/flatlabel.cpp index e4d0a7d8b..6be2345ea 100644 --- a/Telegram/SourceFiles/ui/flatlabel.cpp +++ b/Telegram/SourceFiles/ui/flatlabel.cpp @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "ui/flatlabel.h" -#include "ui/popupmenu.h" +#include "ui/widgets/popup_menu.h" #include "mainwindow.h" #include "lang.h" @@ -414,7 +414,7 @@ void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason) uponSelection = hasSelection; } - _contextMenu = new PopupMenu(); + _contextMenu = new Ui::PopupMenu(); _contextMenuClickHandler = ClickHandler::getActive(); diff --git a/Telegram/SourceFiles/ui/flatlabel.h b/Telegram/SourceFiles/ui/flatlabel.h index b84d775e7..4c16382bb 100644 --- a/Telegram/SourceFiles/ui/flatlabel.h +++ b/Telegram/SourceFiles/ui/flatlabel.h @@ -20,7 +20,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +namespace Ui { class PopupMenu; +} // namespace Ui class FlatLabel : public TWidget, public ClickHandlerHost { Q_OBJECT @@ -137,7 +139,7 @@ private: QPoint _trippleClickPoint; QTimer _trippleClickTimer; - PopupMenu *_contextMenu = nullptr; + Ui::PopupMenu *_contextMenu = nullptr; ClickHandlerPtr _contextMenuClickHandler; QString _contextCopyText; ExpandLinksMode _contextExpandLinksMode = ExpandLinksAll; diff --git a/Telegram/SourceFiles/ui/flattextarea.cpp b/Telegram/SourceFiles/ui/flattextarea.cpp index e1445775e..8b78905cd 100644 --- a/Telegram/SourceFiles/ui/flattextarea.cpp +++ b/Telegram/SourceFiles/ui/flattextarea.cpp @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "flattextarea.h" -#include "ui/popupmenu.h" +#include "ui/widgets/popup_menu.h" #include "mainwindow.h" QByteArray FlatTextarea::serializeTagsList(const TagList &tags) { @@ -1421,6 +1421,6 @@ void FlatTextarea::dropEvent(QDropEvent *e) { void FlatTextarea::contextMenuEvent(QContextMenuEvent *e) { if (auto menu = createStandardContextMenu()) { - (new PopupMenu(menu))->popup(e->globalPos()); + (new Ui::PopupMenu(menu))->popup(e->globalPos()); } } diff --git a/Telegram/SourceFiles/ui/popupmenu.cpp b/Telegram/SourceFiles/ui/popupmenu.cpp deleted file mode 100644 index 3bb06e31f..000000000 --- a/Telegram/SourceFiles/ui/popupmenu.cpp +++ /dev/null @@ -1,678 +0,0 @@ -/* - 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. - - Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE - Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org - */ -#include "stdafx.h" - -#include "popupmenu.h" -#include "flatbutton.h" -#include "pspecific.h" - -#include "application.h" - -#include "lang.h" - -PopupMenu::PopupMenu(const style::PopupMenu &st) : TWidget(nullptr) -, _st(st) -, _itemHeight(_st.itemPadding.top() + _st.itemFont->height + _st.itemPadding.bottom()) -, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom()) -, _shadow(_st.shadow) -, a_opacity(1) -, _a_hide(animation(this, &PopupMenu::step_hide)) { - init(); -} - -PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(nullptr) -, _st(st) -, _menu(menu) -, _itemHeight(_st.itemPadding.top() + _st.itemFont->height + _st.itemPadding.bottom()) -, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom()) -, _shadow(_st.shadow) -, a_opacity(1) -, _a_hide(animation(this, &PopupMenu::step_hide)) { - _menu->setParent(this); - _menu->hide(); - - init(); - for (auto action : menu->actions()) { - addAction(action); - } -} - -void PopupMenu::init() { - _padding = _shadow.getDimensions(_st.shadowShift); - - resetActions(); - - setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Popup | Qt::NoDropShadowWindowHint); - setMouseTracking(true); - - hide(); - - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_TranslucentBackground, true); -} - -QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member) { - QAction *a = new QAction(text, this); - connect(a, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection); - return addAction(a); -} - -QAction *PopupMenu::addAction(QAction *a) { - connect(a, SIGNAL(changed()), this, SLOT(actionChanged())); - _actions.push_back(a); - if (auto submenu = a->menu()) { - _menus.push_back(new PopupMenu(submenu)); - _menus.back()->deleteOnHide(false); - } else { - _menus.push_back(nullptr); - } - _texts.push_back(QString()); - _shortcutTexts.push_back(QString()); - int32 w = processAction(a, _actions.size() - 1, width()); - resize(w, height() + (a->isSeparator() ? _separatorHeight : _itemHeight)); - update(); - - return a; -} - -QAction *PopupMenu::addSeparator() { - QAction *separator = new QAction(this); - separator->setSeparator(true); - return addAction(separator); -} - -int32 PopupMenu::processAction(QAction *a, int32 index, int32 w) { - if (a->isSeparator() || a->text().isEmpty()) { - _texts[index] = _shortcutTexts[index] = QString(); - } else { - QStringList texts = a->text().split('\t'); - int32 textw = _st.itemFont->width(texts.at(0)); - int32 goodw = _padding.left() + _st.itemPadding.left() + textw + _st.itemPadding.right() + _padding.right(); - if (_menus.at(index)) { - goodw += _st.itemPadding.left() + _st.arrow.width(); - } else if (texts.size() > 1) { - goodw += _st.itemPadding.left() + _st.itemFont->width(texts.at(1)); - } - w = snap(goodw, w, int32(_padding.left() + _st.widthMax + _padding.right())); - _texts[index] = (w < goodw) ? _st.itemFont->elided(texts.at(0), w - (goodw - textw)) : texts.at(0); - _shortcutTexts[index] = texts.size() > 1 ? texts.at(1) : QString(); - } - return w; -} - -PopupMenu::Actions &PopupMenu::actions() { - return _actions; -} - -void PopupMenu::actionChanged() { - int32 w = _padding.left() + _st.widthMin + _padding.right(); - for (int32 i = 0, l = _actions.size(); i < l; ++i) { - w = processAction(_actions.at(i), i, w); - } - if (w != width()) { - resize(w, height()); - } - update(); -} - -void PopupMenu::resetActions() { - clearActions(); - resize(_padding.left() + _st.widthMin + _padding.right(), _padding.top() + (_st.skip * 2) + _padding.bottom()); -} - -void PopupMenu::clearActions(bool force) { - if (_menu && !force) return; - - if (!_menu) { - for (int32 i = 0, l = _actions.size(); i < l; ++i) { - delete _actions[i]; - } - } - _actions.clear(); - - for (int32 i = 0, l = _menus.size(); i < l; ++i) { - delete _menus[i]; - } - _menus.clear(); - _childMenuIndex = -1; - - _selected = -1; -} - -void PopupMenu::resizeEvent(QResizeEvent *e) { - _inner = QRect(_padding.left(), _padding.top(), width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom()); - return TWidget::resizeEvent(e); -} - -void PopupMenu::paintEvent(QPaintEvent *e) { - Painter p(this); - - QRect r(e->rect()); - p.setClipRect(r); - QPainter::CompositionMode m = p.compositionMode(); - p.setCompositionMode(QPainter::CompositionMode_Source); - if (_a_hide.animating()) { - p.setOpacity(a_opacity.current()); - p.drawPixmap(0, 0, _cache); - return; - } - - p.fillRect(r, st::almostTransparent->b); - p.setCompositionMode(m); - - _shadow.paint(p, _inner, _st.shadowShift); - - QRect topskip(_padding.left(), _padding.top(), _inner.width(), _st.skip); - QRect bottomskip(_padding.left(), height() - _padding.bottom() - _st.skip, _inner.width(), _st.skip); - if (r.intersects(topskip)) p.fillRect(r.intersected(topskip), _st.itemBg->b); - if (r.intersects(bottomskip)) p.fillRect(r.intersected(bottomskip), _st.itemBg->b); - - int32 y = _padding.top() + _st.skip; - p.translate(_padding.left(), y); - p.setFont(_st.itemFont); - for (int32 i = 0, l = _actions.size(); i < l; ++i) { - if (r.top() + r.height() <= y) break; - int32 h = _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight; - y += h; - if (r.top() < y) { - if (_actions.at(i)->isSeparator()) { - p.fillRect(0, 0, _inner.width(), h, _st.itemBg->b); - p.fillRect(_st.separatorPadding.left(), _st.separatorPadding.top(), _inner.width() - _st.separatorPadding.left() - _st.separatorPadding.right(), _st.separatorWidth, _st.separatorFg->b); - } else { - bool enabled = _actions.at(i)->isEnabled(), selected = (i == _selected && enabled); - p.fillRect(0, 0, _inner.width(), h, (selected ? _st.itemBgOver : _st.itemBg)->b); - p.setPen(selected ? _st.itemFgOver : (enabled ? _st.itemFg : _st.itemFgDisabled)); - p.drawTextLeft(_st.itemPadding.left(), _st.itemPadding.top(), _inner.width(), _texts.at(i)); - if (_menus.at(i)) { - _st.arrow.paint(p, _inner.width() - _st.itemPadding.right() - _st.arrow.width(), (_itemHeight - _st.arrow.height()) / 2, _inner.width()); - } else if (!_shortcutTexts.at(i).isEmpty()) { - p.setPen(selected ? _st.itemFgShortcutOver : (enabled ? _st.itemFgShortcut : _st.itemFgShortcutDisabled)); - p.drawTextRight(_st.itemPadding.right(), _st.itemPadding.top(), _inner.width(), _shortcutTexts.at(i)); - } - } - } - p.translate(0, h); - } -} - -void PopupMenu::updateSelected() { - if (!_mouseSelection) return; - - QPoint p(mapFromGlobal(_mouse) - QPoint(_padding.left(), _padding.top() + _st.skip)); - int32 selected = -1, y = 0; - while (y <= p.y() && ++selected < _actions.size()) { - y += _actions.at(selected)->isSeparator() ? _separatorHeight : _itemHeight; - } - setSelected((selected >= 0 && selected < _actions.size() && _actions.at(selected)->isEnabled() && !_actions.at(selected)->isSeparator()) ? selected : -1); -} - -void PopupMenu::itemPressed(PressSource source) { - if (source == PressSourceMouse && !_mouseSelection) { - return; - } - if (_selected >= 0 && _selected < _actions.size() && _actions[_selected]->isEnabled()) { - if (_menus.at(_selected)) { - if (_childMenuIndex == _selected) { - _menus.at(_childMenuIndex)->hideMenu(true); - } else { - popupChildMenu(source); - } - } else { - hideMenu(); - _triggering = true; - emit _actions[_selected]->trigger(); - _triggering = false; - if (_deleteLater) { - _deleteLater = false; - deleteLater(); - } - } - } -} - -void PopupMenu::popupChildMenu(PressSource source) { - if (_childMenuIndex >= 0) { - _menus.at(_childMenuIndex)->hideMenu(true); - _childMenuIndex = -1; - } - if (_selected >= 0 && _selected < _menus.size() && _menus.at(_selected)) { - QPoint p(_inner.x() + (rtl() ? _padding.right() : _inner.width() - _padding.left()), _inner.y() + _st.skip + itemY(_selected)); - _childMenuIndex = _selected; - _menus.at(_childMenuIndex)->showMenu(geometry().topLeft() + p, this, source); - } -} - -void PopupMenu::keyPressEvent(QKeyEvent *e) { - if (_childMenuIndex >= 0) { - return _menus.at(_childMenuIndex)->keyPressEvent(e); - } - - if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - itemPressed(PressSourceKeyboard); - return; - } else if (e->key() == Qt::Key_Escape) { - hideMenu(_parent ? true : false); - return; - } - if (e->key() == (rtl() ? Qt::Key_Left : Qt::Key_Right)) { - if (_selected >= 0 && _menus.at(_selected)) { - itemPressed(PressSourceKeyboard); - return; - } else if (_selected < 0 && _parent && !_actions.isEmpty()) { - _mouseSelection = false; - setSelected(0); - } - } else if (e->key() == (rtl() ? Qt::Key_Right : Qt::Key_Left)) { - if (_parent) { - hideMenu(true); - } - } - if ((e->key() != Qt::Key_Up && e->key() != Qt::Key_Down) || _actions.size() < 1) return; - - int32 delta = (e->key() == Qt::Key_Down ? 1 : -1), start = _selected; - if (start < 0 || start >= _actions.size()) { - start = (delta > 0) ? (_actions.size() - 1) : 0; - } - int32 newSelected = start; - do { - newSelected += delta; - if (newSelected < 0) { - newSelected += _actions.size(); - } else if (newSelected >= _actions.size()) { - newSelected -= _actions.size(); - } - } while (newSelected != start && (!_actions.at(newSelected)->isEnabled() || _actions.at(newSelected)->isSeparator())); - - if (_actions.at(newSelected)->isEnabled() && !_actions.at(newSelected)->isSeparator()) { - _mouseSelection = false; - setSelected(newSelected); - } -} - -void PopupMenu::enterEvent(QEvent *e) { - QPoint mouse = QCursor::pos(); - if (!_inner.marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)).contains(mapFromGlobal(mouse))) { - if (_mouseSelection && _childMenuIndex < 0) { - _mouseSelection = false; - setSelected(-1); - } - } - return TWidget::enterEvent(e); -} - -void PopupMenu::leaveEvent(QEvent *e) { - if (_mouseSelection && _childMenuIndex < 0) { - _mouseSelection = false; - setSelected(-1); - } - return TWidget::leaveEvent(e); -} - -void PopupMenu::setSelected(int32 newSelected) { - if (newSelected >= _actions.size()) { - newSelected = -1; - } - if (newSelected != _selected) { - updateSelectedItem(); - _selected = newSelected; - if (_mouseSelection) { - popupChildMenu(PressSourceMouse); - } - updateSelectedItem(); - } -} - -int32 PopupMenu::itemY(int32 index) { - if (index > _actions.size()) { - index = _actions.size(); - } - int32 y = 0; - for (int32 i = 0; i < index; ++i) { - y += _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight; - } - return y; -} - -void PopupMenu::updateSelectedItem() { - if (_selected >= 0) { - update(_padding.left(), _padding.top() + _st.skip + itemY(_selected), width() - _padding.left() - _padding.right(), _actions.at(_selected)->isSeparator() ? _separatorHeight : _itemHeight); - } -} - -void PopupMenu::mouseMoveEvent(QMouseEvent *e) { - if (_inner.marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)).contains(mapFromGlobal(e->globalPos()))) { - _mouseSelection = true; - _mouse = e->globalPos(); - updateSelected(); - } else { - if (_mouseSelection && _childMenuIndex < 0) { - _mouseSelection = false; - setSelected(-1); - } - if (_parent) { - _parent->mouseMoveEvent(e); - } - } -} - -void PopupMenu::mousePressEvent(QMouseEvent *e) { - mouseMoveEvent(e); - if (_inner.contains(mapFromGlobal(e->globalPos()))) { - itemPressed(PressSourceMouse); - return; - } - if (_parent) { - _parent->mousePressEvent(e); - } else { - hideMenu(); - } -} - -void PopupMenu::focusOutEvent(QFocusEvent *e) { - hideMenu(); -} - -void PopupMenu::hideEvent(QHideEvent *e) { - if (_deleteOnHide) { - if (_triggering) { - _deleteLater = true; - } else { - deleteLater(); - } - } -} - -void PopupMenu::hideMenu(bool fast) { - if (isHidden()) return; - if (_parent && !_a_hide.animating()) { - _parent->childHiding(this); - } - if (fast) { - if (_a_hide.animating()) { - _a_hide.stop(); - } - a_opacity = anim::fvalue(0, 0); - hideFinish(); - } else { - if (!_a_hide.animating()) { - _cache = myGrab(this); - a_opacity.start(0); - _a_hide.start(); - } - if (_parent) { - _parent->hideMenu(); - } - } - if (_childMenuIndex >= 0) { - _menus.at(_childMenuIndex)->hideMenu(fast); - } -} - -void PopupMenu::childHiding(PopupMenu *child) { - if (_childMenuIndex >= 0 && _menus.at(_childMenuIndex) == child) { - _childMenuIndex = -1; - } -} - -void PopupMenu::hideFinish() { - hide(); -} - -void PopupMenu::step_hide(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_hide.stop(); - a_opacity.finish(); - hideFinish(); - } else { - a_opacity.update(dt, anim::linear); - } - if (timer) update(); -} - -void PopupMenu::deleteOnHide(bool del) { - _deleteOnHide = del; -} - -void PopupMenu::popup(const QPoint &p) { - showMenu(p, 0, PressSourceMouse); -} - -void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, PressSource source) { - _parent = parent; - - QPoint w = p - QPoint(0, _padding.top()); - QRect r = Sandbox::screenGeometry(p); - if (rtl()) { - if (w.x() - width() < r.x() - _padding.left()) { - if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) { - w.setX(w.x() + _parent->width() - _padding.left() - _padding.right()); - } else { - w.setX(r.x() - _padding.left()); - } - } else { - w.setX(w.x() - width()); - } - } else { - if (w.x() + width() - _padding.right() > r.x() + r.width()) { - if (_parent && w.x() - _parent->width() + _padding.left() + _padding.right() - width() + _padding.right() >= r.x() - _padding.left()) { - w.setX(w.x() + _padding.left() + _padding.right() - _parent->width() - width() + _padding.left() + _padding.right()); - } else { - w.setX(r.x() + r.width() - width() + _padding.right()); - } - } - } - if (w.y() + height() - _padding.bottom() > r.y() + r.height()) { - if (_parent) { - w.setY(r.y() + r.height() - height() + _padding.bottom()); - } else { - w.setY(p.y() - height() + _padding.bottom()); - } - } - if (w.y() < r.y()) { - w.setY(r.y()); - } - move(w); - - _mouseSelection = (source == PressSourceMouse); - setSelected((source == PressSourceMouse || _actions.isEmpty()) ? -1 : 0); - psUpdateOverlayed(this); - show(); - psShowOverAll(this); - windowHandle()->requestActivate(); - activateWindow(); - - if (_a_hide.animating()) { - _a_hide.stop(); - _cache = QPixmap(); - } - a_opacity = anim::fvalue(1, 1); -} - -PopupMenu::~PopupMenu() { - clearActions(true); - -#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 - if (auto w = App::wnd()) { - w->onReActivate(); - QTimer::singleShot(200, w, SLOT(onReActivate())); - } -#endif -} - -PopupTooltip *PopupTooltipInstance = 0; - -AbstractTooltipShower::~AbstractTooltipShower() { - if (PopupTooltipInstance && PopupTooltipInstance->_shower == this) { - PopupTooltipInstance->_shower = 0; - } -} - -PopupTooltip::PopupTooltip() : TWidget(0) -, _shower(0) -, _st(0) { - PopupTooltipInstance = this; - - setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint); - setAttribute(Qt::WA_NoSystemBackground, true); - - _showTimer.setSingleShot(true); - connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow())); - - connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); -} - -void PopupTooltip::onShow() { - if (_shower) { - QString text = (App::wnd() && App::wnd()->isActive(false)) ? _shower->tooltipText() : QString(); - if (text.isEmpty()) { - Hide(); - } else { - PopupTooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt()); - } - } -} - -void PopupTooltip::onWndActiveChanged() { - if (!App::wnd() || !App::wnd()->windowHandle() || !App::wnd()->windowHandle()->isActive()) { - PopupTooltip::Hide(); - } -} - -bool PopupTooltip::eventFilter(QObject *o, QEvent *e) { - if (e->type() == QEvent::Leave) { - _hideByLeaveTimer.start(10); - } else if (e->type() == QEvent::Enter) { - _hideByLeaveTimer.stop(); - } else if (e->type() == QEvent::MouseMove) { - if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) { - Hide(); - } - } - return TWidget::eventFilter(o, e); -} - -void PopupTooltip::onHideByLeave() { - Hide(); -} - -PopupTooltip::~PopupTooltip() { - if (PopupTooltipInstance == this) { - PopupTooltipInstance = 0; - } -} - -void PopupTooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) { - if (!_hideByLeaveTimer.isSingleShot()) { - _hideByLeaveTimer.setSingleShot(true); - connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave())); - - Sandbox::installEventFilter(this); - } - - _point = m; - _st = st; - _text = Text(_st->textFont, text, _textPlainOptions, _st->widthMax, true); - - int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right(); - int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom(); - - // count tooltip size - QSize s(addw + _text.maxWidth(), addh + _text.minHeight()); - if (s.width() > _st->widthMax) { - s.setWidth(addw + _text.countWidth(_st->widthMax - addw)); - s.setHeight(addh + _text.countHeight(s.width() - addw)); - } - int32 maxh = addh + (_st->linesMax * _st->textFont->height); - if (s.height() > maxh) { - s.setHeight(maxh); - } - - // count tooltip position - QPoint p(m + _st->shift); - if (rtl()) { - p.setX(m.x() - s.width() - _st->shift.x()); - } - if (s.width() < 2 * _st->shift.x()) { - p.setX(m.x() - (s.width() / 2)); - } - - // adjust tooltip position - QRect r(QApplication::desktop()->screenGeometry(m)); - if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) { - p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width())); - } - if (r.x() + _st->skip > p.x() && p.x() < m.x()) { - p.setX(qMin(m.x(), r.x() + int32(_st->skip))); - } - if (r.y() + r.height() - _st->skip < p.y() + s.height()) { - p.setY(m.y() - s.height() - _st->skip); - } - if (r.y() > p.x()) { - p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height())); - } - - setGeometry(QRect(p, s)); - - _hideByLeaveTimer.stop(); - show(); -} - -void PopupTooltip::paintEvent(QPaintEvent *e) { - Painter p(this); - - p.fillRect(rect(), _st->textBg); - - p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder); - p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder); - p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); - p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); - - int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textFont->height); - - p.setPen(_st->textFg); - _text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines); -} - -void PopupTooltip::hideEvent(QHideEvent *e) { - if (PopupTooltipInstance == this) { - Hide(); - } -} - -void PopupTooltip::Show(int32 delay, const AbstractTooltipShower *shower) { - if (!PopupTooltipInstance) { - new PopupTooltip(); - } - PopupTooltipInstance->_shower = shower; - if (delay >= 0) { - PopupTooltipInstance->_showTimer.start(delay); - } else { - PopupTooltipInstance->onShow(); - } -} - -void PopupTooltip::Hide() { - if (PopupTooltip *instance = PopupTooltipInstance) { - PopupTooltipInstance = 0; - instance->_showTimer.stop(); - instance->_hideByLeaveTimer.stop(); - instance->hide(); - instance->deleteLater(); - } -} diff --git a/Telegram/SourceFiles/ui/popupmenu.h b/Telegram/SourceFiles/ui/popupmenu.h deleted file mode 100644 index 4413ec543..000000000 --- a/Telegram/SourceFiles/ui/popupmenu.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - 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. - - Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE - Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org - */ -#pragma once - -#include "ui/text/text.h" -#include "ui/effects/rect_shadow.h" - -class PopupMenu : public TWidget { - Q_OBJECT - -public: - PopupMenu(const style::PopupMenu &st = st::defaultPopupMenu); - PopupMenu(QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu); - QAction *addAction(const QString &text, const QObject *receiver, const char* member); - QAction *addAction(QAction *a); - QAction *addSeparator(); - void resetActions(); - - typedef QVector Actions; - Actions &actions(); - - void deleteOnHide(bool del); - void popup(const QPoint &p); - void hideMenu(bool fast = false); - - ~PopupMenu(); - -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void leaveEvent(QEvent *e) override; - void enterEvent(QEvent *e) override; - void focusOutEvent(QFocusEvent *e) override; - void hideEvent(QHideEvent *e) override; - -private slots: - void actionChanged(); - -private: - void updateSelected(); - - void childHiding(PopupMenu *child); - - void step_hide(float64 ms, bool timer); - - void init(); - void hideFinish(); - - enum PressSource { - PressSourceMouse, - PressSourceKeyboard, - }; - - void clearActions(bool force = false); - int32 processAction(QAction *a, int32 index, int32 w); - void setSelected(int32 selected); - int32 itemY(int32 index); - void updateSelectedItem(); - void itemPressed(PressSource source); - void popupChildMenu(PressSource source); - void showMenu(const QPoint &p, PopupMenu *parent, PressSource source);; - - const style::PopupMenu &_st; - - typedef QVector PopupMenus; - - QMenu *_menu = nullptr; - Actions _actions; - PopupMenus _menus; - PopupMenu *_parent = nullptr; - QStringList _texts, _shortcutTexts; - - int32 _itemHeight, _separatorHeight; - QRect _inner; - style::margins _padding; - - QPoint _mouse; - bool _mouseSelection = false; - - Ui::RectShadow _shadow; - int _selected = -1; - int _childMenuIndex = -1; - - QPixmap _cache; - anim::fvalue a_opacity; - Animation _a_hide; - - bool _deleteOnHide = true; - bool _triggering = false; - bool _deleteLater = false; - -}; - -class AbstractTooltipShower { -public: - virtual QString tooltipText() const = 0; - virtual QPoint tooltipPos() const = 0; - virtual const style::Tooltip *tooltipSt() const { - return &st::defaultTooltip; - } - virtual ~AbstractTooltipShower(); -}; - -class PopupTooltip : public TWidget { - Q_OBJECT - -public: - bool eventFilter(QObject *o, QEvent *e); - - static void Show(int32 delay, const AbstractTooltipShower *shower); - static void Hide(); - - ~PopupTooltip(); - -public slots: - void onShow(); - void onWndActiveChanged(); - void onHideByLeave(); - -protected: - void paintEvent(QPaintEvent *e); - void hideEvent(QHideEvent *e); - -private: - PopupTooltip(); - - void popup(const QPoint &p, const QString &text, const style::Tooltip *st); - - friend class AbstractTooltipShower; - const AbstractTooltipShower *_shower; - QTimer _showTimer; - - Text _text; - QPoint _point; - - const style::Tooltip *_st; - - QTimer _hideByLeaveTimer; - -}; diff --git a/Telegram/SourceFiles/ui/scrollarea.h b/Telegram/SourceFiles/ui/scrollarea.h index 98a79881c..12c80c027 100644 --- a/Telegram/SourceFiles/ui/scrollarea.h +++ b/Telegram/SourceFiles/ui/scrollarea.h @@ -273,19 +273,3 @@ public: } void paintEvent(QPaintEvent *e); }; - -class ScrolledWidget : public TWidget { - Q_OBJECT - -public: - ScrolledWidget(QWidget *parent = nullptr) : TWidget(parent) { - } - - // Updates the area that is visible inside the scroll container. - virtual void setVisibleTopBottom(int visibleTop, int visibleBottom) { - } - -signals: - void heightUpdated(); - -}; diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h index 07ca80262..8c9c6f964 100644 --- a/Telegram/SourceFiles/ui/twidget.h +++ b/Telegram/SourceFiles/ui/twidget.h @@ -228,6 +228,14 @@ public: } } + // Updates the area that is visible inside the scroll container. + virtual void setVisibleTopBottom(int visibleTop, int visibleBottom) { + } + +signals: + // Child widget is responsible for emitting this signal. + void heightUpdated(); + protected: void enterEventHook(QEvent *e) { return QWidget::enterEvent(e); diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp new file mode 100644 index 000000000..77ecdbf0a --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp @@ -0,0 +1,270 @@ +/* +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. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "ui/widgets/dropdown_menu.h" + +#include "application.h" +#include "lang.h" + +namespace Ui { + +DropdownMenu::DropdownMenu(QWidget *parent, const style::DropdownMenu &st) : InnerDropdown(parent, st.wrap) +, _st(st) +, _menu(this, _st.menu) { + init(); +} + +// Not ready with submenus yet. +//DropdownMenu::DropdownMenu(QWidget *parent, QMenu *menu, const style::DropdownMenu &st) : InnerDropdown(parent, st.wrap) +//, _st(st) +//, _menu(this, menu, _st.menu) { +// init(); +// +// for (auto action : actions()) { +// if (auto submenu = action->menu()) { +// auto it = _submenus.insert(action, new DropdownMenu(submenu, st)); +// it.value()->deleteOnHide(false); +// } +// } +//} + +void DropdownMenu::init() { + connect(this, SIGNAL(beforeHidden()), this, SLOT(onHidden())); + + setOwnedWidget(_menu); + + _menu->setResizedCallback([this] { resizeToContent(); }); + _menu->setActivatedCallback([this](QAction *action, int actionTop, TriggeredSource source) { + handleActivated(action, actionTop, source); + }); + _menu->setTriggeredCallback([this](QAction *action, int actionTop, TriggeredSource source) { + handleTriggered(action, actionTop, source); + }); + _menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); }); + _menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); }); + _menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); }); + + setMouseTracking(true); + + hide(); +} + +QAction *DropdownMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) { + return _menu->addAction(text, receiver, member, icon); +} + +QAction *DropdownMenu::addSeparator() { + return _menu->addSeparator(); +} + +void DropdownMenu::clearActions() { + //for (auto submenu : base::take(_submenus)) { + // delete submenu; + //} + return _menu->clearActions(); +} + +DropdownMenu::Actions &DropdownMenu::actions() { + return _menu->actions(); +} + +void DropdownMenu::handleActivated(QAction *action, int actionTop, TriggeredSource source) { + if (source == TriggeredSource::Mouse) { + if (!popupSubmenuFromAction(action, actionTop, source)) { + if (auto currentSubmenu = base::take(_activeSubmenu)) { + currentSubmenu->hideMenu(true); + } + } + } +} + +void DropdownMenu::handleTriggered(QAction *action, int actionTop, TriggeredSource source) { + if (!popupSubmenuFromAction(action, actionTop, source)) { + hideMenu(); + _triggering = true; + emit action->trigger(); + _triggering = false; + if (_deleteLater) { + _deleteLater = false; + deleteLater(); + } + } +} + +// Not ready with submenus yet. +bool DropdownMenu::popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source) { + //if (auto submenu = _submenus.value(action)) { + // if (_activeSubmenu == submenu) { + // submenu->hideMenu(true); + // } else { + // popupSubmenu(submenu, actionTop, source); + // } + // return true; + //} + return false; +} + +//void DropdownMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source) { +// if (auto currentSubmenu = base::take(_activeSubmenu)) { +// currentSubmenu->hideMenu(true); +// } +// if (submenu) { +// auto menuTopLeft = mapFromGlobal(_menu->mapToGlobal(QPoint(0, 0))); +// auto menuBottomRight = mapFromGlobal(_menu->mapToGlobal(QPoint(_menu->width(), _menu->height()))); +// QPoint p(menuTopLeft.x() + (rtl() ? (width() - menuBottomRight.x()) : menuBottomRight.x()), menuTopLeft.y() + actionTop); +// _activeSubmenu = submenu; +// _activeSubmenu->showMenu(geometry().topLeft() + p, this, source); +// +// _menu->setChildShown(true); +// } else { +// _menu->setChildShown(false); +// } +//} + +void DropdownMenu::forwardKeyPress(int key) { + if (!handleKeyPress(key)) { + _menu->handleKeyPress(key); + } +} + +bool DropdownMenu::handleKeyPress(int key) { + if (_activeSubmenu) { + _activeSubmenu->handleKeyPress(key); + return true; + } else if (key == Qt::Key_Escape) { + hideMenu(_parent ? true : false); + return true; + } else if (key == (rtl() ? Qt::Key_Right : Qt::Key_Left)) { + if (_parent) { + hideMenu(true); + return true; + } + } + return false; +} + +void DropdownMenu::handleMouseMove(QPoint globalPosition) { + if (_parent) { + _parent->forwardMouseMove(globalPosition); + } +} + +void DropdownMenu::handleMousePress(QPoint globalPosition) { + if (_parent) { + _parent->forwardMousePress(globalPosition); + } else { + hideMenu(); + } +} + +void DropdownMenu::focusOutEvent(QFocusEvent *e) { + hideMenu(); +} + +void DropdownMenu::hideEvent(QHideEvent *e) { + if (_deleteOnHide) { + if (_triggering) { + _deleteLater = true; + } else { + deleteLater(); + } + } +} + +void DropdownMenu::hideMenu(bool fast) { + if (isHidden()) return; + if (_parent && !isHiding()) { + _parent->childHiding(this); + } + if (fast) { + hideFast(); + } else { + hideAnimated(); + if (_parent) { + _parent->hideMenu(); + } + } + if (_activeSubmenu) { + _activeSubmenu->hideMenu(fast); + } +} + +void DropdownMenu::childHiding(DropdownMenu *child) { + if (_activeSubmenu && _activeSubmenu == child) { + _activeSubmenu = SubmenuPointer(); + } +} + +void DropdownMenu::hideFinish() { + _menu->clearSelection(); +} + +// Not ready with submenus yet. +//void DropdownMenu::deleteOnHide(bool del) { +// _deleteOnHide = del; +//} + +//void DropdownMenu::popup(const QPoint &p) { +// showMenu(p, nullptr, TriggeredSource::Mouse); +//} +// +//void DropdownMenu::showMenu(const QPoint &p, DropdownMenu *parent, TriggeredSource source) { +// _parent = parent; +// +// auto menuTopLeft = mapFromGlobal(_menu->mapToGlobal(QPoint(0, 0))); +// auto w = p - QPoint(0, menuTopLeft.y()); +// auto r = Sandbox::screenGeometry(p); +// if (rtl()) { +// if (w.x() - width() < r.x() - _padding.left()) { +// if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) { +// w.setX(w.x() + _parent->width() - _padding.left() - _padding.right()); +// } else { +// w.setX(r.x() - _padding.left()); +// } +// } else { +// w.setX(w.x() - width()); +// } +// } else { +// if (w.x() + width() - _padding.right() > r.x() + r.width()) { +// if (_parent && w.x() - _parent->width() + _padding.left() + _padding.right() - width() + _padding.right() >= r.x() - _padding.left()) { +// w.setX(w.x() + _padding.left() + _padding.right() - _parent->width() - width() + _padding.left() + _padding.right()); +// } else { +// w.setX(r.x() + r.width() - width() + _padding.right()); +// } +// } +// } +// if (w.y() + height() - _padding.bottom() > r.y() + r.height()) { +// if (_parent) { +// w.setY(r.y() + r.height() - height() + _padding.bottom()); +// } else { +// w.setY(p.y() - height() + _padding.bottom()); +// } +// } +// if (w.y() < r.y()) { +// w.setY(r.y()); +// } +// move(w); +// +// _menu->setShowSource(source); +//} + +DropdownMenu::~DropdownMenu() { + clearActions(); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h new file mode 100644 index 000000000..e88c33e59 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h @@ -0,0 +1,110 @@ +/* + 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. + + Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE + Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org + */ +#pragma once + +#include "styles/style_widgets.h" +#include "ui/effects/rect_shadow.h" +#include "ui/widgets/inner_dropdown.h" +#include "ui/widgets/menu.h" + +namespace Ui { + +class DropdownMenu : public InnerDropdown { + Q_OBJECT + +public: + DropdownMenu(QWidget *parent, const style::DropdownMenu &st = st::defaultDropdownMenu); + + QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr); + QAction *addSeparator(); + void clearActions(); + + using Actions = Ui::Menu::Actions; + Actions &actions(); + + ~DropdownMenu(); + +protected: + void focusOutEvent(QFocusEvent *e) override; + void hideEvent(QHideEvent *e) override; + + void keyPressEvent(QKeyEvent *e) override { + forwardKeyPress(e->key()); + } + void mouseMoveEvent(QMouseEvent *e) override { + forwardMouseMove(e->globalPos()); + } + void mousePressEvent(QMouseEvent *e) override { + forwardMousePress(e->globalPos()); + } + +private slots: + void onHidden() { + hideFinish(); + } + +private: + // Not ready with submenus yet. + DropdownMenu(QWidget *parent, QMenu *menu, const style::DropdownMenu &st = st::defaultDropdownMenu); + void deleteOnHide(bool del); + void popup(const QPoint &p); + void hideMenu(bool fast = false); + + void childHiding(DropdownMenu *child); + + void init(); + void hideFinish(); + + using TriggeredSource = Ui::Menu::TriggeredSource; + void handleActivated(QAction *action, int actionTop, TriggeredSource source); + void handleTriggered(QAction *action, int actionTop, TriggeredSource source); + void forwardKeyPress(int key); + bool handleKeyPress(int key); + void forwardMouseMove(QPoint globalPosition) { + _menu->handleMouseMove(globalPosition); + } + void handleMouseMove(QPoint globalPosition); + void forwardMousePress(QPoint globalPosition) { + _menu->handleMousePress(globalPosition); + } + void handleMousePress(QPoint globalPosition); + + using SubmenuPointer = QPointer; + bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source); + void popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source); + void showMenu(const QPoint &p, DropdownMenu *parent, TriggeredSource source); + + const style::DropdownMenu &_st; + + ChildWidget _menu; + + // Not ready with submenus yet. + //using Submenus = QMap; + //Submenus _submenus; + + DropdownMenu *_parent = nullptr; + + SubmenuPointer _activeSubmenu; + + bool _deleteOnHide = false; + bool _triggering = false; + bool _deleteLater = false; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp similarity index 60% rename from Telegram/SourceFiles/ui/inner_dropdown.cpp rename to Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index 8065c18e0..d7f5cd622 100644 --- a/Telegram/SourceFiles/ui/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "ui/inner_dropdown.h" +#include "ui/widgets/inner_dropdown.h" #include "mainwindow.h" #include "ui/scrollarea.h" @@ -27,12 +27,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { -InnerDropdown::InnerDropdown(QWidget *parent, const style::InnerDropdown &st, const style::flatScroll &scrollSt) : TWidget(parent) +InnerDropdown::InnerDropdown(QWidget *parent, const style::InnerDropdown &st) : TWidget(parent) , _st(st) , _shadow(_st.shadow) -, _scroll(this, scrollSt) { +, _scroll(this, _st.scroll) { _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart())); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideAnimated())); connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); @@ -43,9 +43,9 @@ InnerDropdown::InnerDropdown(QWidget *parent, const style::InnerDropdown &st, co hide(); } -void InnerDropdown::setOwnedWidget(ScrolledWidget *widget) { - auto container = new internal::Container(_scroll, widget, _st); - connect(container, SIGNAL(heightUpdated()), this, SLOT(onWidgetHeightUpdated())); +void InnerDropdown::setOwnedWidget(TWidget *widget) { + auto container = new Container(_scroll, widget, _st); + connect(widget, SIGNAL(heightUpdated()), this, SLOT(onWidgetHeightUpdated())); _scroll->setOwnedWidget(container); container->resizeToWidth(_scroll->width()); container->moveToLeft(0, 0); @@ -55,23 +55,22 @@ void InnerDropdown::setOwnedWidget(ScrolledWidget *widget) { void InnerDropdown::setMaxHeight(int newMaxHeight) { _maxHeight = newMaxHeight; - updateHeight(); + resizeToContent(); } -void InnerDropdown::onWidgetHeightUpdated() { - updateHeight(); -} - -void InnerDropdown::updateHeight() { - int newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom(); - if (auto widget = static_cast(_scroll->widget())) { +void InnerDropdown::resizeToContent() { + auto newWidth = _st.padding.left() + _st.scrollMargin.left() + _st.scrollMargin.right() + _st.padding.right(); + auto newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom(); + if (auto widget = static_cast(_scroll->widget())) { + widget->resizeToContent(); + newWidth += widget->width(); newHeight += widget->height(); } if (_maxHeight > 0) { accumulate_min(newHeight, _maxHeight); } - if (newHeight != height()) { - resize(width(), newHeight); + if (newWidth != width() || newHeight != height()) { + resize(newWidth, newHeight); } } @@ -83,14 +82,14 @@ void InnerDropdown::onWindowActiveChanged() { void InnerDropdown::resizeEvent(QResizeEvent *e) { _scroll->setGeometry(rect().marginsRemoved(_st.padding).marginsRemoved(_st.scrollMargin)); - if (auto widget = static_cast(_scroll->widget())) { + if (auto widget = static_cast(_scroll->widget())) { widget->resizeToWidth(_scroll->width()); onScroll(); } } void InnerDropdown::onScroll() { - if (auto widget = static_cast(_scroll->widget())) { + if (auto widget = static_cast(_scroll->widget())) { int visibleTop = _scroll->scrollTop(); int visibleBottom = visibleTop + _scroll->height(); widget->setVisibleTopBottom(visibleTop, visibleBottom); @@ -103,9 +102,9 @@ void InnerDropdown::paintEvent(QPaintEvent *e) { if (!_cache.isNull()) { bool animating = _a_appearance.animating(getms()); if (animating) { - p.setOpacity(_a_appearance.current(_hiding)); - } else if (_hiding) { - hidingFinished(); + p.setOpacity(_a_appearance.current(_hiding ? 0. : 1.)); + } else if (_hiding || isHidden()) { + hideFinished(); return; } p.drawPixmap(0, 0, _cache); @@ -117,20 +116,19 @@ void InnerDropdown::paintEvent(QPaintEvent *e) { } // draw shadow - QRect shadowedRect = rect().marginsRemoved(_st.padding); + auto shadowedRect = rect().marginsRemoved(_st.padding); _shadow.paint(p, shadowedRect, _st.shadowShift); p.fillRect(shadowedRect, st::windowBg); } void InnerDropdown::enterEvent(QEvent *e) { - _hideTimer.stop(); - if (_hiding) showingStarted(); + showAnimated(); return TWidget::enterEvent(e); } void InnerDropdown::leaveEvent(QEvent *e) { if (_a_appearance.animating(getms())) { - onHideStart(); + hideAnimated(); } else { _hideTimer.start(300); } @@ -138,25 +136,43 @@ void InnerDropdown::leaveEvent(QEvent *e) { } void InnerDropdown::otherEnter() { - _hideTimer.stop(); - showingStarted(); + showAnimated(); } void InnerDropdown::otherLeave() { if (_a_appearance.animating(getms())) { - onHideStart(); + hideAnimated(); } else { _hideTimer.start(0); } } -void InnerDropdown::onHideStart() { +void InnerDropdown::showAnimated() { + _hideTimer.stop(); + showStarted(); +} + +void InnerDropdown::hideAnimated(HideOption option) { + if (isHidden()) return; + if (option == HideOption::IgnoreShow) { + _ignoreShowEvents = true; + } if (_hiding) return; + _hideTimer.stop(); _hiding = true; startAnimation(); } +void InnerDropdown::hideFast() { + if (isHidden()) return; + + _hideTimer.stop(); + _hiding = false; + _a_appearance.finish(); + hideFinished(); +} + void InnerDropdown::startAnimation() { auto from = _hiding ? 1. : 0.; auto to = _hiding ? 0. : 1.; @@ -168,13 +184,17 @@ void InnerDropdown::startAnimation() { _a_appearance.start([this] { repaintCallback(); }, from, to, _st.duration); } -void InnerDropdown::hidingFinished() { - hide(); -// showChildren(); - emit hidden(); +void InnerDropdown::hideFinished() { + _cache = QPixmap(); + _ignoreShowEvents = false; + if (!isHidden()) { + emit beforeHidden(); + hide(); + } } -void InnerDropdown::showingStarted() { +void InnerDropdown::showStarted() { + if (_ignoreShowEvents) return; if (isHidden()) { show(); } else if (!_hiding) { @@ -188,7 +208,7 @@ void InnerDropdown::repaintCallback() { update(); if (!_a_appearance.animating() && _hiding) { _hiding = false; - hidingFinished(); + hideFinished(); } } @@ -207,35 +227,45 @@ bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) { return false; } -namespace internal { - -Container::Container(QWidget *parent, ScrolledWidget *child, const style::InnerDropdown &st) : ScrolledWidget(parent), _st(st) { - child->setParent(this); - child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top()); - connect(child, SIGNAL(heightUpdated()), this, SLOT(onHeightUpdate())); +int InnerDropdown::resizeGetHeight(int newWidth) { + auto newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom(); + if (auto widget = static_cast(_scroll->widget())) { + widget->resizeToWidth(newWidth - _st.padding.left() - _st.padding.right() - _st.scrollMargin.left() - _st.scrollMargin.right()); + newHeight += widget->height(); + } + if (_maxHeight > 0) { + accumulate_min(newHeight, _maxHeight); + } + return newHeight; } -void Container::setVisibleTopBottom(int visibleTop, int visibleBottom) { - if (auto child = static_cast(children().front())) { +InnerDropdown::Container::Container(QWidget *parent, TWidget *child, const style::InnerDropdown &st) : TWidget(parent), _st(st) { + child->setParent(this); + child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top()); +} + +void InnerDropdown::Container::setVisibleTopBottom(int visibleTop, int visibleBottom) { + if (auto child = static_cast(children().front())) { child->setVisibleTopBottom(visibleTop - _st.scrollPadding.top(), visibleBottom - _st.scrollPadding.top()); } } -void Container::onHeightUpdate() { - int newHeight = _st.scrollPadding.top() + _st.scrollPadding.bottom(); - if (auto child = static_cast(children().front())) { +void InnerDropdown::Container::resizeToContent() { + auto newWidth = _st.scrollPadding.top() + _st.scrollPadding.bottom(); + auto newHeight = _st.scrollPadding.top() + _st.scrollPadding.bottom(); + if (auto child = static_cast(children().front())) { + newWidth += child->width(); newHeight += child->height(); } - if (newHeight != height()) { - resize(width(), newHeight); - emit heightUpdated(); + if (newWidth != width() || newHeight != height()) { + resize(newWidth, newHeight); } } -int Container::resizeGetHeight(int newWidth) { +int InnerDropdown::Container::resizeGetHeight(int newWidth) { int innerWidth = newWidth - _st.scrollPadding.left() - _st.scrollPadding.right(); int result = _st.scrollPadding.top() + _st.scrollPadding.bottom(); - if (auto child = static_cast(children().front())) { + if (auto child = static_cast(children().front())) { child->resizeToWidth(innerWidth); child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top()); result += child->height(); @@ -243,5 +273,4 @@ int Container::resizeGetHeight(int newWidth) { return result; } -} // namespace internal } // namespace Ui diff --git a/Telegram/SourceFiles/ui/inner_dropdown.h b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h similarity index 73% rename from Telegram/SourceFiles/ui/inner_dropdown.h rename to Telegram/SourceFiles/ui/widgets/inner_dropdown.h index 2115679a4..9b251042a 100644 --- a/Telegram/SourceFiles/ui/inner_dropdown.h +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/effects/rect_shadow.h" +#include "styles/style_widgets.h" class ScrollArea; @@ -30,9 +31,9 @@ class InnerDropdown : public TWidget { Q_OBJECT public: - InnerDropdown(QWidget *parent, const style::InnerDropdown &st = st::defaultInnerDropdown, const style::flatScroll &scrollSt = st::scrollDef); + InnerDropdown(QWidget *parent, const style::InnerDropdown &st = st::defaultInnerDropdown); - void setOwnedWidget(ScrolledWidget *widget); + void setOwnedWidget(TWidget *widget); bool overlaps(const QRect &globalRect) { if (isHidden() || _a_appearance.animating()) return false; @@ -41,32 +42,52 @@ public: } void setMaxHeight(int newMaxHeight); + void resizeToContent(); void otherEnter(); void otherLeave(); + void showFast(); + void hideFast(); + + bool isHiding() const { + return _hiding && _a_appearance.animating(); + } + + void showAnimated(); + enum class HideOption { + Default, + IgnoreShow, + }; + void hideAnimated(HideOption option = HideOption::Default); + +signals: + void beforeHidden(); protected: void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; void enterEvent(QEvent *e) override; void leaveEvent(QEvent *e) override; - bool eventFilter(QObject *obj, QEvent *e) override; -signals: - void hidden(); + int resizeGetHeight(int newWidth) override; private slots: - void onHideStart(); + void onHideAnimated() { + hideAnimated(); + } void onWindowActiveChanged(); void onScroll(); - void onWidgetHeightUpdated(); + void onWidgetHeightUpdated() { + resizeToContent(); + } private: + class Container; void repaintCallback(); - void hidingFinished(); - void showingStarted(); + void hideFinished(); + void showStarted(); void startAnimation(); @@ -80,6 +101,7 @@ private: FloatAnimation _a_appearance; QTimer _hideTimer; + bool _ignoreShowEvents = false; RectShadow _shadow; ChildWidget _scroll; @@ -88,17 +110,12 @@ private: }; -namespace internal { - -class Container : public ScrolledWidget { - Q_OBJECT - +class InnerDropdown::Container : public TWidget { public: - Container(QWidget *parent, ScrolledWidget *child, const style::InnerDropdown &st); + Container(QWidget *parent, TWidget *child, const style::InnerDropdown &st); void setVisibleTopBottom(int visibleTop, int visibleBottom) override; -private slots: - void onHeightUpdate(); + void resizeToContent(); protected: int resizeGetHeight(int newWidth) override; @@ -108,5 +125,4 @@ private: }; -} // namespace internal } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/menu.cpp b/Telegram/SourceFiles/ui/widgets/menu.cpp new file mode 100644 index 000000000..46b374dd1 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/menu.cpp @@ -0,0 +1,343 @@ +/* +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. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "ui/widgets/menu.h" + +namespace Ui { + +Menu::Menu(QWidget *parent, const style::Menu &st) : TWidget(parent) +, _st(st) +, _itemHeight(_st.itemPadding.top() + _st.itemFont->height + _st.itemPadding.bottom()) +, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom()) { + init(); +} + +Menu::Menu(QWidget *parent, QMenu *menu, const style::Menu &st) : TWidget(parent) +, _st(st) +, _wappedMenu(menu) +, _itemHeight(_st.itemPadding.top() + _st.itemFont->height + _st.itemPadding.bottom()) +, _separatorHeight(_st.separatorPadding.top() + _st.separatorWidth + _st.separatorPadding.bottom()) { + init(); + + _wappedMenu->setParent(this); + for (auto action : _wappedMenu->actions()) { + addAction(action); + } + _wappedMenu->hide(); +} + +void Menu::init() { + resize(_st.widthMin, _st.skip * 2); + + setMouseTracking(true); + + setAttribute(Qt::WA_OpaquePaintEvent); +} + +QAction *Menu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) { + auto action = new QAction(text, this); + connect(action, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection); + return addAction(action, icon); +} + +QAction *Menu::addAction(QAction *action, const style::icon *icon) { + connect(action, SIGNAL(changed()), this, SLOT(actionChanged())); + _actions.push_back(action); + + ActionData data; + data.icon = icon; + data.hasSubmenu = (action->menu() != nullptr); + _actionsData.push_back(data); + + auto newWidth = qMax(width(), _st.widthMin); + newWidth = processAction(action, _actions.size() - 1, newWidth); + auto newHeight = height() + (action->isSeparator() ? _separatorHeight : _itemHeight); + resize(newWidth, newHeight); + if (_resizedCallback) { + _resizedCallback(); + } + update(); + + return action; +} + +QAction *Menu::addSeparator() { + auto separator = new QAction(this); + separator->setSeparator(true); + return addAction(separator); +} + +void Menu::clearActions() { + _actionsData.clear(); + for (auto action : base::take(_actions)) { + if (action->parent() == this) { + delete action; + } + } + resize(_st.widthMin, _st.skip * 2); + if (_resizedCallback) { + _resizedCallback(); + } +} + +int Menu::processAction(QAction *action, int index, int width) { + auto &data = _actionsData[index]; + if (action->isSeparator() || action->text().isEmpty()) { + data.text = data.shortcut = QString(); + } else { + auto actionTextParts = action->text().split('\t'); + auto actionText = actionTextParts.empty() ? QString() : actionTextParts[0]; + auto actionShortcut = (actionTextParts.size() > 1) ? actionTextParts[1] : QString(); + int textw = _st.itemFont->width(actionText); + int goodw = _st.itemPadding.left() + textw + _st.itemPadding.right(); + if (data.hasSubmenu) { + goodw += _st.itemPadding.left() + _st.arrow.width(); + } else if (!actionShortcut.isEmpty()) { + goodw += _st.itemPadding.left() + _st.itemFont->width(actionShortcut); + } + width = snap(goodw, width, _st.widthMax); + data.text = (width < goodw) ? _st.itemFont->elided(actionText, width - (goodw - textw)) : actionText; + data.shortcut = actionShortcut; + } + return width; +} + +void Menu::setShowSource(TriggeredSource source) { + _mouseSelection = (source == TriggeredSource::Mouse); + setSelected((source == TriggeredSource::Mouse || _actions.isEmpty()) ? -1 : 0); +} + +Menu::Actions &Menu::actions() { + return _actions; +} + +void Menu::actionChanged() { + int newWidth = _st.widthMin; + for (int i = 0, count = _actions.size(); i != count; ++i) { + newWidth = processAction(_actions[i], i, newWidth); + } + if (newWidth != width()) { + resize(newWidth, height()); + if (_resizedCallback) { + _resizedCallback(); + } + } + update(); +} + +void Menu::paintEvent(QPaintEvent *e) { + Painter p(this); + + auto clip = e->rect(); + + auto topskip = QRect(0, 0, width(), _st.skip); + auto bottomskip = QRect(0, height() - _st.skip, width(), _st.skip); + if (clip.intersects(topskip)) p.fillRect(clip.intersected(topskip), _st.itemBg); + if (clip.intersects(bottomskip)) p.fillRect(clip.intersected(bottomskip), _st.itemBg); + + int top = _st.skip; + p.translate(0, top); + p.setFont(_st.itemFont); + for (int i = 0, count = _actions.size(); i != count; ++i) { + if (clip.top() + clip.height() <= top) break; + + auto action = _actions[i]; + auto &data = _actionsData[i]; + auto actionHeight = action->isSeparator() ? _separatorHeight : _itemHeight; + top += actionHeight; + if (clip.top() < top) { + if (action->isSeparator()) { + p.fillRect(0, 0, width(), actionHeight, _st.itemBg); + p.fillRect(_st.separatorPadding.left(), _st.separatorPadding.top(), width() - _st.separatorPadding.left() - _st.separatorPadding.right(), _st.separatorWidth, _st.separatorFg); + } else { + auto enabled = action->isEnabled(), selected = (i == _selected && enabled); + p.fillRect(0, 0, width(), actionHeight, selected ? _st.itemBgOver : _st.itemBg); + if (data.icon) { + p.setOpacity(selected ? _st.itemIconOverOpacity : _st.itemIconOpacity); + data.icon->paint(p, _st.itemIconPosition, width()); + p.setOpacity(1.); + } + p.setPen(selected ? _st.itemFgOver : (enabled ? _st.itemFg : _st.itemFgDisabled)); + p.drawTextLeft(_st.itemPadding.left(), _st.itemPadding.top(), width(), data.text); + if (data.hasSubmenu) { + _st.arrow.paint(p, width() - _st.itemPadding.right() - _st.arrow.width(), (_itemHeight - _st.arrow.height()) / 2, width()); + } else if (!data.shortcut.isEmpty()) { + p.setPen(selected ? _st.itemFgShortcutOver : (enabled ? _st.itemFgShortcut : _st.itemFgShortcutDisabled)); + p.drawTextRight(_st.itemPadding.right(), _st.itemPadding.top(), width(), data.shortcut); + } + } + } + p.translate(0, actionHeight); + } +} + +void Menu::updateSelected(QPoint globalPosition) { + if (!_mouseSelection) return; + + auto p = mapFromGlobal(globalPosition) - QPoint(0, _st.skip); + auto selected = -1, top = 0; + while (top <= p.y() && ++selected < _actions.size()) { + top += _actions[selected]->isSeparator() ? _separatorHeight : _itemHeight; + } + setSelected((selected >= 0 && selected < _actions.size() && _actions[selected]->isEnabled() && !_actions[selected]->isSeparator()) ? selected : -1); +} + +void Menu::itemPressed(TriggeredSource source) { + if (source == TriggeredSource::Mouse && !_mouseSelection) { + return; + } + if (_selected >= 0 && _selected < _actions.size() && _actions[_selected]->isEnabled()) { + if (_triggeredCallback) { + auto actionTop = _st.skip + itemTop(_selected); + _triggeredCallback(_actions[_selected], actionTop, source); + } + } +} + +void Menu::keyPressEvent(QKeyEvent *e) { + auto key = e->key(); + if (!_keyPressDelegate || !_keyPressDelegate(key)) { + handleKeyPress(key); + } +} + +void Menu::handleKeyPress(int key) { + if (key == Qt::Key_Enter || key == Qt::Key_Return) { + itemPressed(TriggeredSource::Keyboard); + return; + } + if (key == (rtl() ? Qt::Key_Left : Qt::Key_Right)) { + if (_selected >= 0 && _actionsData[_selected].hasSubmenu) { + itemPressed(TriggeredSource::Keyboard); + return; + } else if (_selected < 0 && !_actions.isEmpty()) { + _mouseSelection = false; + setSelected(0); + } + } + if ((key != Qt::Key_Up && key != Qt::Key_Down) || _actions.size() < 1) return; + + auto delta = (key == Qt::Key_Down ? 1 : -1), start = _selected; + if (start < 0 || start >= _actions.size()) { + start = (delta > 0) ? (_actions.size() - 1) : 0; + } + auto newSelected = start; + do { + newSelected += delta; + if (newSelected < 0) { + newSelected += _actions.size(); + } else if (newSelected >= _actions.size()) { + newSelected -= _actions.size(); + } + } while (newSelected != start && (!_actions.at(newSelected)->isEnabled() || _actions.at(newSelected)->isSeparator())); + + if (_actions.at(newSelected)->isEnabled() && !_actions.at(newSelected)->isSeparator()) { + _mouseSelection = false; + setSelected(newSelected); + } +} + +void Menu::clearSelection() { + _mouseSelection = false; + setSelected(-1); +} + +void Menu::clearMouseSelection() { + if (_mouseSelection && !_childShown) { + clearSelection(); + } +} + +void Menu::enterEvent(QEvent *e) { + QPoint mouse = QCursor::pos(); + if (!rect().marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)).contains(mapFromGlobal(mouse))) { + clearMouseSelection(); + } + return TWidget::enterEvent(e); +} + +void Menu::leaveEvent(QEvent *e) { + clearMouseSelection(); + return TWidget::leaveEvent(e); +} + +void Menu::setSelected(int selected) { + if (selected >= _actions.size()) { + selected = -1; + } + if (_selected != selected) { + updateSelectedItem(); + _selected = selected; + updateSelectedItem(); + if (_activatedCallback) { + auto actionTop = _st.skip + itemTop(_selected); + auto source = _mouseSelection ? TriggeredSource::Mouse : TriggeredSource::Keyboard; + _activatedCallback((_selected >= 0) ? _actions[_selected] : nullptr, actionTop, source); + } + } +} + +int Menu::itemTop(int index) { + if (index > _actions.size()) { + index = _actions.size(); + } + int top = 0; + for (int i = 0; i < index; ++i) { + top += _actions.at(i)->isSeparator() ? _separatorHeight : _itemHeight; + } + return top; +} + +void Menu::updateSelectedItem() { + if (_selected >= 0) { + update(0, _st.skip + itemTop(_selected), width(), _actions.at(_selected)->isSeparator() ? _separatorHeight : _itemHeight); + } +} + +void Menu::mouseMoveEvent(QMouseEvent *e) { + handleMouseMove(e->globalPos()); +} + +void Menu::handleMouseMove(QPoint globalPosition) { + auto inner = rect().marginsRemoved(QMargins(0, _st.skip, 0, _st.skip)); + auto localPosition = mapFromGlobal(globalPosition); + if (inner.contains(localPosition)) { + _mouseSelection = true; + updateSelected(globalPosition); + } else { + clearMouseSelection(); + if (_mouseMoveDelegate) { + _mouseMoveDelegate(globalPosition); + } + } +} + +void Menu::mousePressEvent(QMouseEvent *e) { + handleMousePress(e->globalPos()); +} + +void Menu::handleMousePress(QPoint globalPosition) { + handleMouseMove(globalPosition); + if (rect().contains(mapFromGlobal(globalPosition))) { + itemPressed(TriggeredSource::Mouse); + } else if (_mousePressDelegate) { + _mousePressDelegate(globalPosition); + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/menu.h b/Telegram/SourceFiles/ui/widgets/menu.h new file mode 100644 index 000000000..64ecf8b11 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/menu.h @@ -0,0 +1,134 @@ +/* +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 "styles/style_widgets.h" + +namespace Ui { + +class Menu : public TWidget { + Q_OBJECT + +public: + Menu(QWidget *parent, const style::Menu &st = st::defaultMenu); + Menu(QWidget *parent, QMenu *menu, const style::Menu &st = st::defaultMenu); + + QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr); + QAction *addSeparator(); + void clearActions(); + + void clearSelection(); + + enum class TriggeredSource { + Mouse, + Keyboard, + }; + void setChildShown(bool shown) { + _childShown = shown; + } + void setShowSource(TriggeredSource source); + + using Actions = QList; + Actions &actions(); + + void setResizedCallback(base::lambda_unique callback) { + _resizedCallback = std_::move(callback); + } + + void setActivatedCallback(base::lambda_unique callback) { + _activatedCallback = std_::move(callback); + } + void setTriggeredCallback(base::lambda_unique callback) { + _triggeredCallback = std_::move(callback); + } + + void setKeyPressDelegate(base::lambda_unique delegate) { + _keyPressDelegate = std_::move(delegate); + } + void handleKeyPress(int key); + + void setMouseMoveDelegate(base::lambda_unique delegate) { + _mouseMoveDelegate = std_::move(delegate); + } + void handleMouseMove(QPoint globalPosition); + + void setMousePressDelegate(base::lambda_unique delegate) { + _mousePressDelegate = std_::move(delegate); + } + void handleMousePress(QPoint globalPosition); + +protected: + void paintEvent(QPaintEvent *e) override; + void keyPressEvent(QKeyEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void enterEvent(QEvent *e) override; + void leaveEvent(QEvent *e) override; + +private slots: + void actionChanged(); + +private: + void updateSelected(QPoint globalPosition); + void init(); + + // Returns the new width. + int processAction(QAction *action, int index, int width); + QAction *addAction(QAction *a, const style::icon *icon = nullptr); + + void setSelected(int selected); + void clearMouseSelection(); + + int itemTop(int index); + void updateSelectedItem(); + void itemPressed(TriggeredSource source); + + const style::Menu &_st; + + base::lambda_unique _resizedCallback; + base::lambda_unique _activatedCallback; + base::lambda_unique _triggeredCallback; + base::lambda_unique _keyPressDelegate; + base::lambda_unique _mouseMoveDelegate; + base::lambda_unique _mousePressDelegate; + + struct ActionData { + bool hasSubmenu = false; + QString text; + QString shortcut; + const style::icon *icon = nullptr; + }; + using ActionsData = QList; + + QMenu *_wappedMenu = nullptr; + Actions _actions; + ActionsData _actionsData; + + int _itemHeight, _separatorHeight; + + bool _mouseSelection = false; + + int _selected = -1; + bool _childShown = false; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index bef2de71b..d4bc4f68c 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -454,7 +454,7 @@ int MultiSelect::resizeGetHeight(int newWidth) { return newHeight; } -MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, const QString &placeholder, ScrollCallback callback) : ScrolledWidget(parent) +MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, const QString &placeholder, ScrollCallback callback) : TWidget(parent) , _st(st) , _scrollCallback(std_::move(callback)) , _field(this, _st.field, placeholder) diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.h b/Telegram/SourceFiles/ui/widgets/multi_select.h index 01854db33..9ac568ee6 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.h +++ b/Telegram/SourceFiles/ui/widgets/multi_select.h @@ -71,7 +71,7 @@ private: }; // This class is hold in header because it requires Qt preprocessing. -class MultiSelect::Inner : public ScrolledWidget { +class MultiSelect::Inner : public TWidget { Q_OBJECT public: diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp new file mode 100644 index 000000000..685a90404 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -0,0 +1,340 @@ +/* + 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. + + Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE + Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org + */ +#include "stdafx.h" +#include "ui/widgets/popup_menu.h" + +#include "pspecific.h" +#include "application.h" +#include "lang.h" + +namespace Ui { + +PopupMenu::PopupMenu(const style::PopupMenu &st) : TWidget(nullptr) +, _st(st) +, _menu(this, _st.menu) +, _shadow(_st.shadow) +, a_opacity(1) +, _a_hide(animation(this, &PopupMenu::step_hide)) { + init(); +} + +PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(nullptr) +, _st(st) +, _menu(this, menu, _st.menu) +, _shadow(_st.shadow) +, a_opacity(1) +, _a_hide(animation(this, &PopupMenu::step_hide)) { + init(); + + for (auto action : actions()) { + if (auto submenu = action->menu()) { + auto it = _submenus.insert(action, new PopupMenu(submenu, st)); + it.value()->deleteOnHide(false); + } + } +} + +void PopupMenu::init() { + _padding = _shadow.getDimensions(_st.shadowShift); + + _menu->setResizedCallback([this] { handleMenuResize(); }); + _menu->setActivatedCallback([this](QAction *action, int actionTop, TriggeredSource source) { + handleActivated(action, actionTop, source); + }); + _menu->setTriggeredCallback([this](QAction *action, int actionTop, TriggeredSource source) { + handleTriggered(action, actionTop, source); + }); + _menu->setKeyPressDelegate([this](int key) { return handleKeyPress(key); }); + _menu->setMouseMoveDelegate([this](QPoint globalPosition) { handleMouseMove(globalPosition); }); + _menu->setMousePressDelegate([this](QPoint globalPosition) { handleMousePress(globalPosition); }); + + _menu->moveToLeft(_padding.left(), _padding.top()); + handleMenuResize(); + + setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::Popup | Qt::NoDropShadowWindowHint); + setMouseTracking(true); + + hide(); + + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TranslucentBackground, true); +} + +void PopupMenu::handleMenuResize() { + resize(_padding.left() + _menu->width() + _padding.right(), _padding.top() + _menu->height() + _padding.bottom()); + _inner = QRect(_padding.left(), _padding.top(), width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom()); +} + +QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) { + return _menu->addAction(text, receiver, member, icon); +} + +QAction *PopupMenu::addSeparator() { + return _menu->addSeparator(); +} + +void PopupMenu::clearActions() { + for (auto submenu : base::take(_submenus)) { + delete submenu; + } + return _menu->clearActions(); +} + +PopupMenu::Actions &PopupMenu::actions() { + return _menu->actions(); +} + +void PopupMenu::paintEvent(QPaintEvent *e) { + Painter p(this); + + auto clip = e->rect(); + p.setClipRect(clip); + auto compositionMode = p.compositionMode(); + p.setCompositionMode(QPainter::CompositionMode_Source); + if (_a_hide.animating()) { + p.setOpacity(a_opacity.current()); + p.drawPixmap(0, 0, _cache); + return; + } + + p.fillRect(clip, st::almostTransparent); + p.setCompositionMode(compositionMode); + + _shadow.paint(p, _inner, _st.shadowShift); +} + +void PopupMenu::handleActivated(QAction *action, int actionTop, TriggeredSource source) { + if (source == TriggeredSource::Mouse) { + if (!popupSubmenuFromAction(action, actionTop, source)) { + if (auto currentSubmenu = base::take(_activeSubmenu)) { + currentSubmenu->hideMenu(true); + } + } + } +} + +void PopupMenu::handleTriggered(QAction *action, int actionTop, TriggeredSource source) { + if (!popupSubmenuFromAction(action, actionTop, source)) { + hideMenu(); + _triggering = true; + emit action->trigger(); + _triggering = false; + if (_deleteLater) { + _deleteLater = false; + deleteLater(); + } + } +} + +bool PopupMenu::popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source) { + if (auto submenu = _submenus.value(action)) { + if (_activeSubmenu == submenu) { + submenu->hideMenu(true); + } else { + popupSubmenu(submenu, actionTop, source); + } + return true; + } + return false; +} + +void PopupMenu::popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source) { + if (auto currentSubmenu = base::take(_activeSubmenu)) { + currentSubmenu->hideMenu(true); + } + if (submenu) { + QPoint p(_inner.x() + (rtl() ? _padding.right() : _inner.width() - _padding.left()), _inner.y() + actionTop); + _activeSubmenu = submenu; + _activeSubmenu->showMenu(geometry().topLeft() + p, this, source); + + _menu->setChildShown(true); + } else { + _menu->setChildShown(false); + } +} + +void PopupMenu::forwardKeyPress(int key) { + if (!handleKeyPress(key)) { + _menu->handleKeyPress(key); + } +} + +bool PopupMenu::handleKeyPress(int key) { + if (_activeSubmenu) { + _activeSubmenu->handleKeyPress(key); + return true; + } else if (key == Qt::Key_Escape) { + hideMenu(_parent ? true : false); + return true; + } else if (key == (rtl() ? Qt::Key_Right : Qt::Key_Left)) { + if (_parent) { + hideMenu(true); + return true; + } + } + return false; +} + +void PopupMenu::handleMouseMove(QPoint globalPosition) { + if (_parent) { + _parent->forwardMouseMove(globalPosition); + } +} + +void PopupMenu::handleMousePress(QPoint globalPosition) { + if (_parent) { + _parent->forwardMousePress(globalPosition); + } else { + hideMenu(); + } +} + +void PopupMenu::focusOutEvent(QFocusEvent *e) { + hideMenu(); +} + +void PopupMenu::hideEvent(QHideEvent *e) { + if (_deleteOnHide) { + if (_triggering) { + _deleteLater = true; + } else { + deleteLater(); + } + } +} + +void PopupMenu::hideMenu(bool fast) { + if (isHidden()) return; + if (_parent && !_a_hide.animating()) { + _parent->childHiding(this); + } + if (fast) { + if (_a_hide.animating()) { + _a_hide.stop(); + } + a_opacity = anim::fvalue(0, 0); + hideFinish(); + } else { + if (!_a_hide.animating()) { + _cache = myGrab(this); + a_opacity.start(0); + _a_hide.start(); + } + if (_parent) { + _parent->hideMenu(); + } + } + if (_activeSubmenu) { + _activeSubmenu->hideMenu(fast); + } +} + +void PopupMenu::childHiding(PopupMenu *child) { + if (_activeSubmenu && _activeSubmenu == child) { + _activeSubmenu = SubmenuPointer(); + } +} + +void PopupMenu::hideFinish() { + hide(); +} + +void PopupMenu::step_hide(float64 ms, bool timer) { + float64 dt = ms / _st.duration; + if (dt >= 1) { + _a_hide.stop(); + a_opacity.finish(); + hideFinish(); + } else { + a_opacity.update(dt, anim::linear); + } + if (timer) update(); +} + +void PopupMenu::deleteOnHide(bool del) { + _deleteOnHide = del; +} + +void PopupMenu::popup(const QPoint &p) { + showMenu(p, nullptr, TriggeredSource::Mouse); +} + +void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source) { + _parent = parent; + + QPoint w = p - QPoint(0, _padding.top()); + QRect r = Sandbox::screenGeometry(p); + if (rtl()) { + if (w.x() - width() < r.x() - _padding.left()) { + if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) { + w.setX(w.x() + _parent->width() - _padding.left() - _padding.right()); + } else { + w.setX(r.x() - _padding.left()); + } + } else { + w.setX(w.x() - width()); + } + } else { + if (w.x() + width() - _padding.right() > r.x() + r.width()) { + if (_parent && w.x() - _parent->width() + _padding.left() + _padding.right() - width() + _padding.right() >= r.x() - _padding.left()) { + w.setX(w.x() + _padding.left() + _padding.right() - _parent->width() - width() + _padding.left() + _padding.right()); + } else { + w.setX(r.x() + r.width() - width() + _padding.right()); + } + } + } + if (w.y() + height() - _padding.bottom() > r.y() + r.height()) { + if (_parent) { + w.setY(r.y() + r.height() - height() + _padding.bottom()); + } else { + w.setY(p.y() - height() + _padding.bottom()); + } + } + if (w.y() < r.y()) { + w.setY(r.y()); + } + move(w); + + _menu->setShowSource(source); + + psUpdateOverlayed(this); + show(); + psShowOverAll(this); + windowHandle()->requestActivate(); + activateWindow(); + + if (_a_hide.animating()) { + _a_hide.stop(); + _cache = QPixmap(); + } + a_opacity = anim::fvalue(1, 1); +} + +PopupMenu::~PopupMenu() { + for (auto submenu : base::take(_submenus)) { + delete submenu; + } +#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 + if (auto w = App::wnd()) { + w->onReActivate(); + QTimer::singleShot(200, w, SLOT(onReActivate())); + } +#endif +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.h b/Telegram/SourceFiles/ui/widgets/popup_menu.h new file mode 100644 index 000000000..7e9047465 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.h @@ -0,0 +1,112 @@ +/* + 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. + + Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE + Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org + */ +#pragma once + +#include "styles/style_widgets.h" +#include "ui/effects/rect_shadow.h" +#include "ui/widgets/menu.h" + +namespace Ui { + +class PopupMenu : public TWidget { +public: + PopupMenu(const style::PopupMenu &st = st::defaultPopupMenu); + PopupMenu(QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu); + + QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr); + QAction *addSeparator(); + void clearActions(); + + using Actions = Ui::Menu::Actions; + Actions &actions(); + + void deleteOnHide(bool del); + void popup(const QPoint &p); + void hideMenu(bool fast = false); + + ~PopupMenu(); + +protected: + void paintEvent(QPaintEvent *e) override; + void focusOutEvent(QFocusEvent *e) override; + void hideEvent(QHideEvent *e) override; + + void keyPressEvent(QKeyEvent *e) override { + forwardKeyPress(e->key()); + } + void mouseMoveEvent(QMouseEvent *e) override { + forwardMouseMove(e->globalPos()); + } + void mousePressEvent(QMouseEvent *e) override { + forwardMousePress(e->globalPos()); + } + +private: + void childHiding(PopupMenu *child); + + void step_hide(float64 ms, bool timer); + + void init(); + void hideFinish(); + + using TriggeredSource = Ui::Menu::TriggeredSource; + void handleMenuResize(); + void handleActivated(QAction *action, int actionTop, TriggeredSource source); + void handleTriggered(QAction *action, int actionTop, TriggeredSource source); + void forwardKeyPress(int key); + bool handleKeyPress(int key); + void forwardMouseMove(QPoint globalPosition) { + _menu->handleMouseMove(globalPosition); + } + void handleMouseMove(QPoint globalPosition); + void forwardMousePress(QPoint globalPosition) { + _menu->handleMousePress(globalPosition); + } + void handleMousePress(QPoint globalPosition); + + using SubmenuPointer = QPointer; + bool popupSubmenuFromAction(QAction *action, int actionTop, TriggeredSource source); + void popupSubmenu(SubmenuPointer submenu, int actionTop, TriggeredSource source); + void showMenu(const QPoint &p, PopupMenu *parent, TriggeredSource source); + + const style::PopupMenu &_st; + + ChildWidget _menu; + + using Submenus = QMap; + Submenus _submenus; + + PopupMenu *_parent = nullptr; + + QRect _inner; + style::margins _padding; + + Ui::RectShadow _shadow; + SubmenuPointer _activeSubmenu; + + QPixmap _cache; + anim::fvalue a_opacity; + Animation _a_hide; + + bool _deleteOnHide = true; + bool _triggering = false; + bool _deleteLater = false; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/tooltip.cpp b/Telegram/SourceFiles/ui/widgets/tooltip.cpp new file mode 100644 index 000000000..a18da282c --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/tooltip.cpp @@ -0,0 +1,185 @@ +/* + 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. + + Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE + Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org + */ +#include "stdafx.h" +#include "ui/widgets/tooltip.h" + +#include "application.h" + +namespace Ui { + +Tooltip *TooltipInstance = nullptr; + +AbstractTooltipShower::~AbstractTooltipShower() { + if (TooltipInstance && TooltipInstance->_shower == this) { + TooltipInstance->_shower = 0; + } +} + +Tooltip::Tooltip() : TWidget(nullptr) { + TooltipInstance = this; + + setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint); + setAttribute(Qt::WA_NoSystemBackground, true); + + _showTimer.setSingleShot(true); + connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow())); + + connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); +} + +void Tooltip::onShow() { + if (_shower) { + QString text = (App::wnd() && App::wnd()->isActive(false)) ? _shower->tooltipText() : QString(); + if (text.isEmpty()) { + Hide(); + } else { + TooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt()); + } + } +} + +void Tooltip::onWndActiveChanged() { + if (!App::wnd() || !App::wnd()->windowHandle() || !App::wnd()->windowHandle()->isActive()) { + Tooltip::Hide(); + } +} + +bool Tooltip::eventFilter(QObject *o, QEvent *e) { + if (e->type() == QEvent::Leave) { + _hideByLeaveTimer.start(10); + } else if (e->type() == QEvent::Enter) { + _hideByLeaveTimer.stop(); + } else if (e->type() == QEvent::MouseMove) { + if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) { + Hide(); + } + } + return TWidget::eventFilter(o, e); +} + +void Tooltip::onHideByLeave() { + Hide(); +} + +Tooltip::~Tooltip() { + if (TooltipInstance == this) { + TooltipInstance = 0; + } +} + +void Tooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) { + if (!_hideByLeaveTimer.isSingleShot()) { + _hideByLeaveTimer.setSingleShot(true); + connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave())); + + Sandbox::installEventFilter(this); + } + + _point = m; + _st = st; + _text = Text(_st->textFont, text, _textPlainOptions, _st->widthMax, true); + + int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right(); + int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom(); + + // count tooltip size + QSize s(addw + _text.maxWidth(), addh + _text.minHeight()); + if (s.width() > _st->widthMax) { + s.setWidth(addw + _text.countWidth(_st->widthMax - addw)); + s.setHeight(addh + _text.countHeight(s.width() - addw)); + } + int32 maxh = addh + (_st->linesMax * _st->textFont->height); + if (s.height() > maxh) { + s.setHeight(maxh); + } + + // count tooltip position + QPoint p(m + _st->shift); + if (rtl()) { + p.setX(m.x() - s.width() - _st->shift.x()); + } + if (s.width() < 2 * _st->shift.x()) { + p.setX(m.x() - (s.width() / 2)); + } + + // adjust tooltip position + QRect r(QApplication::desktop()->screenGeometry(m)); + if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) { + p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width())); + } + if (r.x() + _st->skip > p.x() && p.x() < m.x()) { + p.setX(qMin(m.x(), r.x() + int32(_st->skip))); + } + if (r.y() + r.height() - _st->skip < p.y() + s.height()) { + p.setY(m.y() - s.height() - _st->skip); + } + if (r.y() > p.x()) { + p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height())); + } + + setGeometry(QRect(p, s)); + + _hideByLeaveTimer.stop(); + show(); +} + +void Tooltip::paintEvent(QPaintEvent *e) { + Painter p(this); + + p.fillRect(rect(), _st->textBg); + + p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder); + p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder); + p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); + p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder); + + int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textFont->height); + + p.setPen(_st->textFg); + _text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines); +} + +void Tooltip::hideEvent(QHideEvent *e) { + if (TooltipInstance == this) { + Hide(); + } +} + +void Tooltip::Show(int32 delay, const AbstractTooltipShower *shower) { + if (!TooltipInstance) { + new Tooltip(); + } + TooltipInstance->_shower = shower; + if (delay >= 0) { + TooltipInstance->_showTimer.start(delay); + } else { + TooltipInstance->onShow(); + } +} + +void Tooltip::Hide() { + if (auto instance = TooltipInstance) { + TooltipInstance = nullptr; + instance->_showTimer.stop(); + instance->_hideByLeaveTimer.stop(); + instance->hide(); + instance->deleteLater(); + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/tooltip.h b/Telegram/SourceFiles/ui/widgets/tooltip.h new file mode 100644 index 000000000..eb1611014 --- /dev/null +++ b/Telegram/SourceFiles/ui/widgets/tooltip.h @@ -0,0 +1,69 @@ +/* + 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. + + Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE + Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org + */ +#pragma once + +namespace Ui { + +class AbstractTooltipShower { +public: + virtual QString tooltipText() const = 0; + virtual QPoint tooltipPos() const = 0; + virtual const style::Tooltip *tooltipSt() const { + return &st::defaultTooltip; + } + virtual ~AbstractTooltipShower(); +}; + +class Tooltip : public TWidget { + Q_OBJECT + +public: + static void Show(int32 delay, const AbstractTooltipShower *shower); + static void Hide(); + + private slots: + void onShow(); + void onWndActiveChanged(); + void onHideByLeave(); + +protected: + void paintEvent(QPaintEvent *e) override; + void hideEvent(QHideEvent *e) override; + + bool eventFilter(QObject *o, QEvent *e) override; + +private: + Tooltip(); + ~Tooltip(); + + void popup(const QPoint &p, const QString &text, const style::Tooltip *st); + + friend class AbstractTooltipShower; + const AbstractTooltipShower *_shower = nullptr; + QTimer _showTimer; + + Text _text; + QPoint _point; + + const style::Tooltip *_st = nullptr; + + QTimer _hideByLeaveTimer; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 60453cbb2..5eda18b3c 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -99,6 +99,60 @@ MultiSelect { fieldCancelSkip: pixels; } +Menu { + skip: pixels; + + itemBg: color; + itemBgOver: color; + itemFg: color; + itemFgOver: color; + itemFgDisabled: color; + itemFgShortcut: color; + itemFgShortcutOver: color; + itemFgShortcutDisabled: color; + itemPadding: margins; + itemIconPosition: point; + itemIconOpacity: double; + itemIconOverOpacity: double; + itemFont: font; + + separatorPadding: margins; + separatorWidth: pixels; + separatorFg: color; + + arrow: icon; + + widthMin: pixels; + widthMax: pixels; +} + +PopupMenu { + shadow: icon; + shadowShift: pixels; + + menu: Menu; + + duration: int; +} + +InnerDropdown { + padding: margins; + shadow: icon; + shadowShift: pixels; + + duration: int; + width: pixels; + + scroll: flatScroll; + scrollMargin: margins; + scrollPadding: margins; +} + +DropdownMenu { + wrap: InnerDropdown; + menu: Menu; +} + widgetSlideDuration: 200; widgetFadeDuration: 200; @@ -112,3 +166,52 @@ discreteSliderLabelTop: 17px; discreteSliderLabelFont: normalFont; discreteSliderLabelFg: #1485c2; discreteSliderDuration: 200; + +defaultMenuArrow: icon {{ "dropdown_submenu_arrow", #373737 }}; +defaultMenu: Menu { + skip: 5px; + + itemBg: white; + itemBgOver: overBg; + itemFg: black; + itemFgOver: black; + itemFgDisabled: #ccc; + itemFgShortcut: #999; + itemFgShortcutOver: #7c99b2; + itemFgShortcutDisabled: #ccc; + itemIconPosition: point(0px, 0px); + itemIconOpacity: 1.; + itemIconOverOpacity: 1.; + itemPadding: margins(17px, 8px, 17px, 7px); + itemFont: normalFont; + + separatorPadding: margins(0px, 5px, 0px, 5px); + separatorWidth: 1px; + separatorFg: #f1f1f1; + + arrow: defaultMenuArrow; + + widthMin: 180px; + widthMax: 300px; +} +defaultPopupMenu: PopupMenu { + shadow: defaultDropdownShadow; + shadowShift: defaultDropdownShadowShift; + + menu: defaultMenu; + + duration: 120; +} +defaultInnerDropdown: InnerDropdown { + padding: margins(10px, 10px, 10px, 10px); + shadow: defaultDropdownShadow; + shadowShift: defaultDropdownShadowShift; + + duration: 150; + + scroll: solidScroll; +} +defaultDropdownMenu: DropdownMenu { + wrap: defaultInnerDropdown; + menu: defaultMenu; +} diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index df5f6a41a..1c610778b 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -27,7 +27,6 @@ class PeerAvatarButton; class RoundButton; class IconButton; } // namespace Ui -class IconedButton; namespace Window { diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index b01d35aaf..012edecf9 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -111,8 +111,6 @@ '<(src_loc)/config.h', '<(src_loc)/dialogswidget.cpp', '<(src_loc)/dialogswidget.h', - '<(src_loc)/dropdown.cpp', - '<(src_loc)/dropdown.h', '<(src_loc)/facades.cpp', '<(src_loc)/facades.h', '<(src_loc)/fileuploader.cpp', @@ -241,6 +239,8 @@ '<(src_loc)/dialogs/dialogs_row.h', '<(src_loc)/history/field_autocomplete.cpp', '<(src_loc)/history/field_autocomplete.h', + '<(src_loc)/history/history_drag_area.cpp', + '<(src_loc)/history/history_drag_area.h', '<(src_loc)/history/history_item.cpp', '<(src_loc)/history/history_item.h', '<(src_loc)/history/history_location_manager.cpp', @@ -489,22 +489,30 @@ '<(src_loc)/ui/widgets/continuous_slider.h', '<(src_loc)/ui/widgets/discrete_slider.cpp', '<(src_loc)/ui/widgets/discrete_slider.h', + '<(src_loc)/ui/widgets/dropdown_menu.cpp', + '<(src_loc)/ui/widgets/dropdown_menu.h', '<(src_loc)/ui/widgets/filled_slider.cpp', '<(src_loc)/ui/widgets/filled_slider.h', + '<(src_loc)/ui/widgets/inner_dropdown.cpp', + '<(src_loc)/ui/widgets/inner_dropdown.h', '<(src_loc)/ui/widgets/label_simple.cpp', '<(src_loc)/ui/widgets/label_simple.h', '<(src_loc)/ui/widgets/media_slider.cpp', '<(src_loc)/ui/widgets/media_slider.h', + '<(src_loc)/ui/widgets/menu.cpp', + '<(src_loc)/ui/widgets/menu.h', '<(src_loc)/ui/widgets/multi_select.cpp', '<(src_loc)/ui/widgets/multi_select.h', + '<(src_loc)/ui/widgets/popup_menu.cpp', + '<(src_loc)/ui/widgets/popup_menu.h', '<(src_loc)/ui/widgets/shadow.cpp', '<(src_loc)/ui/widgets/shadow.h', + '<(src_loc)/ui/widgets/tooltip.cpp', + '<(src_loc)/ui/widgets/tooltip.h', '<(src_loc)/ui/animation.cpp', '<(src_loc)/ui/animation.h', '<(src_loc)/ui/button.cpp', '<(src_loc)/ui/button.h', - '<(src_loc)/ui/popupmenu.cpp', - '<(src_loc)/ui/popupmenu.h', '<(src_loc)/ui/countryinput.cpp', '<(src_loc)/ui/countryinput.h', '<(src_loc)/ui/emoji_config.cpp', @@ -523,8 +531,6 @@ '<(src_loc)/ui/flattextarea.h', '<(src_loc)/ui/images.cpp', '<(src_loc)/ui/images.h', - '<(src_loc)/ui/inner_dropdown.cpp', - '<(src_loc)/ui/inner_dropdown.h', '<(src_loc)/ui/scrollarea.cpp', '<(src_loc)/ui/scrollarea.h', '<(src_loc)/ui/twidget.cpp', diff --git a/Telegram/gyp/codegen_rules.gypi b/Telegram/gyp/codegen_rules.gypi index 361723685..058098b9d 100644 --- a/Telegram/gyp/codegen_rules.gypi +++ b/Telegram/gyp/codegen_rules.gypi @@ -33,6 +33,7 @@ 'action': [ '<(PRODUCT_DIR)/codegen_style<(exe_ext)', '-I<(res_loc)', '-I<(src_loc)', + '-w<(PRODUCT_DIR)/../..', '--skip-styles', '<(res_loc)/basic.style', ], 'message': 'Updating sprites..', From 29493a0693a0aedcfa021835421f143af37e4aa7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 27 Oct 2016 17:10:28 +0300 Subject: [PATCH 002/100] All emoji icons moved from sprite. --- Telegram/Resources/basic.style | 200 ------------ Telegram/Resources/basic_types.style | 14 - Telegram/Resources/icons/emoji_activity.png | Bin 0 -> 639 bytes .../Resources/icons/emoji_activity@2x.png | Bin 0 -> 1303 bytes Telegram/Resources/icons/emoji_food.png | Bin 0 -> 501 bytes Telegram/Resources/icons/emoji_food@2x.png | Bin 0 -> 777 bytes Telegram/Resources/icons/emoji_gif.png | Bin 0 -> 518 bytes Telegram/Resources/icons/emoji_gif@2x.png | Bin 0 -> 1033 bytes Telegram/Resources/icons/emoji_nature.png | Bin 0 -> 554 bytes Telegram/Resources/icons/emoji_nature@2x.png | Bin 0 -> 1100 bytes Telegram/Resources/icons/emoji_objects.png | Bin 0 -> 360 bytes Telegram/Resources/icons/emoji_objects@2x.png | Bin 0 -> 628 bytes Telegram/Resources/icons/emoji_people.png | Bin 0 -> 668 bytes Telegram/Resources/icons/emoji_people@2x.png | Bin 0 -> 1150 bytes Telegram/Resources/icons/emoji_recent.png | Bin 0 -> 546 bytes Telegram/Resources/icons/emoji_recent@2x.png | Bin 0 -> 968 bytes Telegram/Resources/icons/emoji_settings.png | Bin 0 -> 670 bytes .../Resources/icons/emoji_settings@2x.png | Bin 0 -> 1222 bytes Telegram/Resources/icons/emoji_symbols.png | Bin 0 -> 432 bytes Telegram/Resources/icons/emoji_symbols@2x.png | Bin 0 -> 859 bytes Telegram/Resources/icons/emoji_travel.png | Bin 0 -> 465 bytes Telegram/Resources/icons/emoji_travel@2x.png | Bin 0 -> 817 bytes Telegram/Resources/icons/emoji_trending.png | Bin 0 -> 546 bytes .../Resources/icons/emoji_trending@2x.png | Bin 0 -> 1189 bytes Telegram/SourceFiles/app.cpp | 1 + Telegram/SourceFiles/boxes/boxes.style | 3 + Telegram/SourceFiles/boxes/members_box.cpp | 6 +- .../history/field_autocomplete.cpp | 2 + Telegram/SourceFiles/history/history.style | 14 + .../inline_bot_layout_internal.cpp | 1 + Telegram/SourceFiles/intro/intro.style | 44 +++ Telegram/SourceFiles/intro/introphone.cpp | 1 + Telegram/SourceFiles/intro/introwidget.cpp | 1 + Telegram/SourceFiles/settings/settings.style | 1 + Telegram/SourceFiles/stickers/emoji_pan.cpp | 294 ++++++++---------- Telegram/SourceFiles/stickers/emoji_pan.h | 23 +- Telegram/SourceFiles/stickers/stickers.style | 126 +++++++- .../ui/buttons/history_down_button.h | 1 + Telegram/SourceFiles/ui/buttons/icon_button.h | 1 + Telegram/SourceFiles/ui/widgets/widgets.style | 22 +- .../SourceFiles/window/top_bar_widget.cpp | 1 + Telegram/SourceFiles/window/window.style | 1 + Telegram/gyp/Telegram.gyp | 1 + 43 files changed, 365 insertions(+), 393 deletions(-) create mode 100644 Telegram/Resources/icons/emoji_activity.png create mode 100644 Telegram/Resources/icons/emoji_activity@2x.png create mode 100644 Telegram/Resources/icons/emoji_food.png create mode 100644 Telegram/Resources/icons/emoji_food@2x.png create mode 100644 Telegram/Resources/icons/emoji_gif.png create mode 100644 Telegram/Resources/icons/emoji_gif@2x.png create mode 100644 Telegram/Resources/icons/emoji_nature.png create mode 100644 Telegram/Resources/icons/emoji_nature@2x.png create mode 100644 Telegram/Resources/icons/emoji_objects.png create mode 100644 Telegram/Resources/icons/emoji_objects@2x.png create mode 100644 Telegram/Resources/icons/emoji_people.png create mode 100644 Telegram/Resources/icons/emoji_people@2x.png create mode 100644 Telegram/Resources/icons/emoji_recent.png create mode 100644 Telegram/Resources/icons/emoji_recent@2x.png create mode 100644 Telegram/Resources/icons/emoji_settings.png create mode 100644 Telegram/Resources/icons/emoji_settings@2x.png create mode 100644 Telegram/Resources/icons/emoji_symbols.png create mode 100644 Telegram/Resources/icons/emoji_symbols@2x.png create mode 100644 Telegram/Resources/icons/emoji_travel.png create mode 100644 Telegram/Resources/icons/emoji_travel@2x.png create mode 100644 Telegram/Resources/icons/emoji_trending.png create mode 100644 Telegram/Resources/icons/emoji_trending@2x.png create mode 100644 Telegram/SourceFiles/intro/intro.style diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 9b919578a..a3cda0de0 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -665,28 +665,6 @@ introErrTop: 15px; introErrHeight: 40px; introErrFont: font(16px); -introErrLabel: flatLabel(labelDefFlat) { - font: introErrFont; - align: align(center); -} -introBackButton: IconButton { - width: 40px; - height: 40px; - - opacity: 0.71; - overOpacity: 1.; - - icon: icon { - { size(40px, 40px), #eeeeee }, - { "title_previous", #969696, point(12px, 12px) }, - }; - iconPosition: point(0px, 0px); - downIconPosition: point(0px, 0px); - - duration: 150; -} -introBackPosition: point(32px, 32px); - setLittleSkip: 9px; setScroll: flatScroll(scrollDef) { bottomsh: 0px; @@ -725,19 +703,6 @@ topBarBackAlpha: 0.8; topBarBackColor: #005faf; topBarBackFont: font(16px); topBarArrowPadding: margins(39px, 8px, 17px, 8px); -topBarSearch: IconButton { - width: 44px; - height: topBarHeight; - - icon: icon {{ "title_search", #000000 }}; - iconPosition: point(13px, 18px); - downIconPosition: point(13px, 18px); - - opacity: 0.22; - overOpacity: 0.36; - - duration: 150; -} topBarMinPadding: 5px; topBarButton: RoundButton { textFg: #0084c4; @@ -1352,171 +1317,6 @@ dpiFont2: linkFont; dpiFont3: linkFont; dpiFont4: linkFont; -stickersMaxHeight: 440px; -stickersPadding: margins(19px, 17px, 19px, 17px); -stickersSize: size(64px, 64px); -stickersScroll: flatScroll(boxScroll) { - round: 2px; - deltax: 7px; - deltat: 23px; - deltab: 9px; -} -stickersReorderPadding: margins(0px, 12px, 0px, 12px); -stickersReorderFg: #777; -stickersRowDisabledOpacity: 0.4; -stickersRowDuration: 200; - -emojiScroll: flatScroll(solidScroll) { - deltat: 48px; -} -emojiRecentOver: sprite(0px, 196px, 21px, 22px); -emojiRecentActive: sprite(245px, 264px, 21px, 22px); -emojiPeopleOver: sprite(21px, 196px, 21px, 22px); -emojiPeopleActive: sprite(266px, 264px, 21px, 22px); -emojiNatureOver: sprite(42px, 196px, 21px, 22px); -emojiNatureActive: sprite(245px, 286px, 21px, 22px); -emojiFoodOver: sprite(63px, 196px, 21px, 22px); -emojiFoodActive: sprite(266px, 286px, 21px, 22px); -emojiActivityOver: sprite(126px, 196px, 21px, 22px); -emojiActivityActive: sprite(287px, 264px, 21px, 22px); -emojiTravelOver: sprite(105px, 196px, 21px, 22px); -emojiTravelActive: sprite(308px, 286px, 21px, 22px); -emojiObjectsOver: sprite(147px, 196px, 21px, 22px); -emojiObjectsActive: sprite(308px, 264px, 21px, 22px); -emojiSymbolsOver: sprite(84px, 196px, 21px, 22px); -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); - -emojiPanCategories: #f7f7f7; - -rbEmoji: flatCheckbox { - textColor: transparent; - bgColor: emojiPanCategories; - disColor: emojiPanCategories; - - width: 42px; - height: 46px; - - textTop: 0px; - textLeft: 0px; - font: font(fsize); - duration: 200; - bgFunc: transition(easeOutCirc); - cursor: cursor(pointer); - - disabledCursor: cursor(default); - imagePos: point(11px, 12px); -} -rbEmojiRecent: flatCheckbox(rbEmoji) { - imageRect: emojiRecentOver; - chkImageRect: emojiRecentActive; - overImageRect: emojiRecentOver; - chkOverImageRect: emojiRecentActive; - disImageRect: emojiRecentOver; - chkDisImageRect: emojiRecentActive; -} -rbEmojiPeople: flatCheckbox(rbEmoji) { - imageRect: emojiPeopleOver; - chkImageRect: emojiPeopleActive; - overImageRect: emojiPeopleOver; - chkOverImageRect: emojiPeopleActive; - disImageRect: emojiPeopleOver; - chkDisImageRect: emojiPeopleActive; -} -rbEmojiNature: flatCheckbox(rbEmoji) { - imageRect: emojiNatureOver; - chkImageRect: emojiNatureActive; - overImageRect: emojiNatureOver; - chkOverImageRect: emojiNatureActive; - disImageRect: emojiNatureOver; - chkDisImageRect: emojiNatureActive; -} -rbEmojiFood: flatCheckbox(rbEmoji) { - imageRect: emojiFoodOver; - chkImageRect: emojiFoodActive; - overImageRect: emojiFoodOver; - chkOverImageRect: emojiFoodActive; - disImageRect: emojiFoodOver; - chkDisImageRect: emojiFoodActive; -} -rbEmojiActivity: flatCheckbox(rbEmoji) { - imageRect: emojiActivityOver; - chkImageRect: emojiActivityActive; - overImageRect: emojiActivityOver; - chkOverImageRect: emojiActivityActive; - disImageRect: emojiActivityOver; - chkDisImageRect: emojiActivityActive; -} -rbEmojiTravel: flatCheckbox(rbEmoji) { - imageRect: emojiTravelOver; - chkImageRect: emojiTravelActive; - overImageRect: emojiTravelOver; - chkOverImageRect: emojiTravelActive; - disImageRect: emojiTravelOver; - chkDisImageRect: emojiTravelActive; -} -rbEmojiObjects: flatCheckbox(rbEmoji) { - imageRect: emojiObjectsOver; - chkImageRect: emojiObjectsActive; - overImageRect: emojiObjectsOver; - chkOverImageRect: emojiObjectsActive; - disImageRect: emojiObjectsOver; - chkDisImageRect: emojiObjectsActive; -} -rbEmojiSymbols: flatCheckbox(rbEmoji) { - imageRect: emojiSymbolsOver; - chkImageRect: emojiSymbolsActive; - overImageRect: emojiSymbolsOver; - chkOverImageRect: emojiSymbolsActive; - disImageRect: emojiSymbolsOver; - chkDisImageRect: emojiSymbolsActive; -} -emojiPanPadding: 12px; -emojiPanSize: size(45px, 41px); -emojiPanWidth: 345px; -emojiPanMaxHeight: 366px; -emojiPanDuration: 200; -emojiPanHover: #f0f4f7; - -emojiPanHeader: 42px; -emojiPanHeaderFont: semiboldFont; -emojiPanHeaderColor: #999; -emojiPanHeaderLeft: 22px; -emojiPanHeaderTop: 12px; -emojiPanHeaderBg: #fffffff2; - -emojiColorsPadding: 5px; -emojiColorsSep: 1px; -emojiColorsSepColor: #d5d5d5; - -emojiSwitchSkip: 27px; -emojiSwitchImgSkip: 21px; -emojiSwitchStickers: sprite(318px, 328px, 8px, 12px); -emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px); -emojiSwitchColor: #42a8db; - -stickerPanSize: size(64px, 64px); -stickerPanPadding: 11px; -stickerPanDelete: sprite(128px, 132px, 12px, 12px); -stickerPanDeleteOpacity: 0.5; -stickerIconPadding: 5px; -stickerIconOpacity: 0.7; -stickerIconSel: 2px; -stickerIconSelColor: #58b2ed; -stickerIconLeft: sprite(342px, 72px, 40px, 1px); -stickerIconRight: sprite(342px, 73px, 40px, 1px); -stickerIconMove: 400; -stickerPreviewDuration: 150; -stickerPreviewBg: #FFFFFFB0; -stickerPreviewMin: 0.1; - botKbDuration: 200; botKbBg: #edf1f5; botKbOverBg: #d8e2ec; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index 3ade742e2..78e722870 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -386,17 +386,3 @@ OutlineButton { font: font; padding: margins; } - -IconButton { - width: pixels; - height: pixels; - - opacity: double; - overOpacity: double; - - icon: icon; - iconPosition: point; - downIconPosition: point; - - duration: int; -} diff --git a/Telegram/Resources/icons/emoji_activity.png b/Telegram/Resources/icons/emoji_activity.png new file mode 100644 index 0000000000000000000000000000000000000000..055ff9f33c91d65a09eb946c04525dcdf7012fdb GIT binary patch literal 639 zcmV-_0)YLAP)u`w8A>t5+o@Sh0B8H==d59rm#=OR)`3n)bJtV08_uAiH-`WcR_#L~ty5hsb1Iy(y-EQ}0{P={r zu5&)0bBTODzZU<2JrF{`?RKyE7K_E&`t$R1>)cL8hGDSTY;rc6vDs|0P$)2+PE$(B zSS-dyqrpR3}%RdIKB2fyEsN~MCgw>Ml~UT)Pkolfu6Vz1ig=jVKXe_t`2OeVB#o3q)B zUaxm+zX0%S=XC%yO+&q2hsWbt`4Wi)Y}>}g#RWp45Q4!VEX#t==R>d8+t2j={(h~F zwOVb}tKDvwb?$u;%aedaH!f>6C_H{CTDjR1}3m2nrz%`%#*v(XuSQ zzP^$qKR!O#>2zqC<~|*q1A#z*kB^UxL?Sc{gVkzvjd3=crJ^W~XHW2ZIKZ+jNGYMJ zDx{P!O%uAVV>le*?CcDS#R4}sH^}93I6XZD07RovwA*dxma5fi+=X~N?)WjL(pG;AaJgKNQsVjf8Ld`pb-SvnNG6j2fMT(T;c&RK6_1*sBsm_B zscG8rLT#E5f}Ku>Bzb##`#tqH%`{Dt Z#21{=GQw=jr27B>002ovPDHLkV1htjKUV+% literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_activity@2x.png b/Telegram/Resources/icons/emoji_activity@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..681df82ddcc473e589ecd7a9f7f50f94f89b5e47 GIT binary patch literal 1303 zcmV+y1?c*TP)4YHA7zA>I6cj%YHOY$J(? zD9bWB91cU@zq-cTB(N+CPN%cu7){gQcDoJze~f6YR)hEVcinMCQ4Ib2CQ4Bha2y9b z&x6fo1B#+_iJY9AbQ~)b3UG08(RHn!bB^OUOe7Nc^74X>Mgw!X97>Xep->2YJ|7MY z3?RcWsy)LnxVgEBZ*OmEeqLT)qSx!~y4Ek~QAfRAFDx%F8|K!-!voyh+(0&)g?Ky; zPft%k2m!y}4?NFeK3!W;6tP~f8xpP8>lg?G%qL0+L7wN)<#HL?@jQ>EQpu2H3#nAUp7#SIXVzCJ4=jULv*#H3G`S}^*@i<&vT|uo@gIcWyilV^l>njwC#kSg@?=3$# zI53Q6G#XXyB}p>0tyZg;&*yPxX9ro9HE-(!fj~!bZ{hm-TC<=a2w1IF4av432!iU@ z*Hwg~C>S3f2SP~4@%Q(4SXfwq=jUg&@8;$PCMG6ea&i(Z7K^&&wOS3D&8E8Tr>Cb- zC=@jF^&Cx}=P{qp>qca4ZB5sg5Yn;f+4mLEN~HpoN=0`(o6YKuedaRv5q;(8@87?5 zPojFgj?rio?RL8v68Lo|hG9??MfJQM9Ubj>yKEts%Y9Yc%^?~Jh4Ag|4d>?O)IN^m zaC380R{>;M#%MIEsrXGNN(e!UqL8L(92^|f$V(&=QRRmafSglsDTCKn^jQZ{Ic!1+L_!IN<^RTqEq<*t33*m4WL{S8qrr{&D zx3^(zY^<%n8xR067z`RpjUMjr?{%vxzu(_hY$GC`pP%=>^_?c{hDM`-q9__Ju(-I` z=C`=G*gMhT;bBat(^#!mv0kqm-hEmmW3iZ_NYgZp*Voq?zf!4$Hk+-#93_N+)oKNY z!vQSI0!`DvFbpsZqsdo`#R7+ihp@iBZut5!IywrwyStjc>FH@$Sy?ekMP2l(Z@TSc zRdsuN`>`T2SDt!lE|;tACfdV2>HCSsVlhpAXEK?7#QuAT+U@qXBek14{3k?%!Jwv4 zNu^Rq({x{Re#`r)+wE4LADhi4#N+X<@1LK6snKK@2Gi-Z8qL+!RkP+cm#82Jcz1V) zh=@l=M}2R3s}TVBr?-vhjMvv!pePC+A0J_Mb{5LzvMKYLa z|01n=w)O0JbT~IV=X}oB?|1eEfcWqz0e~b)sMqViI@oTvF`Z5kMG-vDgJoGS>+JXY zyj(6c41>5O&z+(uax$4PYnY~a-v>rfM3RNz@pzmGlBDN(5A=9ECJXOiUDtC#k_^L; z09XQm?RJ~gLKH=~t_#C3Qgt*<^KG5?`~9P)x~}tjy}sOjYd{e(_QX%y9|PWs;W|!99)QDm&wYg8%~ rEEWqamrIZY0DRr!esYvP4FbS_`2)bZOr&!&00000NkvXXu0mjfQZC}@ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_food@2x.png b/Telegram/Resources/icons/emoji_food@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4f3d8cf7845b60c99d6ddc7ef3efa76917ef6912 GIT binary patch literal 777 zcmV+k1NQuhP)``T{~IA(Rr9R9HeH^aYe&^A18!t(-!0=}V}RYL#Bw zODd-vD)i7(iHYBHcx<2jGs({G&LmI1%psGR%qPDcvm4m~K-hx(+B85mzc;mTE|#|6bH;H6kM7oNK|_woNZDFH3>`aL(!Y_*e{meSHlKREaGXi!__fq>%o~?(Xge zp~YfR_^ni6ZQD+a^y0d15ZdWG-D*`}2$J5^49zcE-s`d%VKYCZZaV!lL;}#qEN#y==u3s z8e6Z|S3+-ZZ!pg1x-FOpP3kM*eC>p(?SC|l5R9(Ktk(9$ZE$||JXZ>IktTl4qL)pC{Zu-2M|5Nx#ofPLTRs;V9m z8e>&e?EC&rR0!b&+qPw;6iX?Gr;bue$x111+tvvSAsAX~CL(T{hCR=FNbH`T=W)|C zOhl};W*B3bh*&B0D?v-86cZ5}V;FtkGZ8%;@>i)KBJTU1pNNP7U^1D&A`C-FDFFaQ zQ2+pfAi!)kLs1mYwzyuesOuU;baUA1+1%W}g7f*z0C>G#yYH-RnddnJaO=100oZQ0 z2*VIbk|4`6NGaWoKf^G(cmClbj$;%>fz4(E0JvN(qk>=I`QBl3nCCf&=q*p5!0zXH zK7Lc+RwMvOk_1r{Ax+a!(Zyl`;8v(n(cNx`IF7Met&n9I>bf2k97c7Ici|^Vg1WAe zrYTOR(}%Dg#_&m5hGEE26mb+q&KCrMTMz9aoz`FdQPb(vwZrHAeh&bc&*#4j^%cwI z5&&>K9`Qe2>M#1K6+9dcb^`oE=eMWY8C90$k7iU^mLF!+3!5*v-bXpu<^TWy07*qo IM6N<$f)`2Rnm*AhG7`Ey}iX~G|K%AjSJ87RMRv%o6YEaJ}*UPvl-Pijq*I7 z;InjQL?O#EeS3Q=jbL1+(oB2c)MJd&bdEQl3HJxa)*-Rxl91f|fs#FjJ%Cc_v91c^-#ytBOlhf%mV)*|4PTTD^WfUmxw3_>vr zdVPHzG4MQ(HX4l|Cm5IA-Q5IPP18R6d7e*9xuPhglUz!>-A;r6&+`=BZZ{E=WlVQo z7{p_?+wIayqtQSZ{eB;d#R9*D#bSYezaP;x8Vz7P9!J;H#Rol`~G)3st{O~h39!e+xLB}&F6C(HdF8$+fE!N2qEBkKCnsCr@?hyn5LP@JQ5E`i5XQ@>C4MY=9c&P_(*HDT3~;6c9uF` zSf3`kt_Qa9zQ1}@*L6se6ww)mk?_uBGWpw3|Abtk7Z(=+N!NAJ>2z>)brtm2YBk8R z4A*sw|8c>!kZDZ_fubk@$(@~@MEp!9lL+yjL?x?3mgV5N*Xv~mc}d758jj0{hlf(g zj2WLbu3ns8kg9LGTv#j<$j z6E#f}+uPf?yu1unX&O1#b^p^gS_EG{j}k&4NmAq>^7QnC(P)&&pI4PDJ^eD|?CfEx>UcR4mJiDEIdE%H))a_!Zu=EK~-A zK}30YcnF4J%H$OR!!S5HI*RB9g8@JYNtATj?e?$T>C;L&gb)euQNCWtUCJtz#!pU8;x89{(^dG`&}y~PW+6E( ztN8h5ZOJwbKlwq-_kB2y1Iw~t7zXa{?(qEloN50DW4+sBo!|0e00000NkvXXu0mjf D84BsU literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_nature.png b/Telegram/Resources/icons/emoji_nature.png new file mode 100644 index 0000000000000000000000000000000000000000..810cdf233e0ddcfce0ede1770d4f586db4265755 GIT binary patch literal 554 zcmV+_0@eMAP)5TEDW_fkquA%tE_D5W2ugk{MG=qCxG=h9=N*Rtf+Z;*QkIkoh-{L@ye z75Kgn)9Dl#V<3dk@Hmd)^?Jemeuw>j57+CpO`-2%+cqI$N8OMiB3YKDar|lTz&QuQ zFtoL@EFla-2*VJ{veYocFo1LZSt;jSQ&JQK*|x1^QWS;evtF;q4@ye;4mcbR9sGDa zzVXxP)WJLHGsb{1)=6i`hI~J^P&@tEY}QHNbzP0)Z+i#Jvb2BGG~sf&!1;Uz0D#SA z1Ey(eKDKRtR{Fm!RaF(4ra9jH%jFV+Ab`na0*o>6JWs>pIELr*36IAEcDo(iZnu7= zbzM`Q=M+T|2_Xhs>}L%bW8`@rMNvd~o)aSKEtTiF7MNujIgUdBKtT|^2a50e1OT*H zEGWw|Ek-G&lSWb0QB>D;=VTl8bzOJ-rIb2p-!y878^w*nK0+dGdi_(-PN%UnP2V*f z@=21U|C&0EGpIrcL7wOJVu(od`F!~9yRJJ}ufDn?;_n8E<2aP2sTQo1>U2nwByEM` s&xsI%q?DwTB19xaB&8HdDL+0b-)qiJN2TyqivR!s07*qoM6N<$f^$UxTL1t6 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_nature@2x.png b/Telegram/Resources/icons/emoji_nature@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..308965ef3a46f4bfdbf7b38b7cfeec2ab5be4b63 GIT binary patch literal 1100 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 ksf39{0tG>^91j5SU%C}O7*h^SSk z7OtyVV+zv*4jiFV>lcR34osG@q9i%^;l~;nM`=I0D#?YHx+z@ecz|n z`el!`mQu<)x>zhSoAcIsy(R#b05FOoG#ZV<13DZIXf~TD0iZDkLWsiX<#Iu_TD?yH zcsv#cADuD)?DzYRpf{%s0MqI8Bj|iS|G5(YSF2T_$#1t?3L);10>FB`&M1oh1fEVO zI*yYJ|E*lteH-w!#pv~Vk6;o7Kq+Nz*cijXVDKA!M)AG2aU5rYT5Ijs a=R04Ea+Xe@V|Td#0000=1?!3`6_O-D5CnXFexg>ZA)n9VvqePL)U`=<)H9Ow)9&^Ye3xMx%~B08k_np=>rwtyasmPovSG zt*x!TeH@{gOoj}@7>Kk=*LB+5-0bNi%QC&Zym;FH_cKBj3pouU69Zf|c#YHpe) zl}aVbX0s#rDVNI>3Wb<$#?nfX1WnUC{e!2K_4Re!+}r@L-A9wjWMH6vQ51!Z8B~_# zKrn8xn;29S#XvA_iAJMr%%D8a2ZC{n-Sn-Y_Au!C`+JZM$Jk8_TC3Fp!MNr5`I(Iw zG@s80f^o~;-5nb<=-Js>AQ-pUP3*}80EI#Uu~;k!Q%|bZDi#+P(QdZ^biLv4?(PPH za?1YxK0^XsGYSBgmzPK+64Q2dq*AG1X=w>xUtjFIo;6b{l^VDtm}J`RHnz97JCJ|_ zv|6nql}b$w)}4cc1NO$$9mh4wINmA!*8TlGg~MS-PtP@Kh2uEnayhK5tc-u!9P;}5 zisj|yj+=aU`mX#0L7<0+hl$VR$Hxc7<8ikYsPih+UXe(IN~O}okh-qZ>gwts%#Ns=F=KPUhS zhr@Jyd^|K#(=-xA(J#_J6aa-nAxfvyzBemQPfuxfc6J!#a8Rp~$)u;fCBrZ%lgV^! zC`=U!fEE@OTz6RN^}5&F&t#$2rLecR*K;XcU0snN2;)GG2Wpip%Ty>7=DFm-(V{%!AjUl2nbfG1S`QpEbIjzK=2I$LRv+zN-G2u z!AcMU{}p$Mmt>Qi;tx~J?6k8x3FVx_ zaU5jn@p!;>T}-D_j7Fn8;1e2#5CXg1E)$-V0+@yp5g23WcDwLA?;W5T8iuB6uq+F{ z?<=8)!y)Tzv285NQbL0ufUfJWLz7!hN(k{9RArmw1(V4nAIdpLtyatR;e0-iK^@1* zh6*7f$mw*-^r5cnvBll*_te9PncsEY)XY@2>GgWC8q+lMXWDMJaXcQQ1B1ap>ErEo z8`TVl!{S9%t5w`?HvmAt-&ZcN*=$B5TCdl!obo@tTrM=5%~YvK`~994iv-Bo!YcmWZ_4k%S@+b8T$(i~eEM~M-00000NkvXX Hu0mjf+Vs)U literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_travel@2x.png b/Telegram/Resources/icons/emoji_travel@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8958c7c62c0bf21e1ce2ebb2cf8f158b8f577b29 GIT binary patch literal 817 zcmV-11J3-3P)^yKaW8x^0C}N-60kgc8zK zWtB>05h10*5Qgt9-kWEdKX>k(Y5w$Zh{-)SznnYYz278zLJ~()RKjF3*(9VqsFR^k2$z?auq?|r=$z?T z52%yja2T~(&DWMzuh%yR>g3_!A@1((q(Fzmq450QW=h`Q-$_+fshV&&Op!=Lczob) zikvvB;_U2fGfN)?5&%roTt8?~NC4<|yXyrNffU&ZY}yKs8N6r_+IDS$KYahH09}<#LEdqrR+KwOZxYG@DI-p!Is4 zJFyHT(ci}#o|&;MOXQjC>xED#moii3lL?-7$bPft(L?RJq& vCVhj_KVc091JAv9&!OG*_%VOq{20(bu7gdULjWwx00000NkvXXu0mjfymo;h literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_trending.png b/Telegram/Resources/icons/emoji_trending.png new file mode 100644 index 0000000000000000000000000000000000000000..4c76cf74a82879ab46541a1524e9ba43b08dfdf7 GIT binary patch literal 546 zcmV+-0^R+IP)St`XwBK`y=8(B@MS)fTwWFcjPoyAy;h1Dc0 zWxsS8C!tW+vLgQij`L*PTvX!J9v*=$Bx zmOp|jih?$q?eEZ1siX^OHk+s@3YBX#8pcq!+l{KK>O%c~KSm-EDtA7g(c|&xLkoq1 z;cijcb~Qnx-Kl;^}lkr_+f5fZ=eMs_XT7@1gm89vx>)W-FCP3ktA4Sm`0M}a^9tX3;LOArJom&*VEFqup!Z?RaA*z5ID z9)U6pLm9W*4J1hdM1)qW1)?ZY{%*G;vB_kjJVKIX8SeKxSS%I*0FWdJR8<84fLtyI z-}T(Y;c$50>3lwi)oMkI{eJ%fdKSlVB%aM?FFqM?EEXff=XqZDZQJd3JRXmTh* zteDLK!lv13O_YT(v+H;8Yku$E-Ouyv*~a^R>#lWQ*Y~Sr#(k&t;Q51#I(b3-vs?lgTgCZg>FE4)| zs7xjc2-2g`(b2C54G#~;)6>(SKoJp}nwq{Aw5_czXh@GF63N$qCMG7L$z%!{=+V&; z^7;JGgANW3h78i9p`oGBf{H{Ue0zKI8^G;$y1F`6&>z5}j*bqt6rvIm5&!@|CX=z|Ha9o> zRg?BA%FD|Gx|@iIy}iAkbvipcS@SC^D+7P@OG`@ugUaP{uUZEO2aNfXlaqmgW@l%k z)9DNtw5qDgt6o`I8DqZDXvCwbcKgg5jYhb-y7HMH85v=$mzS4E8zX`i6cjL;JfbKHT3T9s=9Nk%F<-4#gGQsF zt(TjdOB*AC=I7^wKp%?;e%-U0x??CdO~&t|j1!@~m^ z<_G;BC6Vay@sY8a(P;E}*T%=kfyd*)@$oUE*X44-%gYNf#`GkJpeB=vv6)h-^l3Ic zJk03%RbgRa#CeOwLhB*kYrS3%cDtR{#I#u06d4%_JRXl2)9G}yJ|d__qhT}!^9?jB zD+`1|Au+bUzfbEUf*K44n3|fRH9b8&am4#y5{ZPkShZR$Y;JDS`e>J#LZN_*iwk0m z&*uk#_@62&Dv0wF6BDdn+KoU>O$~AL(ACxD@A1KF6c!c|cVs&|JHa1CdU|?%3L6Xt zjEjro3`#nCy}rJpSS$`66acWlzu%|9z`y_}$ol$v;?t?Asqwdle~sjFIljKWdNpZm zZ1l@37K?q3+jhGhrBW%Ei}nAmR$M!{3h_zYnQYig$N+ULJ~~Fe@vI>#M_k zQIwRF;L6GhA|mSbdW?vOAm+uy#C(1Ra=Bc%ySt0k)zzH)0RXb^J_y>{+5)T93Uav| zY&ILbzrPdnoK7d`^?ERy&7fARVR?BOPEJk&X$k)VEPvLv*VrG600000NkvXXu0mjf D=;b#j literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 6bfed1347..c44efaa90 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_overview.h" #include "styles/style_mediaview.h" +#include "styles/style_stickers.h" #include "lang.h" #include "data/data_abstract_structure.h" #include "history/history_service_layout.h" diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 018503827..1d0266b4c 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -203,3 +203,6 @@ notificationSampleUserpicFg: #40ace3; notificationSampleCloseFg: #d7d7d7; notificationSampleTextFg: #d7d7d7; notificationSampleNameFg: #939393; + +membersAboutPadding: margins(0px, 12px, 0px, 12px); +membersAboutFg: #777; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index c5647ef29..cacffc59c 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -170,8 +170,8 @@ void MembersBox::Inner::paintEvent(QPaintEvent *e) { p.translate(0, _rowHeight); } if (to == _rows.size() && _filter == MembersFilter::Recent && (_rows.size() < _channel->membersCount() || _rows.size() >= Global::ChatSizeMax())) { - p.setPen(st::stickersReorderFg); - _about.draw(p, st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center); + p.setPen(st::membersAboutFg); + _about.draw(p, st::contactsPadding.left(), st::membersAboutPadding.top(), _aboutWidth, style::al_center); } } } @@ -348,7 +348,7 @@ void MembersBox::Inner::refresh() { _aboutHeight = 0; } else { _about.setText(st::boxTextFont, lng_channel_only_last_shown(lt_count, _rows.size())); - _aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom(); + _aboutHeight = st::membersAboutPadding.top() + _about.countHeight(_aboutWidth) + st::membersAboutPadding.bottom(); if (_filter != MembersFilter::Recent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) { _aboutHeight = 0; } diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index bcae2de31..c217e8c70 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "apiwrap.h" #include "localstorage.h" #include "styles/style_history.h" +#include "styles/style_widgets.h" +#include "styles/style_stickers.h" FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent) , _scroll(this, st::mentionScroll) diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 469119da6..7c1ff3156 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -210,3 +210,17 @@ historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { itemPadding: margins(48px, 11px, 48px, 11px); } } + +topBarSearch: IconButton { + width: 44px; + height: topBarHeight; + + icon: icon {{ "title_search", #000000 }}; + iconPosition: point(13px, 18px); + downIconPosition: point(13px, 18px); + + opacity: 0.22; + overOpacity: 0.36; + + duration: 150; +} diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 09ffe49e9..3dd81b3df 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_overview.h" #include "styles/style_history.h" +#include "styles/style_stickers.h" #include "inline_bots/inline_bot_result.h" #include "media/media_audio.h" #include "media/media_clip_reader.h" diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style new file mode 100644 index 000000000..e56d5f7b7 --- /dev/null +++ b/Telegram/SourceFiles/intro/intro.style @@ -0,0 +1,44 @@ +/* +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"; +using "ui/widgets/widgets.style"; + +introErrLabel: flatLabel(labelDefFlat) { + font: introErrFont; + align: align(center); +} +introBackButton: IconButton { + width: 40px; + height: 40px; + + opacity: 0.71; + overOpacity: 1.; + + icon: icon { + { size(40px, 40px), #eeeeee }, + { "title_previous", #969696, point(12px, 12px) }, + }; + iconPosition: point(0px, 0px); + downIconPosition: point(0px, 0px); + + duration: 150; +} +introBackPosition: point(32px, 32px); diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index 5f4e7c017..9bb01a378 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "application.h" #include "intro/introcode.h" +#include "styles/style_intro.h" namespace { class SignUpClickHandler : public LeftButtonClickHandler { diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index dab521dd0..678a0e355 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -34,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/text/text.h" #include "ui/buttons/icon_button.h" #include "ui/effects/widget_fade_wrap.h" +#include "styles/style_intro.h" IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent) , _a_stage(animation(this, &IntroWidget::step_stage)) diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 0bb31a9a5..e7f9881cb 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org using "basic.style"; using "basic_types.style"; using "dialogs/dialogs.style"; +using "ui/widgets/widgets.style"; settingsMaxWidth: 520px; settingsMaxPadding: 48px; diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index fc0dd4799..ffa997456 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stickers/emoji_pan.h" #include "styles/style_stickers.h" +#include "ui/buttons/icon_button.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" #include "boxes/stickers_box.h" @@ -292,7 +293,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { } EmojiPanInner::EmojiPanInner() : TWidget() -, _maxHeight(int(st::emojiPanMaxHeight) - st::rbEmoji.height) +, _maxHeight(int(st::emojiPanMaxHeight) - st::emojiCategory.height) , _a_selected(animation(this, &EmojiPanInner::step_selected)) { resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); @@ -797,9 +798,9 @@ 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)) +, _addWidth(st::stickersTrendingAdd.font->width(_addText)) , _settings(this, lang(lng_stickers_you_have)) { - setMaxHeight(st::emojiPanMaxHeight - st::rbEmoji.height); + setMaxHeight(st::emojiPanMaxHeight - st::emojiCategory.height); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); @@ -864,7 +865,7 @@ void StickerPanInner::readVisibleSets() { } int StickerPanInner::featuredRowHeight() const { - return st::featuredStickersHeader + st::stickerPanSize.height() + st::featuredStickersSkip; + return st::stickersTrendingHeader + st::stickerPanSize.height() + st::stickersTrendingSkip; } int StickerPanInner::countHeight(bool plain) { @@ -915,7 +916,7 @@ StickerPanInner::~StickerPanInner() { QRect StickerPanInner::stickerRect(int tab, int sel) { int x = 0, y = 0; if (_section == Section::Featured) { - y += st::emojiPanHeader + (tab * featuredRowHeight()) + st::featuredStickersHeader; + y += st::emojiPanHeader + (tab * featuredRowHeight()) + st::stickersTrendingHeader; x = st::stickerPanPadding + (sel * st::stickerPanSize.width()); } else { auto &sets = shownSets(); @@ -1018,15 +1019,15 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) { 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; + auto textBg = selected ? st::stickersTrendingAdd.textBgOver : st::stickersTrendingAdd.textBg; + auto textTop = (selected && _selectedFeaturedSetAdd == _pressedFeaturedSetAdd) ? st::stickersTrendingAdd.downTextTop : st::stickersTrendingAdd.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); + p.setFont(st::stickersTrendingAdd.font); + p.setPen(selected ? st::stickersTrendingAdd.textFgOver : st::stickersTrendingAdd.textFg); + p.drawTextLeft(add.x() - (st::stickersTrendingAdd.width / 2), add.y() + textTop, width(), _addText, _addWidth); - widthForTitle -= add.width() - (st::featuredStickersAdd.width / 2); + widthForTitle -= add.width() - (st::stickersTrendingAdd.width / 2); } else { auto add = featuredAddRect(c); int checkx = add.left() + (add.width() - st::stickersFeaturedInstalled.width()) / 2; @@ -1038,29 +1039,29 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) { } auto titleText = set.title; - auto titleWidth = st::featuredStickersHeaderFont->width(titleText); + auto titleWidth = st::stickersTrendingHeaderFont->width(titleText); if (titleWidth > widthForTitle) { - titleText = st::featuredStickersHeaderFont->elided(titleText, widthForTitle); - titleWidth = st::featuredStickersHeaderFont->width(titleText); + titleText = st::stickersTrendingHeaderFont->elided(titleText, widthForTitle); + titleWidth = st::stickersTrendingHeaderFont->width(titleText); } - p.setFont(st::featuredStickersHeaderFont); - p.setPen(st::featuredStickersHeaderFg); - p.drawTextLeft(st::emojiPanHeaderLeft, y + st::featuredStickersHeaderTop, width(), titleText, titleWidth); + p.setFont(st::stickersTrendingHeaderFont); + p.setPen(st::stickersTrendingHeaderFg); + p.drawTextLeft(st::emojiPanHeaderLeft, y + st::stickersTrendingHeaderTop, width(), titleText, titleWidth); if (set.flags & MTPDstickerSet_ClientFlag::f_unread) { p.setPen(Qt::NoPen); p.setBrush(st::stickersFeaturedUnreadBg); p.setRenderHint(QPainter::HighQualityAntialiasing, true); - p.drawEllipse(rtlrect(st::emojiPanHeaderLeft + titleWidth + st::stickersFeaturedUnreadSkip, y + st::featuredStickersHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); + p.drawEllipse(rtlrect(st::emojiPanHeaderLeft + titleWidth + st::stickersFeaturedUnreadSkip, y + st::stickersTrendingHeaderTop + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width())); p.setRenderHint(QPainter::HighQualityAntialiasing, false); } - p.setFont(st::featuredStickersSubheaderFont); - p.setPen(st::featuredStickersSubheaderFg); - p.drawTextLeft(st::emojiPanHeaderLeft, y + st::featuredStickersSubheaderTop, width(), lng_stickers_count(lt_count, size)); + p.setFont(st::stickersTrendingSubheaderFont); + p.setPen(st::stickersTrendingSubheaderFg); + p.drawTextLeft(st::emojiPanHeaderLeft, y + st::stickersTrendingSubheaderTop, width(), lng_stickers_count(lt_count, size)); - y += st::featuredStickersHeader; + y += st::stickersTrendingHeader; if (y >= r.y() + r.height()) break; for (int j = fromcol; j < tocol; ++j) { @@ -1156,10 +1157,10 @@ int StickerPanInner::featuredContentWidth() const { } QRect StickerPanInner::featuredAddRect(int index) const { - int addw = _addWidth - st::featuredStickersAdd.width; - int addh = st::featuredStickersAdd.height; + int addw = _addWidth - st::stickersTrendingAdd.width; + int addh = st::stickersTrendingAdd.height; int addx = featuredContentWidth() - addw; - int addy = st::emojiPanHeader + index * featuredRowHeight() + st::featuredStickersAddTop; + int addy = st::emojiPanHeader + index * featuredRowHeight() + st::stickersTrendingAddTop; return QRect(addx, addy, addw, addh); } @@ -2002,7 +2003,7 @@ void StickerPanInner::fillIcons(QList &icons) { } 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 availw = st::emojiCategory.width - 2 * st::stickerIconPadding, availh = st::emojiCategory.height - 2 * st::stickerIconPadding; int32 thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1; if (availw * thumbh > availh * thumbw) { pixh = availh; @@ -2175,7 +2176,7 @@ void StickerPanInner::updateSelected() { } if (p.y() >= y && p.y() < ytill) { if (featured) { - if (p.y() < y + st::featuredStickersHeader) { + if (p.y() < y + st::stickersTrendingHeader) { if (featuredHasAddButton(c) && myrtlrect(featuredAddRect(c)).contains(p.x(), p.y())) { selectedFeaturedSetAdd = c; } else { @@ -2183,7 +2184,7 @@ void StickerPanInner::updateSelected() { } break; } - y += st::featuredStickersHeader; + y += st::stickersTrendingHeader; } else { y += st::emojiPanHeader; } @@ -2547,18 +2548,18 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) , _maxHeight(st::emojiPanMaxHeight) , _contentMaxHeight(st::emojiPanMaxHeight) , _contentHeight(_contentMaxHeight) -, _contentHeightEmoji(_contentHeight - st::rbEmoji.height) -, _contentHeightStickers(_contentHeight - st::rbEmoji.height) +, _contentHeightEmoji(_contentHeight - st::emojiCategory.height) +, _contentHeightStickers(_contentHeight - st::emojiCategory.height) , _a_appearance(animation(this, &EmojiPan::step_appearance)) , _shadow(st::defaultDropdownShadow) -, _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) +, _recent(this, st::emojiCategoryRecent) +, _people(this, st::emojiCategoryPeople) +, _nature(this, st::emojiCategoryNature) +, _food(this, st::emojiCategoryFood) +, _activity(this, st::emojiCategoryActivity) +, _travel(this, st::emojiCategoryTravel) +, _objects(this, st::emojiCategoryObjects) +, _symbols(this, st::emojiCategorySymbols) , _a_icons(animation(this, &EmojiPan::step_icons)) , _a_slide(animation(this, &EmojiPan::step_slide)) , e_scroll(this, st::emojiScroll) @@ -2589,19 +2590,21 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) e_inner.moveToLeft(0, 0, e_scroll.width()); s_inner.moveToLeft(0, 0, s_scroll.width()); - int32 left = _iconsLeft = st::defaultDropdownPadding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2; - int32 top = _iconsTop = st::defaultDropdownPadding.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); + int32 left = _iconsLeft = st::defaultDropdownPadding.left() + (st::emojiPanWidth - 8 * st::emojiCategory.width) / 2; + int32 top = _iconsTop = st::defaultDropdownPadding.top() + _contentHeight - st::emojiCategory.height; + prepareTab(left, top, _width, _recent, dbietRecent); + prepareTab(left, top, _width, _people, dbietPeople); + prepareTab(left, top, _width, _nature, dbietNature); + prepareTab(left, top, _width, _food, dbietFood); + prepareTab(left, top, _width, _activity, dbietActivity); + prepareTab(left, top, _width, _travel, dbietTravel); + prepareTab(left, top, _width, _objects, dbietObjects); + prepareTab(left, top, _width, _symbols, dbietSymbols); e_inner.fillPanels(e_panels); updatePanelsPositions(e_panels, 0); + setCurrentTabIcon(dbietRecent); + _hideTimer.setSingleShot(true); connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideAnimated())); @@ -2657,8 +2660,8 @@ void EmojiPan::setMaxHeight(int32 h) { void EmojiPan::updateContentHeight() { int32 h = qMin(_contentMaxHeight, _maxHeight); - int32 he = h - st::rbEmoji.height; - int32 hs = h - (s_inner.showSectionIcons() ? st::rbEmoji.height : 0); + int32 he = h - st::emojiCategory.height; + int32 hs = h - (s_inner.showSectionIcons() ? st::emojiCategory.height : 0); if (h == _contentHeight && he == _contentHeightEmoji && hs == _contentHeightStickers) return; int32 was = _contentHeight, wase = _contentHeightEmoji, wass = _contentHeightStickers; @@ -2683,24 +2686,23 @@ void EmojiPan::updateContentHeight() { s_scroll.resize(st::emojiPanWidth, _contentHeightStickers); } - _iconsTop = st::defaultDropdownPadding.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); + _iconsTop = st::defaultDropdownPadding.top() + _contentHeight - st::emojiCategory.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::prepareTab(int &left, int top, int _width, Ui::IconButton *tab, DBIEmojiTab value) { + tab->moveToLeft(left, top, _width); + left += tab->width(); + tab->setClickedCallback([this, value] { setActiveTab(value); }); } void EmojiPan::onWndActiveChanged() { @@ -2718,8 +2720,8 @@ void EmojiPan::onSaveConfigDelayed(int32 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); + int settingsLeft = _iconsLeft + 7 * st::emojiCategory.width; + st::stickersSettings.paint(p, settingsLeft + st::emojiCategory.iconPosition.x(), _iconsTop + st::emojiCategory.iconPosition.y(), width()); } void EmojiPan::paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const { @@ -2727,7 +2729,7 @@ void EmojiPan::paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const { Dialogs::Layout::UnreadBadgeStyle unreadSt; unreadSt.sizeId = Dialogs::Layout::UnreadBadgeInStickersPanel; unreadSt.size = st::stickersSettingsUnreadSize; - int unreadRight = iconLeft + st::rbEmoji.width - st::stickersSettingsUnreadPosition.x(); + int unreadRight = iconLeft + st::emojiCategory.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); @@ -2750,28 +2752,28 @@ void EmojiPan::paintEvent(QPaintEvent *e) { 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); + p.fillRect(r.left(), _iconsTop, r.width(), st::emojiCategory.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); + QRect clip(x, _iconsTop, _iconsLeft + 7 * st::emojiCategory.width - x, st::emojiCategory.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; + return active ? &st::emojiSavedGifsActive : &st::emojiSavedGifs; } else if (setId == Stickers::FeaturedSetId) { - return active ? st::featuredStickersActive : st::featuredStickersOver; + return active ? &st::stickersTrendingActive : &st::stickersTrending; } - return active ? st::rbEmojiRecent.chkImageRect : st::rbEmojiRecent.imageRect; + return active ? &st::emojiRecentActive : &st::emojiRecent; }; int i = 0; - i += _iconsX.current() / int(st::rbEmoji.width); - x -= _iconsX.current() % int(st::rbEmoji.width); + i += _iconsX.current() / int(st::emojiCategory.width); + x -= _iconsX.current() % int(st::emojiCategory.width); selxrel -= _iconsX.current(); for (int l = qMin(_icons.size(), i + 8); i < l; ++i) { auto &s = _icons.at(i); @@ -2779,54 +2781,52 @@ void EmojiPan::paintEvent(QPaintEvent *e) { 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; + p.drawPixmapLeft(x + (st::emojiCategory.width - s.pixw) / 2, _iconsTop + (st::emojiCategory.height - s.pixh) / 2, width(), pix); + x += st::emojiCategory.width; } else { if (true || selxrel != x) { - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), getSpecialSetIcon(s.setId, false)); + getSpecialSetIcon(s.setId, false)->paint(p, x + st::emojiCategory.iconPosition.x(), _iconsTop + st::emojiCategory.iconPosition.y(), width()); } - //if (selxrel < x + st::rbEmoji.width && selxrel > x - st::rbEmoji.width) { - // p.setOpacity(1 - (qAbs(selxrel - x) / float64(st::rbEmoji.width))); + //if (selxrel < x + st::emojiCategory.width && selxrel > x - st::emojiCategory.width) { + // p.setOpacity(1 - (qAbs(selxrel - x) / float64(st::emojiCategory.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; + x += st::emojiCategory.width; } } - if (rtl()) selx = width() - selx - st::rbEmoji.width; + if (rtl()) selx = width() - selx - st::emojiCategory.width; p.setOpacity(1.); - p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor); + p.fillRect(selx, _iconsTop + st::emojiCategory.height - st::stickerIconPadding, st::emojiCategory.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); + p.drawSpriteLeft(QRect(_iconsLeft, _iconsTop, st::stickerIconLeft.pxWidth(), st::emojiCategory.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); + p.drawSpriteRight(QRect(width() - _iconsLeft - 7 * st::emojiCategory.width, _iconsTop, st::stickerIconRight.pxWidth(), st::emojiCategory.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); + int32 x = rtl() ? (_recent->x() + _recent->width()) : (_objects->x() + _objects->width()); + p.fillRect(x, _recent->y(), r.left() + r.width() - x, st::emojiCategory.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); + p.fillRect(r.left(), _iconsTop, r.width(), st::emojiCategory.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.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::emojiCategory.height), st::white->b); + p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::emojiCategory.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()); @@ -2957,7 +2957,7 @@ void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { updateSelected(); if (wasDown == _iconOver && _iconOver >= 0 && _iconOver < _icons.size()) { - _iconSelX = anim::ivalue(_iconOver * st::rbEmoji.width, _iconOver * st::rbEmoji.width); + _iconSelX = anim::ivalue(_iconOver * st::emojiCategory.width, _iconOver * st::emojiCategory.width); s_inner.showStickerSet(_icons.at(_iconOver).setId); } } @@ -3031,7 +3031,7 @@ void EmojiPan::onRefreshIcons(bool scrollAnimation) { _iconsMax = 0; } else { _iconHovers = QVector(_icons.size(), 0); - _iconsMax = qMax(int((_icons.size() - 7) * st::rbEmoji.width), 0); + _iconsMax = qMax(int((_icons.size() - 7) * st::emojiCategory.width), 0); } if (_iconsX.current() > _iconsMax) { _iconsX = anim::ivalue(_iconsMax, _iconsMax); @@ -3070,12 +3070,12 @@ void EmojiPan::updateSelected() { 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) { + if (x >= st::emojiCategory.width * 7 && x < st::emojiCategory.width * 8 && y >= _iconsTop && y < _iconsTop + st::emojiCategory.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) { + if (y >= _iconsTop && y < _iconsTop + st::emojiCategory.height && x >= 0 && x < 7 * st::emojiCategory.width && x < _icons.size() * st::emojiCategory.width) { x += _iconsX.current(); - newOver = qFloor(x / st::rbEmoji.width); + newOver = qFloor(x / st::emojiCategory.width); } } if (newOver != _iconOver) { @@ -3108,7 +3108,7 @@ void EmojiPan::updateIcons() { if (!_stickersShown || !s_inner.showSectionIcons()) return; QRect r(st::defaultDropdownPadding.left(), st::defaultDropdownPadding.top(), _width - st::defaultDropdownPadding.left() - st::defaultDropdownPadding.right(), _height - st::defaultDropdownPadding.top() - st::defaultDropdownPadding.bottom()); - update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); + update(r.left(), _iconsTop, r.width(), st::emojiCategory.height); } void EmojiPan::step_icons(uint64 ms, bool timer) { @@ -3229,11 +3229,7 @@ void EmojiPan::hideFinish() { _hiding = false; e_scroll.scrollToY(0); - if (!_recent.checked()) { - _noTabUpdate = true; - _recent.setChecked(true); - _noTabUpdate = false; - } + setCurrentTabIcon(dbietRecent); s_scroll.scrollToY(0); _iconOver = _iconDown = -1; _iconSel = 0; @@ -3349,55 +3345,46 @@ bool EmojiPan::ui_isInlineItemBeingChosen() { void EmojiPan::showAll() { if (_stickersShown) { s_scroll.show(); - _recent.hide(); - _people.hide(); - _nature.hide(); - _food.hide(); - _activity.hide(); - _travel.hide(); - _objects.hide(); - _symbols.hide(); + _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(); + _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(); + _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::setActiveTab(DBIEmojiTab tab) { + e_inner.showEmojiPack(tab); } void EmojiPan::updatePanelsPositions(const QVector &panels, int32 st) { @@ -3423,27 +3410,22 @@ void EmojiPan::onScrollEmoji() { 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; - } + setCurrentTabIcon(e_inner.currentTab(st)); e_inner.setVisibleTopBottom(st, st + e_scroll.height()); } +void EmojiPan::setCurrentTabIcon(DBIEmojiTab tab) { + _recent->setIcon((tab == dbietRecent) ? &st::emojiRecentActive : nullptr); + _people->setIcon((tab == dbietPeople) ? &st::emojiPeopleActive : nullptr); + _nature->setIcon((tab == dbietNature) ? &st::emojiNatureActive : nullptr); + _food->setIcon((tab == dbietFood) ? &st::emojiFoodActive : nullptr); + _activity->setIcon((tab == dbietActivity) ? &st::emojiActivityActive : nullptr); + _travel->setIcon((tab == dbietTravel) ? &st::emojiTravelActive : nullptr); + _objects->setIcon((tab == dbietObjects) ? &st::emojiObjectsActive : nullptr); + _symbols->setIcon((tab == dbietSymbols) ? &st::emojiSymbolsActive : nullptr); +} + void EmojiPan::onScrollStickers() { auto st = s_scroll.scrollTop(); @@ -3468,13 +3450,13 @@ void EmojiPan::validateSelectedIcon(ValidateIconAnimations animations) { } if (newSel != _iconSel) { _iconSel = newSel; - auto iconSelXFinal = newSel * st::rbEmoji.width; + auto iconSelXFinal = newSel * st::emojiCategory.width; if (animations == ValidateIconAnimations::Full) { _iconSelX.start(iconSelXFinal); } else { _iconSelX = anim::ivalue(iconSelXFinal, iconSelXFinal); } - auto iconsXFinal = snap((2 * newSel - 7) * int(st::rbEmoji.width) / 2, 0, _iconsMax); + auto iconsXFinal = snap((2 * newSel - 7) * int(st::emojiCategory.width) / 2, 0, _iconsMax); if (animations == ValidateIconAnimations::None) { _iconsX = anim::ivalue(iconsXFinal, iconsXFinal); _a_icons.stop(); diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index a90ae4d1e..3beab8728 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -30,6 +30,10 @@ class ItemBase; class Result; } // namespace InlineBots +namespace Ui { +class IconButton; +} // namesapce Ui + namespace internal { constexpr int InlineItemsMaxPerRow = 5; @@ -545,7 +549,6 @@ private slots: void onWndActiveChanged(); - void onTabChange(); void onScrollEmoji(); void onScrollStickers(); void onSwitch(); @@ -577,6 +580,8 @@ private: bool preventAutoHide() const; void installSetDone(const MTPmessages_StickerSetInstallResult &result); bool installSetFail(uint64 setId, const RPCError &error); + void setActiveTab(DBIEmojiTab tab); + void setCurrentTabIcon(DBIEmojiTab tab); void paintStickerSettingsIcon(Painter &p) const; void paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const; @@ -596,8 +601,8 @@ private: void updateSelected(); void updateIcons(); - void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab); - void updatePanelsPositions(const QVector &panels, int32 st); + void prepareTab(int &left, int top, int _width, Ui::IconButton *tab, DBIEmojiTab value); + void updatePanelsPositions(const QVector &panels, int st); void showAll(); void hideAll(); @@ -605,8 +610,6 @@ private: int32 _maxHeight, _contentMaxHeight, _contentHeight, _contentHeightEmoji, _contentHeightStickers; bool _horizontal = false; - bool _noTabUpdate = false; - int32 _width, _height, _bottom; bool _hiding = false; QPixmap _cache; @@ -618,7 +621,15 @@ private: Ui::RectShadow _shadow; - FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols; + ChildWidget _recent; + ChildWidget _people; + ChildWidget _nature; + ChildWidget _food; + ChildWidget _activity; + ChildWidget _travel; + ChildWidget _objects; + ChildWidget _symbols; + QList _icons; QVector _iconHovers; int _iconOver = -1; diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index c1a9168a1..4eea840ee 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -21,19 +21,20 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org using "basic.style"; using "boxes/boxes.style"; +using "ui/widgets/widgets.style"; -featuredStickersHeader: 45px; -featuredStickersSkip: 15px; +stickersTrendingHeader: 45px; +stickersTrendingSkip: 15px; -featuredStickersHeaderFont: semiboldFont; -featuredStickersHeaderFg: windowTextFg; -featuredStickersHeaderTop: 0px; -featuredStickersSubheaderFont: normalFont; -featuredStickersSubheaderFg: #777; -featuredStickersSubheaderTop: 20px; +stickersTrendingHeaderFont: semiboldFont; +stickersTrendingHeaderFg: windowTextFg; +stickersTrendingHeaderTop: 0px; +stickersTrendingSubheaderFont: normalFont; +stickersTrendingSubheaderFg: #777; +stickersTrendingSubheaderTop: 20px; -featuredStickersAddTop: 3px; -featuredStickersAdd: RoundButton(defaultActiveButton) { +stickersTrendingAddTop: 3px; +stickersTrendingAdd: RoundButton(defaultActiveButton) { width: -17px; height: 26px; textTop: 4px; @@ -56,3 +57,108 @@ stickersFeaturedUnreadSize: 5px; stickersFeaturedUnreadSkip: 5px; stickersFeaturedUnreadTop: 7px; stickersFeaturedInstalled: icon {{ "mediaview_save_check", #40ace3 }}; + +stickersMaxHeight: 440px; +stickersPadding: margins(19px, 17px, 19px, 17px); +stickersSize: size(64px, 64px); +stickersScroll: flatScroll(boxScroll) { + round: 2px; + deltax: 7px; + deltat: 23px; + deltab: 9px; +} +stickersReorderPadding: margins(0px, 12px, 0px, 12px); +stickersReorderFg: #777; +stickersRowDisabledOpacity: 0.4; +stickersRowDuration: 200; + +stickersSettings: icon {{ "emoji_settings", #b3b3b3 }}; +stickersTrending: icon {{ "emoji_trending", #b3b3b3 }}; +stickersTrendingActive: icon {{ "emoji_trending", #58b2ed }}; + +stickersSettingsUnreadSize: 17px; +stickersSettingsUnreadPosition: point(4px, 5px); + +emojiScroll: flatScroll(solidScroll) { + deltat: 48px; +} +emojiRecent: icon {{ "emoji_recent", #b3b3b3 }}; +emojiRecentActive: icon {{ "emoji_recent", #58b2ed }}; +emojiPeople: icon {{ "emoji_people", #b3b3b3 }}; +emojiPeopleActive: icon {{ "emoji_people", #58b2ed }}; +emojiNature: icon {{ "emoji_nature", #b3b3b3 }}; +emojiNatureActive: icon {{ "emoji_nature", #58b2ed }}; +emojiFood: icon {{ "emoji_food", #b3b3b3 }}; +emojiFoodActive: icon {{ "emoji_food", #58b2ed }}; +emojiActivity: icon {{ "emoji_activity", #b3b3b3 }}; +emojiActivityActive: icon {{ "emoji_activity", #58b2ed }}; +emojiTravel: icon {{ "emoji_travel", #b3b3b3 }}; +emojiTravelActive: icon {{ "emoji_travel", #58b2ed }}; +emojiObjects: icon {{ "emoji_objects", #b3b3b3 }}; +emojiObjectsActive: icon {{ "emoji_objects", #58b2ed }}; +emojiSymbols: icon {{ "emoji_symbols", #b3b3b3 }}; +emojiSymbolsActive: icon {{ "emoji_symbols", #58b2ed }}; +emojiSavedGifs: icon {{ "emoji_gif", #b3b3b3 }}; +emojiSavedGifsActive: icon {{ "emoji_gif", #58b2ed }}; + +emojiPanCategories: #f7f7f7; + +emojiCategory: IconButton { + width: 42px; + height: 46px; + + opacity: 1.; + overOpacity: 1.; + + iconPosition: point(11px, 12px); + downIconPosition: point(11px, 12px); + + duration: 0; +} +emojiCategoryRecent: IconButton(emojiCategory) { icon: emojiRecent; } +emojiCategoryPeople: IconButton(emojiCategory) { icon: emojiPeople; } +emojiCategoryNature: IconButton(emojiCategory) { icon: emojiNature; } +emojiCategoryFood: IconButton(emojiCategory) { icon: emojiFood; } +emojiCategoryActivity: IconButton(emojiCategory) { icon: emojiActivity; } +emojiCategoryTravel: IconButton(emojiCategory) { icon: emojiTravel; } +emojiCategoryObjects: IconButton(emojiCategory) { icon: emojiObjects; } +emojiCategorySymbols: IconButton(emojiCategory) { icon: emojiSymbols; } + +emojiPanPadding: 12px; +emojiPanSize: size(45px, 41px); +emojiPanWidth: 345px; +emojiPanMaxHeight: 366px; +emojiPanDuration: 200; +emojiPanHover: #f0f4f7; + +emojiPanHeader: 42px; +emojiPanHeaderFont: semiboldFont; +emojiPanHeaderColor: #999; +emojiPanHeaderLeft: 22px; +emojiPanHeaderTop: 12px; +emojiPanHeaderBg: #fffffff2; + +emojiColorsPadding: 5px; +emojiColorsSep: 1px; +emojiColorsSepColor: #d5d5d5; + +emojiSwitchSkip: 27px; +emojiSwitchImgSkip: 21px; +emojiSwitchStickers: sprite(318px, 328px, 8px, 12px); +emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px); +emojiSwitchColor: #42a8db; + +stickerPanSize: size(64px, 64px); +stickerPanPadding: 11px; +stickerPanDelete: sprite(128px, 132px, 12px, 12px); +stickerPanDeleteOpacity: 0.5; +stickerIconPadding: 5px; +stickerIconOpacity: 0.7; +stickerIconSel: 2px; +stickerIconSelColor: #58b2ed; +stickerIconLeft: sprite(342px, 72px, 40px, 1px); +stickerIconRight: sprite(342px, 73px, 40px, 1px); +stickerIconMove: 400; +stickerPreviewDuration: 150; +stickerPreviewBg: #FFFFFFB0; +stickerPreviewMin: 0.1; diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.h b/Telegram/SourceFiles/ui/buttons/history_down_button.h index 0ab821ee5..03e2d7976 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.h +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/button.h" +#include "styles/style_widgets.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.h b/Telegram/SourceFiles/ui/buttons/icon_button.h index 488a49df1..104a5825a 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.h +++ b/Telegram/SourceFiles/ui/buttons/icon_button.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/button.h" +#include "styles/style_widgets.h" namespace Ui { diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 5eda18b3c..959c7cc75 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -27,10 +27,18 @@ LabelSimple { textFg: color; } -defaultLabelSimple: LabelSimple { - font: normalFont; - maxWidth: 0px; - textFg: windowTextFg; +IconButton { + width: pixels; + height: pixels; + + opacity: double; + overOpacity: double; + + icon: icon; + iconPosition: point; + downIconPosition: point; + + duration: int; } MediaSlider { @@ -153,6 +161,12 @@ DropdownMenu { menu: Menu; } +defaultLabelSimple: LabelSimple { + font: normalFont; + maxWidth: 0px; + textFg: windowTextFg; +} + widgetSlideDuration: 200; widgetFadeDuration: 200; diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 703adf37a..6aa796332 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "window/top_bar_widget.h" +#include "styles/style_history.h" #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" #include "mainwidget.h" diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 8722c4d3b..a7da80f3b 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -20,6 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "basic.style"; +using "ui/widgets/widgets.style"; titleIconPosition: point(9px, 9px); titleIcon: icon { diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 012edecf9..9ffc4d0f8 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -39,6 +39,7 @@ '<(src_loc)/boxes/boxes.style', '<(src_loc)/dialogs/dialogs.style', '<(src_loc)/history/history.style', + '<(src_loc)/intro/intro.style', '<(src_loc)/media/view/mediaview.style', '<(src_loc)/media/player/media_player.style', '<(src_loc)/overview/overview.style', From f7374aa7b7a75d2ed3b2b9222b4d6990967e3dab Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 28 Oct 2016 00:19:38 +0300 Subject: [PATCH 003/100] Almost all icons moved from sprite. It is a broken build, some of sprite()s are in stickers.style file. --- Telegram/Resources/art/sprite.png | Bin 22300 -> 5286 bytes Telegram/Resources/art/sprite_200x.png | Bin 48811 -> 10769 bytes Telegram/Resources/basic.style | 113 ++-------- .../{box_search_cancel.png => box_cancel.png} | Bin ...search_cancel@2x.png => box_cancel@2x.png} | Bin .../Resources/icons/dialogs_add_contact.png | Bin 0 -> 124 bytes .../icons/dialogs_add_contact@2x.png | Bin 0 -> 163 bytes .../Resources/icons/dialogs_cancel_search.png | Bin 0 -> 197 bytes .../icons/dialogs_cancel_search@2x.png | Bin 0 -> 273 bytes .../Resources/icons/history_action_edit.png | Bin 0 -> 445 bytes .../icons/history_action_edit@2x.png | Bin 0 -> 1011 bytes .../icons/history_action_forward.png | Bin 0 -> 311 bytes .../icons/history_action_forward@2x.png | Bin 0 -> 600 bytes .../Resources/icons/history_action_reply.png | Bin 0 -> 446 bytes .../icons/history_action_reply@2x.png | Bin 0 -> 864 bytes Telegram/Resources/icons/media_video_play.png | Bin 0 -> 419 bytes .../Resources/icons/media_video_play@2x.png | Bin 0 -> 720 bytes .../Resources/icons/media_video_play_bg.png | Bin 0 -> 899 bytes .../icons/media_video_play_bg@2x.png | Bin 0 -> 2591 bytes .../Resources/icons/media_youtube_play.png | Bin 0 -> 286 bytes .../Resources/icons/media_youtube_play@2x.png | Bin 0 -> 530 bytes .../Resources/icons/media_youtube_play_bg.png | Bin 0 -> 812 bytes .../icons/media_youtube_play_bg@2x.png | Bin 0 -> 1731 bytes Telegram/Resources/icons/new_chat_photo.png | Bin 0 -> 642 bytes .../Resources/icons/new_chat_photo@2x.png | Bin 0 -> 1268 bytes .../icons/send_control_silent_off.png | Bin 0 -> 518 bytes .../icons/send_control_silent_off@2x.png | Bin 0 -> 1029 bytes .../icons/send_control_silent_on.png | Bin 0 -> 685 bytes .../icons/send_control_silent_on@2x.png | Bin 0 -> 1487 bytes Telegram/Resources/icons/settings_close.png | Bin 187 -> 0 bytes .../Resources/icons/settings_close@2x.png | Bin 296 -> 0 bytes Telegram/Resources/icons/simple_close.png | Bin 0 -> 178 bytes Telegram/Resources/icons/simple_close@2x.png | Bin 0 -> 354 bytes Telegram/SourceFiles/boxes/addcontactbox.cpp | 2 +- Telegram/SourceFiles/boxes/boxes.style | 32 ++- .../SourceFiles/boxes/notifications_box.cpp | 3 +- Telegram/SourceFiles/boxes/sessionsbox.cpp | 16 +- Telegram/SourceFiles/boxes/sessionsbox.h | 6 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 1 + Telegram/SourceFiles/dialogs/dialogs.style | 26 ++- Telegram/SourceFiles/dialogswidget.cpp | 51 ++--- Telegram/SourceFiles/dialogswidget.h | 9 +- .../history/field_autocomplete.cpp | 4 +- Telegram/SourceFiles/history/history.style | 39 +++- .../history/history_media_types.cpp | 4 +- Telegram/SourceFiles/historywidget.cpp | 212 +++++++++--------- Telegram/SourceFiles/historywidget.h | 28 ++- Telegram/SourceFiles/intro/intro.style | 7 +- Telegram/SourceFiles/intro/introsignup.cpp | 2 +- Telegram/SourceFiles/overviewwidget.cpp | 20 +- Telegram/SourceFiles/overviewwidget.h | 3 +- Telegram/SourceFiles/settings/settings.style | 6 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 6 +- Telegram/SourceFiles/stickers/emoji_pan.h | 2 +- Telegram/SourceFiles/stickers/stickers.style | 9 + .../SourceFiles/ui/buttons/icon_button.cpp | 6 +- Telegram/SourceFiles/ui/widgets/widgets.style | 6 + .../window/notifications_manager_default.h | 2 +- Telegram/SourceFiles/window/window.style | 15 +- 59 files changed, 316 insertions(+), 314 deletions(-) rename Telegram/Resources/icons/{box_search_cancel.png => box_cancel.png} (100%) rename Telegram/Resources/icons/{box_search_cancel@2x.png => box_cancel@2x.png} (100%) create mode 100644 Telegram/Resources/icons/dialogs_add_contact.png create mode 100644 Telegram/Resources/icons/dialogs_add_contact@2x.png create mode 100644 Telegram/Resources/icons/dialogs_cancel_search.png create mode 100644 Telegram/Resources/icons/dialogs_cancel_search@2x.png create mode 100644 Telegram/Resources/icons/history_action_edit.png create mode 100644 Telegram/Resources/icons/history_action_edit@2x.png create mode 100644 Telegram/Resources/icons/history_action_forward.png create mode 100644 Telegram/Resources/icons/history_action_forward@2x.png create mode 100644 Telegram/Resources/icons/history_action_reply.png create mode 100644 Telegram/Resources/icons/history_action_reply@2x.png create mode 100644 Telegram/Resources/icons/media_video_play.png create mode 100644 Telegram/Resources/icons/media_video_play@2x.png create mode 100644 Telegram/Resources/icons/media_video_play_bg.png create mode 100644 Telegram/Resources/icons/media_video_play_bg@2x.png create mode 100644 Telegram/Resources/icons/media_youtube_play.png create mode 100644 Telegram/Resources/icons/media_youtube_play@2x.png create mode 100644 Telegram/Resources/icons/media_youtube_play_bg.png create mode 100644 Telegram/Resources/icons/media_youtube_play_bg@2x.png create mode 100644 Telegram/Resources/icons/new_chat_photo.png create mode 100644 Telegram/Resources/icons/new_chat_photo@2x.png create mode 100644 Telegram/Resources/icons/send_control_silent_off.png create mode 100644 Telegram/Resources/icons/send_control_silent_off@2x.png create mode 100644 Telegram/Resources/icons/send_control_silent_on.png create mode 100644 Telegram/Resources/icons/send_control_silent_on@2x.png delete mode 100644 Telegram/Resources/icons/settings_close.png delete mode 100644 Telegram/Resources/icons/settings_close@2x.png create mode 100644 Telegram/Resources/icons/simple_close.png create mode 100644 Telegram/Resources/icons/simple_close@2x.png diff --git a/Telegram/Resources/art/sprite.png b/Telegram/Resources/art/sprite.png index e8c0edafc83ff70bc7fb89b08fae1f6d319de58f..d76672e0b11f672fbcdc43c98d539ad48c70b9e1 100644 GIT binary patch literal 5286 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 literal 48811 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 VO!Xy8_;F%J5sK;xdGZ$i{|kilPSgMZ diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index a3cda0de0..cc07382ad 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -1027,54 +1027,6 @@ btnUnblock: flatButton(btnSend) { downColor: #db6352; } -silentToggle: flatCheckbox { - textColor: black; - bgColor: white; - disColor: black; - - width: 33px; - height: 46px; - duration: 200; - bgFunc: transition(easeOutCirc); - cursor: cursor(pointer); - - font: normalFont; - - imageRect: sprite(354px, 242px, 21px, 21px); - chkImageRect: sprite(354px, 221px, 21px, 21px); - overImageRect: sprite(375px, 242px, 21px, 21px); - chkOverImageRect: sprite(375px, 221px, 21px, 21px); - disImageRect: sprite(354px, 242px, 21px, 21px); - chkDisImageRect: sprite(354px, 221px, 21px, 21px); - - imagePos: point(6px, 12px); -} - -replySkip: 51px; -replyColor: #377aae; -replyHeight: 49px; -replyTop: 8px; -replyBottom: 6px; -replyIconPos: point(13px, 13px); -replyIcon: sprite(343px, 197px, 24px, 24px); -editIcon: sprite(371px, 286px, 24px, 24px); -replyCancel: iconedButton(btnDefIconed) { - icon: sprite(165px, 24px, 14px, 14px); - iconPos: point(17px, 17px); - downIcon: sprite(165px, 24px, 14px, 14px); - downIconPos: point(17px, 18px); - bgColor: white; - overBgColor: white; - width: 49px; - height: 49px; -} -inlineBotCancel: iconedButton(replyCancel) { - height: 46px; - iconPos: point(-1px, 16px); // < 0 means draw in the center of the button - downIconPos: point(-1px, 17px); -} -forwardIcon: sprite(368px, 197px, 24px, 24px); - historyScroll: flatScroll(scrollDef) { barColor: #89a0b47a; bgColor: #89a0b44c; @@ -1179,31 +1131,7 @@ contactsScroll: flatScroll(boxScroll) { deltab: 0px; } -btnAddContact: iconedButton(btnDefIconed) { - icon: sprite(188px, 93px, 18px, 18px); - iconPos: point(8px, 8px); - downIcon: sprite(188px, 93px, 18px, 18px); - downIconPos: point(8px, 9px); - - bgColor: transparent; - overBgColor: transparent; - width: 36px; - height: 36px; -} -btnCancelSearch: iconedButton(btnAddContact) { - icon: sprite(188px, 43px, 18px, 18px); - downIcon: sprite(188px, 43px, 18px, 18px); -} - -simpleClose: iconedButton(btnDefIconed) { - icon: sprite(167px, 130px, 10px, 10px); - iconPos: point(10px, 10px); - downIcon: sprite(167px, 130px, 10px, 10px); - downIconPos: point(10px, 11px); - - width: 30px; - height: 30px; -} +simpleCloseIcon: icon {{ "simple_close", #000000 }}; boxPhotoPadding: margins(28px, 28px, 28px, 18px); boxPhotoCompressedPadding: margins(0px, 2px, 0px, 22px); @@ -1243,7 +1171,7 @@ newGroupLinkFont: font(16px); newGroupPhotoSize: 76px; newGroupPhotoBg: #4eb5f0; newGroupPhotoBgOver: #3fa9e7; -newGroupPhotoIcon: sprite(74px, 104px, 30px, 27px); +newGroupPhotoIcon: icon {{ "new_chat_photo", #ffffff }}; newGroupPhotoIconPosition: point(23px, 25px); newGroupNamePosition: point(27px, 20px); @@ -1449,8 +1377,14 @@ usernameTextStyle: textStyle(defaultTextStyle) { } usernameDefaultFg: #777; -youtubeIcon: sprite(116px, 338px, 72px, 50px); -videoIcon: sprite(0px, 340px, 60px, 60px); +youtubeIcon: icon { + { "media_youtube_play_bg", #e83131c8 }, + { "media_youtube_play", #ffffff, point(24px, 12px) }, +}; +videoIcon: icon { + { "media_video_play_bg", #0000007f }, + { "media_video_play", #ffffff, point(12px, 12px) }, +}; locationSize: size(320px, 240px); boxOptionListPadding: margins(2px, 20px, 2px, 2px); @@ -1501,30 +1435,6 @@ mentionFgOver: #707070; mentionFgActive: #0080c0; mentionFgOverActive: #0077b3; -sessionsScroll: flatScroll(boxScroll) { - round: 2px; - deltax: 5px; - width: 14px; -} -sessionsHeight: 440px; -sessionHeight: 70px; -sessionCurrentPadding: margins(0px, 7px, 0px, 4px); -sessionCurrentHeight: 118px; -sessionPadding: margins(21px, 10px, 21px, 0px); -sessionNameFont: msgNameFont; -sessionActiveFont: msgDateFont; -sessionActiveColor: #aaa; -sessionInfoFont: msgFont; -sessionInfoColor: #888888; -sessionTerminateTop: 30px; -sessionTerminateSkip: 18px; -sessionTerminate: iconedButton(simpleClose) { - iconPos: point(3px, 3px); - downIconPos: point(3px, 4px); - width: 16px; - height: 16px; -} - webPageLeft: 10px; webPageBar: 2px; webPageTitleFont: semiboldFont; @@ -1575,3 +1485,6 @@ infoButton: PeerAvatarButton { // forward declaration for single "title_previous" usage. profileTopBarBackIconFg: #0290d7; profileTopBarBackIcon: icon {{ "title_previous", profileTopBarBackIconFg }}; + +boxCancelIcon: icon {{ "box_cancel", #000000 }}; +historyReplyCancelIcon: icon {{ "box_cancel", #adadad }}; diff --git a/Telegram/Resources/icons/box_search_cancel.png b/Telegram/Resources/icons/box_cancel.png similarity index 100% rename from Telegram/Resources/icons/box_search_cancel.png rename to Telegram/Resources/icons/box_cancel.png diff --git a/Telegram/Resources/icons/box_search_cancel@2x.png b/Telegram/Resources/icons/box_cancel@2x.png similarity index 100% rename from Telegram/Resources/icons/box_search_cancel@2x.png rename to Telegram/Resources/icons/box_cancel@2x.png diff --git a/Telegram/Resources/icons/dialogs_add_contact.png b/Telegram/Resources/icons/dialogs_add_contact.png new file mode 100644 index 0000000000000000000000000000000000000000..0569c8732303693ade3a5a4c946cf16abde251bb GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|XHOT$5R2Zo6Ak$o6nL2U|Nl>) z6XV@}%%Z}0=Om>AJR5=zn3e4lb&cKkVv$FK_2+3qnL7eSEpk<-F8V(2uj@>U`sut6 X77K{~dMWuGXdHv5tDnm{r-UW|cb6&o literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs_add_contact@2x.png b/Telegram/Resources/icons/dialogs_add_contact@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6fca9095dd81bd93cd18068d16a86c467c18e9e1 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBvOHZJLn2z=UUTGXFyLW0=aP;7yEdz}J|9G`+Lg+ZCHT9FHg@2^e-rxELL? zHTi$*w)3&67xJ9hCjRE#`}F9J*>LGwth)cd#b<9{$jxlV>zvM7`wVC+gQu&X%Q~lo FCIEcWKMMc= literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs_cancel_search.png b/Telegram/Resources/icons/dialogs_cancel_search.png new file mode 100644 index 0000000000000000000000000000000000000000..211db7e3539f55466275cbc578ca63f407e8f43a GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rhe9#0p?5R2ZmlfC&E6giwv-}xVB zJ8iRp)E^O(jh)g5kNfjD9AvK5SK#h=^6~K8e8vEeY0Ju%`w5?{OE0ip)uUy6J8e-- z+oe4R?JYRBm_#4kl+%7Gq(oHXj7E=^g5rxXp(&=nIZVvWW?+0g^CIw8IKzA{Cy85}Sb4q9e0K|(-8~^|S literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs_cancel_search@2x.png b/Telegram/Resources/icons/dialogs_cancel_search@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f20842fbc65d666aa29f8521a84b8682fe414455 GIT binary patch literal 273 zcmV+s0q*{ZP)N!ew+v`b6DTA5T!kt&rq4un7!5gF15ue$|o@d!G@R1g3GKwmJz zJGKMBSOH-7KRbfF^GQrmQQpuQGP6?n0En4|?L)iCdMd$1BVuvT39EDY#6|CNT~7!# z?>vx_fs--B%)DegU6>^1x}d12!tI`#bSSD)YTC6QBL5z;R)@M(+PAtGTa}Jh#kE!Y z(W*gf)xt8xWH>{mLsiAyBc@;B?xLz;`@C literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/history_action_edit.png b/Telegram/Resources/icons/history_action_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..27b1e77a918e59bc1a2055bcd57a9799c5372bee GIT binary patch literal 445 zcmV;u0Yd(XP)nH)125g_VgtrHL_tsOJ^FnFq?x zpy4LB050ddoI3+307Q);b&7ul5P9C}I1Wyy6Fkp*yQYJmrsFt_qKG;?&m#beNV2Y) zsJ?kL8l}WvAs+*>Ixm6bcbZInemou*ij!m*hQ$C8VWZJVrA_A~8OJdvlZib*cW)ye zMbWExF+hvk*b>JvUDvf+MM>X3w51&Ir7iD(r0=%2+-|o#Te1PVXgZx*iR;b_#F=_& zJRT1KK&R6|v)M#m-0yeHW;1Ly8;A&$Qn|IM84QO*_WOMT;BvVv?c6f|57z57&*w9H zy&eH@u~@tf)WY+8z;3rA`P}N=ZkGV)`+lb9l>zN`o0rSQbQ^}Dc@p3EIiJrnH=#Aa zb=}N0u4j@IuLAhdlO7BPX6o1L6^FwC`~4oRR?B>$%2rEO7=Tg=tJMnI?G`~0;CMWm nA`vl5xN0c;`BYcb{#Wq}%RU(QrQn)>00000NkvXXu0mjf$xz9D literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/history_action_edit@2x.png b/Telegram/Resources/icons/history_action_edit@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..13571d1b258174f243ca849684d1c46ae530c92d GIT binary patch literal 1011 zcmVQr0`eRSP7C82A4$(|AOrzf`3J96dOSsOR==Ew6n3%6=Nl8vn+~XC509m zH{RF03Hi=UHoJMkyAWm#!*I?$=ib~z4FKU62WuL@xbdGr)6ob4##n5%EDM*Hm+1HV z=ytoA`^4|8plM!=F=C97VHgB}Ow**7mlqNd1OausT>?P5u7`UWW5hYnI&-=N0EFNd z78YnQ7*H}sqY`dX zqA>!pna?62?9{jdrfJgi^RqN~5z(5EA|R?=_*SgqzVKu6itb6Swry7eFF_(Lb+mN} z?{Utsnh9{6R2XFByr(+*L7BZK8Bd=(-D4v z5E1R}?$XT6Ol*!X>hoovF2^P?KR-`jUtdz-ecz|Y$4BaPI!azof)`&xDuAaJ5@A^u z48us?1_1cJkB5f`IF5s}voko3gQuq__`VNK(+~s!IOhn00IgOFqtPgL75g+z!_UtT z0EG8cB1K?zbv5?)-Q69oudi`+b%pcubM*Ute13k$_UO7^2|VXK1kV`5_xHCFcmUAw z*GD2wPfzjw{*H@_3pkF0UayzR3S$hKrh$kGLHoWB!!S}ACn9K?1^}@$IOobFQUrcm h{QpmV)A8#G`~d}~!uw$HEdT%j002ovPDHLkV1fOR@F@cD5eDRwe!NrIc3Osc9NBdmyF!QoOEfbBiW~co$Ymp(qNo zn|uNGeMg$6=5LV@0_(a$N~!C424IXKiX!d&_bsxezxQQ6)d%EX-|dGOmfQdU002ov JPDHLkV1j1uhDQJZ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/history_action_forward@2x.png b/Telegram/Resources/icons/history_action_forward@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f3eae397ea13b7130bdd80ea83d58e06e96e1b9e GIT binary patch literal 600 zcmV-e0;m0nP)6vfYmR|Kn|g@u^f#`OLn*a_)GLB-N4U}q(^`3Pd=2hh?|Foh7(307KISSTVE zS_Bk@aJSmWzV)GNj2QzSxXqB9oFB~0oyi9Pf{(?q4)Cbn0Ic-}V68X6ZzM?)0)fC& zygdO(rBWCUhX{p2u6uhng%XJb48wpZimv*4x&~sg7);ZIBuTD$dcKGwkqAbk5oB3* z#gEA@BnScqg8>SK!oyz7wo#tv(d+e~s_H{NO#1-GacH;O&@|1thl_a}tJ!R#)9HZY z?vL~5=Rl*;a3$1tWipwY&s3{bgu`JJi$xp`hr9ggd$E>jniLEM>1VDh^;!T#M6=n9 zL{aoF01?q@wW4@D?q2{RqWykPvMl=-fQaaLJW@WN_b&huk!{N~Mw` z?~D6@*Rm`Nxm*sqt~+|4DZqBSg`y}JkH?NZm;o#n3uLod%;)ome3%?1CX)%0$>hV~ z0R{lNu0xh(Y&IKL{5%EdcDqm%1(s#G=II$gtJOlKQo;FrcHR5wec%@dGRJjsq4Ar1 maiO`UHvntB0a)t|z*?VlC%C|0+Cjqr0000&wsAZjpSy#4y-wS<8CKivmc3q&nx;K2QM=vd`FswY{8Fy#ayT3^ zl}ZHzTCLVgiGxm*&1U_8cs$P8Z1xeze!mYo2`I}l2ZO;!AoKbBcAIv)-J5}qRZgeV#{dA-G>xjN oKK};EG668=QKHh{f0^&<8#_n(9Dd1JU;qFB07*qoM6N<$g2rjhMgRZ+ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/history_action_reply@2x.png b/Telegram/Resources/icons/history_action_reply@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..84fbad64700ed14843b8ed1df88433795deb56f0 GIT binary patch literal 864 zcmV-m1E2hfP)4hYb0VRlL<=I8cBz662E<8(F5=)KLPg^26c88xfg=_dU8E3DT%3x7gWysq zh|qQt1VM!iDToLbiV*yPqTJu`(T{5BXObq(InWE9B`wc;Uk!bqFFsVk%-rM9OGDEetsU=Y!kNm(?gf&`Bwt=$S{}ZZN~OYNGU;4E(=>j3d;}W4mbJAt`vL<4 z1FTdkL1(Tbole`o10y3Nyt%mvI&&Qx8yoij1JP)dS65d-XRbq4Rr|Y;EX&O2^Fe3+ zgk@Qb$K&?rfFwygIywqE_b0O1tbG9h6a;~LdwW6W{>0qeoPB|6rBW%!3q3wQvQ#S3 zG))?YLDMu@E|;4YD3wZ-B*~co04^^t+nN!ptE-I`AOMP@$ap-?+1XjHudlOMEIOV& znx;7y06HGD41WHRmr0B~q%h^A@!CQz;Z{(f$6 zZ`X-DJw36rv(vpmwfXt^xvv7%ibNvRb)E0;@Bgj!WlMF}xT~v+xm?b7fods=!jqE| zlH|_LPV)i)*wN9!OeWJ3fod%*EHIbLc_RS)JGIm4bjt()u)Dk4JArD+vfMI(+G=>L q^0)A3ofEpCEf6$qfuLy%1kFGCtxuAmha+?V0000+0I5@2*$F9AcmPQu^1tFS z51tQRHZkjN;Rn+!3Hy=E&dd&o2&55)YJq1yLQMz@O{Y_uOeQj2h^U3oVzHp>^-88ygBuQY~wjM%ERaJ$>WH zS(au0HRbmSozG{iRx9<#o==ozS!xR-*PPL4B(#TAA;GFFU#Ml@k|)%$v&#{BVfN`) zsE6sWAE6%S=k7vL6v?<@{D}cuHNg$=a=C~+-Wg$`ccFazc;+M2#20-u$Ri7-+JyiB N002ovPDHLkV1k%Jw|@Wt literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_video_play@2x.png b/Telegram/Resources/icons/media_video_play@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2fffaea0a9a35071f7d6ef5fb3040e38576a41a5 GIT binary patch literal 720 zcmV;>0x$iEP)f((!#10=`*2{J%}43Hqt zff!>Di^YI3J?wpX6^`S;U@*`I3B9}+W5_$7&)90UkTK@z*9iFX4~VzfY%rNjstV;l z5N|e{VI&e!4JtSwUbov-2`P9WL`27NuwJjL03`>AXIT~|5(&ysa)Ef0$%HBr4Io6s z%jJTdPKO#49U$KEc*IhvLIxd=hL=|K0v(bbc&2I zub}h;;(4^KqAw8Q{oj^at@aL--atIdvM?TxzXBBwAl`U9eikWQK!{IotAr1TXWO=L zTjhf*c@<*}T-WvTQ~KU&wSs&;4*&qS+wI#uJ&r!#ESt@O>$=eE^&ajSpf8on<%f?M zAOJX>PEf5@A)QYDct5m@G@|&g?T9}=6Z*~PbEs4*0Pwgle=T%C+wB$#g#sK7hyTLx zLbC>4uUBX^8jwn*V87r03Wult8Z;aZUw%DW|B5Y_ODGnLPrX}yO1pQEZQGd3<;;fJ z-}!X5uLsZz+Zf>h3ShoSO(4f{-rY*o7bxHvM|FTaKF-kxNb;ODT_B&2l+^|bU?@RN zpa92O)B=()7^Mb~#L+tXK$3?u=>icM@1+MsY(Sh25Ya~rXaEs@NJTD?0*^V#15)Hc zxnEtLPj~26XwJo6%>W59K!OaAAOj@G00}Zcg8l$RAbZ^#X72z10000AX{7{|XoGB|Vy!6`#L?bSr7v5eF5=B_+ESgjt-K^LAnP5LOUwB#KFl)G=oFH z!2O5*64LgcwzPVe#t%G04imoLd%VkeK6eLzq6g;lIaF1Jq9~AM8ImMnI-NokMcm)t z0|1()L0Oh4iUN6_W4qlVO;f~ij4aFgkcB&RXjs>En5K!vVgbW2Fquq#^gC5mA&Mew zHXE$hYrMR?bR`kMtKlDqAPCg5Eb4h4t?Qb0OYdhk|fl&ZAu8~ z$M9@6qmJY7_W1$Fap-I|>(;QQY1H?9-rg?a`##k)tsBF-uG26Kd3*YjFbt`#>%TLs zY1*GKdn63Q?_~P&n7!uP_!7SFe>JB6e>T3x3F^Bzj`P{<{jt%uZQh!HjcwcCGi(^f zRs7HXmLy3oEl%DxxUS1vYiGEw`?+DuvbZc4`=V{z4{L9Ne+Ytr<#LJ9XoOo~G#X*K zTtX0pH|?kF-YVx=K4tjv@sVx!GvvKDs_S|$5AWAPUDu~&CjkJ{G%+5Jd2~J##^W(e z(*ytz09Y&*JoPZ)=<#kopVQ~(=O1sl{S~UJ!o$M@h^neLZx#S1lL=H+1yK}*w-#n7 ziUK0bGH)HskYyP}k|f?bm?23L$aFg8t%DiU=@dj1Mcz7?A&MgI+P0;)^uX{Lhr&tEn(liZk4a{ho2Ba)Y-a433mL*706ufmXqbLfHJkNRSU`C$jAlvPhw+?1(w_A`j zO?m5JMw+G|aU7$nD&AUpr>ZK%aSW1W8KNlSt*3XQC_|QPX^jsa8OZ9 zs>E6$p|+C25Jn|@P$7+FGM3n4tKQe#`^Ww3zMu2H=Y5{f**?#C&i8i+uFm!{QmRrQ z5J<++0pSkR2}iG_IPf(MhW!ZwotSq-*xX1cTAprqGK&OBtO*w~@K3d5)A1q-IaY2X z!|q^P6NKI7v2*$OIVsKbGyB#>nlH=BY*J*kE~|#;M1*Bz{_#})d&y^#gpT2oRo83x z8`c>yRzA#=;KpxetX4nL8Rn~UpcI{jC^#nyj&7J39ve#=8gh<}jqSMe&XxqpD9>nW zf;BcZiGWH|!9y{bDf|2T z=LRn0@pvN(3u*fF5QH%{c1l)GEh5)dMEI$bi&c6?$Y z;OxTju0736&BEu;wP=)FoR!P39#*C2f!~%EQ^3CwozzzQb5vH3?F|p@GS`f|CLhym z&y`nQtn-4lH=v&|zDw!U;Hx1|ON$d2++9ntfqLY@ee1r^hHA$9i24brVE+puk5}qj z3vmp-cdzh6;3XD!5D8V*8WX7CD4(r zU%6K^<{1vJ+!3RVy}IN4t##}VPrrZlV;kvhdqY*#!A}zipjYp0?A`cS`ZO-BN;a_0 zi;Q9aGSkPy3VtXvyDVLixBR((1x=mYg@_-gQEr-ZJr284G}e~v@i;5g)fhI<5SWR- zu*3JD?LuB4Gt1FoVQ;2-Omm_7`ZjLQ>hlfXq8&-K&CMArD=SrxqGMy@B(4582tuHd6mb_IhgKnDbrRfn|ed*y}v!DO<*nH8A(_tom559m`=G3L-v*HWze ziJlQIkS6BlzO4FEO7cC664X0kPy?_zGb^P1CB5;$I&^60{PI&LiU7#&z|c@}7F+*DZuWq@zH3ofRCI7(KM{umr?_q_U1e*C z+H~yf>@X&0P|;?^K!*P2t4pL12n4xYcRR5sL?UdEsS~9>5e~~H5|yVtZ>;_PyMzDn z=*=(f-PEIlqPJI)g8}0BqP_j=@}6&JJ=A2%yLp4zQ9>@fSdb z)^czQi$9?9_UyGaqiRpRhwKyy35mil78>@Y(-8~~N8`vNE?yu$x2P9;5%etzr9Nr| zPD*nY11&Buhp6H!YKE88)4OfTM|8JleCNmedDxwOp`mDccjv&DWFH?N03RA_EeoXs zp$#^RfBh9${OF3~&6}rF0hq8%;kmJKakh(#i|8Y_y}d+g*E40|h8NjWlBf2n_o})h zNdwnptCsHCF9h|N#CPo0L-#&ZfOZP z>NYqilgj5~j@*7I)OP^Hs$-&b93hJGa{@TF1@}v+y`%eHnMn)7U6v|ONTWGW{zybn1x?~K% ztPnr9Xlh}B2$0%aKKY>As6cFPF>!|H+nM7aDijKlBGH>iG*cwB`ajUy`TSXQHmbl; zmsk=7&(6)wrSk3egU^D&A1#5+{Av!pnDF3%Ixd2CPh0WC->a*qk1~bF(~gUe=c{H` z+>Vbwou8j?M@<_438o}9ySv*GQ3f|FDJ@lyO0$|eEaMaxpACE2L=7T^X{P7B#$qXNZ*L=IK8!R_sp(`gne;%kJkPV574BoKkM96lk^cBLv29?$sk)}-pVMrwsZdQOy8)-Up{dDplRR?seNv$l z2_cdU;>SZ)JT&ArNRXt-GyV6n#BUMZef|A=dV1`%zXlhbFfcHvrO`66Po(ts&x5;W zjXXU)t7Lme>gwy=1;Xu+wvWf;ZR9fms(z8(!tCO#0MJe(wcWbtQ!u_9cZAl3J^mui z`R}-&U-3L|8G+`oEg1nS)ukIXfF4D+_>DdD_3s2_OfKuxd1Y_Kd=&)NsYKJK$MSrW z0C!atq!3xJ_DEf?5UJ?lfw@3!N;xK9!*+6iqr|VK)EDt)1p!haXOz-mWxLVB}YF!uqa@G$?brwOoEeEuscLH`kz#yj(QXDT^ec5X_C zN$cjx-#*mM4BTsGlYzFC$vhqp)s}ZQtLOLib?gL+W#ZA9mIWR7uqwZqvM{}o&&?Rgb%;~o&}BMmOsy{vu{^x371ckPm?DM28JMk0|fyoY`&a)8v*=~=G2 zH`e8y^HeDuk;+lR$cS5ZcJ{Y!N{v)%1M$Y^!$Y;%C>4Ne<1;gnumr9Cxs+1W>|ZXJ|Cq@WcdxNR$O|=0Fm4QyO8h@GO=m$k3i=X=SCE+s9d1 xkzI;arBbN?dpftat|}5l0mcA@d)t%#6(nq;6z(=~rhtnd$kEOjL9s=r{2NMEFXjLM literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_youtube_play.png b/Telegram/Resources/icons/media_youtube_play.png new file mode 100644 index 0000000000000000000000000000000000000000..9cf8ff40eb25f05d1dc92847c49674138511a31d GIT binary patch literal 286 zcmV+(0pb3MP)! zF$#k)6o#L15G1pk;1%3Fi3jiyZXQ5z6CAvN$8dJ>5aIy@aS4RKLrEzul+yO6_(32; zcpu~?f8vP<{907*qoM6N<$g70*94gdfE literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_youtube_play@2x.png b/Telegram/Resources/icons/media_youtube_play@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa7f511753e2cbcd5f9eb3a8cc575c613f641ee GIT binary patch literal 530 zcmV+t0`2{YP)197A*gvp1dGkGB4$C%B3Nu@vj(vVViv?=w)qEG7KgAlEXFYe+hk}M9Nrt` z$mMe34)r=;@PUAPolXaurX?NxeF6&Gwo$26cm)7JN{QuiiF`iKDWC|$5X0dR z>2#V?K(XKN(QGz(1r(dj2IX>@R{#K_D8g(ugJBq)0*d4Dh+eM;UDr7U6t3%{Ua#{C zDAwyWN~IF70C3HZ$z<}3vLqP+h41_5cDs+1CCLgXw%aYL)he$5a4kz#s}%}`0;hl? z3`2}YBV;leP637Id1$p-yaEc#vS6AfuK;k(kNJH5TUo*(pa_Bh{eJ%zS4vs@1rY1l z%gAQ4;&Qq0j#W(46haWrg-)lF7z_r&ah%&biX4zqVzF3oCNb@A)bl*F+ikv!jJA|e z#ayvij1|*c2{?|!Sx(b7G*J{`I-PR1l(bE~>$+$(8qW&+bpk;Ua1OSpM~jwaaSnHB z$Ls3)r(1APub6DxzW4fny?~VR7C7aawN|TPx7)qlqC56f;$5L@>VNVPNE&*9FLF1? Uizp4#IsgCw07*qoM6N<$f~9xuo&W#< literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/media_youtube_play_bg.png b/Telegram/Resources/icons/media_youtube_play_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..76e71cfe0832a799ea00c9cd6434a49cc6218326 GIT binary patch literal 812 zcmV+{1JnG8P)NklFdc?;kTy`-Rr;~%!liZPx(lDjS14Kd0Iqxlap5*^5W8{NO>4W5LKike z8YCu}ySP`mtu}JuwlmoIK|sWh!yz$-%)bIap#)>#9ztqh_<2+EugJOm#XP<+|?14*qQGPqy1_6-ANm{NE;u zBGqoUw->}YSA)SocV2ABqR>205%&h9~5*n+koMN#O^U^EiPvF;2;vxiuT`Fw5*DwC!uNS0-~a~O>zNuoQ0 zv5KOwhgg|7j%`8P*wcK8IF3OUi-qnS##$^EAW}-*IgBNxw5RzpS(e#7sEm{nWICPd z&S9+SbP6&ak9FrT)_6QdUueZKV+Ho8ehN13k+J#}L9LL!_ir~8Lk1i<;!%c%N?}s-y z=c?Q7syxqi=lwQ$o~v%RyJ=2i^Ry2DRjbuhzu({KqVhaf{eEB7YPC;=j+D~1!MU#-rdyIy;^VC=DJ7QIm}#2g?d=U|nj%RO q#Bq$Lrzea?BLqQ!=jZ3r&;JFUzxyXec%VZ70000hPxFJPn$Y-@h_0Ei~bK|2M^wc7x6FHVK`7k zO4()7$&S*>piJh5E9hpLg^-3m-@|6H)6SZ-X4&`sf}r_9{6ya5dGfmpfItWI_4Ps1 zG+e)a9jdBARaNM^4qexws%pbLMNtro#Q*@hu0xjP`t)ctiby0F(4@bD0pWx=v67={7EFzVyq zzI}sfnkbjc9od#Hv>`yfy}cM48$%|O!QH!ekxHeYC`ud3qjqr|2Rl1Ec=zrda=9Ef zH#c!~bkvr03onB#%VK6`My#!^iE6bfC|s)5s#sfF6Eib2LY8Gu8Td6^J~&UFJP~_) zdqMvXs7?0v_QcbtPlci=oefYXlM#hNA?WR;4*BrmgBTqhZGV6iMG=dOi$QN6U6IAb z#g_L*7j~li`}^_g)hpb)ckfS&(LW}i&*S04hZi2b{V_oCcpSN04ugY(?XHhPpj0Yh zY-|jrQmN@OO#?JAFo3PCEes3{bgDiIf&Kk`jEsz6e}Dhn`RAT_i^XEt+}vb%2m=EH z*xcN#pXmMFxdB>UUPdC3=xAXS9*IN(%gf8>&N~yJ+1Xi4PfvHeC<>S9>1oW)&Yqdx z&kJ9lt!a8RGtum@ULSYUXlDP7mGu&~fDt-c3JBof%! z*$K@RZ3;%MRzoV4La|r`aB2@UH#f)d(1j#P!ra_keY^{Rh(sdz`t>WDFKUTl7`S=! zCT!dOxd)neg>?FRSmih3Mo}pA<42FbR85@vMfVtcCRbZMSi{Lg12yx zH0b@=nt$YryqHS9$g<2PjQnAM$QK5Pd|`md7ha7g&mxfsx`YrN z`P>uzKd=cSUl<_rg#jX87$EY60U}?d(;s;yx(v&*Ak}KMpzENJa$T2A82Q5hkuMAo z`N9B^FOuUpLDxVbW!p9++qQ$QgF?!295!L(4^JeLXQpXFs#dE(*FhmwtyXy^n!Kyk zYHY&DJJU4Ti6(z|7*C!#j>7!6S_ z41?!^$h%st#yt>uXBY((ul%jKVYpmMp4_4W0j>!OfYUtdSLTn2FJ z(dHK~UIbPUh0E!zbOETp=J)N}w-^~238E$niLI?I+`oUnKHhLs{rvnqczXvmQ>)c5 zKR@3vtsy}9d>%_nOIKPHg~!s;67u?s2P#D$&ci$o$3Jb3T`IuV2IO+gW@E z=y$!ny+|gL7#tjguItcs9g#=`Jv}{;Wf_{LHT*oel~z?%oO}RSmSyzx^fauqnUnQ| zk?XpDJYjoubX5QS$B!R4d8fDIIQ5fyhGEp#bNXkcQh{yTFbo5xY2xF@k0=xh4eM-& Z{{Xkbo3;j4WHSH&002ovPDHLkV1oJkPPza9 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/new_chat_photo.png b/Telegram/Resources/icons/new_chat_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..3487852470dd88294003875e468f38cf00e38e5e GIT binary patch literal 642 zcmV-|0)737P)Nkl#Nyjx zuLHaHn#Aj_`@z70oP0lqbI!~V5y5X|vsoC10Y4drflMaj>-Vv|6B3C8R;!gS^seP{ ziFiEz8aNt_LRHnk(7O~xK_n9K21g7zEq4 zkxVA(opKz91ORv|^+P|N{lRe@n$PECnkKofOOhl}v)QC{I{lnc(7ITb1zDC64u?N= zXgC~3x7)>byA53DK;ZRyjZ&%PJDa&&4!W-Y9k69tzd@_jDi~wXG!2@jVKSLOk|h2; zpU=axtk=P^EOXONr-SSD`YGpfxuD%{^SoZK_Zrx?ZC=xAwfZIWuJiefa=FZFzT53Q z!6C24o6qNTyWNP0XfPO1G#Y)F8;ixL-|rI<(d~94MNvGP+7oP=CXZ{inx_#FQN3R0 zv0)hB2fME83wtC%5WWwVB#Fn1#ljb+X&R3!m5O(=R|D*JJKkK9B;j(oJj^|vPAC?O zyt(%Ky=Qazj#IbWNK_FaYSfj(k4vdt??01#C8(AeOMphGCE(2vn(5sL^OpDwTT1=KnneLdWCr?NG;YC^VbR-VUBlr$nMCzU_Hy+cw7I cF+@?sPp*R`VvaNZVWmn${xSggN3m__pXTC%gYNcEG&FY;Mv((6h)CLFd|~5Qo)&-nXd&rH8q8$Qi(cjRmEZv zCnqPr2Jra!I2H;8$zZFxySu}&v9Znq>vTHIWHOS&R&{-SjYC62bbuL#LA%|KJkR6f z<73+(q|(#V69$7pTv=H`mSx`qYqeU8L?WHeD4C+sC~CFZS73%=zBO!B7Z(@EFbw`- zwOaqQSS;|}rBvWd3s8lLJ7b0S(gS1oK zoet1${WxHm)B^~D07pkhaD03Wp->1?sTADb-va=ELZJYiP6v9u9!w?^OixdPTrQV3 zkW}klKA%Uo+l>PQ15I!Hlu9LfJRV8+=8^#W{eD!bRKLC7Rjbt)2n3{vMi*EVMO<85 zq;-r#o6Sc3j7<$#6h)kyo1+F>ZDzBXDm1l8%Vx8|{{DW$*IAYYqtOTkg8?)e4FCY7 z(`n#&9!^e9KoEqw@7vwo1(iw#TU%Rng3|cZ{C@cp@54%gP! zu&1Y|p%1O!G%;{KpKmxpjgF4u<>h75UWr5kIgV>Mkz1+-^5fC`3e; z%T?F=@bHkxmk8Kqv(*G*S@zQjs##?+8I;LnHU18VgUFZY@DK`xYWj>uBlP$8lh^{l zYlUKRaZxJw2F6 zB!1gB5{V$gFg3m^l?p4B3Q1)0!-L1;sk;T_I1aPfY|DPv*Vi~aJY3h)=kt-qB?m4P z3aD1A>joSd8NpaA*0fh75@`sl(P;4D;ej+Rof1zV5NLRCXIU0qF4w0Q=jn7BS65f- zevPYbZ*PxARHSy*vb41HX&FW?m&3%w1Q-m4w;j~g)fMnO52vT6btkB*91aI;Zf??u zNvFhv_*%p>o6XdVc=Pk~`274#D<=Iit<`F!2J3J*sKO#jREQ4*0u3j!W|~H$q4t~o zD9Nr`3WWlCz23U(bfczHsnF;1QJIY!N%H&QN~HqF$H#A%X~|^r-DMibac`GtCX)$d zGHLJk{=X3a?)rJa<#M^x8QP{&sRZ5ec)Zg&+NO9s4&6IDJDtwaHf?Wj1F$TM(P&h% zb?>Vv91d3xC$E1ly|}pObSB9Z4u^4YaIm_;`R5q7yu6I(=jT`~7CW7hSjA!y&(6-! eZnwWXcK!t)SfnY(G(FY;0000~#3^?Hyb3CH6R0MO}l9N&HFXEKw?004%=q2r@&w+jHs=kxxd?RFag+&TdO3PE8s)A zu0z*#Fbo4C0z*VtEEbf_W@$Q|(r7dyj^ke2{w?Zs#dFH7zV}T@elVUNrGir z_)A*^{I zwOXy7Ix|J6vodyMbgQknxrX65GsUdV+n3VkZ4z%5Q4Tr+pM+QqK%ut7STG|v`BK3LJ%bc zEs7EbLo9rj*AH}IVSyBi$Ky4x%Ixec@o#Q! z)( zb#+B+d#96=6B1jkR%#zLs3b|X5q+Z9*Vn(rf6>1fsqZux47Cw`qH@rNh6ZXMHK^Ha zCUK!qpe|6U`}=zmcXoDC`=~+N+uKQ;PN%60RO;^Tj>Li>Q2VGs`}+Dwe0Fw5U7%98 zx3?s2X=$PMQG=FmQ$C-kE>NlC<6{y#oz9xPRDG=cq~!5o z6BC+O)a7!Ke4;4ov-KvENpX;{*=(9Y0RS&9E=bOvo*wO69|#11Wmy0Kh(@EDp7?UD zuC9`JYHEtg^W{vk*({m4*Xz|~H30xSIyw{wX^JD?=NCsuM@gb=Hme(?JfhJk$sZ1f z)qxs~M$G5)BvBv`&>gh9yIZlUZEbC}gDx#Ckp%nu`?^EQBNB;_I=j2OwSoG4KE+IX zdwc5!%CfAY*38Td9jL?Mz?YX7Vw{_ss~e;|Mn*;yutP&bUxPL`H)AfBBZi%wow`HH zqx=`HBuVIUxjqJUI-NfTDUY?aH3h8S? zt?G6a3I*zSKgV&<+S*$6K7+vkrBX@p`yT+{9cV2wnM@!ELe)tz3CtEPY1ZT8;~0y@ zP!vT>r_-27Brq5ZBEvAcdXv)*Vk97tE&qpg~MS0Ha0fOLnSdZGz7rc*BABm z^+j9sJdfk!=xEVZibkV&p2x?>2h-Ej0L;zJ6^7Q;)nR5_TwD|d=KJO4W$>YRo<}qq{RzFk zzQ)X`t*xyHn%_Ra)zwv?hb8Is^i=Kb?K(I((BR;p+S=OW`@V{q<2Z6%SCf;IIzKGShbO-)T|Zf@4|^RvR?uzcTFV`HOS*OlwK>gedu-QAtu-rm&O+N#^zTg}eS>c>ju zdngnll}Z8N`#u0qPfs|GQ}BEw63N9%zl?Z1&dbXSW=1xfE%gumTTHI~|7pJg6tt^= Tk_K-Y00000NkvXXu0mjfPZ3b2 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/send_control_silent_on@2x.png b/Telegram/Resources/icons/send_control_silent_on@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..58a7460a484eecf25f6a5f51172c8bf9b3c3ad09 GIT binary patch literal 1487 zcmV;=1u*)FP)>V;SIhc!>((I}jbeCs7@<%IFJ8PTv@0+l-Me=$ zCnhFnR6cz8z~<)Wy!!e}S62FbK3=$RA*0ikD_7FOsH)0%JkG_%MJ_EZrM2<<{lx+G z`Ft{HI#{pQtLu1vex6-jU97FGrNv^Q*=(lSY-U+m8J$iiJ3Bi$IXS8G@%em(g9d{^ z8Bhqpd-v|iwq3h+Q3xU1A3S(qxTB7a4xR7e!-or>`ktO1-3UB=`jh~8?bdExx%^TS5c8raUK~WR{fXn5=)vH(Y0u@3)Q52BGnl)>R2MPemWD<3Cbr={J zkg&II-Gblm&l&Xl_wN7zMNzPJt?`2~r?dXlsZ+Rn_pbc*csv*!9LyyImY0`hxxBpG zuwTxXlFOGb@4hdyCMHrEGElEU89V z%gZ2%ii!%nd-o3Y_4TX9nVFf9<%)_5!=Qk{^w8hmPm-*usgZSCwrt_l)RgYTX>D!I z8pr8$O54%V(clH=O9RwtKR--$0Iv?^5lupzn(Umjc?z+)xoy4 zwaNB7ckamg)H`q9pi@&*GGI$fi(ybrQ*TQo+0)ZQ0Nl21n;g+ZBEe0YHsuehZ6cC< z`SNAXkeYUPck5uaOLOW4y}P?RKhX8-*K>Y;UIsdO@?_qi0C@iVd6_v#a%5zL)z#IE z#bUCLojZ5t4w^a&Iqb}=+Su4AZ}>t827^J4jEqS5p`oGNKwDZ`bW^o||Nf$Y0$^oj zB^MVL<@!`rRrVu{2DxLH7>(`40sj10iVmKVu0j8FlnwqkMTCG;j&dy4w z;o;$;L#Aq9Utd~SRaLof-@f#q4u?Z7Y#{_ECMNz-NKHFCJ6C{e;&!{Goubi>j*i0R zasdEHA`*$<)vH&itgMvJhg1#xhx{f0-oAZ139r|S6`;9@!{I<# zSsA{3`I7OCP7-Fb8B0q``1tXo;n;q^A0Cefmo8nx{rmTS#mZNYA3wh863`?|BF~s> z-EMd4Yof69^7!%NRo8SR5-CP1D?Aqt4i097j7FohSSQe5vA0Psf3~7fWLqQl4?ll$^=w;G$5T{@&Ip zbk5B4+>>T3X#T+%w}OlB5&w$|j%WN;8NNysPd-?ce|Proi?)9k#;vHATWGZ-Z-cX7 m-C&U>J17(8A5T-G@yGywp}X-3=t diff --git a/Telegram/Resources/icons/settings_close@2x.png b/Telegram/Resources/icons/settings_close@2x.png deleted file mode 100644 index 40d3def3d2c56edb57dc818be15c1add75a32652..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 296 zcmV+@0oVSCP)CU0sydW8>EyV#t0+joP&9uVc+-f zo-niILPYsU&C5CWy6=MV< us`fKmy!Tx`KwVn9@0000f4ReHKOhDb~;J-?CnhyqXR!~Yz+ zcP#j>;4|f?#>5x{X61kD*7J zRbKDszVTeeRiJL2wKg&B$=??@%H0p}yH&U|X#P={VO{uMr1V_i$GyuR|KYc_P8Y0P ds4gbB?`Nj}=c0M@7Xlr@;OXk;vd$@?2>?z*N0|Ts literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/simple_close@2x.png b/Telegram/Resources/icons/simple_close@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7825b270a12b2ea01fb0a82e7228125846fd0146 GIT binary patch literal 354 zcmV-o0iFJdP)U5QX6%FnuD0#5G4D6~{=Eb8xFvHD6?<3ZCj>?y4^X_^v-A%0O5gkeaUrf9AIg%w3XP?lwOI2<^gPQ-Cc(=@bgy9>+n zoIpgfvMkw(e#1nB0LVmyE$9*kd_VwK)P~JK7jz8+@PK&-@O_^oNr>Z^F;c)Pwg~=4 zhhYFfDP_d~{I6Z$^?LDqKB?;(r4&jj>bkaKRuJHPJ`+XJbdI8k%jIIbvJCzlfH4MR zjMGNVru+>X-^>`}l+!=pQZz&aV+_aR(TdJMmz=Gk6X=4upaGC=!Pd|~Yn^SyY^Zm; z-DD3omgjj~uh(f!Ro8V?RkaI)&xexdIq+G8HzU_L$-_0k>;M1&07*qoM6N<$g6pc8 A8vpb); } - p.drawSprite(phRect.topLeft() + st::newGroupPhotoIconPosition, st::newGroupPhotoIcon); + st::newGroupPhotoIcon.paint(p, phRect.topLeft() + st::newGroupPhotoIconPosition, width()); } else { p.drawPixmap(phRect.topLeft(), _photoSmall); } diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 1d0266b4c..c04b07c32 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -136,18 +136,16 @@ contactsMultiSelect: MultiSelect { fieldIcon: fieldSearchIcon; fieldIconSkip: 36px; - fieldCancel: IconButton { + fieldCancel: IconButton(defaultIconButton) { width: 41px; height: 48px; opacity: 0.3; overOpacity: 0.4; - icon: icon {{ "box_search_cancel", #000000 }}; + icon: boxCancelIcon; iconPosition: point(8px, 18px); downIconPosition: point(8px, 19px); - - duration: 150; } fieldCancelSkip: 34px; } @@ -206,3 +204,29 @@ notificationSampleNameFg: #939393; membersAboutPadding: margins(0px, 12px, 0px, 12px); membersAboutFg: #777; + +sessionsScroll: flatScroll(boxScroll) { + round: 2px; + deltax: 5px; + width: 14px; +} +sessionsHeight: 440px; +sessionHeight: 70px; +sessionCurrentPadding: margins(0px, 7px, 0px, 4px); +sessionCurrentHeight: 118px; +sessionPadding: margins(21px, 10px, 21px, 0px); +sessionNameFont: msgNameFont; +sessionActiveFont: msgDateFont; +sessionActiveColor: #aaa; +sessionInfoFont: msgFont; +sessionInfoColor: #888888; +sessionTerminateTop: 30px; +sessionTerminateSkip: 18px; +sessionTerminate: IconButton(defaultIconButton) { + width: 16px; + height: 16px; + + icon: simpleCloseIcon; + iconPosition: point(3px, 3px); + downIconPosition: point(3px, 4px); +} diff --git a/Telegram/SourceFiles/boxes/notifications_box.cpp b/Telegram/SourceFiles/boxes/notifications_box.cpp index 98e9a4fe6..dbd16898d 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.cpp +++ b/Telegram/SourceFiles/boxes/notifications_box.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "ui/buttons/round_button.h" +#include "ui/buttons/icon_button.h" #include "ui/widgets/discrete_slider.h" #include "styles/style_boxes.h" #include "styles/style_dialogs.h" @@ -289,7 +290,7 @@ void NotificationsBox::prepareNotificationSampleLarge() { p.drawText(rectForName.left(), rectForName.top() + st::msgNameFont->ascent, notifyTitle); p.setOpacity(st::notifyClose.opacity); - p.drawSpriteLeft(w - st::notifyClosePos.x() - st::notifyClose.width + st::notifyClose.iconPos.x(), st::notifyClosePos.y() + st::notifyClose.iconPos.y(), w, st::notifyClose.icon); + st::notifyClose.icon.paint(p, w - st::notifyClosePos.x() - st::notifyClose.width + st::notifyClose.iconPosition.x(), st::notifyClosePos.y() + st::notifyClose.iconPosition.y(), w); } _notificationSampleLarge = App::pixmapFromImageInPlace(std_::move(sampleImage)); diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index 7cd8d1e25..cd3c10ad7 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -19,16 +19,16 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +#include "boxes/sessionsbox.h" + #include "lang.h" - #include "localstorage.h" - -#include "sessionsbox.h" #include "mainwidget.h" #include "mainwindow.h" - #include "countries.h" -#include "confirmbox.h" +#include "boxes/confirmbox.h" +#include "ui/buttons/icon_button.h" +#include "styles/style_boxes.h" SessionsBox::SessionsBox() : ScrollableBox(st::sessionsScroll) , _loading(true) @@ -90,7 +90,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) { _shortPollRequest = 0; int32 availCurrent = st::boxWideWidth - st::sessionPadding.left() - st::sessionTerminateSkip; - int32 availOther = availCurrent - st::sessionTerminate.iconPos.x();// -st::sessionTerminate.width - st::sessionTerminateSkip; + int32 availOther = availCurrent - st::sessionTerminate.iconPosition.x();// -st::sessionTerminate.width - st::sessionTerminateSkip; _list.clear(); const auto &v(result.c_account_authorizations().vauthorizations.c_vector().v); @@ -258,7 +258,7 @@ void SessionsBox::Inner::paintEvent(QPaintEvent *e) { Painter p(this); p.fillRect(r, st::white->b); - int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPos.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip; + int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPosition.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip; int32 w = width(); if (_current->active.isEmpty() && _list->isEmpty()) { @@ -414,7 +414,7 @@ void SessionsBox::Inner::listUpdated() { for (int32 i = 0, l = _list->size(); i < l; ++i) { TerminateButtons::iterator j = _terminateButtons.find(_list->at(i).hash); if (j == _terminateButtons.cend()) { - j = _terminateButtons.insert(_list->at(i).hash, new IconedButton(this, st::sessionTerminate)); + j = _terminateButtons.insert(_list->at(i).hash, new Ui::IconButton(this, st::sessionTerminate)); connect(j.value(), SIGNAL(clicked()), this, SLOT(onTerminate())); } j.value()->moveToRight(st::sessionTerminateSkip, st::sessionCurrentHeight + i * st::sessionHeight + st::sessionTerminateTop, width()); diff --git a/Telegram/SourceFiles/boxes/sessionsbox.h b/Telegram/SourceFiles/boxes/sessionsbox.h index 0021162cf..0b32c9bad 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.h +++ b/Telegram/SourceFiles/boxes/sessionsbox.h @@ -25,6 +25,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class ConfirmBox; +namespace Ui { +class IconButton; +} // namespace Ui + class SessionsBox : public ScrollableBox, public RPCSender { Q_OBJECT @@ -106,7 +110,7 @@ private: SessionsBox::List *_list; SessionsBox::Data *_current; - typedef QMap TerminateButtons; + typedef QMap TerminateButtons; TerminateButtons _terminateButtons; uint64 _terminating; diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 03c13475e..d83b2f360 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -29,6 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "apiwrap.h" #include "localstorage.h" #include "dialogs/dialogs_layout.h" +#include "styles/style_boxes.h" #include "styles/style_stickers.h" namespace { diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index abf2223dc..ec82cc590 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org using "basic.style"; using "basic_types.style"; +using "ui/widgets/widgets.style"; + dialogsUnreadFg: #ffffff; dialogsUnreadFgActive: #5b94bf; dialogsUnreadBg: windowActiveBg; @@ -81,21 +83,21 @@ dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) { linkFgDown: #c6e1f7; } -dialogsNewChatIcon: icon {{ "dialogs_new_chat", #b7b7b7, point(9px, 10px) }}; -dialogsNewChatButton: RoundButton { +dialogsNewChatButton: IconButton(defaultIconButton) { width: 36px; height: 36px; - icon: dialogsNewChatIcon; - textTop: 5px; - downTextTop: 6px; - - textFg: transparent; - textFgOver: transparent; - secondaryTextFg: transparent; - secondaryTextFgOver: transparent; - textBg: transparent; - textBgOver: transparent; + icon: icon {{ "dialogs_new_chat", #b7b7b7 }}; + iconPosition: point(9px, 10px); + downIconPosition: point(9px, 11px); +} +dialogsAddContact: IconButton(dialogsNewChatButton) { + icon: icon {{ "dialogs_add_contact", #a6a6a6 }}; + iconPosition: point(8px, 8px); + downIconPosition: point(8px, 9px); +} +dialogsCancelSearch: IconButton(dialogsAddContact) { + icon: icon {{ "dialogs_cancel_search", #a6a6a6 }}; } dialogsChatTypeSkip: 22px; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index eb9007f36..b6de91dbe 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -24,7 +24,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_indexed_list.h" #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" -#include "ui/buttons/round_button.h" +#include "styles/style_stickers.h" +#include "ui/buttons/icon_button.h" #include "ui/widgets/popup_menu.h" #include "data/data_drafts.h" #include "lang.h" @@ -43,7 +44,7 @@ DialogsInner::DialogsInner(QWidget *parent, MainWidget *main) : SplittedWidget(p , contactsNoDialogs(std_::make_unique(Dialogs::SortMode::Name)) , contacts(std_::make_unique(Dialogs::SortMode::Name)) , _addContactLnk(this, lang(lng_add_contact_button)) -, _cancelSearchInPeer(this, st::btnCancelSearch) { +, _cancelSearchInPeer(this, st::dialogsCancelSearch) { if (Global::DialogsModeEnabled()) { importantDialogs = std_::make_unique(Dialogs::SortMode::Date); } @@ -51,8 +52,8 @@ DialogsInner::DialogsInner(QWidget *parent, MainWidget *main) : SplittedWidget(p connect(main, SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(onPeerPhotoChanged(PeerData*))); connect(main, SIGNAL(dialogRowReplaced(Dialogs::Row*,Dialogs::Row*)), this, SLOT(onDialogRowReplaced(Dialogs::Row*,Dialogs::Row*))); connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); - connect(&_cancelSearchInPeer, SIGNAL(clicked()), this, SIGNAL(cancelSearchInPeer())); - _cancelSearchInPeer.hide(); + connect(_cancelSearchInPeer, SIGNAL(clicked()), this, SIGNAL(cancelSearchInPeer())); + _cancelSearchInPeer->hide(); subscribe(FileDownload::ImageLoaded(), [this] { update(); }); subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { @@ -126,8 +127,8 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO p.fillRect(0, 0, w, st::mentionHeight, (selected ? st::mentionBgOver : st::white)->b); if (!paintingOther) { if (selected) { - int skip = (st::mentionHeight - st::simpleClose.icon.pxHeight()) / 2; - p.drawSprite(QPoint(w - st::simpleClose.icon.pxWidth() - skip, skip), st::simpleClose.icon); + int skip = (st::mentionHeight - st::simpleCloseIcon.height()) / 2; + st::simpleCloseIcon.paint(p, QPoint(w - st::simpleCloseIcon.width() - skip, skip), width()); } QString first = (_hashtagFilter.size() < 2) ? QString() : ('#' + _hashtagResults.at(from).mid(0, _hashtagFilter.size() - 1)), second = (_hashtagFilter.size() < 2) ? ('#' + _hashtagResults.at(from)) : _hashtagResults.at(from).mid(_hashtagFilter.size() - 1); int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second); @@ -299,7 +300,7 @@ void DialogsInner::searchInPeerPaint(Painter &p, int32 w, bool onlyBackground) c _searchInPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), fullWidth()); int32 nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; - int32 namewidth = w - nameleft - st::dialogsPadding.x() * 2 - st::btnCancelSearch.width; + int32 namewidth = w - nameleft - st::dialogsPadding.x() * 2 - st::dialogsCancelSearch.width; QRect rectForName(nameleft, st::dialogsPadding.y() + st::dialogsNameTop, namewidth, st::msgNameFont->height); if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(_searchInPeer, false)) { @@ -408,7 +409,7 @@ void DialogsInner::mousePressEvent(QMouseEvent *e) { void DialogsInner::resizeEvent(QResizeEvent *e) { _addContactLnk.move((width() - _addContactLnk.width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); - _cancelSearchInPeer.move(width() - st::dialogsPadding.x() - st::btnCancelSearch.width, (st::dialogsRowHeight - st::btnCancelSearch.height) / 2); + _cancelSearchInPeer->move(width() - st::dialogsPadding.x() - st::dialogsCancelSearch.width, (st::dialogsRowHeight - st::dialogsCancelSearch.height) / 2); } void DialogsInner::onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow) { @@ -1260,9 +1261,9 @@ void DialogsInner::searchInPeer(PeerData *peer) { _searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0; if (_searchInPeer) { onHashtagFilterUpdate(QStringRef()); - _cancelSearchInPeer.show(); + _cancelSearchInPeer->show(); } else { - _cancelSearchInPeer.hide(); + _cancelSearchInPeer->hide(); } } @@ -1780,9 +1781,9 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : TWidget(parent) , _dialogsRequest(0) , _contactsRequest(0) , _filter(this, st::dlgFilter, lang(lng_dlg_filter)) -, _newGroup(this, QString(), st::dialogsNewChatButton) -, _addContact(this, st::btnAddContact) -, _cancelSearch(this, st::btnCancelSearch) +, _newGroup(this, st::dialogsNewChatButton) +, _addContact(this, st::dialogsAddContact) +, _cancelSearch(this, st::dialogsCancelSearch) , _scroll(this, st::dialogsScroll) , _inner(&_scroll, parent) , _a_show(animation(this, &DialogsWidget::step_show)) @@ -1807,9 +1808,9 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : TWidget(parent) connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); connect(&_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int))); connect(parent, SIGNAL(dialogsUpdated()), this, SLOT(onListScroll())); - connect(&_addContact, SIGNAL(clicked()), this, SLOT(onAddContact())); + connect(_addContact, SIGNAL(clicked()), this, SLOT(onAddContact())); connect(_newGroup, SIGNAL(clicked()), this, SLOT(onNewGroup())); - connect(&_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); + connect(_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); _chooseByDragTimer.setSingleShot(true); connect(&_chooseByDragTimer, SIGNAL(timeout()), this, SLOT(onChooseByDrag())); @@ -1824,12 +1825,12 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : TWidget(parent) _filter.move(st::dialogsPadding.x(), st::dialogsFilterPadding); _filter.setFocusPolicy(Qt::StrongFocus); _filter.customUpDown(true); - _addContact.hide(); + _addContact->hide(); _newGroup->show(); - _cancelSearch.hide(); + _cancelSearch->hide(); _newGroup->move(width() - _newGroup->width() - st::dialogsPadding.x(), 0); - _addContact.move(width() - _addContact.width() - st::dialogsPadding.x(), 0); - _cancelSearch.move(width() - _cancelSearch.width() - st::dialogsPadding.x(), 0); + _addContact->move(width() - _addContact->width() - st::dialogsPadding.x(), 0); + _cancelSearch->move(width() - _cancelSearch->width() - st::dialogsPadding.x(), 0); } void DialogsWidget::activate() { @@ -1874,7 +1875,7 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window: _scroll.hide(); _filter.hide(); - _cancelSearch.hide(); + _cancelSearch->hide(); _newGroup->hide(); int delta = st::slideShift; @@ -2377,10 +2378,10 @@ void DialogsWidget::onFilterUpdate(bool force) { _searchCache.clear(); _searchQueries.clear(); _searchQuery = QString(); - _cancelSearch.hide(); + _cancelSearch->hide(); _newGroup->show(); - } else if (_cancelSearch.isHidden()) { - _cancelSearch.show(); + } else if (_cancelSearch->isHidden()) { + _cancelSearch->show(); _newGroup->hide(); } if (filterText.size() < MinUsernameLength) { @@ -2445,8 +2446,8 @@ void DialogsWidget::resizeEvent(QResizeEvent *e) { int32 w = width(); _filter.setGeometry(st::dialogsPadding.x(), st::dialogsFilterPadding, w - 2 * st::dialogsPadding.x(), _filter.height()); _newGroup->move(w - _newGroup->width() - st::dialogsPadding.x(), _filter.y()); - _addContact.move(w - _addContact.width() - st::dialogsPadding.x(), _filter.y()); - _cancelSearch.move(w - _cancelSearch.width() - st::dialogsPadding.x(), _filter.y()); + _addContact->move(w - _addContact->width() - st::dialogsPadding.x(), _filter.y()); + _cancelSearch->move(w - _cancelSearch->width() - st::dialogsPadding.x(), _filter.y()); _scroll.move(0, _filter.height() + 2 * st::dialogsFilterPadding); int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0; diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 930349072..e3aec8945 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -29,7 +29,7 @@ class IndexedList; } // namespace Dialogs namespace Ui { -class RoundButton; +class IconButton; class PopupMenu; } // namespace Ui @@ -221,7 +221,7 @@ private: void paintDialog(QPainter &p, Dialogs::Row *dialog); LinkButton _addContactLnk; - IconedButton _cancelSearchInPeer; + ChildWidget _cancelSearchInPeer; bool _overDelete = false; @@ -336,8 +336,9 @@ private: mtpRequestId _dialogsRequest, _contactsRequest; FlatInput _filter; - ChildWidget _newGroup; - IconedButton _addContact, _cancelSearch; + ChildWidget _newGroup; + ChildWidget _addContact; + ChildWidget _cancelSearch; ScrollArea _scroll; DialogsInner _inner; diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index c217e8c70..d04c2ab2f 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -605,9 +605,9 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { bool selected = (i == _sel); if (selected) { p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b); - int skip = (st::mentionHeight - st::simpleClose.icon.pxHeight()) / 2; + int skip = (st::mentionHeight - st::simpleCloseIcon.height()) / 2; if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) { - p.drawSprite(QPoint(width() - st::simpleClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), st::simpleClose.icon); + st::simpleCloseIcon.paint(p, QPoint(width() - st::simpleCloseIcon.width() - skip, i * st::mentionHeight + skip), width()); } } p.setPen(st::black->p); diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 7c1ff3156..eb07e0fe1 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -147,18 +147,13 @@ historyMediaTypeSong: icon {{ "media_type_song", #bebebe, point(0px, 0px) }}; historyMediaTypeVoice: icon {{ "media_type_voice", #bebebe, point(2px, 2px) }}; historyMediaTypeLink: icon {{ "media_type_link", #bebebe, point(2px, 2px) }}; -historyAttachDocument: IconButton { +historyAttachDocument: IconButton(defaultIconButton) { width: 46px; height: 46px; - opacity: 0.78; - overOpacity: 1.; - icon: historyMediaTypeFile; iconPosition: point(9px, 9px); downIconPosition: point(9px, 10px); - - duration: 150; } historyAttachPhoto: IconButton(historyAttachDocument) { icon: historyMediaTypePhoto; @@ -211,7 +206,35 @@ historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { } } -topBarSearch: IconButton { +historySilentToggle: IconButton(historyBotKeyboardShow) { + icon: icon {{ "send_control_silent_off", #b3b3b3 }}; +} +historySilentToggleOn: icon {{ "send_control_silent_on", #b3b3b3 }}; + +historyReplySkip: 51px; +historyReplyColor: #377aae; +historyReplyHeight: 49px; +historyReplyTop: 8px; +historyReplyBottom: 6px; +historyReplyIconPosition: point(13px, 13px); +historyReplyIcon: icon {{ "history_action_reply", #6fa1d2 }}; +historyForwardIcon: icon {{ "history_action_forward", #6fa1d2 }}; +historyEditIcon: icon {{ "history_action_edit", #6fa1d2 }}; +historyReplyCancel: IconButton(defaultIconButton) { + width: 49px; + height: 49px; + + icon: historyReplyCancelIcon; + iconPosition: point(17px, 17px); + downIconPosition: point(17px, 18px); +} +historyInlineBotCancel: IconButton(historyReplyCancel) { + height: 46px; + iconPosition: point(-1px, 16px); // < 0 means draw in the center of the button + downIconPosition: point(-1px, 17px); +} + +topBarSearch: IconButton(defaultIconButton) { width: 44px; height: topBarHeight; @@ -221,6 +244,4 @@ topBarSearch: IconButton { opacity: 0.22; overOpacity: 0.36; - - duration: 150; } diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 490ebb78e..0281440e0 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -2733,9 +2733,9 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u if (_data->type == WebPageVideo && _attach->type() == MediaTypePhoto) { if (_attach->isReadyForOpen()) { if (_data->siteName == qstr("YouTube")) { - p.drawSprite(QPoint((pixwidth - st::youtubeIcon.pxWidth()) / 2, (pixheight - st::youtubeIcon.pxHeight()) / 2), st::youtubeIcon); + st::youtubeIcon.paint(p, (pixwidth - st::youtubeIcon.width()) / 2, (pixheight - st::youtubeIcon.height()) / 2, _width); } else { - p.drawSprite(QPoint((pixwidth - st::videoIcon.pxWidth()) / 2, (pixheight - st::videoIcon.pxHeight()) / 2), st::videoIcon); + st::videoIcon.paint(p, (pixwidth - st::videoIcon.width()) / 2, (pixheight - st::videoIcon.height()) / 2, _width); } } if (_durationWidth) { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index e48517114..9cd79baef 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2913,12 +2913,12 @@ HistoryHider::~HistoryHider() { parent()->noHider(this); } -SilentToggle::SilentToggle(QWidget *parent) : FlatCheckbox(parent, QString(), false, st::silentToggle) { +SilentToggle::SilentToggle(QWidget *parent) : IconButton(parent, st::historySilentToggle) { setMouseTracking(true); } void SilentToggle::mouseMoveEvent(QMouseEvent *e) { - FlatCheckbox::mouseMoveEvent(e); + IconButton::mouseMoveEvent(e); if (rect().contains(e->pos())) { Ui::Tooltip::Show(1000, this); } else { @@ -2926,21 +2926,29 @@ void SilentToggle::mouseMoveEvent(QMouseEvent *e) { } } +void SilentToggle::setChecked(bool checked) { + if (_checked != checked) { + _checked = checked; + setIcon(_checked ? &st::historySilentToggleOn : nullptr); + } +} + void SilentToggle::leaveEvent(QEvent *e) { Ui::Tooltip::Hide(); } void SilentToggle::mouseReleaseEvent(QMouseEvent *e) { - FlatCheckbox::mouseReleaseEvent(e); + setChecked(!_checked); + IconButton::mouseReleaseEvent(e); Ui::Tooltip::Show(0, this); - PeerData *p = App::main() ? App::main()->peer() : nullptr; + auto p = App::main() ? App::main()->peer() : nullptr; if (p && p->isChannel() && p->notify != UnknownNotifySettings) { - App::main()->updateNotifySetting(p, NotifySettingDontChange, checked() ? SilentNotifiesSetSilent : SilentNotifiesSetNotify); + App::main()->updateNotifySetting(p, NotifySettingDontChange, _checked ? SilentNotifiesSetSilent : SilentNotifiesSetNotify); } } QString SilentToggle::tooltipText() const { - return lang(checked() ? lng_wont_be_notified : lng_will_be_notified); + return lang(_checked ? lng_wont_be_notified : lng_will_be_notified); } QPoint SilentToggle::tooltipPos() const { @@ -2984,7 +2992,7 @@ TextWithTags::Tags textTagsFromEntities(const EntitiesInText &entities) { } HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) -, _fieldBarCancel(this, st::replyCancel) +, _fieldBarCancel(this, st::historyReplyCancel) , _scroll(this, st::historyScroll, false) , _historyToEnd(this) , _fieldAutocomplete(this) @@ -3024,13 +3032,13 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide())); connect(&_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); connect(_historyToEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); - connect(&_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel())); + connect(_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel())); connect(&_send, SIGNAL(clicked()), this, SLOT(onSend())); connect(&_unblock, SIGNAL(clicked()), this, SLOT(onUnblock())); connect(&_botStart, SIGNAL(clicked()), this, SLOT(onBotStart())); connect(&_joinChannel, SIGNAL(clicked()), this, SLOT(onJoinChannel())); connect(&_muteUnmute, SIGNAL(clicked()), this, SLOT(onMuteUnmute())); - connect(&_silent, SIGNAL(clicked()), this, SLOT(onBroadcastSilentChange())); + connect(_silent, SIGNAL(clicked()), this, SLOT(onBroadcastSilentChange())); connect(_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect())); connect(_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect())); connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); @@ -3077,7 +3085,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onDraftSaveDelayed())); connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onCheckFieldAutocomplete()), Qt::QueuedConnection); - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); _scroll.hide(); @@ -3115,7 +3123,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _attachEmoji->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); - _silent.hide(); + _silent->hide(); _botCommandStart->hide(); _attachDocument->installEventFilter(_attachType); @@ -3489,7 +3497,7 @@ void HistoryWidget::onRecordDone(QByteArray result, VoiceWaveform waveform, qint App::wnd()->activateWindow(); int32 duration = samples / AudioVoiceMsgFrequency; - _fileLoader.addTask(new FileLoadTask(result, duration, waveform, FileLoadTo(_peer->id, _silent.checked(), replyToId()))); + _fileLoader.addTask(new FileLoadTask(result, duration, waveform, FileLoadTo(_peer->id, _silent->checked(), replyToId()))); cancelReplyAfterMediaSend(lastForceReplyReplied()); } @@ -4203,7 +4211,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _editMsgId = _replyToId = 0; _previewData = 0; _previewCache.clear(); - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); _membersDropdownShowTimer.stop(); if (_list) _list->deleteLater(); @@ -4363,8 +4371,8 @@ void HistoryWidget::updateNotifySettings() { _muteUnmute.setText(lang(_history->mute() ? lng_channel_unmute : lng_channel_mute)); if (_peer->notify != UnknownNotifySettings) { - _silent.setChecked(_peer->notify != EmptyNotifySettings && (_peer->notify->flags & MTPDpeerNotifySettings::Flag::f_silent)); - if (_silent.isHidden() && hasSilentToggle()) { + _silent->setChecked(_peer->notify != EmptyNotifySettings && (_peer->notify->flags & MTPDpeerNotifySettings::Flag::f_silent)); + if (_silent->isHidden() && hasSilentToggle()) { updateControlsVisibility(); } } @@ -4499,11 +4507,11 @@ void HistoryWidget::updateControlsVisibility() { _muteUnmute.hide(); _fieldAutocomplete->hide(); _field.hide(); - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); _attachDocument->hide(); _attachPhoto->hide(); _attachEmoji->hide(); - _silent.hide(); + _silent->hide(); _historyToEnd->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); @@ -4561,9 +4569,9 @@ void HistoryWidget::updateControlsVisibility() { _botStart.hide(); _attachDocument->hide(); _attachPhoto->hide(); - _silent.hide(); + _silent->hide(); _kbScroll.hide(); - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); _attachDocument->hide(); _attachPhoto->hide(); _attachEmoji->hide(); @@ -4597,9 +4605,9 @@ void HistoryWidget::updateControlsVisibility() { _botCommandStart->hide(); _attachDocument->hide(); _attachPhoto->hide(); - _silent.hide(); + _silent->hide(); _kbScroll.hide(); - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); } else { _unblock.hide(); _botStart.hide(); @@ -4627,7 +4635,7 @@ void HistoryWidget::updateControlsVisibility() { _botCommandStart->hide(); _attachDocument->hide(); _attachPhoto->hide(); - _silent.hide(); + _silent->hide(); if (_kbShown) { _kbScroll.show(); } else { @@ -4671,20 +4679,20 @@ void HistoryWidget::updateControlsVisibility() { _attachPhoto->hide(); } if (hasSilentToggle()) { - _silent.show(); + _silent->show(); } else { - _silent.hide(); + _silent->hide(); } updateFieldPlaceholder(); } if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { - if (_fieldBarCancel.isHidden()) { - _fieldBarCancel.show(); + if (_fieldBarCancel->isHidden()) { + _fieldBarCancel->show(); resizeEvent(0); update(); } } else { - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); } } } else { @@ -4697,9 +4705,9 @@ void HistoryWidget::updateControlsVisibility() { _muteUnmute.hide(); _attachDocument->hide(); _attachPhoto->hide(); - _silent.hide(); + _silent->hide(); _kbScroll.hide(); - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); _attachDocument->hide(); _attachPhoto->hide(); _attachEmoji->hide(); @@ -4719,7 +4727,7 @@ void HistoryWidget::updateControlsVisibility() { } void HistoryWidget::updateMouseTracking() { - bool trackMouse = !_fieldBarCancel.isHidden() || _pinnedBar || (cHasAudioCapture() && _send.isHidden() && !_field.isHidden()); + bool trackMouse = !_fieldBarCancel->isHidden() || _pinnedBar || (cHasAudioCapture() && _send.isHidden() && !_field.isHidden()); setMouseTracking(trackMouse); } @@ -5244,7 +5252,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { message.history = _history; message.textWithTags = _field.getTextWithTags(); message.replyTo = replyTo; - message.silent = _silent.checked(); + message.silent = _silent->checked(); message.webPageId = webPageId; App::main()->sendMessage(message); @@ -5388,7 +5396,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const bool channelPost = p->isChannel() && !p->isMegagroup(); bool showFromName = !channelPost || p->asChannel()->addsSignature(); - bool silentPost = channelPost && _silent.checked(); + bool silentPost = channelPost && _silent->checked(); if (channelPost) { flags |= MTPDmessage::Flag::f_views; flags |= MTPDmessage::Flag::f_post; @@ -5404,7 +5412,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const App::historyRegRandom(randomId, newId); - App::main()->finishForwarding(history, _silent.checked()); + App::main()->finishForwarding(history, _silent->checked()); cancelReplyAfterMediaSend(lastKeyboardUsed); } @@ -5459,12 +5467,12 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: _attachPhoto->hide(); _attachEmoji->hide(); _fieldAutocomplete->hide(); - _silent.hide(); + _silent->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); _field.hide(); - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); _send.hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); _unblock.hide(); @@ -5664,8 +5672,8 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { QPoint pos(e ? e->pos() : mapFromGlobal(QCursor::pos())); bool inRecord = _send.geometry().contains(pos); bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); - bool inReplyEdit = QRect(st::replySkip, _field.y() - st::sendPadding - st::replyHeight, width() - st::replySkip - _fieldBarCancel.width(), st::replyHeight).contains(pos) && (_editMsgId || replyToId()); - bool inPinnedMsg = QRect(0, 0, width(), st::replyHeight).contains(pos) && _pinnedBar; + bool inReplyEdit = QRect(st::historyReplySkip, _field.y() - st::sendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId()); + bool inPinnedMsg = QRect(0, 0, width(), st::historyReplyHeight).contains(pos) && _pinnedBar; bool startAnim = false; if (inRecord != _inRecord) { _inRecord = inRecord; @@ -5699,7 +5707,7 @@ void HistoryWidget::leaveToChildEvent(QEvent *e, QWidget *child) { // e -- from void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) { if (_replyForwardPressed) { _replyForwardPressed = false; - update(0, _field.y() - st::sendPadding - st::replyHeight, width(), st::replyHeight); + update(0, _field.y() - st::sendPadding - st::historyReplyHeight, width(), st::historyReplyHeight); } if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; @@ -6150,7 +6158,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_editMsgId && !_replyToId) { - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); updateMouseTracking(); } } else { @@ -6175,7 +6183,7 @@ void HistoryWidget::onKbToggle(bool manual) { if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) { updateReplyToName(); _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); - _fieldBarCancel.show(); + _fieldBarCancel->show(); updateMouseTracking(); } if (manual && _history) { @@ -6194,7 +6202,7 @@ void HistoryWidget::onKbToggle(bool manual) { if (_kbReplyTo && !_editMsgId && !_replyToId) { updateReplyToName(); _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); - _fieldBarCancel.show(); + _fieldBarCancel->show(); updateMouseTracking(); } if (manual && _history) { @@ -6455,10 +6463,10 @@ void HistoryWidget::moveFieldControls() { right -= _attachEmoji->width(); _botKeyboardShow->move(right - _botKeyboardShow->width(), buttonsBottom); _botCommandStart->move(right - _botCommandStart->width(), buttonsBottom); - _silent.move(right - _silent.width(), buttonsBottom); + _silent->move(right - _silent->width(), buttonsBottom); right = w; - _fieldBarCancel.move(right - _fieldBarCancel.width(), _field.y() - st::sendPadding - _fieldBarCancel.height()); + _fieldBarCancel->move(right - _fieldBarCancel->width(), _field.y() - st::sendPadding - _fieldBarCancel->height()); _attachType->move(0, _attachDocument->y() - _attachType->height()); _emojiPan->moveBottom(_attachEmoji->y()); @@ -6475,7 +6483,7 @@ void HistoryWidget::updateFieldSize() { fieldWidth -= _attachEmoji->width(); if (kbShowShown) fieldWidth -= _botKeyboardShow->width(); if (_cmdStartShown) fieldWidth -= _botCommandStart->width(); - if (hasSilentToggle()) fieldWidth -= _silent.width(); + if (hasSilentToggle()) fieldWidth -= _silent->width(); if (_field.width() != fieldWidth) { _field.resize(fieldWidth, _field.height()); @@ -6497,7 +6505,7 @@ void HistoryWidget::clearInlineBot() { void HistoryWidget::inlineBotChanged() { bool isInlineBot = _inlineBot && (_inlineBot != LookingUpInlineBot); if (isInlineBot && !_inlineBotCancel) { - _inlineBotCancel = std_::make_unique(this, st::inlineBotCancel); + _inlineBotCancel = std_::make_unique(this, st::historyInlineBotCancel); connect(_inlineBotCancel.get(), SIGNAL(clicked()), this, SLOT(onInlineBotCancel())); _inlineBotCancel->setGeometry(_send.geometry()); _attachEmoji->raise(); @@ -6543,7 +6551,7 @@ void HistoryWidget::updateFieldPlaceholder() { if (_inlineBot && _inlineBot != LookingUpInlineBot) { _field.setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); } else { - _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_silent.checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_message_ph)); + _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_silent->checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_message_ph)); } _send.setText(lang(lng_send_button)); } @@ -6553,7 +6561,7 @@ void HistoryWidget::uploadImage(const QImage &img, PrepareMediaType type, FileLo if (!_history) return; App::wnd()->activateWindow(); - auto task = new FileLoadTask(img, type, FileLoadTo(_peer->id, _silent.checked(), replyToId()), confirm, source); + auto task = new FileLoadTask(img, type, FileLoadTo(_peer->id, _silent->checked(), replyToId()), confirm, source); if (withText) { _confirmWithTextId = task->fileid(); } @@ -6564,7 +6572,7 @@ void HistoryWidget::uploadFile(const QString &file, PrepareMediaType type, FileL if (!_history) return; App::wnd()->activateWindow(); - FileLoadTask *task = new FileLoadTask(file, type, FileLoadTo(_peer->id, _silent.checked(), replyToId()), confirm); + FileLoadTask *task = new FileLoadTask(file, type, FileLoadTo(_peer->id, _silent->checked(), replyToId()), confirm); if (withText) { _confirmWithTextId = task->fileid(); } @@ -6578,7 +6586,7 @@ void HistoryWidget::uploadFiles(const QStringList &files, PrepareMediaType type) App::wnd()->activateWindow(); - FileLoadTo to(_peer->id, _silent.checked(), replyToId()); + FileLoadTo to(_peer->id, _silent->checked(), replyToId()); TasksList tasks; tasks.reserve(files.size()); @@ -6594,7 +6602,7 @@ void HistoryWidget::uploadFileContent(const QByteArray &fileContent, PrepareMedi if (!_history) return; App::wnd()->activateWindow(); - _fileLoader.addTask(new FileLoadTask(fileContent, type, FileLoadTo(_peer->id, _silent.checked(), replyToId()))); + _fileLoader.addTask(new FileLoadTask(fileContent, type, FileLoadTo(_peer->id, _silent->checked(), replyToId()))); cancelReplyAfterMediaSend(lastForceReplyReplied()); } @@ -7017,13 +7025,13 @@ void HistoryWidget::updateControlsGeometry() { moveFieldControls(); if (_pinnedBar) { - if (_scroll.y() != st::replyHeight) { - _scroll.move(0, st::replyHeight); - _reportSpamPanel.move(0, st::replyHeight); + if (_scroll.y() != st::historyReplyHeight) { + _scroll.move(0, st::historyReplyHeight); + _reportSpamPanel.move(0, st::historyReplyHeight); _fieldAutocomplete->setBoundings(_scroll.geometry()); } _pinnedBar->cancel->move(width() - _pinnedBar->cancel->width(), 0); - _pinnedBar->shadow->setGeometry(0, st::replyHeight, width(), st::lineWidth); + _pinnedBar->shadow->setGeometry(0, st::historyReplyHeight, width(), st::lineWidth); } else if (_scroll.y() != 0) { _scroll.move(0, 0); _reportSpamPanel.move(0, 0); @@ -7114,14 +7122,14 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh newScrollHeight -= (_field.height() + 2 * st::sendPadding); } if (_editMsgId || replyToId() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) { - newScrollHeight -= st::replyHeight; + newScrollHeight -= st::historyReplyHeight; } if (_kbShown) { newScrollHeight -= _kbScroll.height(); } } if (_pinnedBar) { - newScrollHeight -= st::replyHeight; + newScrollHeight -= st::historyReplyHeight; } int wasScrollTop = _scroll.scrollTop(); bool wasAtBottom = wasScrollTop + 1 > _scroll.scrollTopMax(); @@ -7339,7 +7347,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { if (_kbReplyTo && !_replyToId) { updateReplyToName(); _replyEditMsgText.setText(st::msgFont, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); - _fieldBarCancel.show(); + _fieldBarCancel->show(); updateMouseTracking(); } } else { @@ -7354,7 +7362,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _kbShown = false; _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); updateMouseTracking(); } } @@ -7370,7 +7378,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _kbShown = false; _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId && !_editMsgId) { - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); updateMouseTracking(); } } @@ -7414,8 +7422,8 @@ void HistoryWidget::updateToEndVisibility() { } void HistoryWidget::mousePressEvent(QMouseEvent *e) { - _replyForwardPressed = QRect(0, _field.y() - st::sendPadding - st::replyHeight, st::replySkip, st::replyHeight).contains(e->pos()); - if (_replyForwardPressed && !_fieldBarCancel.isHidden()) { + _replyForwardPressed = QRect(0, _field.y() - st::sendPadding - st::historyReplyHeight, st::historyReplySkip, st::historyReplyHeight).contains(e->pos()); + if (_replyForwardPressed && !_fieldBarCancel->isHidden()) { updateField(); } else if (_inRecord && cHasAudioCapture()) { emit audioCapture()->start(); @@ -7505,7 +7513,7 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot } bool channelPost = _peer->isChannel() && !_peer->isMegagroup(); bool showFromName = !channelPost || _peer->asChannel()->addsSignature(); - bool silentPost = channelPost && _silent.checked(); + bool silentPost = channelPost && _silent->checked(); if (channelPost) { flags |= MTPDmessage::Flag::f_views; flags |= MTPDmessage::Flag::f_post; @@ -7528,7 +7536,7 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot result->addToHistory(_history, flags, messageId, messageFromId, messageDate, messageViaBotId, replyToId()); _history->sendRequestId = MTP::send(MTPmessages_SendInlineBotResult(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_long(randomId), MTP_long(result->getQueryId()), MTP_string(result->getId())), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); - App::main()->finishForwarding(_history, _silent.checked()); + App::main()->finishForwarding(_history, _silent->checked()); cancelReply(lastKeyboardUsed); App::historyRegRandom(randomId, newId); @@ -7557,7 +7565,7 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot HistoryWidget::PinnedBar::PinnedBar(MsgId msgId, HistoryWidget *parent) : msgId(msgId) -, cancel(parent, st::replyCancel) +, cancel(parent, st::historyReplyCancel) , shadow(parent, st::shadowColor) { } @@ -7632,7 +7640,7 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { result = true; if (_scroll.scrollTop() != unreadBarTop()) { - _scroll.scrollToY(_scroll.scrollTop() + st::replyHeight); + _scroll.scrollToY(_scroll.scrollTop() + st::historyReplyHeight); } } else if (_pinnedBar->msgId != pinnedMsgId) { _pinnedBar->msgId = pinnedMsgId; @@ -7648,7 +7656,7 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { destroyPinnedBar(); result = true; if (_scroll.scrollTop() != unreadBarTop()) { - _scroll.scrollToY(_scroll.scrollTop() - st::replyHeight); + _scroll.scrollToY(_scroll.scrollTop() - st::historyReplyHeight); } resizeEvent(0); } @@ -7687,7 +7695,7 @@ bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti } bool channelPost = _peer->isChannel() && !_peer->isMegagroup(); bool showFromName = !channelPost || _peer->asChannel()->addsSignature(); - bool silentPost = channelPost && _silent.checked(); + bool silentPost = channelPost && _silent->checked(); if (channelPost) { flags |= MTPDmessage::Flag::f_views; flags |= MTPDmessage::Flag::f_post; @@ -7701,7 +7709,7 @@ bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti _history->addNewDocument(newId.msg, flags, 0, replyToId(), date(MTP_int(unixtime())), showFromName ? MTP::authedId() : 0, doc, caption, MTPnullMarkup); _history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaDocument(mtpInput, MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); - App::main()->finishForwarding(_history, _silent.checked()); + App::main()->finishForwarding(_history, _silent->checked()); cancelReplyAfterMediaSend(lastKeyboardUsed); if (doc->sticker()) App::main()->incrementSticker(doc); @@ -7742,7 +7750,7 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption) } bool channelPost = _peer->isChannel() && !_peer->isMegagroup(); bool showFromName = !channelPost || _peer->asChannel()->addsSignature(); - bool silentPost = channelPost && _silent.checked(); + bool silentPost = channelPost && _silent->checked(); if (channelPost) { flags |= MTPDmessage::Flag::f_views; flags |= MTPDmessage::Flag::f_post; @@ -7756,7 +7764,7 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption) _history->addNewPhoto(newId.msg, flags, 0, replyToId(), date(MTP_int(unixtime())), showFromName ? MTP::authedId() : 0, photo, caption, MTPnullMarkup); _history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaPhoto(MTP_inputPhoto(MTP_long(photo->id), MTP_long(photo->access)), MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); - App::main()->finishForwarding(_history, _silent.checked()); + App::main()->finishForwarding(_history, _silent->checked()); cancelReplyAfterMediaSend(lastKeyboardUsed); App::historyRegRandom(randomId, newId); @@ -7818,7 +7826,7 @@ void HistoryWidget::onReplyToMessage() { updateBotKeyboard(); - if (!_field.isHidden()) _fieldBarCancel.show(); + if (!_field.isHidden()) _fieldBarCancel->show(); updateMouseTracking(); updateReplyToName(); resizeEvent(0); @@ -7871,7 +7879,7 @@ void HistoryWidget::onEditMessage() { updateBotKeyboard(); - if (!_field.isHidden()) _fieldBarCancel.show(); + if (!_field.isHidden()) _fieldBarCancel->show(); updateFieldPlaceholder(); updateMouseTracking(); updateReplyToName(); @@ -7964,7 +7972,7 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) { _replyToId = 0; mouseMoveEvent(0); if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) { - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); updateMouseTracking(); } @@ -8026,7 +8034,7 @@ void HistoryWidget::cancelEdit() { mouseMoveEvent(nullptr); if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !replyToId()) { - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); updateMouseTracking(); } @@ -8089,7 +8097,7 @@ void HistoryWidget::previewCancel() { _previewLinks.clear(); updatePreview(); if (!_editMsgId && !_replyToId && !readyToForward() && !_kbReplyTo) { - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); updateMouseTracking(); } } @@ -8155,7 +8163,7 @@ void HistoryWidget::gotPreview(QString links, const MTPMessageMedia &result, mtp void HistoryWidget::updatePreview() { _previewTimer.stop(); if (_previewData && _previewData->pendingTill >= 0) { - _fieldBarCancel.show(); + _fieldBarCancel->show(); updateMouseTracking(); if (_previewData->pendingTill) { _previewTitle.setText(st::msgServiceNameFont, lang(lng_preview_loading), _textNameOptions); @@ -8199,7 +8207,7 @@ void HistoryWidget::updatePreview() { _previewDescription.setText(st::msgFont, textClean(desc), _textDlgOptions); } } else if (!readyToForward() && !replyToId() && !_editMsgId) { - _fieldBarCancel.hide(); + _fieldBarCancel->hide(); updateMouseTracking(); } resizeEvent(0); @@ -8461,7 +8469,7 @@ void HistoryWidget::updateReplyEditTexts(bool force) { updateBotKeyboard(); if (!_field.isHidden() || _recording) { - _fieldBarCancel.show(); + _fieldBarCancel->show(); updateMouseTracking(); } updateReplyToName(); @@ -8505,21 +8513,21 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { if (!_editMsgId && drawMsgText && drawMsgText->author()->nameVersion > _replyToNameVersion) { updateReplyToName(); } - backy -= st::replyHeight; - backh += st::replyHeight; + backy -= st::historyReplyHeight; + backh += st::historyReplyHeight; } else if (hasForward) { App::main()->fillForwardingInfo(from, text, serviceColor, preview); - backy -= st::replyHeight; - backh += st::replyHeight; + backy -= st::historyReplyHeight; + backh += st::historyReplyHeight; } else if (_previewData && _previewData->pendingTill >= 0) { - backy -= st::replyHeight; - backh += st::replyHeight; + backy -= st::historyReplyHeight; + backh += st::historyReplyHeight; } bool drawPreview = (_previewData && _previewData->pendingTill >= 0) && !_replyForwardPressed; p.fillRect(0, backy, width(), backh, st::taMsgField.bgColor); if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { - int32 replyLeft = st::replySkip; - p.drawSprite(QPoint(st::replyIconPos.x(), backy + st::replyIconPos.y()), _editMsgId ? st::editIcon : st::replyIcon); + int32 replyLeft = st::historyReplySkip; + (_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); if (!drawPreview) { if (drawMsgText) { if (drawMsgText->getMedia() && drawMsgText->getMedia()->hasReplyPreview()) { @@ -8530,23 +8538,23 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { } replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } - p.setPen(st::replyColor); + p.setPen(st::historyReplyColor); if (_editMsgId) { paintEditHeader(p, rect, replyLeft, backy); } else { - _replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); + _replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); } p.setPen((((drawMsgText->toHistoryMessage() && drawMsgText->toHistoryMessage()->emptyText()) || drawMsgText->serviceMsg()) ? st::msgInDateFg : st::msgColor)->p); - _replyEditMsgText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); + _replyEditMsgText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); } else { p.setFont(st::msgDateFont->f); p.setPen(st::msgInDateFg->p); - p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - replyLeft - _fieldBarCancel.width() - st::msgReplyPadding.right())); + p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - replyLeft - _fieldBarCancel->width() - st::msgReplyPadding.right())); } } } else if (from && text) { - int32 forwardLeft = st::replySkip; - p.drawSprite(QPoint(st::replyIconPos.x(), backy + st::replyIconPos.y()), st::forwardIcon); + int forwardLeft = st::historyReplySkip; + st::historyForwardIcon.paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); if (!drawPreview) { if (!preview->isNull()) { QRect to(forwardLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); @@ -8558,15 +8566,15 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { } forwardLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } - p.setPen(st::replyColor->p); - from->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top(), width() - forwardLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); + p.setPen(st::historyReplyColor->p); + from->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top(), width() - forwardLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); p.setPen((serviceColor ? st::msgInDateFg : st::msgColor)->p); - text->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - forwardLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); + text->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - forwardLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); } } if (drawPreview) { - int32 previewLeft = st::replySkip + st::webPageLeft; - p.fillRect(st::replySkip, backy + st::msgReplyPadding.top(), st::webPageBar, st::msgReplyBarSize.height(), st::msgInReplyBarColor->b); + int32 previewLeft = st::historyReplySkip + st::webPageLeft; + p.fillRect(st::historyReplySkip, backy + st::msgReplyPadding.top(), st::webPageBar, st::msgReplyBarSize.height(), st::msgInReplyBarColor->b); if ((_previewData->photo && !_previewData->photo->thumb->isNull()) || (_previewData->document && !_previewData->document->thumb->isNull())) { ImagePtr replyPreview = _previewData->photo ? _previewData->photo->makeReplyPreview() : _previewData->document->makeReplyPreview(); if (!replyPreview->isNull()) { @@ -8580,10 +8588,10 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { } previewLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } - p.setPen(st::replyColor->p); - _previewTitle.drawElided(p, previewLeft, backy + st::msgReplyPadding.top(), width() - previewLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); + p.setPen(st::historyReplyColor->p); + _previewTitle.drawElided(p, previewLeft, backy + st::msgReplyPadding.top(), width() - previewLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); p.setPen(st::msgColor->p); - _previewDescription.drawElided(p, previewLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - previewLeft - _fieldBarCancel.width() - st::msgReplyPadding.right()); + _previewDescription.drawElided(p, previewLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - previewLeft - _fieldBarCancel->width() - st::msgReplyPadding.right()); } } @@ -8676,7 +8684,7 @@ void HistoryWidget::drawPinnedBar(Painter &p) { Text *from = 0, *text = 0; bool serviceColor = false, hasForward = readyToForward(); ImagePtr preview; - p.fillRect(0, 0, width(), st::replyHeight, st::taMsgField.bgColor); + p.fillRect(0, 0, width(), st::historyReplyHeight, st::taMsgField.bgColor); QRect rbar(rtlrect(st::msgReplyBarSkip + st::msgReplyBarPos.x(), st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), width())); p.fillRect(rbar, st::msgInReplyBarColor); @@ -8691,7 +8699,7 @@ void HistoryWidget::drawPinnedBar(Painter &p) { } left += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } - p.setPen(st::replyColor); + p.setPen(st::historyReplyColor); p.setFont(st::msgServiceNameFont); p.drawText(left, st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, lang(lng_pinned_message)); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index d5a81ba86..87999b2bb 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localimageloader.h" #include "ui/effects/rect_shadow.h" #include "ui/widgets/tooltip.h" +#include "ui/buttons/icon_button.h" #include "history/history_common.h" #include "history/field_autocomplete.h" #include "window/section_widget.h" @@ -508,18 +509,27 @@ private: }; -class SilentToggle : public FlatCheckbox, public Ui::AbstractTooltipShower { +class SilentToggle : public Ui::IconButton, public Ui::AbstractTooltipShower { public: - SilentToggle(QWidget *parent); - void mouseMoveEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void leaveEvent(QEvent *e) override; + + void setChecked(bool checked); + bool checked() const { + return _checked; + } // AbstractTooltipShower interface QString tooltipText() const override; QPoint tooltipPos() const override; +protected: + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + +private: + bool _checked = false; + }; EntitiesInText entitiesFromTextTags(const TextWithTags::Tags &tags); @@ -885,7 +895,7 @@ private: Text _replyEditMsgText; mutable SingleTimer _updateEditTimeLeftDisplay; - IconedButton _fieldBarCancel; + ChildWidget _fieldBarCancel; void updateReplyEditTexts(bool force = false); struct PinnedBar { @@ -895,7 +905,7 @@ private: MsgId msgId = 0; HistoryItem *msg = nullptr; Text text; - ChildWidget cancel; + ChildWidget cancel; ChildWidget shadow; }; std_::unique_ptr _pinnedBar; @@ -1075,7 +1085,7 @@ private: UserData *_inlineBot = nullptr; QString _inlineBotUsername; mtpRequestId _inlineBotResolveRequestId = 0; - std_::unique_ptr _inlineBotCancel; + std_::unique_ptr _inlineBotCancel; void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result); bool inlineBotResolveFail(QString name, const RPCError &error); @@ -1096,7 +1106,7 @@ private: ChildWidget _botKeyboardShow; ChildWidget _botKeyboardHide; ChildWidget _botCommandStart; - SilentToggle _silent; + ChildWidget _silent; bool _cmdStartShown = false; MessageField _field; Animation _a_record, _a_recording; diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index e56d5f7b7..a6d8e2077 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -25,20 +25,15 @@ introErrLabel: flatLabel(labelDefFlat) { font: introErrFont; align: align(center); } -introBackButton: IconButton { +introBackButton: IconButton(defaultIconButton) { width: 40px; height: 40px; - opacity: 0.71; - overOpacity: 1.; - icon: icon { { size(40px, 40px), #eeeeee }, { "title_previous", #969696, point(12px, 12px) }, }; iconPosition: point(0px, 0px); downIconPosition: point(0px, 0px); - - duration: 150; } introBackPosition: point(32px, 32px); diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index 74034244f..153e470a4 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -138,7 +138,7 @@ void IntroSignup::paintEvent(QPaintEvent *e) { } else { p.fillRect(phRect, st::newGroupPhotoBg); } - p.drawSpriteCenter(phRect, st::newGroupPhotoIcon); + st::newGroupPhotoIcon.paintInCenter(p, phRect); } else { p.drawPixmap(_phLeft, _phTop, _photoSmall); } diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index e75fd1d1d..4bf11a7b8 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -21,12 +21,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "styles/style_overview.h" +#include "styles/style_dialogs.h" #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" #include "boxes/photocropbox.h" #include "ui/filedialog.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/tooltip.h" +#include "ui/buttons/icon_button.h" #include "window/top_bar_widget.h" #include "window/chat_background.h" #include "lang.h" @@ -52,7 +54,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD , _channel(peerToChannel(_peer->id)) , _rowWidth(st::msgMinWidth) , _search(this, st::dlgFilter, lang(lng_dlg_filter)) -, _cancelSearch(this, st::btnCancelSearch) +, _cancelSearch(this, st::dialogsCancelSearch) , _itemsToBeLoaded(LinksOverviewPerPage * 2) , _width(st::wndMinWidth) { subscribe(FileDownload::ImageLoaded(), [this] { update(); }); @@ -73,14 +75,14 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD mediaOverviewUpdated(); setMouseTracking(true); - connect(&_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); + connect(_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); connect(&_search, SIGNAL(cancelled()), this, SLOT(onCancel())); connect(&_search, SIGNAL(changed()), this, SLOT(onSearchUpdate())); _searchTimer.setSingleShot(true); connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchMessages())); - _cancelSearch.hide(); + _cancelSearch->hide(); if (_type == OverviewLinks || _type == OverviewFiles) { _search.show(); } else { @@ -1280,7 +1282,7 @@ int32 OverviewInner::resizeToWidth(int32 nwidth, int32 scrollTop, int32 minHeigh _rowsLeft = (_width - _rowWidth) / 2; _search.setGeometry(_rowsLeft, st::linksSearchMargin.top(), _rowWidth, _search.height()); - _cancelSearch.moveToLeft(_rowsLeft + _rowWidth - _cancelSearch.width(), _search.y()); + _cancelSearch->moveToLeft(_rowsLeft + _rowWidth - _cancelSearch->width(), _search.y()); if (_type == OverviewPhotos || _type == OverviewVideos) { for (int32 i = 0, l = _items.size(); i < l; ++i) { @@ -1341,7 +1343,7 @@ void OverviewInner::switchType(MediaOverviewType type) { _search.updatePlaceholder(); onSearchUpdate(); } - _cancelSearch.hide(); + _cancelSearch->hide(); resizeToWidth(_width, 0, _minHeight, true); } @@ -1470,9 +1472,9 @@ void OverviewInner::onSearchUpdate() { _searchQueries.clear(); _searchQuery = QString(); _searchResults.clear(); - _cancelSearch.hide(); - } else if (_cancelSearch.isHidden()) { - _cancelSearch.show(); + _cancelSearch->hide(); + } else if (_cancelSearch->isHidden()) { + _cancelSearch->show(); } if (changed) { @@ -1494,7 +1496,7 @@ void OverviewInner::onCancel() { bool OverviewInner::onCancelSearch() { if (_search.isHidden()) return false; bool clearing = !_search.text().isEmpty(); - _cancelSearch.hide(); + _cancelSearch->hide(); _search.clear(); _search.updatePlaceholder(); onSearchUpdate(); diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index abd092a8d..32a7c7e47 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -34,6 +34,7 @@ class Date; namespace Ui { class PlainShadow; class PopupMenu; +class IconButton; } // namespace Ui class OverviewWidget; @@ -179,7 +180,7 @@ private: int32 setLayoutItem(int32 index, Overview::Layout::AbstractItem *item, int32 top); FlatInput _search; - IconedButton _cancelSearch; + ChildWidget _cancelSearch; QVector _results; int32 _itemsToBeLoaded; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index e7f9881cb..7e3d6c00a 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -33,18 +33,16 @@ settingsFixedBarFont: font(14px semibold); settingsFixedBarFg: windowTextFg; settingsFixedBarTextLeft: 20px; settingsFixedBarTextTop: 16px; -settingsFixedBarClose: IconButton { +settingsFixedBarClose: IconButton(defaultIconButton) { width: settingsFixedBarHeight; height: settingsFixedBarHeight; opacity: 0.31; overOpacity: 0.5; - icon: icon {{ "settings_close", #000000, point(0px, 0px) }}; + icon: boxCancelIcon; iconPosition: point(20px, 20px); downIconPosition: point(20px, 20px); - - duration: 200; } settingsFixedBarShadowBg1: #00000021; settingsFixedBarShadowBg2: #0000000b; diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index ffa997456..c3475d53c 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -2427,14 +2427,14 @@ EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool , _setId(setId) , _special(special) , _deleteVisible(false) -, _delete(special ? 0 : new IconedButton(this, st::simpleClose)) { // Stickers::NoneSetId if in emoji +, _delete(special ? 0 : new Ui::IconButton(this, st::hashtagClose)) { // 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::simpleClose.icon.pxWidth()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); + _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::hashtagClose.icon.width()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); } } @@ -2452,7 +2452,7 @@ void EmojiPanel::updateText() { int32 availw = st::emojiPanWidth - st::emojiPanHeaderLeft * 2; if (_deleteVisible) { if (!_special && _setId != Stickers::NoneSetId) { - availw -= st::simpleClose.icon.pxWidth() + st::emojiPanHeaderLeft; + availw -= st::hashtagClose.icon.width() + st::emojiPanHeaderLeft; } } else { auto switchText = ([this]() { diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index 3beab8728..7545ae805 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -459,7 +459,7 @@ private: QString _text, _fullText; uint64 _setId; bool _special, _deleteVisible; - IconedButton *_delete; + Ui::IconButton *_delete; }; diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 4eea840ee..f5d08bb54 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -162,3 +162,12 @@ stickerIconMove: 400; stickerPreviewDuration: 150; stickerPreviewBg: #FFFFFFB0; stickerPreviewMin: 0.1; + +hashtagClose: IconButton(defaultIconButton) { + width: 30px; + height: 30px; + + icon: simpleCloseIcon; + iconPosition: point(10px, 10px); + downIconPosition: point(10px, 11px); +} diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.cpp b/Telegram/SourceFiles/ui/buttons/icon_button.cpp index 2a562d289..9720e0288 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/icon_button.cpp @@ -40,8 +40,12 @@ void IconButton::paintEvent(QPaintEvent *e) { auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); p.setOpacity(over * _st.overOpacity + (1. - over) * _st.opacity); + auto icon = (_iconOverride ? _iconOverride : &_st.icon); auto position = (_state & StateDown) ? _st.downIconPosition : _st.iconPosition; - (_iconOverride ? _iconOverride : &_st.icon)->paint(p, position, width()); + if (position.x() < 0) { + position.setX((width() - icon->width()) / 2); + } + icon->paint(p, position, width()); } void IconButton::onStateChanged(int oldState, ButtonStateChangeSource source) { diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 959c7cc75..48c61dd98 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -167,6 +167,12 @@ defaultLabelSimple: LabelSimple { textFg: windowTextFg; } +defaultIconButton: IconButton { + opacity: 0.78; + overOpacity: 1.; + duration: 150; +} + widgetSlideDuration: 200; widgetFadeDuration: 200; diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index 490bad48b..a4ba92062 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -244,7 +244,7 @@ private: PeerData *_author; HistoryItem *_item; int _forwardedCount; - ChildWidget _close; + ChildWidget _close; ChildWidget _reply; ChildWidget _background = { nullptr }; ChildWidget _replyArea = { nullptr }; diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index a7da80f3b..7f7cb09b2 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -37,7 +37,13 @@ notifyPhotoSize: 62px; notifyMacPhotoSize: 64px; notifyPhotoPos: point(9px, 9px); notifyClosePos: point(1px, 2px); -notifyClose: iconedButton(simpleClose) { +notifyClose: IconButton(defaultIconButton) { + width: 30px; + height: 30px; + + icon: simpleCloseIcon; + iconPosition: point(10px, 10px); + downIconPosition: point(10px, 11px); } notifyItemTop: 12px; notifyTextLeft: 12px; @@ -64,16 +70,11 @@ notifyReplyArea: InputArea(defaultInputArea) { borderActive: 0px; borderError: 0px; } -notifySendReply: IconButton { +notifySendReply: IconButton(defaultIconButton) { width: 36px; height: 36px; - opacity: 0.78; - overOpacity: 1.; - icon: icon {{ "notification_send", windowActiveBg, point(3px, 9px) }}; iconPosition: point(0px, 0px); downIconPosition: point(0px, 1px); - - duration: notifyFastAnim; } From 42e544abff73aa389c12513894d355a0daaae75e Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 28 Oct 2016 11:02:45 +0300 Subject: [PATCH 004/100] All icons were moved from sprites to separate icon files. --- Telegram/Resources/art/sprite.png | Bin 5286 -> 775 bytes Telegram/Resources/art/sprite_200x.png | Bin 10769 -> 2825 bytes Telegram/Resources/basic.style | 11 ++-- Telegram/Resources/icons/emoji_delete.png | Bin 0 -> 131 bytes Telegram/Resources/icons/emoji_delete@2x.png | Bin 0 -> 182 bytes Telegram/Resources/icons/emoji_delete_bg.png | Bin 0 -> 137 bytes .../Resources/icons/emoji_delete_bg@2x.png | Bin 0 -> 243 bytes .../Resources/icons/emoji_switch_left.png | Bin 0 -> 145 bytes .../Resources/icons/emoji_switch_left@2x.png | Bin 0 -> 236 bytes .../Resources/icons/emoji_switch_right.png | Bin 0 -> 145 bytes .../Resources/icons/emoji_switch_right@2x.png | Bin 0 -> 213 bytes .../inline_bot_layout_internal.cpp | 6 +- Telegram/SourceFiles/mediaview.cpp | 54 +++++++++++------- Telegram/SourceFiles/stickers/emoji_pan.cpp | 24 ++++---- Telegram/SourceFiles/stickers/stickers.style | 13 ++--- Telegram/SourceFiles/window/window.style | 2 - 16 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 Telegram/Resources/icons/emoji_delete.png create mode 100644 Telegram/Resources/icons/emoji_delete@2x.png create mode 100644 Telegram/Resources/icons/emoji_delete_bg.png create mode 100644 Telegram/Resources/icons/emoji_delete_bg@2x.png create mode 100644 Telegram/Resources/icons/emoji_switch_left.png create mode 100644 Telegram/Resources/icons/emoji_switch_left@2x.png create mode 100644 Telegram/Resources/icons/emoji_switch_right.png create mode 100644 Telegram/Resources/icons/emoji_switch_right@2x.png diff --git a/Telegram/Resources/art/sprite.png b/Telegram/Resources/art/sprite.png index d76672e0b11f672fbcdc43c98d539ad48c70b9e1..5c0c7a7ac15a78aadeb00b712a4e2f958c7c724c 100644 GIT binary patch literal 775 zcmeAS@N?(olHy`uVBq!ia0y~yVC(_ny&P;n5jJ^8Ck6(lbDl1aAr*7po-^cSFyLS? zkaJPWo0Y~XDR4{PKsEJLlA@$}>QwcwQN>FVdQ&MBb@ E08Dllvj6}9 literal 5286 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 diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index cc07382ad..e3c5f77eb 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -1363,10 +1363,6 @@ radialLine: 3px; radialDuration: 350; radialPeriod: 3000; radialBgOpacity: 0.4; -radialDownload: sprite(346px, 0px, 50px, 50px); -radialDownloadOpacity: 0.8; -radialCancel: sprite(378px, 50px, 18px, 18px); -radialCancelOpacity: 1.0; downloadPathSkip: 10px; @@ -1488,3 +1484,10 @@ profileTopBarBackIcon: icon {{ "title_previous", profileTopBarBackIconFg }}; boxCancelIcon: icon {{ "box_cancel", #000000 }}; historyReplyCancelIcon: icon {{ "box_cancel", #adadad }}; + +notifyBg: white; +notifyFadeRight: icon {{ "fade_horizontal_right", notifyBg }}; + +emojiPanCategories: #f7f7f7; +stickerIconLeft: icon {{ "fade_horizontal_left", emojiPanCategories }}; +stickerIconRight: icon {{ "fade_horizontal_right", emojiPanCategories }}; diff --git a/Telegram/Resources/icons/emoji_delete.png b/Telegram/Resources/icons/emoji_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..8023fa4239288690840881fb33506c7229d1c148 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kFHaZ85RHj*`#rfB6nK~y|NkF6 z&um^x7xy;Xt%+>2)Hqd_r9bf9u$OT;kLL}x17_=e5+fV9_izR7IbP0l+XkKhT1Ka literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_delete@2x.png b/Telegram/Resources/icons/emoji_delete@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bae82620aca7ef381c0e863294fc36683c98a73b GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjwVp1HAs(G~Lml}JC~zG9``^D- z%6g}c6xEyu mOWwJAqtRyz0l@=bq|60kwqO2~IP*WyYz9wPKbLh*2~7azdo-*7 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_delete_bg@2x.png b/Telegram/Resources/icons/emoji_delete_bg@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..08c32768571e0f9152b4cf9e21e59c28b467450f GIT binary patch literal 243 zcmVBQer8Ez4yz~-aAVvOeuZbmM4~&y~0CyPO1O^002ovPDHLkV1oE`VRQfh literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_switch_left.png b/Telegram/Resources/icons/emoji_switch_left.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b3f9089f5c9fa2f05da7cd2de6db5b57ad0d2f GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^96-#&!3HGb=lz)rq#`_B9780g)=oIcd%%E$+4riV z;3GxMg*w90JL;}WW$ vUP-OFr8G_V??g_k>vJyrU;OU!?08$x71Q`0%t-MA+QH!I>gTe~DWM4f0`@g# literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_switch_left@2x.png b/Telegram/Resources/icons/emoji_switch_left@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..63c5dc067c68f9112a8123b3b7e641d0c3980f73 GIT binary patch literal 236 zcmVi%1m62~rq)GL zb3zDtUJvI?(_|AI!7)a4&XolP_`kpap9_rev7iFp3o7AXK?A%A8sU%N0;~_*kn2Y% zN&3FG2^uA~BBhkhB=#zwNa`+AN=b)dunCNkwg)9ko&Z=X$~?~y5sc%w&bqF{vMgJX mYIj0X0qCpr_wSJ0eZK*G=#7uARLa}{00005o(<^SS^K-+#2S08D+QH!I>gTe~DWM4fx4tz^ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_switch_right@2x.png b/Telegram/Resources/icons/emoji_switch_right@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5230585da16b18a8bc78483214f41bd1b19ab98d GIT binary patch literal 213 zcmV;`04o29P)} zf?hkdR=&#wNhxuR5rKd~$Z`olwuck44>%ylpb>JOngQ~;K^Gz4zv&m`em2`y{ig&G z@i)<6J_1C9b57qNIm{a%=j<^rfPk3@nE|4d;y14)sH)e@2Y}rwx32Xk^SY@uYgRtM P00000NkvXXu0mjfz>rjA literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 3dd81b3df..ed7b1e7db 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -199,16 +199,16 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons if (_delete && (_state & StateFlag::Over)) { float64 deleteOver = _a_deleteOver.current(context->ms, (_state & StateFlag::DeleteOver) ? 1 : 0); - QPoint deletePos = QPoint(_width - st::stickerPanDelete.pxWidth(), 0); + QPoint deletePos = QPoint(_width - st::stickerPanDelete.width(), 0); p.setOpacity(deleteOver + (1 - deleteOver) * st::stickerPanDeleteOpacity); - p.drawSpriteLeft(deletePos, _width, st::stickerPanDelete); + st::stickerPanDelete.paint(p, deletePos, _width); p.setOpacity(1); } } void Gif::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const { if (x >= 0 && x < _width && y >= 0 && y < st::inlineMediaHeight) { - if (_delete && (rtl() ? _width - x : x) >= _width - st::stickerPanDelete.pxWidth() && y < st::stickerPanDelete.pxHeight()) { + if (_delete && (rtl() ? _width - x : x) >= _width - st::stickerPanDelete.width() && y < st::stickerPanDelete.height()) { link = _delete; } else { link = _send; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 6b497473f..fd3538c89 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -19,9 +19,9 @@ 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 "mediaview.h" + +#include "lang.h" #include "mainwidget.h" #include "mainwindow.h" #include "application.h" @@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/media_clip_reader.h" #include "media/view/media_clip_controller.h" #include "styles/style_mediaview.h" +#include "styles/style_history.h" #include "media/media_audio.h" #include "history/history_media_types.h" @@ -501,8 +502,9 @@ void MediaView::step_radial(uint64 ms, bool timer) { _radial.stop(); return; } + auto wasAnimating = _radial.animating(); _radial.update(radialProgress(), !radialLoading(), ms + radialTimeShift()); - if (timer && _radial.animating()) { + if (timer && (wasAnimating || _radial.animating())) { update(radialRect()); } if (_doc && _doc->loaded() && _doc->size < MediaViewImageSizeLimit && (!_radial.animating() || _doc->isAnimation() || _doc->isVideo())) { @@ -1649,7 +1651,7 @@ void MediaView::paintEvent(QPaintEvent *e) { } } else { if (_docRect.intersects(r)) { - p.fillRect(_docRect, st::mvDocBg->b); + p.fillRect(_docRect, st::mvDocBg); if (_docIconRect.intersects(r)) { bool radial = false; float64 radialOpacity = 0; @@ -1815,30 +1817,40 @@ void MediaView::paintEvent(QPaintEvent *e) { void MediaView::paintDocRadialLoading(Painter &p, bool radial, float64 radialOpacity) { float64 o = overLevel(OverIcon); - if (radial) { - if (!_doc->loaded() && radialOpacity < 1) { - p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity) * (1 - radialOpacity)); - p.drawSpriteCenter(_docIconRect, st::radialDownload); - } - + if (radial || (_doc && !_doc->loaded())) { QRect inner(QPoint(_docIconRect.x() + ((_docIconRect.width() - st::radialSize.width()) / 2), _docIconRect.y() + ((_docIconRect.height() - st::radialSize.height()) / 2)), st::radialSize); + p.setPen(Qt::NoPen); - p.setBrush(st::black); - p.setOpacity(radialOpacity * st::radialBgOpacity); + if (o == 0.) { + p.setOpacity(_doc->loaded() ? radialOpacity : 1.); + p.setBrush(st::msgDateImgBg); + } else if (o == 1.) { + p.setOpacity(_doc->loaded() ? radialOpacity : 1.); + p.setBrush(st::msgDateImgBgOver); + } else { + p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - o)) + (st::msgDateImgBgOver->c.alphaF() * o)); + p.setBrush(st::black); + } p.setRenderHint(QPainter::HighQualityAntialiasing); p.drawEllipse(inner); p.setRenderHint(QPainter::HighQualityAntialiasing, false); - p.setOpacity((o * 1. + (1 - o) * st::radialCancelOpacity) * radialOpacity); - p.drawSpriteCenter(_docIconRect, st::radialCancel); - p.setOpacity(1); - - QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine))); - _radial.draw(p, arc, st::radialLine, st::white); - } else if (_doc && !_doc->loaded()) { - p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity)); - p.drawSpriteCenter(_docIconRect, st::radialDownload); + p.setOpacity(1.); + auto icon = ([radial, this]() -> const style::icon* { + if (radial || _doc->loading()) { + return &st::historyFileInCancel; + } + return &st::historyFileInDownload; + })(); + if (icon) { + icon->paintInCenter(p, inner); + } + if (radial) { + p.setOpacity(1); + QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine))); + _radial.draw(p, arc, st::radialLine, st::white); + } } } diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index c3475d53c..e1252378c 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -1137,9 +1137,9 @@ void StickerPanInner::paintSticker(Painter &p, Set &set, int y, int index) { 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); + QPoint xPos = pos + QPoint(st::stickerPanSize.width() - st::stickerPanDelete.width(), 0); p.setOpacity(hover * (xHover + (1 - xHover) * st::stickerPanDeleteOpacity)); - p.drawSpriteLeft(xPos, width(), st::stickerPanDelete); + st::stickerPanDelete.paint(p, xPos, width()); p.setOpacity(1); } } @@ -2197,7 +2197,7 @@ void StickerPanInner::updateSelected() { } 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()) { + if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.width() && iny < st::stickerPanDelete.height()) { selIndex += set.pack.size(); } } @@ -2531,14 +2531,14 @@ void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { void EmojiSwitchButton::paintEvent(QPaintEvent *e) { Painter p(this); - p.setFont(st::emojiPanHeaderFont->f); - p.setPen(st::emojiSwitchColor->p); + p.setFont(st::emojiPanHeaderFont); + p.setPen(st::emojiSwitchColor); 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); + st::emojiSwitchStickers.paint(p, width() - st::emojiSwitchImgSkip, (st::emojiPanHeader - st::emojiSwitchStickers.height()) / 2, width()); } 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); + p.drawTextRight(st::emojiSwitchImgSkip - st::emojiSwitchEmoji.width(), st::emojiPanHeaderTop, width(), lang(lng_switch_emoji), _textWidth); + st::emojiSwitchEmoji.paint(p, width() - st::emojiSwitchSkip - _textWidth, (st::emojiPanHeader - st::emojiSwitchEmoji.height()) / 2, width()); } } @@ -2803,15 +2803,15 @@ void EmojiPan::paintEvent(QPaintEvent *e) { p.setOpacity(1.); p.fillRect(selx, _iconsTop + st::emojiCategory.height - st::stickerIconPadding, st::emojiCategory.width, st::stickerIconSel, st::stickerIconSelColor); - float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.); + float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.width(), 0., 1.); if (o_left > 0) { p.setOpacity(o_left); - p.drawSpriteLeft(QRect(_iconsLeft, _iconsTop, st::stickerIconLeft.pxWidth(), st::emojiCategory.height), width(), st::stickerIconLeft); + st::stickerIconLeft.fill(p, rtlrect(_iconsLeft, _iconsTop, st::stickerIconLeft.width(), st::emojiCategory.height, width())); } - float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.pxWidth(), 0., 1.); + float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), 0., 1.); if (o_right > 0) { p.setOpacity(o_right); - p.drawSpriteRight(QRect(width() - _iconsLeft - 7 * st::emojiCategory.width, _iconsTop, st::stickerIconRight.pxWidth(), st::emojiCategory.height), width(), st::stickerIconRight); + st::stickerIconRight.fill(p, rtlrect(width() - _iconsLeft - 7 * st::emojiCategory.width, _iconsTop, st::stickerIconRight.width(), st::emojiCategory.height, width())); } } } else if (_stickersShown) { diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index f5d08bb54..2d2c67324 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -101,8 +101,6 @@ emojiSymbolsActive: icon {{ "emoji_symbols", #58b2ed }}; emojiSavedGifs: icon {{ "emoji_gif", #b3b3b3 }}; emojiSavedGifsActive: icon {{ "emoji_gif", #58b2ed }}; -emojiPanCategories: #f7f7f7; - emojiCategory: IconButton { width: 42px; height: 46px; @@ -144,20 +142,21 @@ emojiColorsSepColor: #d5d5d5; emojiSwitchSkip: 27px; emojiSwitchImgSkip: 21px; -emojiSwitchStickers: sprite(318px, 328px, 8px, 12px); -emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px); emojiSwitchColor: #42a8db; +emojiSwitchStickers: icon {{ "emoji_switch_right", emojiSwitchColor }}; +emojiSwitchEmoji: icon {{ "emoji_switch_left", emojiSwitchColor }}; stickerPanSize: size(64px, 64px); stickerPanPadding: 11px; -stickerPanDelete: sprite(128px, 132px, 12px, 12px); +stickerPanDelete: icon { + { "emoji_delete_bg", #000000cc }, + { "emoji_delete", #ffffff }, +}; stickerPanDeleteOpacity: 0.5; stickerIconPadding: 5px; stickerIconOpacity: 0.7; stickerIconSel: 2px; stickerIconSelColor: #58b2ed; -stickerIconLeft: sprite(342px, 72px, 40px, 1px); -stickerIconRight: sprite(342px, 73px, 40px, 1px); stickerIconMove: 400; stickerPreviewDuration: 150; stickerPreviewBg: #FFFFFFB0; diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 7f7cb09b2..378c81bae 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -29,7 +29,6 @@ titleIcon: icon { }; titleCounterPosition: point(17px, 17px); -notifyBg: white; notifyBorder: #f1f1f1; notifyBorderWidth: 1px; notifySlowHide: 4000; @@ -60,7 +59,6 @@ notifyActionsDuration: 200; notifyHideAll: RoundButton(defaultBoxButton) { } -notifyFadeRight: icon {{ "fade_horizontal_right", notifyBg }}; notifyReplyArea: InputArea(defaultInputArea) { font: normalFont; textMargins: margins(8px, 8px, 8px, 6px); From 2e4426aac087be01f1b7b3cd4101520b1823d1b9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 28 Oct 2016 12:20:24 +0300 Subject: [PATCH 005/100] Old sprites removed completely. Only #rrggbb and #rrggbbaa in styles. Also moved Checkbox and Radiobutton to Ui namespace. Also child widgets in many boxes were wrapped in ChildWidget<>. --- Telegram/Resources/art/blank.gif | Bin 49 -> 0 bytes Telegram/Resources/art/sprite.png | Bin 775 -> 0 bytes Telegram/Resources/art/sprite_200x.png | Bin 2825 -> 0 bytes Telegram/Resources/basic.style | 120 ++-- Telegram/Resources/basic_types.style | 49 -- Telegram/Resources/telegram.qrc | 4 - Telegram/SourceFiles/app.cpp | 4 - Telegram/SourceFiles/app.h | 1 - Telegram/SourceFiles/boxes/addcontactbox.cpp | 533 +++++++++--------- Telegram/SourceFiles/boxes/addcontactbox.h | 65 ++- Telegram/SourceFiles/boxes/autolockbox.cpp | 3 +- Telegram/SourceFiles/boxes/autolockbox.h | 6 +- Telegram/SourceFiles/boxes/boxes.style | 8 +- Telegram/SourceFiles/boxes/confirmbox.cpp | 107 ++-- Telegram/SourceFiles/boxes/confirmbox.h | 29 +- Telegram/SourceFiles/boxes/connectionbox.cpp | 207 +++---- Telegram/SourceFiles/boxes/connectionbox.h | 37 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 103 ++-- Telegram/SourceFiles/boxes/contactsbox.h | 16 +- .../SourceFiles/boxes/downloadpathbox.cpp | 68 +-- Telegram/SourceFiles/boxes/downloadpathbox.h | 14 +- Telegram/SourceFiles/boxes/languagebox.cpp | 13 +- Telegram/SourceFiles/boxes/languagebox.h | 6 +- Telegram/SourceFiles/boxes/photosendbox.cpp | 94 +-- Telegram/SourceFiles/boxes/photosendbox.h | 17 +- Telegram/SourceFiles/boxes/report_box.cpp | 1 + Telegram/SourceFiles/boxes/report_box.h | 12 +- .../SourceFiles/codegen/style/generator.cpp | 15 +- .../SourceFiles/codegen/style/options.cpp | 8 - Telegram/SourceFiles/codegen/style/options.h | 2 - .../SourceFiles/codegen/style/parsed_file.cpp | 81 +-- .../SourceFiles/codegen/style/parsed_file.h | 2 - .../SourceFiles/codegen/style/processor.cpp | 47 +- .../codegen/style/sprite_generator.cpp | 180 ------ .../codegen/style/sprite_generator.h | 57 -- .../codegen/style/structure_types.cpp | 15 - .../codegen/style/structure_types.h | 8 - Telegram/SourceFiles/dialogs/dialogs.style | 2 +- Telegram/SourceFiles/history/history.style | 4 +- .../SourceFiles/media/view/mediaview.style | 8 +- Telegram/SourceFiles/overview/overview.style | 10 +- .../profile/profile_settings_widget.cpp | 4 +- .../profile/profile_settings_widget.h | 5 +- .../settings/settings_background_widget.cpp | 1 + .../settings/settings_background_widget.h | 7 +- .../settings/settings_block_widget.cpp | 10 +- .../settings/settings_block_widget.h | 11 +- .../settings_chat_settings_widget.cpp | 1 + .../settings/settings_chat_settings_widget.h | 8 +- .../settings/settings_general_widget.cpp | 2 +- .../settings/settings_general_widget.h | 14 +- .../settings_notifications_widget.cpp | 2 +- .../settings/settings_notifications_widget.h | 12 +- .../settings/settings_scale_widget.cpp | 1 + .../settings/settings_scale_widget.h | 5 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 9 +- Telegram/SourceFiles/stickers/stickers.style | 6 +- .../checkbox.cpp} | 180 +----- .../ui/{flatcheckbox.h => buttons/checkbox.h} | 63 +-- Telegram/SourceFiles/ui/flatbutton.cpp | 103 ---- Telegram/SourceFiles/ui/flatbutton.h | 31 - Telegram/SourceFiles/ui/style/style_core.cpp | 2 - Telegram/SourceFiles/ui/style/style_core.h | 4 - .../SourceFiles/ui/style/style_core_types.cpp | 42 -- .../SourceFiles/ui/style/style_core_types.h | 47 -- Telegram/SourceFiles/ui/twidget.h | 43 -- Telegram/SourceFiles/ui/widgets/widgets.style | 6 +- Telegram/gyp/Telegram.gyp | 4 +- Telegram/gyp/codegen.gyp | 2 - Telegram/gyp/codegen_rules.gypi | 21 +- 70 files changed, 876 insertions(+), 1726 deletions(-) delete mode 100644 Telegram/Resources/art/blank.gif delete mode 100644 Telegram/Resources/art/sprite.png delete mode 100644 Telegram/Resources/art/sprite_200x.png delete mode 100644 Telegram/SourceFiles/codegen/style/sprite_generator.cpp delete mode 100644 Telegram/SourceFiles/codegen/style/sprite_generator.h rename Telegram/SourceFiles/ui/{flatcheckbox.cpp => buttons/checkbox.cpp} (69%) rename Telegram/SourceFiles/ui/{flatcheckbox.h => buttons/checkbox.h} (72%) diff --git a/Telegram/Resources/art/blank.gif b/Telegram/Resources/art/blank.gif deleted file mode 100644 index 75b945d2553848b8b6f41fe5e24599c0687b8472..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49 zcmZ?wbhEHbWMp7unE0RJ|Ns9C3=9Vj8~~DvKUo+V7?>DzfNY>Fh|Ltj$Y2csQN9XW diff --git a/Telegram/Resources/art/sprite.png b/Telegram/Resources/art/sprite.png deleted file mode 100644 index 5c0c7a7ac15a78aadeb00b712a4e2f958c7c724c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 775 zcmeAS@N?(olHy`uVBq!ia0y~yVC(_ny&P;n5jJ^8Ck6(lbDl1aAr*7po-^cSFyLS? zkaJPWo0Y~XDR4{PKsEJLlA@$}>QwcwQN>FVdQ&MBb@ E08Dllvj6}9 diff --git a/Telegram/Resources/art/sprite_200x.png b/Telegram/Resources/art/sprite_200x.png deleted file mode 100644 index 231f501d70b304e783546cafadf907d9a9e24491..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2825 zcmeAS@N?(olHy`uVBq!ia0y~yV3q*lDh@V~h~s-x1_rJRo-U3d6?5L6GZbV{;9=Mp zwX&Y|J)fuyb7#{TpsGart/newmsg.wav art/bg.jpg art/bg0.png - art/sprite.png - art/sprite_125x.png - art/sprite_150x.png - art/sprite_200x.png art/icon256.png art/iconbig256.png diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index c44efaa90..475d302a6 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2370,10 +2370,6 @@ namespace { return ::monofont; } - const QPixmap &sprite() { - return style::spritePixmap(); - } - const QPixmap &emoji() { return *::emoji; } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index ec27125c1..0181a1660 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -212,7 +212,6 @@ namespace App { void clearMousedItems(); const style::font &monofont(); - const QPixmap &sprite(); const QPixmap &emoji(); const QPixmap &emojiLarge(); const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight); diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index df76cbf67..33389489a 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "confirmbox.h" #include "photocropbox.h" #include "ui/filedialog.h" +#include "ui/buttons/checkbox.h" #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" @@ -43,7 +44,7 @@ AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : Abst , _phone(this, st::defaultInputField, lang(lng_contact_phone), phone) , _invertOrder(langFirstNameGoesSecond()) { if (!phone.isEmpty()) { - _phone.setDisabled(true); + _phone->setDisabled(true); } initBox(); @@ -58,47 +59,47 @@ AddContactBox::AddContactBox(UserData *user) : AbstractBox(st::boxWidth) , _last(this, st::defaultInputField, lang(lng_signup_lastname), user->lastName) , _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone()) , _invertOrder(langFirstNameGoesSecond()) { - _phone.setDisabled(true); + _phone->setDisabled(true); initBox(); } void AddContactBox::initBox() { if (_invertOrder) { - setTabOrder(&_last, &_first); + setTabOrder(_last, _first); } if (_user) { _boxTitle = lang(lng_edit_contact_title); } else { - bool readyToAdd = !_phone.getLastText().isEmpty() && (!_first.getLastText().isEmpty() || !_last.getLastText().isEmpty()); + bool readyToAdd = !_phone->getLastText().isEmpty() && (!_first->getLastText().isEmpty() || !_last->getLastText().isEmpty()); _boxTitle = lang(readyToAdd ? lng_confirm_contact_data : lng_enter_contact_data); } - setMaxHeight(st::boxTitleHeight + st::contactPadding.top() + _first.height() + st::contactSkip + _last.height() + st::contactPhoneSkip + _phone.height() + st::contactPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); - _retry.hide(); + setMaxHeight(st::boxTitleHeight + st::contactPadding.top() + _first->height() + st::contactSkip + _last->height() + st::contactPhoneSkip + _phone->height() + st::contactPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom()); + _retry->hide(); - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_retry, SIGNAL(clicked()), this, SLOT(onRetry())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_retry, SIGNAL(clicked()), this, SLOT(onRetry())); - connect(&_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_phone, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_phone, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); prepare(); } void AddContactBox::showAll() { - _first.show(); - _last.show(); - _phone.show(); - _save.show(); - _cancel.show(); + _first->show(); + _last->show(); + _phone->show(); + _save->show(); + _cancel->show(); } void AddContactBox::doSetInnerFocus() { - if ((_first.getLastText().isEmpty() && _last.getLastText().isEmpty()) || !_phone.isEnabled()) { - (_invertOrder ? _last : _first).setFocus(); + if ((_first->getLastText().isEmpty() && _last->getLastText().isEmpty()) || !_phone->isEnabled()) { + (_invertOrder ? _last : _first)->setFocus(); } else { - _phone.setFocus(); + _phone->setFocus(); } } @@ -108,47 +109,47 @@ void AddContactBox::paintEvent(QPaintEvent *e) { paintTitle(p, _boxTitle); - if (_retry.isHidden()) { - st::contactUserIcon.paint(p, st::boxPadding.left(), _first.y() + st::contactIconTop, width()); - st::contactPhoneIcon.paint(p, st::boxPadding.left(), _phone.y() + st::contactIconTop, width()); + if (_retry->isHidden()) { + st::contactUserIcon.paint(p, st::boxPadding.left(), _first->y() + st::contactIconTop, width()); + st::contactPhoneIcon.paint(p, st::boxPadding.left(), _phone->y() + st::contactIconTop, width()); } else { p.setPen(st::black->p); p.setFont(st::boxTextFont->f); - int32 h = height() - st::boxTitleHeight - st::contactPadding.top() - st::contactPadding.bottom() - st::boxPadding.bottom() - st::boxButtonPadding.top() - _retry.height() - st::boxButtonPadding.bottom(); + int32 h = height() - st::boxTitleHeight - st::contactPadding.top() - st::contactPadding.bottom() - st::boxPadding.bottom() - st::boxButtonPadding.top() - _retry->height() - st::boxButtonPadding.bottom(); p.drawText(QRect(st::boxPadding.left(), st::boxTitleHeight + st::contactPadding.top(), width() - st::boxPadding.left() - st::boxPadding.right(), h), lng_contact_not_joined(lt_name, _sentName), style::al_topleft); } } void AddContactBox::resizeEvent(QResizeEvent *e) { - _first.resize(width() - st::boxPadding.left() - st::contactPadding.left() - st::boxPadding.right(), _first.height()); - _last.resize(_first.width(), _last.height()); - _phone.resize(_first.width(), _last.height()); + _first->resize(width() - st::boxPadding.left() - st::contactPadding.left() - st::boxPadding.right(), _first->height()); + _last->resize(_first->width(), _last->height()); + _phone->resize(_first->width(), _last->height()); if (_invertOrder) { - _last.moveToLeft(st::boxPadding.left() + st::contactPadding.left(), st::boxTitleHeight + st::contactPadding.top()); - _first.moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _last.y() + _last.height() + st::contactSkip); - _phone.moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _first.y() + _first.height() + st::contactPhoneSkip); + _last->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), st::boxTitleHeight + st::contactPadding.top()); + _first->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _last->y() + _last->height() + st::contactSkip); + _phone->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _first->y() + _first->height() + st::contactPhoneSkip); } else { - _first.moveToLeft(st::boxPadding.left() + st::contactPadding.left(), st::boxTitleHeight + st::contactPadding.top()); - _last.moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _first.y() + _first.height() + st::contactSkip); - _phone.moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _last.y() + _last.height() + st::contactPhoneSkip); + _first->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), st::boxTitleHeight + st::contactPadding.top()); + _last->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _first->y() + _first->height() + st::contactSkip); + _phone->moveToLeft(st::boxPadding.left() + st::contactPadding.left(), _last->y() + _last->height() + st::contactPhoneSkip); } - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _retry.moveToRight(st::boxButtonPadding.right(), _save.y()); - _cancel.moveToRight(st::boxButtonPadding.right() + (_retry.isHidden() ? _save.width() : _retry.width()) + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _retry->moveToRight(st::boxButtonPadding.right(), _save->y()); + _cancel->moveToRight(st::boxButtonPadding.right() + (_retry->isHidden() ? _save->width() : _retry->width()) + st::boxButtonPadding.left(), _save->y()); AbstractBox::resizeEvent(e); } void AddContactBox::onSubmit() { - if (_first.hasFocus()) { - _last.setFocus(); - } else if (_last.hasFocus()) { - if (_phone.isEnabled()) { - _phone.setFocus(); + if (_first->hasFocus()) { + _last->setFocus(); + } else if (_last->hasFocus()) { + if (_phone->isEnabled()) { + _phone->setFocus(); } else { onSave(); } - } else if (_phone.hasFocus()) { + } else if (_phone->hasFocus()) { onSave(); } } @@ -156,21 +157,21 @@ void AddContactBox::onSubmit() { void AddContactBox::onSave() { if (_addRequest) return; - QString firstName = prepareText(_first.getLastText()); - QString lastName = prepareText(_last.getLastText()); - QString phone = _phone.getLastText().trimmed(); + QString firstName = prepareText(_first->getLastText()); + QString lastName = prepareText(_last->getLastText()); + QString phone = _phone->getLastText().trimmed(); if (firstName.isEmpty() && lastName.isEmpty()) { if (_invertOrder) { - _last.setFocus(); - _last.showError(); + _last->setFocus(); + _last->showError(); } else { - _first.setFocus(); - _first.showError(); + _first->setFocus(); + _first->showError(); } return; } else if (!_user && !App::isValidPhone(phone)) { - _phone.setFocus(); - _phone.showError(); + _phone->setFocus(); + _phone->showError(); return; } if (firstName.isEmpty()) { @@ -194,17 +195,17 @@ bool AddContactBox::onSaveUserFail(const RPCError &error) { _addRequest = 0; QString err(error.type()); - QString firstName = _first.getLastText().trimmed(), lastName = _last.getLastText().trimmed(); + QString firstName = _first->getLastText().trimmed(), lastName = _last->getLastText().trimmed(); if (err == "CHAT_TITLE_NOT_MODIFIED") { _user->setName(firstName, lastName, _user->nameOrPhone, _user->username); onClose(); return true; } else if (err == "NO_CHAT_TITLE") { - _first.setFocus(); - _first.showError(); + _first->setFocus(); + _first->showError(); return true; } - _first.setFocus(); + _first->setFocus(); return true; } @@ -226,11 +227,11 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) { Notify::userIsContactChanged(user, true); Ui::hideLayer(); } else { - _save.hide(); - _first.hide(); - _last.hide(); - _phone.hide(); - _retry.show(); + _save->hide(); + _first->hide(); + _last->hide(); + _phone->hide(); + _retry->show(); resizeEvent(0); update(); } @@ -245,17 +246,17 @@ void AddContactBox::onSaveUserDone(const MTPcontacts_ImportedContacts &res) { void AddContactBox::onRetry() { _addRequest = 0; _contactId = 0; - _save.show(); - _retry.hide(); + _save->show(); + _retry->hide(); resizeEvent(0); showAll(); - _first.setText(QString()); - _first.updatePlaceholder(); - _last.setText(QString()); - _last.updatePlaceholder(); - _phone.clearText(); - _phone.setDisabled(false); - _first.setFocus(); + _first->setText(QString()); + _first->updatePlaceholder(); + _last->setText(QString()); + _last->updatePlaceholder(); + _phone->clearText(); + _phone->setDisabled(false); + _first->setFocus(); update(); } @@ -268,19 +269,19 @@ _aboutChannel(st::normalFont, lang(lng_create_channel_about), _defaultOptions, _ _next(this, lang(lng_create_group_next), st::defaultBoxButton), _cancel(this, lang(lng_cancel), st::cancelBoxButton) { _aboutGroupHeight = _aboutGroup.countHeight(_aboutGroupWidth); - setMaxHeight(st::boxPadding.top() + st::newGroupPadding.top() + _group.height() + _aboutGroupHeight + st::newGroupSkip + _channel.height() + _aboutChannel.countHeight(_aboutGroupWidth) + st::newGroupPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _next.height() + st::boxButtonPadding.bottom()); + setMaxHeight(st::boxPadding.top() + st::newGroupPadding.top() + _group->height() + _aboutGroupHeight + st::newGroupSkip + _channel->height() + _aboutChannel.countHeight(_aboutGroupWidth) + st::newGroupPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _next->height() + st::boxButtonPadding.bottom()); - connect(&_next, SIGNAL(clicked()), this, SLOT(onNext())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_next, SIGNAL(clicked()), this, SLOT(onNext())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); prepare(); } void NewGroupBox::showAll() { - _group.show(); - _channel.show(); - _cancel.show(); - _next.show(); + _group->show(); + _channel->show(); + _cancel->show(); + _next->show(); } void NewGroupBox::keyPressEvent(QKeyEvent *e) { @@ -297,24 +298,24 @@ void NewGroupBox::paintEvent(QPaintEvent *e) { p.setPen(st::newGroupAboutFg->p); - QRect aboutGroup(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _group.y() + _group.height() + st::lineWidth, _aboutGroupWidth, _aboutGroupHeight); + QRect aboutGroup(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _group->y() + _group->height() + st::lineWidth, _aboutGroupWidth, _aboutGroupHeight); _aboutGroup.drawLeft(p, aboutGroup.x(), aboutGroup.y(), aboutGroup.width(), width()); - QRect aboutChannel(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _channel.y() + _channel.height() + st::lineWidth, _aboutGroupWidth, _aboutGroupHeight); + QRect aboutChannel(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _channel->y() + _channel->height() + st::lineWidth, _aboutGroupWidth, _aboutGroupHeight); _aboutChannel.drawLeft(p, aboutChannel.x(), aboutChannel.y(), aboutChannel.width(), width()); } void NewGroupBox::resizeEvent(QResizeEvent *e) { - _group.moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), st::boxPadding.top() + st::newGroupPadding.top()); - _channel.moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), _group.y() + _group.height() + _aboutGroupHeight + st::newGroupSkip); + _group->moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), st::boxPadding.top() + st::newGroupPadding.top()); + _channel->moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), _group->y() + _group->height() + _aboutGroupHeight + st::newGroupSkip); - _next.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _next.width() + st::boxButtonPadding.left(), _next.y()); + _next->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _next->width() + st::boxButtonPadding.left(), _next->y()); AbstractBox::resizeEvent(e); } void NewGroupBox::onNext() { - Ui::showLayer(new GroupInfoBox(_group.checked() ? CreatingGroupGroup : CreatingGroupChannel, true), KeepOtherLayers); + Ui::showLayer(new GroupInfoBox(_group->checked() ? CreatingGroupGroup : CreatingGroupChannel, true), KeepOtherLayers); } GroupInfoBox::GroupInfoBox(CreatingGroupType creating, bool fromTypeChoose) : AbstractBox(), @@ -330,20 +331,20 @@ _creationRequestId(0), _createdChannel(0) { setMouseTracking(true); - _title.setMaxLength(MaxGroupChannelTitle); + _title->setMaxLength(MaxGroupChannelTitle); - _description.setMaxLength(MaxChannelDescription); - _description.resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _description.height()); + _description->setMaxLength(MaxChannelDescription); + _description->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _description->height()); updateMaxHeight(); - connect(&_description, SIGNAL(resized()), this, SLOT(onDescriptionResized())); - connect(&_description, SIGNAL(submitted(bool)), this, SLOT(onNext())); - connect(&_description, SIGNAL(cancelled()), this, SLOT(onClose())); + connect(_description, SIGNAL(resized()), this, SLOT(onDescriptionResized())); + connect(_description, SIGNAL(submitted(bool)), this, SLOT(onNext())); + connect(_description, SIGNAL(cancelled()), this, SLOT(onClose())); - connect(&_title, SIGNAL(submitted(bool)), this, SLOT(onNameSubmit())); + connect(_title, SIGNAL(submitted(bool)), this, SLOT(onNameSubmit())); - connect(&_next, SIGNAL(clicked()), this, SLOT(onNext())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_next, SIGNAL(clicked()), this, SLOT(onNext())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) { notifyFileQueryUpdated(update); @@ -353,18 +354,18 @@ _creationRequestId(0), _createdChannel(0) { } void GroupInfoBox::showAll() { - _title.show(); + _title->show(); if (_creating == CreatingGroupChannel) { - _description.show(); + _description->show(); } else { - _description.hide(); + _description->hide(); } - _cancel.show(); - _next.show(); + _cancel->show(); + _next->show(); } void GroupInfoBox::doSetInnerFocus() { - _title.setFocus(); + _title->setFocus(); } void GroupInfoBox::paintEvent(QPaintEvent *e) { @@ -400,13 +401,13 @@ void GroupInfoBox::paintEvent(QPaintEvent *e) { void GroupInfoBox::resizeEvent(QResizeEvent *e) { int32 nameLeft = st::newGroupPhotoSize + st::newGroupNamePosition.x(); - _title.resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right() - nameLeft, _title.height()); - _title.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left() + nameLeft, st::boxPadding.top() + st::newGroupInfoPadding.top() + st::newGroupNamePosition.y()); + _title->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right() - nameLeft, _title->height()); + _title->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left() + nameLeft, st::boxPadding.top() + st::newGroupInfoPadding.top() + st::newGroupNamePosition.y()); - _description.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxPadding.top() + st::newGroupInfoPadding.top() + st::newGroupPhotoSize + st::newGroupDescriptionPadding.top()); + _description->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxPadding.top() + st::newGroupInfoPadding.top() + st::newGroupPhotoSize + st::newGroupDescriptionPadding.top()); - _next.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _next.width() + st::boxButtonPadding.left(), _next.y()); + _next->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _next->width() + st::boxButtonPadding.left(), _next->y()); AbstractBox::resizeEvent(e); } @@ -452,23 +453,23 @@ void GroupInfoBox::step_photoOver(float64 ms, bool timer) { } void GroupInfoBox::onNameSubmit() { - if (_title.getLastText().trimmed().isEmpty()) { - _title.setFocus(); - _title.showError(); - } else if (_description.isHidden()) { + if (_title->getLastText().trimmed().isEmpty()) { + _title->setFocus(); + _title->showError(); + } else if (_description->isHidden()) { onNext(); } else { - _description.setFocus(); + _description->setFocus(); } } void GroupInfoBox::onNext() { if (_creationRequestId) return; - QString title = prepareText(_title.getLastText()), description = prepareText(_description.getLastText(), true); + QString title = prepareText(_title->getLastText()), description = prepareText(_description->getLastText(), true); if (title.isEmpty()) { - _title.setFocus(); - _title.showError(); + _title->setFocus(); + _title->showError(); return; } if (_creating == CreatingGroupGroup) { @@ -513,8 +514,8 @@ bool GroupInfoBox::creationFail(const RPCError &error) { _creationRequestId = 0; if (error.type() == "NO_CHAT_TITLE") { - _title.setFocus(); - _title.showError(); + _title->setFocus(); + _title->showError(); return true; } else if (error.type() == qstr("USER_RESTRICTED")) { Ui::showLayer(new InformBox(lang(lng_cant_do_this))); @@ -541,9 +542,9 @@ QRect GroupInfoBox::photoRect() const { } void GroupInfoBox::updateMaxHeight() { - int32 h = st::boxPadding.top() + st::newGroupInfoPadding.top() + st::newGroupPhotoSize + st::boxPadding.bottom() + st::newGroupInfoPadding.bottom() + st::boxButtonPadding.top() + _next.height() + st::boxButtonPadding.bottom(); + int32 h = st::boxPadding.top() + st::newGroupInfoPadding.top() + st::newGroupPhotoSize + st::boxPadding.bottom() + st::newGroupInfoPadding.bottom() + st::boxButtonPadding.top() + _next->height() + st::boxButtonPadding.bottom(); if (_creating == CreatingGroupChannel) { - h += st::newGroupDescriptionPadding.top() + _description.height() + st::newGroupDescriptionPadding.bottom(); + h += st::newGroupDescriptionPadding.top() + _description->height() + st::newGroupDescriptionPadding.bottom(); } setMaxHeight(h); } @@ -601,54 +602,54 @@ SetupChannelBox::SetupChannelBox(ChannelData *channel, bool existing) : Abstract _aboutPublicHeight = _aboutPublic.countHeight(_aboutPublicWidth); updateMaxHeight(); - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_skip, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_skip, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_link, SIGNAL(changed()), this, SLOT(onChange())); + connect(_link, SIGNAL(changed()), this, SLOT(onChange())); _checkTimer.setSingleShot(true); connect(&_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck())); - connect(&_public, SIGNAL(changed()), this, SLOT(onPrivacyChange())); - connect(&_private, SIGNAL(changed()), this, SLOT(onPrivacyChange())); + connect(_public, SIGNAL(changed()), this, SLOT(onPrivacyChange())); + connect(_private, SIGNAL(changed()), this, SLOT(onPrivacyChange())); prepare(); } void SetupChannelBox::showAll() { - _public.show(); - _private.show(); - if (_public.checked()) { - _link.show(); + _public->show(); + _private->show(); + if (_public->checked()) { + _link->show(); } else { - _link.hide(); + _link->hide(); } - _save.show(); - _skip.show(); + _save->show(); + _skip->show(); } void SetupChannelBox::doSetInnerFocus() { - if (_link.isHidden()) { + if (_link->isHidden()) { setFocus(); } else { - _link.setFocus(); + _link->setFocus(); } } void SetupChannelBox::updateMaxHeight() { - if (!_channel->isMegagroup() || _public.checked()) { - setMaxHeight(st::boxPadding.top() + st::newGroupPadding.top() + _public.height() + _aboutPublicHeight + st::newGroupSkip + _private.height() + _aboutPrivate.countHeight(_aboutPublicWidth) + st::newGroupSkip + st::newGroupPadding.bottom() + st::newGroupLinkPadding.top() + _link.height() + st::newGroupLinkPadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + if (!_channel->isMegagroup() || _public->checked()) { + setMaxHeight(st::boxPadding.top() + st::newGroupPadding.top() + _public->height() + _aboutPublicHeight + st::newGroupSkip + _private->height() + _aboutPrivate.countHeight(_aboutPublicWidth) + st::newGroupSkip + st::newGroupPadding.bottom() + st::newGroupLinkPadding.top() + _link->height() + st::newGroupLinkPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom()); } else { - setMaxHeight(st::boxPadding.top() + st::newGroupPadding.top() + _public.height() + _aboutPublicHeight + st::newGroupSkip + _private.height() + _aboutPrivate.countHeight(_aboutPublicWidth) + st::newGroupSkip + st::newGroupPadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + setMaxHeight(st::boxPadding.top() + st::newGroupPadding.top() + _public->height() + _aboutPublicHeight + st::newGroupSkip + _private->height() + _aboutPrivate.countHeight(_aboutPublicWidth) + st::newGroupSkip + st::newGroupPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom()); } } void SetupChannelBox::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - if (_link.hasFocus()) { - if (_link.text().trimmed().isEmpty()) { - _link.setFocus(); - _link.showError(); + if (_link->hasFocus()) { + if (_link->text().trimmed().isEmpty()) { + _link->setFocus(); + _link->showError(); } else { onSave(); } @@ -664,19 +665,19 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) { p.setPen(st::newGroupAboutFg); - QRect aboutPublic(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _public.y() + _public.height(), _aboutPublicWidth, _aboutPublicHeight); + QRect aboutPublic(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _public->y() + _public->height(), _aboutPublicWidth, _aboutPublicHeight); _aboutPublic.drawLeft(p, aboutPublic.x(), aboutPublic.y(), aboutPublic.width(), width()); - QRect aboutPrivate(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _private.y() + _private.height(), _aboutPublicWidth, _aboutPublicHeight); + QRect aboutPrivate(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _private->y() + _private->height(), _aboutPublicWidth, _aboutPublicHeight); _aboutPrivate.drawLeft(p, aboutPrivate.x(), aboutPrivate.y(), aboutPrivate.width(), width()); - if (!_channel->isMegagroup() || !_link.isHidden()) { + if (!_channel->isMegagroup() || !_link->isHidden()) { p.setPen(st::black); p.setFont(st::newGroupLinkFont); - p.drawTextLeft(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultInputField.textMargins.left(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop, width(), lang(_link.isHidden() ? lng_create_group_invite_link : lng_create_group_link)); + p.drawTextLeft(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultInputField.textMargins.left(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop, width(), lang(_link->isHidden() ? lng_create_group_invite_link : lng_create_group_link)); } - if (_link.isHidden()) { + if (_link->isHidden()) { if (!_channel->isMegagroup()) { QTextOption option(style::al_left); option.setWrapMode(QTextOption::WrapAnywhere); @@ -687,7 +688,7 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) { p.setOpacity(a_goodOpacity.current()); p.setPen(st::setGoodColor); p.setFont(st::boxTextFont); - p.drawTextRight(st::boxPadding.right(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _goodTextLink); + p.drawTextRight(st::boxPadding.right(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _goodTextLink); p.setOpacity(1); } } @@ -695,25 +696,25 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) { if (!_errorText.isEmpty()) { p.setPen(st::setErrColor); p.setFont(st::boxTextFont); - p.drawTextRight(st::boxPadding.right(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _errorText); + p.drawTextRight(st::boxPadding.right(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _errorText); } else if (!_goodText.isEmpty()) { p.setPen(st::setGoodColor); p.setFont(st::boxTextFont); - p.drawTextRight(st::boxPadding.right(), _link.y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _goodText); + p.drawTextRight(st::boxPadding.right(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _goodText); } } } void SetupChannelBox::resizeEvent(QResizeEvent *e) { - _public.moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), st::boxPadding.top() + st::newGroupPadding.top()); - _private.moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), _public.y() + _public.height() + _aboutPublicHeight + st::newGroupSkip); + _public->moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), st::boxPadding.top() + st::newGroupPadding.top()); + _private->moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), _public->y() + _public->height() + _aboutPublicHeight + st::newGroupSkip); - _link.resize(width() - st::boxPadding.left() - st::newGroupLinkPadding.left() - st::boxPadding.right(), _link.height()); - _link.moveToLeft(st::boxPadding.left() + st::newGroupLinkPadding.left(), _private.y() + _private.height() + _aboutPrivate.countHeight(_aboutPublicWidth) + st::newGroupSkip + st::newGroupPadding.bottom() + st::newGroupLinkPadding.top()); - _invitationLink = QRect(_link.x(), _link.y() + (_link.height() / 2) - st::boxTextFont->height, _link.width(), 2 * st::boxTextFont->height); + _link->resize(width() - st::boxPadding.left() - st::newGroupLinkPadding.left() - st::boxPadding.right(), _link->height()); + _link->moveToLeft(st::boxPadding.left() + st::newGroupLinkPadding.left(), _private->y() + _private->height() + _aboutPrivate.countHeight(_aboutPublicWidth) + st::newGroupSkip + st::newGroupPadding.bottom() + st::newGroupLinkPadding.top()); + _invitationLink = QRect(_link->x(), _link->y() + (_link->height() / 2) - st::boxTextFont->height, _link->width(), 2 * st::boxTextFont->height); - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _skip.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _skip->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); AbstractBox::resizeEvent(e); } @@ -763,7 +764,7 @@ void SetupChannelBox::closePressed() { } void SetupChannelBox::onSave() { - if (!_public.checked()) { + if (!_public->checked()) { if (_existing) { _sentUsername = QString(); _saveRequestId = MTP::send(MTPchannels_UpdateUsername(_channel->inputChannel, MTP_string(_sentUsername)), rpcDone(&SetupChannelBox::onUpdateDone), rpcFail(&SetupChannelBox::onUpdateFail)); @@ -774,10 +775,10 @@ void SetupChannelBox::onSave() { if (_saveRequestId) return; - QString link = _link.text().trimmed(); + QString link = _link->text().trimmed(); if (link.isEmpty()) { - _link.setFocus(); - _link.showError(); + _link->setFocus(); + _link->showError(); return; } @@ -786,7 +787,7 @@ void SetupChannelBox::onSave() { } void SetupChannelBox::onChange() { - QString name = _link.text().trimmed(); + QString name = _link->text().trimmed(); if (name.isEmpty()) { if (!_errorText.isEmpty() || !_goodText.isEmpty()) { _errorText = _goodText = QString(); @@ -826,7 +827,7 @@ void SetupChannelBox::onCheck() { if (_checkRequestId) { MTP::cancel(_checkRequestId); } - QString link = _link.text().trimmed(); + QString link = _link->text().trimmed(); if (link.size() >= MinUsernameLength) { _checkUsername = link; _checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string(link)), rpcDone(&SetupChannelBox::onCheckDone), rpcFail(&SetupChannelBox::onCheckFail)); @@ -834,21 +835,21 @@ void SetupChannelBox::onCheck() { } void SetupChannelBox::onPrivacyChange() { - if (_public.checked()) { + if (_public->checked()) { if (_tooMuchUsernames) { - _private.setChecked(true); + _private->setChecked(true); Ui::showLayer(new RevokePublicLinkBox([this, weak_this = weakThis()]() { if (!weak_this) return; _tooMuchUsernames = false; - _public.setChecked(true); + _public->setChecked(true); onCheck(); }), KeepOtherLayers); return; } - _link.show(); - _link.setFocus(); + _link->show(); + _link->setFocus(); } else { - _link.hide(); + _link->hide(); setFocus(); } if (_channel->isMegagroup()) { @@ -872,19 +873,19 @@ bool SetupChannelBox::onUpdateFail(const RPCError &error) { onClose(); return true; } else if (err == "USERNAME_INVALID") { - _link.setFocus(); - _link.showError(); + _link->setFocus(); + _link->showError(); _errorText = lang(lng_create_channel_link_invalid); update(); return true; } else if (err == "USERNAME_OCCUPIED" || err == "USERNAMES_UNAVAILABLE") { - _link.setFocus(); - _link.showError(); + _link->setFocus(); + _link->showError(); _errorText = lang(lng_create_channel_link_occupied); update(); return true; } - _link.setFocus(); + _link->setFocus(); return true; } @@ -912,7 +913,7 @@ bool SetupChannelBox::onCheckFail(const RPCError &error) { showRevokePublicLinkBoxForEdit(); } else { _tooMuchUsernames = true; - _private.setChecked(true); + _private->setChecked(true); onPrivacyChange(); } return true; @@ -926,7 +927,7 @@ bool SetupChannelBox::onCheckFail(const RPCError &error) { return true; } _goodText = QString(); - _link.setFocus(); + _link->setFocus(); return true; } @@ -950,13 +951,13 @@ bool SetupChannelBox::onFirstCheckFail(const RPCError &error) { showRevokePublicLinkBoxForEdit(); } else { _tooMuchUsernames = true; - _private.setChecked(true); + _private->setChecked(true); onPrivacyChange(); } return true; } _goodText = QString(); - _link.setFocus(); + _link->setFocus(); return true; } @@ -969,64 +970,64 @@ _last(this, st::defaultInputField, lang(lng_signup_lastname), peer->isUser() ? p _invertOrder(!peer->isChat() && langFirstNameGoesSecond()), _requestId(0) { if (_invertOrder) { - setTabOrder(&_last, &_first); + setTabOrder(_last, _first); } - _first.setMaxLength(MaxGroupChannelTitle); - _last.setMaxLength(MaxGroupChannelTitle); + _first->setMaxLength(MaxGroupChannelTitle); + _last->setMaxLength(MaxGroupChannelTitle); - int32 h = st::boxTitleHeight + st::contactPadding.top() + _first.height(); + int32 h = st::boxTitleHeight + st::contactPadding.top() + _first->height(); if (_peer->isUser()) { _boxTitle = lang(_peer == App::self() ? lng_edit_self_title : lng_edit_contact_title); - h += st::contactSkip + _last.height(); + h += st::contactSkip + _last->height(); } else if (_peer->isChat()) { _boxTitle = lang(lng_edit_group_title); } - h += st::boxPadding.bottom() + st::contactPadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(); + h += st::boxPadding.bottom() + st::contactPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); setMaxHeight(h); - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); prepare(); } void EditNameTitleBox::showAll() { - _first.show(); + _first->show(); if (_peer->isChat()) { - _last.hide(); + _last->hide(); } else { - _last.show(); + _last->show(); } - _save.show(); - _cancel.show(); + _save->show(); + _cancel->show(); } void EditNameTitleBox::doSetInnerFocus() { - (_invertOrder ? _last : _first).setFocus(); + (_invertOrder ? _last : _first)->setFocus(); } void EditNameTitleBox::onSubmit() { - if (_first.hasFocus()) { + if (_first->hasFocus()) { if (_peer->isChat()) { - if (_first.getLastText().trimmed().isEmpty()) { - _first.setFocus(); - _first.showError(); + if (_first->getLastText().trimmed().isEmpty()) { + _first->setFocus(); + _first->showError(); } else { onSave(); } } else { - _last.setFocus(); + _last->setFocus(); } - } else if (_last.hasFocus()) { - if (_first.getLastText().trimmed().isEmpty()) { - _first.setFocus(); - _first.showError(); - } else if (_last.getLastText().trimmed().isEmpty()) { - _last.setFocus(); - _last.showError(); + } else if (_last->hasFocus()) { + if (_first->getLastText().trimmed().isEmpty()) { + _first->setFocus(); + _first->showError(); + } else if (_last->getLastText().trimmed().isEmpty()) { + _last->setFocus(); + _last->showError(); } else { onSave(); } @@ -1041,32 +1042,32 @@ void EditNameTitleBox::paintEvent(QPaintEvent *e) { } void EditNameTitleBox::resizeEvent(QResizeEvent *e) { - _first.resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _first.height()); - _last.resize(_first.size()); + _first->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _first->height()); + _last->resize(_first->size()); if (_invertOrder) { - _last.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::contactPadding.top()); - _first.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _last.y() + _last.height() + st::contactSkip); + _last->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::contactPadding.top()); + _first->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _last->y() + _last->height() + st::contactSkip); } else { - _first.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::contactPadding.top()); - _last.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _first.y() + _first.height() + st::contactSkip); + _first->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::contactPadding.top()); + _last->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _first->y() + _first->height() + st::contactSkip); } - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); AbstractBox::resizeEvent(e); } void EditNameTitleBox::onSave() { if (_requestId) return; - QString first = prepareText(_first.getLastText()), last = prepareText(_last.getLastText()); + QString first = prepareText(_first->getLastText()), last = prepareText(_last->getLastText()); if (first.isEmpty() && last.isEmpty()) { if (_invertOrder) { - _last.setFocus(); - _last.showError(); + _last->setFocus(); + _last->showError(); } else { - _first.setFocus(); - _first.showError(); + _first->setFocus(); + _first->showError(); } return; } @@ -1092,21 +1093,21 @@ bool EditNameTitleBox::onSaveSelfFail(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; QString err(error.type()); - QString first = textOneLine(_first.getLastText().trimmed()), last = textOneLine(_last.getLastText().trimmed()); + QString first = textOneLine(_first->getLastText().trimmed()), last = textOneLine(_last->getLastText().trimmed()); if (err == "NAME_NOT_MODIFIED") { App::self()->setName(first, last, QString(), textOneLine(App::self()->username)); onClose(); return true; } else if (err == "FIRSTNAME_INVALID") { - _first.setFocus(); - _first.showError(); + _first->setFocus(); + _first->showError(); return true; } else if (err == "LASTNAME_INVALID") { - _last.setFocus(); - _last.showError(); + _last->setFocus(); + _last->showError(); return true; } - _first.setFocus(); + _first->setFocus(); return true; } @@ -1122,11 +1123,11 @@ bool EditNameTitleBox::onSaveChatFail(const RPCError &error) { onClose(); return true; } else if (err == qstr("NO_CHAT_TITLE")) { - _first.setFocus(); - _first.showError(); + _first->setFocus(); + _first->showError(); return true; } - _first.setFocus(); + _first->setFocus(); return true; } @@ -1150,48 +1151,48 @@ EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox() setMouseTracking(true); - _title.setMaxLength(MaxGroupChannelTitle); - _description.setMaxLength(MaxChannelDescription); - _description.resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _description.height()); - myEnsureResized(&_description); + _title->setMaxLength(MaxGroupChannelTitle); + _description->setMaxLength(MaxChannelDescription); + _description->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _description->height()); + myEnsureResized(_description); updateMaxHeight(); - connect(&_description, SIGNAL(resized()), this, SLOT(onDescriptionResized())); - connect(&_description, SIGNAL(submitted(bool)), this, SLOT(onSave())); - connect(&_description, SIGNAL(cancelled()), this, SLOT(onClose())); + connect(_description, SIGNAL(resized()), this, SLOT(onDescriptionResized())); + connect(_description, SIGNAL(submitted(bool)), this, SLOT(onSave())); + connect(_description, SIGNAL(cancelled()), this, SLOT(onClose())); - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_publicLink, SIGNAL(clicked()), this, SLOT(onPublicLink())); + connect(_publicLink, SIGNAL(clicked()), this, SLOT(onPublicLink())); prepare(); } void EditChannelBox::showAll() { - _title.show(); - _description.show(); - _save.show(); - _cancel.show(); + _title->show(); + _description->show(); + _save->show(); + _cancel->show(); if (_channel->canEditUsername()) { - _publicLink.show(); + _publicLink->show(); } else { - _publicLink.hide(); + _publicLink->hide(); } if (_channel->isMegagroup()) { - _sign.hide(); + _sign->hide(); } else { - _sign.show(); + _sign->show(); } } void EditChannelBox::doSetInnerFocus() { - _title.setFocus(); + _title->setFocus(); } void EditChannelBox::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - if (_title.hasFocus()) { + if (_title->hasFocus()) { onSave(); } } else { @@ -1208,8 +1209,8 @@ void EditChannelBox::paintEvent(QPaintEvent *e) { void EditChannelBox::peerUpdated(PeerData *peer) { if (peer == _channel) { - _publicLink.setText(lang(_channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link)); - _sign.setChecked(_channel->addsSignature()); + _publicLink->setText(lang(_channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link)); + _sign->setChecked(_channel->addsSignature()); } } @@ -1219,44 +1220,44 @@ void EditChannelBox::onDescriptionResized() { } void EditChannelBox::updateMaxHeight() { - int32 h = st::boxTitleHeight + st::newGroupInfoPadding.top() + _title.height(); - h += st::newGroupDescriptionPadding.top() + _description.height() + st::newGroupDescriptionPadding.bottom(); + int32 h = st::boxTitleHeight + st::newGroupInfoPadding.top() + _title->height(); + h += st::newGroupDescriptionPadding.top() + _description->height() + st::newGroupDescriptionPadding.bottom(); if (!_channel->isMegagroup()) { - h += st::newGroupPublicLinkPadding.top() + _sign.height() + st::newGroupPublicLinkPadding.bottom(); + h += st::newGroupPublicLinkPadding.top() + _sign->height() + st::newGroupPublicLinkPadding.bottom(); } if (_channel->canEditUsername()) { - h += st::newGroupPublicLinkPadding.top() + _publicLink.height() + st::newGroupPublicLinkPadding.bottom(); + h += st::newGroupPublicLinkPadding.top() + _publicLink->height() + st::newGroupPublicLinkPadding.bottom(); } - h += st::boxPadding.bottom() + st::newGroupInfoPadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(); + h += st::boxPadding.bottom() + st::newGroupInfoPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); setMaxHeight(h); } void EditChannelBox::resizeEvent(QResizeEvent *e) { - _title.resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _title.height()); - _title.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::newGroupInfoPadding.top() + st::newGroupNamePosition.y()); + _title->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _title->height()); + _title->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::boxTitleHeight + st::newGroupInfoPadding.top() + st::newGroupNamePosition.y()); - _description.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _title.y() + _title.height() + st::newGroupDescriptionPadding.top()); + _description->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _title->y() + _title->height() + st::newGroupDescriptionPadding.top()); - _sign.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description.y() + _description.height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); + _sign->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); if (_channel->isMegagroup()) { - _publicLink.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description.y() + _description.height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); + _publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); } else { - _publicLink.moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _sign.y() + _sign.height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); + _publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _sign->y() + _sign->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); } - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); AbstractBox::resizeEvent(e); } void EditChannelBox::onSave() { if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId) return; - QString title = prepareText(_title.getLastText()), description = prepareText(_description.getLastText(), true); + QString title = prepareText(_title->getLastText()), description = prepareText(_description->getLastText(), true); if (title.isEmpty()) { - _title.setFocus(); - _title.showError(); + _title->setFocus(); + _title->showError(); return; } _sentTitle = title; @@ -1281,10 +1282,10 @@ void EditChannelBox::saveDescription() { } void EditChannelBox::saveSign() { - if (_channel->isMegagroup() || _channel->addsSignature() == _sign.checked()) { + if (_channel->isMegagroup() || _channel->addsSignature() == _sign->checked()) { onClose(); } else { - _saveSignRequestId = MTP::send(MTPchannels_ToggleSignatures(_channel->inputChannel, MTP_bool(_sign.checked())), rpcDone(&EditChannelBox::onSaveSignDone), rpcFail(&EditChannelBox::onSaveFail)); + _saveSignRequestId = MTP::send(MTPchannels_ToggleSignatures(_channel->inputChannel, MTP_bool(_sign->checked())), rpcDone(&EditChannelBox::onSaveSignDone), rpcFail(&EditChannelBox::onSaveFail)); } } @@ -1299,11 +1300,11 @@ bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) { saveDescription(); return true; } else if (err == qstr("NO_CHAT_TITLE")) { - _title.setFocus(); - _title.showError(); + _title->setFocus(); + _title->showError(); return true; } else { - _title.setFocus(); + _title->setFocus(); } } else if (req == _saveDescriptionRequestId) { _saveDescriptionRequestId = 0; @@ -1316,7 +1317,7 @@ bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) { saveSign(); return true; } else { - _description.setFocus(); + _description->setFocus(); } } else if (req == _saveSignRequestId) { _saveSignRequestId = 0; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index 09a28dc5b..8ae48c95f 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -27,6 +27,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org class FlatLabel; class ConfirmBox; +namespace Ui { +class Checkbox; +class Radiobutton; +} // namespace Ui + class AddContactBox : public AbstractBox, public RPCSender { Q_OBJECT @@ -57,9 +62,13 @@ private: UserData *_user = nullptr; QString _boxTitle; - BoxButton _save, _cancel, _retry; - InputField _first, _last; - PhoneInput _phone; + ChildWidget _first; + ChildWidget _last; + ChildWidget _phone; + + ChildWidget _save; + ChildWidget _cancel; + ChildWidget _retry; bool _invertOrder; @@ -86,10 +95,14 @@ protected: void showAll() override; private: - Radiobutton _group, _channel; + ChildWidget _group; + ChildWidget _channel; + int32 _aboutGroupWidth, _aboutGroupHeight; Text _aboutGroup, _aboutChannel; - BoxButton _next, _cancel; + + ChildWidget _next; + ChildWidget _cancel; }; @@ -132,12 +145,14 @@ private: Animation _a_photoOver; bool _photoOver; - InputField _title; - InputArea _description; + ChildWidget _title; + ChildWidget _description; QImage _photoBig; QPixmap _photoSmall; - BoxButton _next, _cancel; + + ChildWidget _next; + ChildWidget _cancel; // channel creation int32 _creationRequestId; @@ -194,13 +209,17 @@ private: ChannelData *_channel; bool _existing; - Radiobutton _public, _private; + ChildWidget _public, _private; int32 _aboutPublicWidth, _aboutPublicHeight; Text _aboutPublic, _aboutPrivate; - UsernameInput _link; + + ChildWidget _link; + QRect _invitationLink; bool _linkOver; - BoxButton _save, _skip; + + ChildWidget _save; + ChildWidget _skip; bool _tooMuchUsernames = false; @@ -243,8 +262,11 @@ private: PeerData *_peer; QString _boxTitle; - BoxButton _save, _cancel; - InputField _first, _last; + ChildWidget _first; + ChildWidget _last; + + ChildWidget _save; + ChildWidget _cancel; bool _invertOrder; @@ -287,14 +309,19 @@ private: ChannelData *_channel; - BoxButton _save, _cancel; - InputField _title; - InputArea _description; - Checkbox _sign; + ChildWidget _title; + ChildWidget _description; + ChildWidget _sign; - LinkButton _publicLink; + ChildWidget _publicLink; + + ChildWidget _save; + ChildWidget _cancel; + + mtpRequestId _saveTitleRequestId; + mtpRequestId _saveDescriptionRequestId; + mtpRequestId _saveSignRequestId; - mtpRequestId _saveTitleRequestId, _saveDescriptionRequestId, _saveSignRequestId; QString _sentTitle, _sentDescription; }; diff --git a/Telegram/SourceFiles/boxes/autolockbox.cpp b/Telegram/SourceFiles/boxes/autolockbox.cpp index 238941a3a..b0fa448ce 100644 --- a/Telegram/SourceFiles/boxes/autolockbox.cpp +++ b/Telegram/SourceFiles/boxes/autolockbox.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "confirmbox.h" #include "mainwidget.h" #include "mainwindow.h" +#include "ui/buttons/checkbox.h" AutoLockBox::AutoLockBox() : _close(this, lang(lng_box_ok), st::defaultBoxButton) { @@ -41,7 +42,7 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { _options.reserve(cnt); for (int32 i = 0; i < cnt; ++i) { int32 v = opts[i]; - _options.push_back(new Radiobutton(this, qsl("autolock"), v, (v % 3600) ? lng_passcode_autolock_minutes(lt_count, v / 60) : lng_passcode_autolock_hours(lt_count, v / 3600), (Global::AutoLock() == v), st::langsButton)); + _options.push_back(new Ui::Radiobutton(this, qsl("autolock"), v, (v % 3600) ? lng_passcode_autolock_minutes(lt_count, v / 60) : lng_passcode_autolock_hours(lt_count, v / 3600), (Global::AutoLock() == v), st::langsButton)); _options.back()->move(st::boxPadding.left() + st::boxOptionListPadding.left(), y); y += _options.back()->height() + st::boxOptionListPadding.top(); connect(_options.back(), SIGNAL(changed()), this, SLOT(onChange())); diff --git a/Telegram/SourceFiles/boxes/autolockbox.h b/Telegram/SourceFiles/boxes/autolockbox.h index 075576ad0..b542042e4 100644 --- a/Telegram/SourceFiles/boxes/autolockbox.h +++ b/Telegram/SourceFiles/boxes/autolockbox.h @@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" +namespace Ui { +class Radiobutton; +} // namespace Ui + class AutoLockBox : public AbstractBox { Q_OBJECT @@ -37,7 +41,7 @@ protected: void showAll() override; private: - QVector _options; + QVector _options; BoxButton _close; }; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index c04b07c32..c055abfbf 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -120,8 +120,8 @@ contactsMultiSelect: MultiSelect { textBg: transparent; textMargins: margins(2px, 7px, 2px, 0px); - placeholderFg: #999; - placeholderFgActive: #aaa; + placeholderFg: #999999; + placeholderFgActive: #aaaaaa; placeholderMargins: margins(2px, 0px, 2px, 0px); border: 0px; @@ -203,7 +203,7 @@ notificationSampleTextFg: #d7d7d7; notificationSampleNameFg: #939393; membersAboutPadding: margins(0px, 12px, 0px, 12px); -membersAboutFg: #777; +membersAboutFg: #777777; sessionsScroll: flatScroll(boxScroll) { round: 2px; @@ -217,7 +217,7 @@ sessionCurrentHeight: 118px; sessionPadding: margins(21px, 10px, 21px, 0px); sessionNameFont: msgNameFont; sessionActiveFont: msgDateFont; -sessionActiveColor: #aaa; +sessionActiveColor: #aaaaaa; sessionInfoFont: msgFont; sessionInfoColor: #888888; sessionTerminateTop: 30px; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 25265a48a..47d17bf6b 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "apiwrap.h" #include "application.h" +#include "ui/buttons/checkbox.h" #include "core/click_handler_types.h" #include "styles/style_boxes.h" #include "localstorage.h" @@ -56,10 +57,10 @@ ConfirmBox::ConfirmBox(const QString &text, const QString &doneText, const style void ConfirmBox::init(const QString &text) { _text.setText(st::boxTextFont, text, _informative ? _confirmBoxTextOptions : _textPlainOptions); - connect(&_confirm, SIGNAL(clicked()), this, SLOT(onConfirmPressed())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onCancel())); + connect(_confirm, SIGNAL(clicked()), this, SLOT(onConfirmPressed())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onCancel())); if (_informative) { - _cancel.hide(); + _cancel->hide(); connect(this, SIGNAL(confirmed()), this, SLOT(onCancel())); } onTextUpdated(); @@ -78,7 +79,7 @@ void ConfirmBox::onTextUpdated() { textstyleSet(&st::boxTextStyle); _textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right(); _textHeight = qMin(_text.countHeight(_textWidth), 16 * int(st::boxTextStyle.lineHeight)); - setMaxHeight(st::boxPadding.top() + _textHeight + st::boxPadding.bottom() + st::boxButtonPadding.top() + _confirm.height() + st::boxButtonPadding.bottom()); + setMaxHeight(st::boxPadding.top() + _textHeight + st::boxPadding.bottom() + st::boxButtonPadding.top() + _confirm->height() + st::boxButtonPadding.bottom()); textstyleRestore(); setMouseTracking(_text.hasLinks()); @@ -144,10 +145,10 @@ void ConfirmBox::closePressed() { void ConfirmBox::showAll() { if (_informative) { - _confirm.show(); + _confirm->show(); } else { - _confirm.show(); - _cancel.show(); + _confirm->show(); + _cancel->show(); } } @@ -171,8 +172,8 @@ void ConfirmBox::paintEvent(QPaintEvent *e) { } void ConfirmBox::resizeEvent(QResizeEvent *e) { - _confirm.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _confirm.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _confirm.width() + st::boxButtonPadding.left(), _confirm.y()); + _confirm->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _confirm->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _confirm->width() + st::boxButtonPadding.left(), _confirm->y()); AbstractBox::resizeEvent(e); } @@ -219,9 +220,9 @@ MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth) _textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right(); _textHeight = qMin(_text.countHeight(_textWidth), 16 * int(st::boxTextStyle.lineHeight)); - setMaxHeight(st::boxPadding.top() + _textHeight + st::boxTextFont->height + st::boxTextFont->height * 2 + st::newGroupLinkPadding.bottom() + st::boxButtonPadding.top() + _close.height() + st::boxButtonPadding.bottom()); + setMaxHeight(st::boxPadding.top() + _textHeight + st::boxTextFont->height + st::boxTextFont->height * 2 + st::newGroupLinkPadding.bottom() + st::boxButtonPadding.top() + _close->height() + st::boxButtonPadding.bottom()); - connect(&_close, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_close, SIGNAL(clicked()), this, SLOT(onClose())); prepare(); } @@ -267,7 +268,7 @@ void MaxInviteBox::step_good(float64 ms, bool timer) { } void MaxInviteBox::showAll() { - _close.show(); + _close->show(); } void MaxInviteBox::paintEvent(QPaintEvent *e) { @@ -287,13 +288,13 @@ void MaxInviteBox::paintEvent(QPaintEvent *e) { p.setOpacity(a_goodOpacity.current()); p.setPen(st::setGoodColor); p.setFont(st::boxTextFont); - p.drawTextLeft(st::boxPadding.left(), height() - st::boxButtonPadding.bottom() - _close.height() + st::defaultBoxButton.textTop + st::defaultBoxButton.font->ascent - st::boxTextFont->ascent, width(), _goodTextLink); + p.drawTextLeft(st::boxPadding.left(), height() - st::boxButtonPadding.bottom() - _close->height() + st::defaultBoxButton.textTop + st::defaultBoxButton.font->ascent - st::boxTextFont->ascent, width(), _goodTextLink); p.setOpacity(1); } } void MaxInviteBox::resizeEvent(QResizeEvent *e) { - _close.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close.height()); + _close->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _close->height()); _invitationLink = myrtlrect(st::boxPadding.left(), st::boxPadding.top() + _textHeight + st::boxTextFont->height, width() - st::boxPadding.left() - st::boxPadding.right(), 2 * st::boxTextFont->height); AbstractBox::resizeEvent(e); } @@ -315,11 +316,11 @@ ConvertToSupergroupBox::ConvertToSupergroupBox(ChatData *chat) : AbstractBox(st: _note.setText(st::boxTextFont, lng_profile_convert_warning(lt_bold_start, textcmdStartSemibold(), lt_bold_end, textcmdStopSemibold()), _confirmBoxTextOptions); _textWidth = st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right(); _textHeight = _text.countHeight(_textWidth); - setMaxHeight(st::boxTitleHeight + _textHeight + st::boxPadding.bottom() + _note.countHeight(_textWidth) + st::boxButtonPadding.top() + _convert.height() + st::boxButtonPadding.bottom()); + setMaxHeight(st::boxTitleHeight + _textHeight + st::boxPadding.bottom() + _note.countHeight(_textWidth) + st::boxButtonPadding.top() + _convert->height() + st::boxButtonPadding.bottom()); textstyleRestore(); - connect(&_convert, SIGNAL(clicked()), this, SLOT(onConvert())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_convert, SIGNAL(clicked()), this, SLOT(onConvert())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); prepare(); } @@ -360,8 +361,8 @@ bool ConvertToSupergroupBox::convertFail(const RPCError &error) { } void ConvertToSupergroupBox::showAll() { - _convert.show(); - _cancel.show(); + _convert->show(); + _cancel->show(); } void ConvertToSupergroupBox::keyPressEvent(QKeyEvent *e) { @@ -387,8 +388,8 @@ void ConvertToSupergroupBox::paintEvent(QPaintEvent *e) { } void ConvertToSupergroupBox::resizeEvent(QResizeEvent *e) { - _convert.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _convert.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _convert.width() + st::boxButtonPadding.left(), _convert.y()); + _convert->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _convert->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _convert->width() + st::boxButtonPadding.left(), _convert->y()); AbstractBox::resizeEvent(e); } @@ -399,18 +400,18 @@ PinMessageBox::PinMessageBox(ChannelData *channel, MsgId msgId) : AbstractBox(st , _notify(this, lang(lng_pinned_notify), true, st::defaultBoxCheckbox) , _pin(this, lang(lng_pinned_pin), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { - _text.resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()); - setMaxHeight(st::boxPadding.top() + _text.height() + st::boxMediumSkip + _notify.height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _pin.height() + st::boxButtonPadding.bottom()); + _text->resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()); + setMaxHeight(st::boxPadding.top() + _text->height() + st::boxMediumSkip + _notify->height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _pin->height() + st::boxButtonPadding.bottom()); - connect(&_pin, SIGNAL(clicked()), this, SLOT(onPin())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_pin, SIGNAL(clicked()), this, SLOT(onPin())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); } void PinMessageBox::resizeEvent(QResizeEvent *e) { - _text.moveToLeft(st::boxPadding.left(), st::boxPadding.top()); - _notify.moveToLeft(st::boxPadding.left(), _text.y() + _text.height() + st::boxMediumSkip); - _pin.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _pin.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _pin.width() + st::boxButtonPadding.left(), _pin.y()); + _text->moveToLeft(st::boxPadding.left(), st::boxPadding.top()); + _notify->moveToLeft(st::boxPadding.left(), _text->y() + _text->height() + st::boxMediumSkip); + _pin->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _pin->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _pin->width() + st::boxButtonPadding.left(), _pin->y()); AbstractBox::resizeEvent(e); } @@ -418,17 +419,17 @@ void PinMessageBox::onPin() { if (_requestId) return; MTPchannels_UpdatePinnedMessage::Flags flags = 0; - if (!_notify.checked()) { + if (!_notify->checked()) { flags |= MTPchannels_UpdatePinnedMessage::Flag::f_silent; } _requestId = MTP::send(MTPchannels_UpdatePinnedMessage(MTP_flags(flags), _channel->inputChannel, MTP_int(_msgId)), rpcDone(&PinMessageBox::pinDone), rpcFail(&PinMessageBox::pinFail)); } void PinMessageBox::showAll() { - _text.show(); - _notify.show(); - _pin.show(); - _cancel.show(); + _text->show(); + _notify->show(); + _pin->show(); + _cancel->show(); } void PinMessageBox::pinDone(const MTPUpdates &updates) { @@ -456,31 +457,31 @@ RichDeleteMessageBox::RichDeleteMessageBox(ChannelData *channel, UserData *from, , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { t_assert(_channel != nullptr); - _text.resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()); - setMaxHeight(st::boxPadding.top() + _text.height() + st::boxMediumSkip + _banUser.height() + st::boxLittleSkip + _reportSpam.height() + st::boxLittleSkip + _deleteAll.height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _delete.height() + st::boxButtonPadding.bottom()); + _text->resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()); + setMaxHeight(st::boxPadding.top() + _text->height() + st::boxMediumSkip + _banUser->height() + st::boxLittleSkip + _reportSpam->height() + st::boxLittleSkip + _deleteAll->height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _delete->height() + st::boxButtonPadding.bottom()); - connect(&_delete, SIGNAL(clicked()), this, SLOT(onDelete())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); } void RichDeleteMessageBox::resizeEvent(QResizeEvent *e) { - _text.moveToLeft(st::boxPadding.left(), st::boxPadding.top()); - _banUser.moveToLeft(st::boxPadding.left(), _text.y() + _text.height() + st::boxMediumSkip); - _reportSpam.moveToLeft(st::boxPadding.left(), _banUser.y() + _banUser.height() + st::boxLittleSkip); - _deleteAll.moveToLeft(st::boxPadding.left(), _reportSpam.y() + _reportSpam.height() + st::boxLittleSkip); - _delete.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _delete.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _delete.width() + st::boxButtonPadding.left(), _delete.y()); + _text->moveToLeft(st::boxPadding.left(), st::boxPadding.top()); + _banUser->moveToLeft(st::boxPadding.left(), _text->y() + _text->height() + st::boxMediumSkip); + _reportSpam->moveToLeft(st::boxPadding.left(), _banUser->y() + _banUser->height() + st::boxLittleSkip); + _deleteAll->moveToLeft(st::boxPadding.left(), _reportSpam->y() + _reportSpam->height() + st::boxLittleSkip); + _delete->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _delete->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _delete->width() + st::boxButtonPadding.left(), _delete->y()); AbstractBox::resizeEvent(e); } void RichDeleteMessageBox::onDelete() { - if (_banUser.checked()) { + if (_banUser->checked()) { MTP::send(MTPchannels_KickFromChannel(_channel->inputChannel, _from->inputUser, MTP_boolTrue()), App::main()->rpcDone(&MainWidget::sentUpdatesReceived)); } - if (_reportSpam.checked()) { + if (_reportSpam->checked()) { MTP::send(MTPchannels_ReportSpam(_channel->inputChannel, _from->inputUser, MTP_vector(1, MTP_int(_msgId)))); } - if (_deleteAll.checked()) { + if (_deleteAll->checked()) { App::main()->deleteAllFromUser(_channel, _from); } if (HistoryItem *item = App::histItemById(_channel ? peerToChannel(_channel->id) : 0, _msgId)) { @@ -497,12 +498,12 @@ void RichDeleteMessageBox::onDelete() { } void RichDeleteMessageBox::showAll() { - _text.show(); - _banUser.show(); - _reportSpam.show(); - _deleteAll.show(); - _delete.show(); - _cancel.show(); + _text->show(); + _banUser->show(); + _reportSpam->show(); + _deleteAll->show(); + _delete->show(); + _cancel->show(); } KickMemberBox::KickMemberBox(PeerData *chat, UserData *member) diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index ecd8525d3..38b545cdd 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -24,6 +24,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/flatlabel.h" #include "core/lambda_wrap.h" +namespace Ui { +class Checkbox; +} // namespace Ui + class InformBox; class ConfirmBox : public AbstractBox, public ClickHandlerHost { Q_OBJECT @@ -81,7 +85,8 @@ private: QPoint _lastMousePos; - BoxButton _confirm, _cancel; + ChildWidget _confirm; + ChildWidget _cancel; base::lambda_unique _confirmedCallback; @@ -159,7 +164,8 @@ private: void updateSelected(const QPoint &cursorGlobalPosition); void step_good(float64 ms, bool timer); - BoxButton _close; + ChildWidget _close; + Text _text; int32 _textWidth, _textHeight; @@ -199,7 +205,8 @@ private: Text _text, _note; int32 _textWidth, _textHeight; - BoxButton _convert, _cancel; + ChildWidget _convert; + ChildWidget _cancel; }; @@ -224,10 +231,11 @@ private: ChannelData *_channel; MsgId _msgId; - FlatLabel _text; - Checkbox _notify; + ChildWidget _text; + ChildWidget _notify; - BoxButton _pin, _cancel; + ChildWidget _pin; + ChildWidget _cancel; mtpRequestId _requestId = 0; @@ -252,10 +260,13 @@ private: UserData *_from; MsgId _msgId; - FlatLabel _text; - Checkbox _banUser, _reportSpam, _deleteAll; + ChildWidget _text; + ChildWidget _banUser; + ChildWidget _reportSpam; + ChildWidget _deleteAll; - BoxButton _delete, _cancel; + ChildWidget _delete; + ChildWidget _cancel; }; diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index cc9e59743..dfaf522b5 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "connectionbox.h" #include "mainwidget.h" #include "mainwindow.h" +#include "ui/buttons/checkbox.h" #include "history/history_location_manager.h" ConnectionBox::ConnectionBox() : AbstractBox(st::boxWidth) @@ -40,51 +41,51 @@ ConnectionBox::ConnectionBox() : AbstractBox(st::boxWidth) , _save(this, lang(lng_connection_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_autoRadio, SIGNAL(changed()), this, SLOT(onChange())); - connect(&_httpProxyRadio, SIGNAL(changed()), this, SLOT(onChange())); - connect(&_tcpProxyRadio, SIGNAL(changed()), this, SLOT(onChange())); + connect(_autoRadio, SIGNAL(changed()), this, SLOT(onChange())); + connect(_httpProxyRadio, SIGNAL(changed()), this, SLOT(onChange())); + connect(_tcpProxyRadio, SIGNAL(changed()), this, SLOT(onChange())); - connect(&_hostInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_portInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_userInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_passwordInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_hostInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_portInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_userInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); + connect(_passwordInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); prepare(); } void ConnectionBox::showAll() { - _autoRadio.show(); - _httpProxyRadio.show(); - _tcpProxyRadio.show(); - _tryIPv6.show(); + _autoRadio->show(); + _httpProxyRadio->show(); + _tcpProxyRadio->show(); + _tryIPv6->show(); - int32 h = st::boxTitleHeight + st::boxOptionListPadding.top() + _autoRadio.height() + st::boxOptionListPadding.top() + _httpProxyRadio.height() + st::boxOptionListPadding.top() + _tcpProxyRadio.height() + st::boxOptionListPadding.top() + st::connectionIPv6Skip + _tryIPv6.height() + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(); - if (_httpProxyRadio.checked() || _tcpProxyRadio.checked()) { - h += 2 * st::boxOptionListPadding.top() + 2 * _hostInput.height(); - _hostInput.show(); - _portInput.show(); - _userInput.show(); - _passwordInput.show(); + int32 h = st::boxTitleHeight + st::boxOptionListPadding.top() + _autoRadio->height() + st::boxOptionListPadding.top() + _httpProxyRadio->height() + st::boxOptionListPadding.top() + _tcpProxyRadio->height() + st::boxOptionListPadding.top() + st::connectionIPv6Skip + _tryIPv6->height() + st::boxOptionListPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); + if (_httpProxyRadio->checked() || _tcpProxyRadio->checked()) { + h += 2 * st::boxOptionListPadding.top() + 2 * _hostInput->height(); + _hostInput->show(); + _portInput->show(); + _userInput->show(); + _passwordInput->show(); } else { - _hostInput.hide(); - _portInput.hide(); - _userInput.hide(); - _passwordInput.hide(); + _hostInput->hide(); + _portInput->hide(); + _userInput->hide(); + _passwordInput->hide(); } - _save.show(); - _cancel.show(); + _save->show(); + _cancel->show(); setMaxHeight(h); resizeEvent(0); } void ConnectionBox::doSetInnerFocus() { - if (!_hostInput.isHidden()) { - _hostInput.setFocus(); + if (!_hostInput->isHidden()) { + _hostInput->setFocus(); } } @@ -96,70 +97,70 @@ void ConnectionBox::paintEvent(QPaintEvent *e) { } void ConnectionBox::resizeEvent(QResizeEvent *e) { - _autoRadio.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxTitleHeight + st::boxOptionListPadding.top()); - _httpProxyRadio.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _autoRadio.y() + _autoRadio.height() + st::boxOptionListPadding.top()); + _autoRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxTitleHeight + st::boxOptionListPadding.top()); + _httpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _autoRadio->y() + _autoRadio->height() + st::boxOptionListPadding.top()); int32 inputy = 0; - if (_httpProxyRadio.checked()) { - inputy = _httpProxyRadio.y() + _httpProxyRadio.height() + st::boxOptionListPadding.top(); - _tcpProxyRadio.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), inputy + st::boxOptionListPadding.top() + 2 * _hostInput.height() + st::boxOptionListPadding.top()); + if (_httpProxyRadio->checked()) { + inputy = _httpProxyRadio->y() + _httpProxyRadio->height() + st::boxOptionListPadding.top(); + _tcpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), inputy + st::boxOptionListPadding.top() + 2 * _hostInput->height() + st::boxOptionListPadding.top()); } else { - _tcpProxyRadio.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _httpProxyRadio.y() + _httpProxyRadio.height() + st::boxOptionListPadding.top()); - if (_tcpProxyRadio.checked()) { - inputy = _tcpProxyRadio.y() + _tcpProxyRadio.height() + st::boxOptionListPadding.top(); + _tcpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _httpProxyRadio->y() + _httpProxyRadio->height() + st::boxOptionListPadding.top()); + if (_tcpProxyRadio->checked()) { + inputy = _tcpProxyRadio->y() + _tcpProxyRadio->height() + st::boxOptionListPadding.top(); } } if (inputy) { - _hostInput.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultRadiobutton.textPosition.x() - st::defaultInputField.textMargins.left(), inputy); - _portInput.moveToRight(st::boxPadding.right(), inputy); - _userInput.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultRadiobutton.textPosition.x() - st::defaultInputField.textMargins.left(), _hostInput.y() + _hostInput.height() + st::boxOptionListPadding.top()); - _passwordInput.moveToRight(st::boxPadding.right(), _userInput.y()); + _hostInput->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultRadiobutton.textPosition.x() - st::defaultInputField.textMargins.left(), inputy); + _portInput->moveToRight(st::boxPadding.right(), inputy); + _userInput->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultRadiobutton.textPosition.x() - st::defaultInputField.textMargins.left(), _hostInput->y() + _hostInput->height() + st::boxOptionListPadding.top()); + _passwordInput->moveToRight(st::boxPadding.right(), _userInput->y()); } - int32 tryipv6y = (_tcpProxyRadio.checked() ? (_userInput.y() + _userInput.height()) : (_tcpProxyRadio.y() + _tcpProxyRadio.height())) + st::boxOptionListPadding.top() + st::connectionIPv6Skip; - _tryIPv6.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), tryipv6y); + int32 tryipv6y = (_tcpProxyRadio->checked() ? (_userInput->y() + _userInput->height()) : (_tcpProxyRadio->y() + _tcpProxyRadio->height())) + st::boxOptionListPadding.top() + st::connectionIPv6Skip; + _tryIPv6->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), tryipv6y); - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); AbstractBox::resizeEvent(e); } void ConnectionBox::onChange() { showAll(); - if (_httpProxyRadio.checked() || _tcpProxyRadio.checked()) { - _hostInput.setFocus(); - if (_httpProxyRadio.checked() && !_portInput.getLastText().toInt()) { - _portInput.setText(qsl("80")); - _portInput.updatePlaceholder(); + if (_httpProxyRadio->checked() || _tcpProxyRadio->checked()) { + _hostInput->setFocus(); + if (_httpProxyRadio->checked() && !_portInput->getLastText().toInt()) { + _portInput->setText(qsl("80")); + _portInput->updatePlaceholder(); } } update(); } void ConnectionBox::onSubmit() { - if (_hostInput.hasFocus()) { - if (!_hostInput.getLastText().trimmed().isEmpty()) { - _portInput.setFocus(); + if (_hostInput->hasFocus()) { + if (!_hostInput->getLastText().trimmed().isEmpty()) { + _portInput->setFocus(); } else { - _hostInput.showError(); + _hostInput->showError(); } - } else if (_portInput.hasFocus()) { - if (_portInput.getLastText().trimmed().toInt() > 0) { - _userInput.setFocus(); + } else if (_portInput->hasFocus()) { + if (_portInput->getLastText().trimmed().toInt() > 0) { + _userInput->setFocus(); } else { - _portInput.showError(); + _portInput->showError(); } - } else if (_userInput.hasFocus()) { - _passwordInput.setFocus(); - } else if (_passwordInput.hasFocus()) { - if (_hostInput.getLastText().trimmed().isEmpty()) { - _hostInput.setFocus(); - _hostInput.showError(); - } else if (_portInput.getLastText().trimmed().toInt() <= 0) { - _portInput.setFocus(); - _portInput.showError(); + } else if (_userInput->hasFocus()) { + _passwordInput->setFocus(); + } else if (_passwordInput->hasFocus()) { + if (_hostInput->getLastText().trimmed().isEmpty()) { + _hostInput->setFocus(); + _hostInput->showError(); + } else if (_portInput->getLastText().trimmed().toInt() <= 0) { + _portInput->setFocus(); + _portInput->showError(); } else { onSave(); } @@ -167,20 +168,20 @@ void ConnectionBox::onSubmit() { } void ConnectionBox::onSave() { - if (_httpProxyRadio.checked() || _tcpProxyRadio.checked()) { + if (_httpProxyRadio->checked() || _tcpProxyRadio->checked()) { ProxyData p; - p.host = _hostInput.getLastText().trimmed(); - p.user = _userInput.getLastText().trimmed(); - p.password = _passwordInput.getLastText().trimmed(); - p.port = _portInput.getLastText().toInt(); + p.host = _hostInput->getLastText().trimmed(); + p.user = _userInput->getLastText().trimmed(); + p.password = _passwordInput->getLastText().trimmed(); + p.port = _portInput->getLastText().toInt(); if (p.host.isEmpty()) { - _hostInput.setFocus(); + _hostInput->setFocus(); return; } else if (!p.port) { - _portInput.setFocus(); + _portInput->setFocus(); return; } - if (_httpProxyRadio.checked()) { + if (_httpProxyRadio->checked()) { Global::SetConnectionType(dbictHttpProxy); } else { Global::SetConnectionType(dbictTcpProxy); @@ -194,8 +195,8 @@ void ConnectionBox::onSave() { QNetworkProxyFactory::setUseSystemConfiguration(true); #endif // !TDESKTOP_DISABLE_NETWORK_PROXY } - if (cPlatform() == dbipWindows && Global::TryIPv6() != _tryIPv6.checked()) { - Global::SetTryIPv6(_tryIPv6.checked()); + if (cPlatform() == dbipWindows && Global::TryIPv6() != _tryIPv6->checked()) { + Global::SetTryIPv6(_tryIPv6->checked()); Local::writeSettings(); Global::RefConnectionTypeChanged().notify(); @@ -203,7 +204,7 @@ void ConnectionBox::onSave() { cSetRestartingToSettings(true); App::quit(); } else { - Global::SetTryIPv6(_tryIPv6.checked()); + Global::SetTryIPv6(_tryIPv6->checked()); Local::writeSettings(); Global::RefConnectionTypeChanged().notify(); @@ -226,25 +227,25 @@ AutoDownloadBox::AutoDownloadBox() : AbstractBox(st::boxWidth) , _save(this, lang(lng_connection_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { - setMaxHeight(3 * _sectionHeight + st::setLittleSkip + _gifPlay.height() + st::setLittleSkip + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + setMaxHeight(3 * _sectionHeight + st::setLittleSkip + _gifPlay->height() + st::setLittleSkip + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom()); - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); prepare(); } void AutoDownloadBox::showAll() { - _photoPrivate.show(); - _photoGroups.show(); - _audioPrivate.show(); - _audioGroups.show(); - _gifPrivate.show(); - _gifGroups.show(); - _gifPlay.show(); + _photoPrivate->show(); + _photoGroups->show(); + _audioPrivate->show(); + _audioGroups->show(); + _gifPrivate->show(); + _gifGroups->show(); + _gifPlay->show(); - _save.show(); - _cancel.show(); + _save->show(); + _cancel->show(); } void AutoDownloadBox::paintEvent(QPaintEvent *e) { @@ -259,25 +260,25 @@ void AutoDownloadBox::paintEvent(QPaintEvent *e) { } void AutoDownloadBox::resizeEvent(QResizeEvent *e) { - _photoPrivate.moveToLeft(st::boxTitlePosition.x(), st::boxTitleHeight + st::setLittleSkip); - _photoGroups.moveToLeft(st::boxTitlePosition.x(), _photoPrivate.y() + _photoPrivate.height() + st::setLittleSkip); + _photoPrivate->moveToLeft(st::boxTitlePosition.x(), st::boxTitleHeight + st::setLittleSkip); + _photoGroups->moveToLeft(st::boxTitlePosition.x(), _photoPrivate->y() + _photoPrivate->height() + st::setLittleSkip); - _audioPrivate.moveToLeft(st::boxTitlePosition.x(), _sectionHeight + st::boxTitleHeight + st::setLittleSkip); - _audioGroups.moveToLeft(st::boxTitlePosition.x(), _audioPrivate.y() + _audioPrivate.height() + st::setLittleSkip); + _audioPrivate->moveToLeft(st::boxTitlePosition.x(), _sectionHeight + st::boxTitleHeight + st::setLittleSkip); + _audioGroups->moveToLeft(st::boxTitlePosition.x(), _audioPrivate->y() + _audioPrivate->height() + st::setLittleSkip); - _gifPrivate.moveToLeft(st::boxTitlePosition.x(), 2 * _sectionHeight + st::boxTitleHeight + st::setLittleSkip); - _gifGroups.moveToLeft(st::boxTitlePosition.x(), _gifPrivate.y() + _gifPrivate.height() + st::setLittleSkip); - _gifPlay.moveToLeft(st::boxTitlePosition.x(), _gifGroups.y() + _gifGroups.height() + st::setLittleSkip); + _gifPrivate->moveToLeft(st::boxTitlePosition.x(), 2 * _sectionHeight + st::boxTitleHeight + st::setLittleSkip); + _gifGroups->moveToLeft(st::boxTitlePosition.x(), _gifPrivate->y() + _gifPrivate->height() + st::setLittleSkip); + _gifPlay->moveToLeft(st::boxTitlePosition.x(), _gifGroups->y() + _gifGroups->height() + st::setLittleSkip); - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); AbstractBox::resizeEvent(e); } void AutoDownloadBox::onSave() { bool changed = false; - int32 autoDownloadPhoto = (_photoPrivate.checked() ? 0 : dbiadNoPrivate) | (_photoGroups.checked() ? 0 : dbiadNoGroups); + int32 autoDownloadPhoto = (_photoPrivate->checked() ? 0 : dbiadNoPrivate) | (_photoGroups->checked() ? 0 : dbiadNoGroups); if (cAutoDownloadPhoto() != autoDownloadPhoto) { bool enabledPrivate = ((cAutoDownloadPhoto() & dbiadNoPrivate) && !(autoDownloadPhoto & dbiadNoPrivate)); bool enabledGroups = ((cAutoDownloadPhoto() & dbiadNoGroups) && !(autoDownloadPhoto & dbiadNoGroups)); @@ -290,7 +291,7 @@ void AutoDownloadBox::onSave() { } changed = true; } - int32 autoDownloadAudio = (_audioPrivate.checked() ? 0 : dbiadNoPrivate) | (_audioGroups.checked() ? 0 : dbiadNoGroups); + int32 autoDownloadAudio = (_audioPrivate->checked() ? 0 : dbiadNoPrivate) | (_audioGroups->checked() ? 0 : dbiadNoGroups); if (cAutoDownloadAudio() != autoDownloadAudio) { bool enabledPrivate = ((cAutoDownloadAudio() & dbiadNoPrivate) && !(autoDownloadAudio & dbiadNoPrivate)); bool enabledGroups = ((cAutoDownloadAudio() & dbiadNoGroups) && !(autoDownloadAudio & dbiadNoGroups)); @@ -305,7 +306,7 @@ void AutoDownloadBox::onSave() { } changed = true; } - int32 autoDownloadGif = (_gifPrivate.checked() ? 0 : dbiadNoPrivate) | (_gifGroups.checked() ? 0 : dbiadNoGroups); + int32 autoDownloadGif = (_gifPrivate->checked() ? 0 : dbiadNoPrivate) | (_gifGroups->checked() ? 0 : dbiadNoGroups); if (cAutoDownloadGif() != autoDownloadGif) { bool enabledPrivate = ((cAutoDownloadGif() & dbiadNoPrivate) && !(autoDownloadGif & dbiadNoPrivate)); bool enabledGroups = ((cAutoDownloadGif() & dbiadNoGroups) && !(autoDownloadGif & dbiadNoGroups)); @@ -320,8 +321,8 @@ void AutoDownloadBox::onSave() { } changed = true; } - if (cAutoPlayGif() != _gifPlay.checked()) { - cSetAutoPlayGif(_gifPlay.checked()); + if (cAutoPlayGif() != _gifPlay->checked()) { + cSetAutoPlayGif(_gifPlay->checked()); if (!cAutoPlayGif()) { App::stopGifItems(); } diff --git a/Telegram/SourceFiles/boxes/connectionbox.h b/Telegram/SourceFiles/boxes/connectionbox.h index 98f69e5b1..6d66477e6 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.h +++ b/Telegram/SourceFiles/boxes/connectionbox.h @@ -22,6 +22,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" +namespace Ui { +class Checkbox; +class Radiobutton; +} // namespace Ui + class ConnectionBox : public AbstractBox { Q_OBJECT @@ -41,14 +46,17 @@ protected: void doSetInnerFocus() override; private: - InputField _hostInput; - PortInput _portInput; - InputField _userInput; - PasswordField _passwordInput; - Radiobutton _autoRadio, _httpProxyRadio, _tcpProxyRadio; - Checkbox _tryIPv6; + ChildWidget _hostInput; + ChildWidget _portInput; + ChildWidget _userInput; + ChildWidget _passwordInput; + ChildWidget _autoRadio; + ChildWidget _httpProxyRadio; + ChildWidget _tcpProxyRadio; + ChildWidget _tryIPv6; - BoxButton _save, _cancel; + ChildWidget _save; + ChildWidget _cancel; }; @@ -68,12 +76,17 @@ protected: void showAll() override; private: - Checkbox _photoPrivate, _photoGroups; - Checkbox _audioPrivate, _audioGroups; - Checkbox _gifPrivate, _gifGroups, _gifPlay; + ChildWidget _photoPrivate; + ChildWidget _photoGroups; + ChildWidget _audioPrivate; + ChildWidget _audioGroups; + ChildWidget _gifPrivate; + ChildWidget _gifGroups; + ChildWidget _gifPlay; - int32 _sectionHeight; + int _sectionHeight; - BoxButton _save, _cancel; + ChildWidget _save; + ChildWidget _cancel; }; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 879935f68..e50d15116 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "mainwindow.h" #include "application.h" +#include "ui/buttons/checkbox.h" #include "ui/filedialog.h" #include "ui/widgets/multi_select.h" #include "ui/effects/widget_slide_wrap.h" @@ -104,7 +105,7 @@ void ContactsBox::init() { auto inviting = (_inner->creating() == CreatingGroupGroup) || (_inner->channel() && _inner->membersFilter() == MembersFilter::Recent) || _inner->chat(); auto topSkip = getTopScrollSkip(); - auto bottomSkip = inviting ? (st::boxButtonPadding.top() + _next.height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip; + auto bottomSkip = inviting ? (st::boxButtonPadding.top() + _next->height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip; ItemListBox::init(_inner, bottomSkip, topSkip); connect(_inner, SIGNAL(addRequested()), App::wnd(), SLOT(onShowAddContact())); @@ -127,22 +128,22 @@ void ContactsBox::init() { }); if (_inner->channel() && _inner->membersFilter() == MembersFilter::Admins) { - _next.hide(); - _cancel.hide(); + _next->hide(); + _cancel->hide(); } else if (_inner->chat() && _inner->membersFilter() == MembersFilter::Admins) { - connect(&_next, SIGNAL(clicked()), this, SLOT(onSaveAdmins())); - _bottomShadow = new ScrollableBoxShadow(this); + connect(_next, SIGNAL(clicked()), this, SLOT(onSaveAdmins())); + _bottomShadow.create(this); } else if (_inner->chat() || _inner->channel()) { - connect(&_next, SIGNAL(clicked()), this, SLOT(onInvite())); - _bottomShadow = new ScrollableBoxShadow(this); + connect(_next, SIGNAL(clicked()), this, SLOT(onInvite())); + _bottomShadow.create(this); } else if (_inner->creating() != CreatingGroupNone) { - connect(&_next, SIGNAL(clicked()), this, SLOT(onCreate())); - _bottomShadow = new ScrollableBoxShadow(this); + connect(_next, SIGNAL(clicked()), this, SLOT(onCreate())); + _bottomShadow.create(this); } else { - _next.hide(); - _cancel.hide(); + _next->hide(); + _cancel->hide(); } - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll())); _select->entity()->setQueryChangedCallback([this](const QString &query) { onFilterUpdate(query); }); _select->entity()->setItemRemovedCallback([this](uint64 itemId) { @@ -237,19 +238,19 @@ void ContactsBox::showAll() { _select->showFast(); } if (_inner->channel() && _inner->membersFilter() == MembersFilter::Admins) { - _next.hide(); - _cancel.hide(); + _next->hide(); + _cancel->hide(); } else if (_inner->chat() || _inner->channel()) { - _next.show(); - _cancel.show(); + _next->show(); + _cancel->show(); } else if (_inner->creating() != CreatingGroupNone) { - _next.show(); - _cancel.show(); + _next->show(); + _cancel->show(); } else { - _next.hide(); - _cancel.hide(); + _next->hide(); + _cancel->hide(); } - _topShadow.show(); + _topShadow->show(); if (_bottomShadow) _bottomShadow->show(); ItemListBox::showAll(); } @@ -317,14 +318,14 @@ void ContactsBox::updateScrollSkips() { auto oldScrollHeight = scrollArea()->height(); auto inviting = (_inner->creating() == CreatingGroupGroup) || (_inner->channel() && _inner->membersFilter() == MembersFilter::Recent) || _inner->chat(); auto topSkip = getTopScrollSkip(); - auto bottomSkip = inviting ? (st::boxButtonPadding.top() + _next.height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip; + auto bottomSkip = inviting ? (st::boxButtonPadding.top() + _next->height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip; setScrollSkips(bottomSkip, topSkip); auto scrollHeightDelta = scrollArea()->height() - oldScrollHeight; if (scrollHeightDelta) { scrollArea()->scrollToY(scrollArea()->scrollTop() - scrollHeightDelta); } - _topShadow.setGeometry(0, topSkip, width(), st::lineWidth); + _topShadow->setGeometry(0, topSkip, width(), st::lineWidth); } void ContactsBox::resizeEvent(QResizeEvent *e) { @@ -336,9 +337,9 @@ void ContactsBox::resizeEvent(QResizeEvent *e) { updateScrollSkips(); _inner->resize(width(), _inner->height()); - _next.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _next.width() + st::boxButtonPadding.left(), _next.y()); - if (_bottomShadow) _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _next.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); + _next->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _next->width() + st::boxButtonPadding.left(), _next->y()); + if (_bottomShadow) _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _next->height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth); } void ContactsBox::closePressed() { @@ -647,8 +648,8 @@ ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent) void ContactsBox::Inner::init() { subscribe(FileDownload::ImageLoaded(), [this] { update(); }); - connect(&_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); - connect(&_allAdmins, SIGNAL(changed()), this, SLOT(onAllAdminsChanged())); + connect(_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); + connect(_allAdmins, SIGNAL(changed()), this, SLOT(onAllAdminsChanged())); setAttribute(Qt::WA_OpaquePaintEvent); @@ -676,7 +677,7 @@ void ContactsBox::Inner::initList() { for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) { if (i.key()->id == peerFromUser(_chat->creator)) continue; - if (!_allAdmins.checked() && _chat->admins.contains(i.key())) { + if (!_allAdmins->checked() && _chat->admins.contains(i.key())) { admins.push_back(i.key()); if (!_checkedContacts.contains(i.key())) { _checkedContacts.insert(i.key()); @@ -743,8 +744,8 @@ void ContactsBox::Inner::onNoAddAdminBox(QObject *obj) { } void ContactsBox::Inner::onAllAdminsChanged() { - if (_saving && _allAdmins.checked() != _allAdminsChecked) { - _allAdmins.setChecked(_allAdminsChecked); + if (_saving && _allAdmins->checked() != _allAdminsChecked) { + _allAdmins->setChecked(_allAdminsChecked); } else if (_allAdminsChangedCallback) { _allAdminsChangedCallback(); } @@ -797,7 +798,7 @@ bool ContactsBox::Inner::addAdminFail(const RPCError &error, mtpRequestId req) { void ContactsBox::Inner::saving(bool flag) { _saving = flag; - _allAdminsChecked = _allAdmins.checked(); + _allAdminsChecked = _allAdmins->checked(); update(); } @@ -933,7 +934,7 @@ void ContactsBox::Inner::paintDialog(Painter &p, uint64 ms, PeerData *peer, Cont UserData *user = peer->asUser(); if (_chat && _membersFilter == MembersFilter::Admins) { - if (_allAdmins.checked() || peer->id == peerFromUser(_chat->creator) || _saving) { + if (_allAdmins->checked() || peer->id == peerFromUser(_chat->creator) || _saving) { sel = false; } } else { @@ -944,7 +945,7 @@ void ContactsBox::Inner::paintDialog(Painter &p, uint64 ms, PeerData *peer, Cont auto paintDisabledCheck = data->disabledChecked; if (_chat && _membersFilter == MembersFilter::Admins) { - if (peer->id == peerFromUser(_chat->creator) || _allAdmins.checked()) { + if (peer->id == peerFromUser(_chat->creator) || _allAdmins->checked()) { paintDisabledCheck = true; } } @@ -1059,7 +1060,7 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { p.setPen(st::black); p.drawTextLeft(st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(lng_chat_all_members_admins)); int aboutw = width() - st::contactsPadding.left() - st::contactsPadding.right(); - (_allAdmins.checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsNewItemHeight + st::contactsAboutTop, aboutw); + (_allAdmins->checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsNewItemHeight + st::contactsAboutTop, aboutw); } else { p.fillRect(0, 0, width(), st::contactsNewItemHeight, (_newItemSel ? st::contactsBgOver : st::white)->b); p.setFont(st::contactsNameFont); @@ -1114,7 +1115,7 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { p.setPen(st::black); p.drawTextLeft(st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(lng_chat_all_members_admins)); int aboutw = width() - st::contactsPadding.left() - st::contactsPadding.right(); - (_allAdmins.checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsNewItemHeight + st::contactsAboutTop, aboutw); + (_allAdmins->checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsNewItemHeight + st::contactsAboutTop, aboutw); p.translate(0, _newItemHeight); } else if (cContactsReceived() && !_searching) { text = lang(lng_no_contacts); @@ -1383,7 +1384,7 @@ void ContactsBox::Inner::changeCheckState(Dialogs::Row *row) { void ContactsBox::Inner::changeCheckState(ContactData *data, PeerData *peer) { t_assert(usingMultiSelect()); - if (_chat && _membersFilter == MembersFilter::Admins && _allAdmins.checked()) { + if (_chat && _membersFilter == MembersFilter::Admins && _allAdmins->checked()) { } else if (data->checkbox->checked()) { changePeerCheckState(data, peer, false); } else if (selectedCount() < ((_channel && _channel->isMegagroup()) ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) { @@ -1499,8 +1500,8 @@ void ContactsBox::Inner::updateFilter(QString filter) { _sel = 0; refresh(); } else { - if (!_addContactLnk.isHidden()) _addContactLnk.hide(); - if (!_allAdmins.isHidden()) _allAdmins.hide(); + if (!_addContactLnk->isHidden()) _addContactLnk->hide(); + if (!_allAdmins->isHidden()) _allAdmins->hide(); QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; _filtered.clear(); @@ -1678,28 +1679,28 @@ void ContactsBox::Inner::peopleReceived(const QString &query, const QVectorisHidden()) _allAdmins->show(); } else { - if (!_allAdmins.isHidden()) _allAdmins.hide(); + if (!_allAdmins->isHidden()) _allAdmins->hide(); } if (!_contacts->isEmpty() || !_byUsername.isEmpty()) { - if (!_addContactLnk.isHidden()) _addContactLnk.hide(); + if (!_addContactLnk->isHidden()) _addContactLnk->hide(); resize(width(), _newItemHeight + (_contacts->size() * _rowHeight) + (_byUsername.isEmpty() ? 0 : (st::searchedBarHeight + _byUsername.size() * _rowHeight))); } else if (_chat && _membersFilter == MembersFilter::Admins) { - if (!_addContactLnk.isHidden()) _addContactLnk.hide(); + if (!_addContactLnk->isHidden()) _addContactLnk->hide(); resize(width(), _newItemHeight + st::noContactsHeight); } else { if (cContactsReceived() && !bot()) { - if (_addContactLnk.isHidden()) _addContactLnk.show(); + if (_addContactLnk->isHidden()) _addContactLnk->show(); } else { - if (!_addContactLnk.isHidden()) _addContactLnk.hide(); + if (!_addContactLnk->isHidden()) _addContactLnk->hide(); } resize(width(), st::noContactsHeight); } } else { - if (!_allAdmins.isHidden()) _allAdmins.hide(); + if (!_allAdmins->isHidden()) _allAdmins->hide(); if (_filtered.isEmpty() && _byUsernameFiltered.isEmpty()) { - if (!_addContactLnk.isHidden()) _addContactLnk.hide(); + if (!_addContactLnk->isHidden()) _addContactLnk->hide(); resize(width(), st::noContactsHeight); } else { resize(width(), (_filtered.size() * _rowHeight) + (_byUsernameFiltered.isEmpty() ? 0 : (st::searchedBarHeight + _byUsernameFiltered.size() * _rowHeight))); @@ -1745,8 +1746,8 @@ ContactsBox::Inner::~Inner() { } void ContactsBox::Inner::resizeEvent(QResizeEvent *e) { - _addContactLnk.move((width() - _addContactLnk.width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); - _allAdmins.moveToLeft(st::contactsPadding.left(), st::contactsNewItemTop); + _addContactLnk->move((width() - _addContactLnk->width()) / 2, (st::noContactsHeight + st::noContactsFont->height) / 2); + _allAdmins->moveToLeft(st::contactsPadding.left(), st::contactsNewItemTop); } void ContactsBox::Inner::selectSkip(int32 dir) { @@ -1940,3 +1941,7 @@ QVector ContactsBox::Inner::selectedInputs() { } return result; } + +bool ContactsBox::Inner::allAdmins() const { + return _allAdmins->checked(); +} diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index c56f97b50..d0020347b 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -31,6 +31,7 @@ class IndexedList; } // namespace Dialogs namespace Ui { +class Checkbox; class MultiSelect; template class WidgetSlideWrap; @@ -91,11 +92,12 @@ private: ChildWidget _inner; ChildWidget> _select; - BoxButton _next, _cancel; + ChildWidget _next; + ChildWidget _cancel; MembersFilter _membersFilter; - ScrollableBoxShadow _topShadow; - ScrollableBoxShadow *_bottomShadow = nullptr; + ChildWidget _topShadow; + ChildWidget _bottomShadow = { nullptr }; void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req); bool peopleFailed(const RPCError &error, mtpRequestId req); @@ -152,9 +154,7 @@ public: QVector selected(); QVector selectedInputs(); - bool allAdmins() const { - return _allAdmins.checked(); - } + bool allAdmins() const; void setAllAdminsChangedCallback(base::lambda_unique allAdminsChangedCallback) { _allAdminsChangedCallback = std_::move(allAdminsChangedCallback); } @@ -263,7 +263,7 @@ private: CreatingGroupType _creating = CreatingGroupNone; MembersAlreadyIn _already; - Checkbox _allAdmins; + ChildWidget _allAdmins; int32 _aboutWidth; Text _aboutAllAdmins, _aboutAdmins; base::lambda_unique _allAdminsChangedCallback; @@ -301,7 +301,7 @@ private: int _byUsernameSel = -1; QPoint _lastMousePos; - LinkButton _addContactLnk; + ChildWidget _addContactLnk; bool _saving = false; bool _allAdminsChecked = false; diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.cpp b/Telegram/SourceFiles/boxes/downloadpathbox.cpp index 980263537..b680a07b5 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.cpp +++ b/Telegram/SourceFiles/boxes/downloadpathbox.cpp @@ -19,12 +19,12 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +#include "boxes/downloadpathbox.h" + #include "lang.h" - #include "localstorage.h" - -#include "downloadpathbox.h" #include "ui/filedialog.h" +#include "ui/buttons/checkbox.h" #include "pspecific.h" DownloadPathBox::DownloadPathBox() : AbstractBox() @@ -37,14 +37,14 @@ DownloadPathBox::DownloadPathBox() : AbstractBox() , _save(this, lang(lng_connection_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_default, SIGNAL(changed()), this, SLOT(onChange())); - connect(&_temp, SIGNAL(changed()), this, SLOT(onChange())); - connect(&_dir, SIGNAL(changed()), this, SLOT(onChange())); + connect(_default, SIGNAL(changed()), this, SLOT(onChange())); + connect(_temp, SIGNAL(changed()), this, SLOT(onChange())); + connect(_dir, SIGNAL(changed()), this, SLOT(onChange())); - connect(&_pathLink, SIGNAL(clicked()), this, SLOT(onEditPath())); + connect(_pathLink, SIGNAL(clicked()), this, SLOT(onEditPath())); if (!_path.isEmpty() && _path != qsl("tmp")) { setPathText(QDir::toNativeSeparators(_path)); } @@ -52,22 +52,22 @@ DownloadPathBox::DownloadPathBox() : AbstractBox() } void DownloadPathBox::showAll() { - _default.show(); - _temp.show(); - _dir.show(); + _default->show(); + _temp->show(); + _dir->show(); - if (_dir.checked()) { - _pathLink.show(); + if (_dir->checked()) { + _pathLink->show(); } else { - _pathLink.hide(); + _pathLink->hide(); } - _save.show(); - _cancel.show(); + _save->show(); + _cancel->show(); - int32 h = st::boxTitleHeight + st::boxOptionListPadding.top() + _default.height() + st::boxOptionListPadding.top() + _temp.height() + st::boxOptionListPadding.top() + _dir.height(); - if (_dir.checked()) h += st::downloadPathSkip + _pathLink.height(); - h += st::boxOptionListPadding.bottom() + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(); + int32 h = st::boxTitleHeight + st::boxOptionListPadding.top() + _default->height() + st::boxOptionListPadding.top() + _temp->height() + st::boxOptionListPadding.top() + _dir->height(); + if (_dir->checked()) h += st::downloadPathSkip + _pathLink->height(); + h += st::boxOptionListPadding.bottom() + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); setMaxHeight(h); } @@ -80,31 +80,31 @@ void DownloadPathBox::paintEvent(QPaintEvent *e) { } void DownloadPathBox::resizeEvent(QResizeEvent *e) { - _default.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxTitleHeight + st::boxOptionListPadding.top()); - _temp.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _default.y() + _default.height() + st::boxOptionListPadding.top()); - _dir.moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _temp.y() + _temp.height() + st::boxOptionListPadding.top()); + _default->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxTitleHeight + st::boxOptionListPadding.top()); + _temp->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _default->y() + _default->height() + st::boxOptionListPadding.top()); + _dir->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _temp->y() + _temp->height() + st::boxOptionListPadding.top()); int32 inputx = st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultRadiobutton.textPosition.x(); - int32 inputy = _dir.y() + _dir.height() + st::downloadPathSkip; + int32 inputy = _dir->y() + _dir->height() + st::downloadPathSkip; - _pathLink.moveToLeft(inputx, inputy); + _pathLink->moveToLeft(inputx, inputy); - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); AbstractBox::resizeEvent(e); } void DownloadPathBox::onChange() { - if (_dir.checked()) { + if (_dir->checked()) { if (_path.isEmpty() || _path == qsl("tmp")) { - (_path.isEmpty() ? _default : _temp).setChecked(true); + (_path.isEmpty() ? _default : _temp)->setChecked(true); onEditPath(); if (!_path.isEmpty() && _path != qsl("tmp")) { - _dir.setChecked(true); + _dir->setChecked(true); } } else { setPathText(QDir::toNativeSeparators(_path)); } - } else if (_temp.checked()) { + } else if (_temp->checked()) { _path = qsl("tmp"); } else { _path = QString(); @@ -130,8 +130,8 @@ void DownloadPathBox::onEditPath() { } void DownloadPathBox::onSave() { - Global::SetDownloadPath(_default.checked() ? QString() : (_temp.checked() ? qsl("tmp") : _path)); - Global::SetDownloadPathBookmark((_default.checked() || _temp.checked()) ? QByteArray() : _pathBookmark); + Global::SetDownloadPath(_default->checked() ? QString() : (_temp->checked() ? qsl("tmp") : _path)); + Global::SetDownloadPathBookmark((_default->checked() || _temp->checked()) ? QByteArray() : _pathBookmark); Local::writeUserSettings(); Global::RefDownloadPathChanged().notify(); onClose(); @@ -139,5 +139,5 @@ void DownloadPathBox::onSave() { void DownloadPathBox::setPathText(const QString &text) { int32 availw = st::boxWideWidth - st::boxPadding.left() - st::defaultRadiobutton.textPosition.x() - st::boxPadding.right(); - _pathLink.setText(st::boxTextFont->elided(text, availw)); + _pathLink->setText(st::boxTextFont->elided(text, availw)); } diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.h b/Telegram/SourceFiles/boxes/downloadpathbox.h index 7c4f26a3b..a0fdfe5fd 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.h +++ b/Telegram/SourceFiles/boxes/downloadpathbox.h @@ -23,6 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" #include "core/observer.h" +namespace Ui { +class Radiobutton; +} // namespace Ui + class DownloadPathBox : public AbstractBox { Q_OBJECT @@ -46,8 +50,12 @@ private: QString _path; QByteArray _pathBookmark; - Radiobutton _default, _temp, _dir; - LinkButton _pathLink; - BoxButton _save, _cancel; + ChildWidget _default; + ChildWidget _temp; + ChildWidget _dir; + ChildWidget _pathLink; + + ChildWidget _save; + ChildWidget _cancel; }; diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp index f79bdaed6..15fafe9ba 100644 --- a/Telegram/SourceFiles/boxes/languagebox.cpp +++ b/Telegram/SourceFiles/boxes/languagebox.cpp @@ -19,15 +19,14 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" +#include "boxes/languagebox.h" + #include "lang.h" - +#include "ui/buttons/checkbox.h" #include "localstorage.h" - -#include "languagebox.h" -#include "confirmbox.h" +#include "boxes/confirmbox.h" #include "mainwidget.h" #include "mainwindow.h" - #include "langloaderplain.h" LanguageBox::LanguageBox() : @@ -38,7 +37,7 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { int32 y = st::boxTitleHeight + st::boxOptionListPadding.top(); _langs.reserve(languageCount + (haveTestLang ? 1 : 0)); if (haveTestLang) { - _langs.push_back(new Radiobutton(this, qsl("lang"), languageTest, qsl("Custom Lang"), (cLang() == languageTest), st::langsButton)); + _langs.push_back(new Ui::Radiobutton(this, qsl("lang"), languageTest, qsl("Custom Lang"), (cLang() == languageTest), st::langsButton)); _langs.back()->move(st::boxPadding.left() + st::boxOptionListPadding.left(), y); y += _langs.back()->height() + st::boxOptionListPadding.top(); connect(_langs.back(), SIGNAL(changed()), this, SLOT(onChange())); @@ -51,7 +50,7 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { } else { result.insert(lng_language_name, langOriginal(lng_language_name)); } - _langs.push_back(new Radiobutton(this, qsl("lang"), i, result.value(lng_language_name, LanguageCodes[i].c_str() + qsl(" language")), (cLang() == i), st::langsButton)); + _langs.push_back(new Ui::Radiobutton(this, qsl("lang"), i, result.value(lng_language_name, LanguageCodes[i].c_str() + qsl(" language")), (cLang() == i), st::langsButton)); _langs.back()->move(st::boxPadding.left() + st::boxOptionListPadding.left(), y); y += _langs.back()->height() + st::boxOptionListPadding.top(); connect(_langs.back(), SIGNAL(changed()), this, SLOT(onChange())); diff --git a/Telegram/SourceFiles/boxes/languagebox.h b/Telegram/SourceFiles/boxes/languagebox.h index 8b0332e39..9714fcde7 100644 --- a/Telegram/SourceFiles/boxes/languagebox.h +++ b/Telegram/SourceFiles/boxes/languagebox.h @@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" +namespace Ui { +class Radiobutton; +} // namespace Ui + class LanguageBox : public AbstractBox { Q_OBJECT @@ -40,7 +44,7 @@ protected: void showAll() override; private: - QVector _langs; + QVector _langs; BoxButton _close; }; diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 98aac9648..988e5b439 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "photosendbox.h" #include "history/history_media_types.h" +#include "ui/buttons/checkbox.h" #include "styles/style_history.h" PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxWideWidth) @@ -44,8 +45,8 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW , _isImage(false) , _replyTo(_file->to.replyTo) , _confirmed(false) { - connect(&_send, SIGNAL(clicked()), this, SLOT(onSend())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_send, SIGNAL(clicked()), this, SLOT(onSend())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); _animated = false; QSize dimensions; @@ -134,16 +135,16 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW _isImage = fileIsImage(_file->filename, _file->filemime); } if (_file->type != PreparePhoto) { - _compressed.hide(); + _compressed->hide(); } updateBoxSize(); - _caption.setMaxLength(MaxPhotoCaption); - _caption.setCtrlEnterSubmit(CtrlEnterSubmitBoth); - connect(&_compressed, SIGNAL(changed()), this, SLOT(onCompressedChange())); - connect(&_caption, SIGNAL(resized()), this, SLOT(onCaptionResized())); - connect(&_caption, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); - connect(&_caption, SIGNAL(cancelled()), this, SLOT(onClose())); + _caption->setMaxLength(MaxPhotoCaption); + _caption->setCtrlEnterSubmit(CtrlEnterSubmitBoth); + connect(_compressed, SIGNAL(changed()), this, SLOT(onCompressedChange())); + connect(_caption, SIGNAL(resized()), this, SLOT(onCaptionResized())); + connect(_caption, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); + connect(_caption, SIGNAL(cancelled()), this, SLOT(onClose())); prepare(); } @@ -164,10 +165,10 @@ PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QSt , _lname(lname) , _replyTo(replyTo) , _confirmed(false) { - connect(&_send, SIGNAL(clicked()), this, SLOT(onSend())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_send, SIGNAL(clicked()), this, SLOT(onSend())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - _compressed.hide(); + _compressed->hide(); _name.setText(st::semiboldFont, lng_full_name(lt_first_name, _fname, lt_last_name, _lname), _textNameOptions); _status = _phone; @@ -179,10 +180,10 @@ PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QSt void PhotoSendBox::onCompressedChange() { showAll(); - if (_caption.isHidden()) { + if (_caption->isHidden()) { setFocus(); } else { - _caption.setFocus(); + _caption->setFocus(); } updateBoxSize(); resizeEvent(0); @@ -197,11 +198,11 @@ void PhotoSendBox::onCaptionResized() { void PhotoSendBox::updateBoxSize() { if (_file && (_file->type == PreparePhoto || _animated)) { - setMaxHeight(st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + (_animated ? 0 : (st::boxPhotoCompressedPadding.top() + _compressed.height())) + st::boxPhotoCompressedPadding.bottom() + _caption.height() + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom()); + setMaxHeight(st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + (_animated ? 0 : (st::boxPhotoCompressedPadding.top() + _compressed->height())) + st::boxPhotoCompressedPadding.bottom() + _caption->height() + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom()); } else if (_thumbw) { - setMaxHeight(st::boxPhotoPadding.top() + st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() + (_file ? (st::boxPhotoCompressedPadding.bottom() + _caption.height()) : 0) + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom()); + setMaxHeight(st::boxPhotoPadding.top() + st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() + (_file ? (st::boxPhotoCompressedPadding.bottom() + _caption->height()) : 0) + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom()); } else { - setMaxHeight(st::boxPhotoPadding.top() + st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom() + (_file ? (st::boxPhotoCompressedPadding.bottom() + _caption.height()) : 0) + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom()); + setMaxHeight(st::boxPhotoPadding.top() + st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom() + (_file ? (st::boxPhotoCompressedPadding.bottom() + _caption->height()) : 0) + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom()); } } @@ -291,11 +292,11 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) { } void PhotoSendBox::resizeEvent(QResizeEvent *e) { - _send.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _send.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _send.width() + st::boxButtonPadding.left(), _send.y()); - _caption.resize(st::boxWideWidth - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(), _caption.height()); - _caption.moveToLeft(st::boxPhotoPadding.left(), _send.y() - st::boxButtonPadding.top() - _caption.height()); - _compressed.moveToLeft(st::boxPhotoPadding.left(), st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + st::boxPhotoCompressedPadding.top()); + _send->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _send->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _send->width() + st::boxButtonPadding.left(), _send->y()); + _caption->resize(st::boxWideWidth - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(), _caption->height()); + _caption->moveToLeft(st::boxPhotoPadding.left(), _send->y() - st::boxButtonPadding.top() - _caption->height()); + _compressed->moveToLeft(st::boxPhotoPadding.left(), st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + st::boxPhotoCompressedPadding.top()); AbstractBox::resizeEvent(e); } @@ -310,47 +311,47 @@ void PhotoSendBox::closePressed() { } void PhotoSendBox::showAll() { - _send.show(); - _cancel.show(); + _send->show(); + _cancel->show(); if (_file) { if (_file->type == PreparePhoto) { - _compressed.show(); + _compressed->show(); } - _caption.show(); + _caption->show(); } else { - _caption.hide(); - _compressed.hide(); + _caption->hide(); + _compressed->hide(); } } void PhotoSendBox::doSetInnerFocus() { - if (_caption.isHidden()) { + if (_caption->isHidden()) { setFocus(); } else { - _caption.setFocus(); + _caption->setFocus(); } } void PhotoSendBox::onSend(bool ctrlShiftEnter) { if (App::main()) { if (_file) { - if (_compressed.isHidden()) { + if (_compressed->isHidden()) { if (_file->type == PrepareAuto) { _file->type = PrepareDocument; } } else { - if (_compressedFromSettings && _compressed.checked() != cCompressPastedImage()) { - cSetCompressPastedImage(_compressed.checked()); + if (_compressedFromSettings && _compressed->checked() != cCompressPastedImage()) { + cSetCompressPastedImage(_compressed->checked()); Local::writeUserSettings(); } - if (_compressed.checked()) { + if (_compressed->checked()) { _file->type = PreparePhoto; } else { _file->type = PrepareDocument; } } - if (!_caption.isHidden()) { - _file->caption = prepareText(_caption.getLastText(), true); + if (!_caption->isHidden()) { + _file->caption = prepareText(_caption->getLastText(), true); } App::main()->onSendFileConfirm(_file, ctrlShiftEnter); } else { @@ -366,7 +367,6 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) , _animated(false) , _photo(false) , _doc(false) -, _field(0) , _save(this, lang(lng_settings_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _thumbx(0) @@ -377,8 +377,8 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) , _isImage(false) , _previewCancelled(false) , _saveRequestId(0) { - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); + connect(_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); QSize dimensions; ImagePtr image; @@ -490,13 +490,13 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth) } if (_animated || _photo || _doc) { - _field = new InputArea(this, st::confirmCaptionArea, lang(lng_photo_caption), caption); + _field.create(this, st::confirmCaptionArea, lang(lng_photo_caption), caption); _field->setMaxLength(MaxPhotoCaption); _field->setCtrlEnterSubmit(CtrlEnterSubmitBoth); } else { auto original = msg->originalText(); QString text = textApplyEntities(original.text, original.entities); - _field = new InputArea(this, st::editTextArea, lang(lng_photo_caption), text); + _field.create(this, st::editTextArea, lang(lng_photo_caption), text); // _field->setMaxLength(MaxMessageSize); // entities can make text in input field larger but still valid _field->setCtrlEnterSubmit(cCtrlEnter() ? CtrlEnterSubmitCtrlEnter : CtrlEnterSubmitEnter); } @@ -523,7 +523,7 @@ void EditCaptionBox::onCaptionResized() { } void EditCaptionBox::updateBoxSize() { - int32 bottomh = st::boxPhotoCompressedPadding.bottom() + _field->height() + st::normalFont->height + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(); + int32 bottomh = st::boxPhotoCompressedPadding.bottom() + _field->height() + st::normalFont->height + st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom(); if (_photo || _animated) { setMaxHeight(st::boxPhotoPadding.top() + _thumbh + bottomh); } else if (_thumbw) { @@ -620,16 +620,16 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { } void EditCaptionBox::resizeEvent(QResizeEvent *e) { - _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); - _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); + _save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height()); + _cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y()); _field->resize(st::boxWideWidth - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(), _field->height()); - _field->moveToLeft(st::boxPhotoPadding.left(), _save.y() - st::boxButtonPadding.top() - st::normalFont->height - _field->height()); + _field->moveToLeft(st::boxPhotoPadding.left(), _save->y() - st::boxButtonPadding.top() - st::normalFont->height - _field->height()); AbstractBox::resizeEvent(e); } void EditCaptionBox::showAll() { - _save.show(); - _cancel.show(); + _save->show(); + _cancel->show(); _field->show(); } diff --git a/Telegram/SourceFiles/boxes/photosendbox.h b/Telegram/SourceFiles/boxes/photosendbox.h index 4856131d3..8c41c31f1 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.h +++ b/Telegram/SourceFiles/boxes/photosendbox.h @@ -23,6 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" #include "localimageloader.h" +namespace Ui { +class Checkbox; +} // namespace Ui + class PhotoSendBox : public AbstractBox { Q_OBJECT @@ -52,10 +56,12 @@ private: QPixmap _thumb; - InputArea _caption; + ChildWidget _caption; bool _compressedFromSettings; - Checkbox _compressed; - BoxButton _send, _cancel; + ChildWidget _compressed; + + ChildWidget _send; + ChildWidget _cancel; int32 _thumbx, _thumby, _thumbw, _thumbh; Text _name; @@ -101,8 +107,9 @@ private: QPixmap _thumb; - InputArea *_field; - BoxButton _save, _cancel; + ChildWidget _field = { nullptr }; + ChildWidget _save; + ChildWidget _cancel; int32 _thumbx, _thumby, _thumbw, _thumbh; Text _name; diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index 42cb287ff..8c93392f5 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "styles/style_profile.h" #include "boxes/confirmbox.h" +#include "ui/buttons/checkbox.h" #include "mainwindow.h" ReportBox::ReportBox(ChannelData *channel) : AbstractBox(st::boxWidth) diff --git a/Telegram/SourceFiles/boxes/report_box.h b/Telegram/SourceFiles/boxes/report_box.h index 39954c348..c4f81d358 100644 --- a/Telegram/SourceFiles/boxes/report_box.h +++ b/Telegram/SourceFiles/boxes/report_box.h @@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" +namespace Ui { +class Radiobutton; +} // namespace Ui + class ReportBox : public AbstractBox, public RPCSender { Q_OBJECT @@ -50,10 +54,10 @@ private: ChannelData *_channel; - ChildWidget _reasonSpam; - ChildWidget _reasonViolence; - ChildWidget _reasonPornography; - ChildWidget _reasonOther; + ChildWidget _reasonSpam; + ChildWidget _reasonViolence; + ChildWidget _reasonPornography; + ChildWidget _reasonOther; ChildWidget _reasonOtherText = { nullptr }; ChildWidget _report, _cancel; diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 4dbd47fc1..62db76caf 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -194,7 +194,6 @@ QString Generator::typeToString(structure::Type type) const { case Tag::String: return "QString"; case Tag::Color: return "style::color"; case Tag::Point: return "style::point"; - case Tag::Sprite: return "style::sprite"; case Tag::Size: return "style::size"; case Tag::Transition: return "style::transition"; case Tag::Cursor: return "style::cursor"; @@ -217,7 +216,6 @@ QString Generator::typeToDefaultValue(structure::Type type) const { case Tag::String: return "QString()"; case Tag::Color: return "{ Qt::Uninitialized }"; case Tag::Point: return "{ 0, 0 }"; - case Tag::Sprite: return "{ 0, 0, 0, 0 }"; case Tag::Size: return "{ 0, 0 }"; case Tag::Transition: return "anim::linear"; case Tag::Cursor: return "style::cur_default"; @@ -260,10 +258,6 @@ QString Generator::valueAssignmentCode(structure::Value value) const { auto v(value.Point()); return QString("{ %1, %2 }").arg(pxValueName(v.x)).arg(pxValueName(v.y)); } break; - case Tag::Sprite: { - auto v(value.Sprite()); - return QString("{ %1, %2, %3, %4 }").arg(pxValueName(v.left)).arg(pxValueName(v.top)).arg(pxValueName(v.width)).arg(pxValueName(v.height)); - } break; case Tag::Size: { auto v(value.Size()); return QString("{ %1, %2 }").arg(pxValueName(v.width)).arg(pxValueName(v.height)); @@ -694,13 +688,6 @@ bool Generator::collectUniqueValues() { pxValues_.insert(v.x, true); pxValues_.insert(v.y, true); } break; - case Tag::Sprite: { - auto v(value.Sprite()); - pxValues_.insert(v.left, true); - pxValues_.insert(v.top, true); - pxValues_.insert(v.width, true); - pxValues_.insert(v.height, true); - } break; case Tag::Size: { auto v(value.Size()); pxValues_.insert(v.width, true); @@ -722,7 +709,7 @@ bool Generator::collectUniqueValues() { } break; case Tag::Icon: { auto v(value.Icon()); - for (const auto &part : v.parts) { + for (auto &part : v.parts) { pxValues_.insert(part.offset.Point().x, true); pxValues_.insert(part.offset.Point().y, true); if (!iconMasks_.contains(part.filename)) { diff --git a/Telegram/SourceFiles/codegen/style/options.cpp b/Telegram/SourceFiles/codegen/style/options.cpp index 2793e3cf7..1b4d08a2c 100644 --- a/Telegram/SourceFiles/codegen/style/options.cpp +++ b/Telegram/SourceFiles/codegen/style/options.cpp @@ -49,14 +49,6 @@ Options parseOptions() { if (arg == "--rebuild") { result.rebuildDependencies = true; - // Skip generating style modules - } else if (arg == "--skip-styles") { - result.skipStyles = true; - - // Skip generating sprite_125x.png and sprite_150x.png - } else if (arg == "--skip-sprites") { - result.skipSprites = true; - // Include paths } else if (arg == "-I") { if (++i == count) { diff --git a/Telegram/SourceFiles/codegen/style/options.h b/Telegram/SourceFiles/codegen/style/options.h index f1f591b7c..34746ee2d 100644 --- a/Telegram/SourceFiles/codegen/style/options.h +++ b/Telegram/SourceFiles/codegen/style/options.h @@ -31,8 +31,6 @@ struct Options { QString outputPath = "."; QString inputPath; bool rebuildDependencies = false; - bool skipStyles = false; - bool skipSprites = false; }; // Parsing failed if inputPath is empty in the result. diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index 1f5ab35f9..7f9cc0cc3 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -64,7 +64,7 @@ QString tokenValue(const BasicToken &token) { bool isValidColor(const QString &str) { auto len = str.size(); - if (len != 3 && len != 4 && len != 6 && len != 8) { + if (len != 6 && len != 8) { return false; } @@ -89,17 +89,11 @@ uchar readHexUchar(QChar char1, QChar char2) { structure::data::color convertWebColor(const QString &str) { uchar r = 0, g = 0, b = 0, a = 255; if (isValidColor(str)) { - auto len = str.size(); - if (len == 3 || len == 4) { - r = readHexUchar(str.at(0), str.at(0)); - g = readHexUchar(str.at(1), str.at(1)); - b = readHexUchar(str.at(2), str.at(2)); - if (len == 4) a = readHexUchar(str.at(3), str.at(3)); - } else { - r = readHexUchar(str.at(0), str.at(1)); - g = readHexUchar(str.at(2), str.at(3)); - b = readHexUchar(str.at(4), str.at(5)); - if (len == 8) a = readHexUchar(str.at(6), str.at(7)); + r = readHexUchar(str.at(0), str.at(1)); + g = readHexUchar(str.at(2), str.at(3)); + b = readHexUchar(str.at(4), str.at(5)); + if (str.size() == 8) { + a = readHexUchar(str.at(6), str.at(7)); } } return { r, g, b, a }; @@ -120,7 +114,6 @@ std::string logType(const structure::Type &type) { { structure::TypeTag::String , "string" }, { structure::TypeTag::Color , "color" }, { structure::TypeTag::Point , "point" }, - { structure::TypeTag::Sprite , "sprite" }, { structure::TypeTag::Size , "size" }, { structure::TypeTag::Transition, "transition" }, { structure::TypeTag::Cursor , "cursor" }, @@ -285,8 +278,6 @@ structure::Value ParsedFile::readValue() { return colorValue; } else if (auto pointValue = readPointValue()) { return pointValue; - } else if (auto spriteValue = readSpriteValue()) { - return spriteValue; } else if (auto sizeValue = readSizeValue()) { return sizeValue; } else if (auto transitionValue = readTransitionValue()) { @@ -509,41 +500,6 @@ structure::Value ParsedFile::readColorValue() { } else { logErrorUnexpectedToken() << "color value in #ccc, #ccca, #cccccc or #ccccccaa format"; } - } else if (auto rgbaToken = file_.getToken(BasicType::Name)) { - if (tokenValue(rgbaToken) == "rgba") { - assertNextToken(BasicType::LeftParenthesis); - - auto r = readNumericValue(); assertNextToken(BasicType::Comma); - auto g = readNumericValue(); assertNextToken(BasicType::Comma); - auto b = readNumericValue(); assertNextToken(BasicType::Comma); - auto a = readNumericValue(); - if (r.type().tag != structure::TypeTag::Int || r.Int() < 0 || r.Int() > 255 || - g.type().tag != structure::TypeTag::Int || g.Int() < 0 || g.Int() > 255 || - b.type().tag != structure::TypeTag::Int || b.Int() < 0 || b.Int() > 255 || - a.type().tag != structure::TypeTag::Int || a.Int() < 0 || a.Int() > 255) { - logErrorTypeMismatch() << "expected four 0-255 values for the rgba color"; - } - - assertNextToken(BasicType::RightParenthesis); - - return { convertIntColor(r.Int(), g.Int(), b.Int(), a.Int()) }; - } else if (tokenValue(rgbaToken) == "rgb") { - assertNextToken(BasicType::LeftParenthesis); - - auto r = readNumericValue(); assertNextToken(BasicType::Comma); - auto g = readNumericValue(); assertNextToken(BasicType::Comma); - auto b = readNumericValue(); - if (r.type().tag != structure::TypeTag::Int || r.Int() < 0 || r.Int() > 255 || - g.type().tag != structure::TypeTag::Int || g.Int() < 0 || g.Int() > 255 || - b.type().tag != structure::TypeTag::Int || b.Int() < 0 || b.Int() > 255) { - logErrorTypeMismatch() << "expected three int values for the rgb color"; - } - - assertNextToken(BasicType::RightParenthesis); - - return { convertIntColor(r.Int(), g.Int(), b.Int(), 255) }; - } - file_.putBack(); } return {}; } @@ -569,31 +525,6 @@ structure::Value ParsedFile::readPointValue() { return {}; } -structure::Value ParsedFile::readSpriteValue() { - if (auto font = file_.getToken(BasicType::Name)) { - if (tokenValue(font) == "sprite") { - assertNextToken(BasicType::LeftParenthesis); - - auto x = readNumericValue(); assertNextToken(BasicType::Comma); - auto y = readNumericValue(); assertNextToken(BasicType::Comma); - auto w = readNumericValue(); assertNextToken(BasicType::Comma); - auto h = readNumericValue(); - if (x.type().tag != structure::TypeTag::Pixels || - y.type().tag != structure::TypeTag::Pixels || - w.type().tag != structure::TypeTag::Pixels || - h.type().tag != structure::TypeTag::Pixels) { - logErrorTypeMismatch() << "expected four px values for the sprite"; - } - - assertNextToken(BasicType::RightParenthesis); - - return { structure::data::sprite { x.Int(), y.Int(), w.Int(), h.Int() } }; - } - file_.putBack(); - } - return {}; -} - structure::Value ParsedFile::readSizeValue() { if (auto font = file_.getToken(BasicType::Name)) { if (tokenValue(font) == "size") { diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.h b/Telegram/SourceFiles/codegen/style/parsed_file.h index 8eea16483..520e0aad2 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.h +++ b/Telegram/SourceFiles/codegen/style/parsed_file.h @@ -88,7 +88,6 @@ private: structure::Value readStringValue(); structure::Value readColorValue(); structure::Value readPointValue(); - structure::Value readSpriteValue(); structure::Value readSizeValue(); structure::Value readTransitionValue(); structure::Value readCursorValue(); @@ -127,7 +126,6 @@ private: { "string" , { structure::TypeTag::String } }, { "color" , { structure::TypeTag::Color } }, { "point" , { structure::TypeTag::Point } }, - { "sprite" , { structure::TypeTag::Sprite } }, { "size" , { structure::TypeTag::Size } }, { "transition", { structure::TypeTag::Transition } }, { "cursor" , { structure::TypeTag::Cursor } }, diff --git a/Telegram/SourceFiles/codegen/style/processor.cpp b/Telegram/SourceFiles/codegen/style/processor.cpp index 4b534e7c2..b73bc503f 100644 --- a/Telegram/SourceFiles/codegen/style/processor.cpp +++ b/Telegram/SourceFiles/codegen/style/processor.cpp @@ -25,7 +25,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "codegen/common/cpp_file.h" #include "codegen/style/parsed_file.h" #include "codegen/style/generator.h" -#include "codegen/style/sprite_generator.h" namespace codegen { namespace style { @@ -66,38 +65,28 @@ int Processor::launch() { bool Processor::write(const structure::Module &module) const { bool forceReGenerate = false; - bool onlyStyles = options_.skipSprites; - bool onlySprites = options_.skipStyles; - if (!onlyStyles) { - SpriteGenerator spriteGenerator(module, forceReGenerate); - if (!spriteGenerator.writeSprites()) { - return false; - } + QDir dir(options_.outputPath); + if (!dir.mkpath(".")) { + common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); + return false; } - if (!onlySprites) { - QDir dir(options_.outputPath); - if (!dir.mkpath(".")) { - common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); - return false; - } - QFileInfo srcFile(module.filepath()); - QString dstFilePath = dir.absolutePath() + '/' + destFileBaseName(module); + QFileInfo srcFile(module.filepath()); + QString dstFilePath = dir.absolutePath() + '/' + destFileBaseName(module); - common::ProjectInfo project = { - "codegen_style", - srcFile.fileName(), - "stdafx.h", - forceReGenerate - }; + common::ProjectInfo project = { + "codegen_style", + srcFile.fileName(), + "stdafx.h", + forceReGenerate + }; - Generator generator(module, dstFilePath, project); - if (!generator.writeHeader()) { - return false; - } - if (!generator.writeSource()) { - return false; - } + Generator generator(module, dstFilePath, project); + if (!generator.writeHeader()) { + return false; + } + if (!generator.writeSource()) { + return false; } return true; } diff --git a/Telegram/SourceFiles/codegen/style/sprite_generator.cpp b/Telegram/SourceFiles/codegen/style/sprite_generator.cpp deleted file mode 100644 index c7d07be97..000000000 --- a/Telegram/SourceFiles/codegen/style/sprite_generator.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* -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 "codegen/style/sprite_generator.h" - -#include -#include -#include -#include -#include -#include -#include "codegen/style/parsed_file.h" - -using Module = codegen::style::structure::Module; -using Struct = codegen::style::structure::Struct; -using Variable = codegen::style::structure::Variable; -using Tag = codegen::style::structure::TypeTag; - -namespace codegen { -namespace style { - -using structure::logFullName; - -namespace { - -constexpr int kErrorSpritesIntersect = 841; -constexpr int kErrorCouldNotGenerate = 842; -constexpr int kErrorCouldNotSerialize = 843; -constexpr int kErrorCouldNotOpen = 844; -constexpr int kErrorCouldNotWrite = 845; - -} // namespace - -SpriteGenerator::SpriteGenerator(const structure::Module &module, bool forceReGenerate) -: module_(module) -, forceReGenerate_(forceReGenerate) -, basePath_(QFileInfo(module.filepath()).dir().absolutePath()) { -} - -bool SpriteGenerator::writeSprites() { - if (!collectSprites()) { - return false; - } - if (sprites_.isEmpty()) { - return true; - } - - sprite2x_ = QImage(basePath_ + "/art/sprite_200x.png"); - if (sprite2x_.isNull()) { - common::logError(common::kErrorFileNotFound, "/art/sprite_200x.png") << "sprite file was not found"; - return false; - } - std::vector sizes = { 5, 6 }; - std::vector postfixes = { "125", "150" }; - for (int i = 0, l = sizes.size(); i < l; ++i) { - auto sprite = generateSprite(sizes[i]); - QString filepath = basePath_ + "/art/sprite_" + postfixes[i] + "x.png"; - if (sprite.isNull()) { - common::logError(kErrorCouldNotGenerate, filepath) << "could not generate sprite file"; - return false; - } - QByteArray spriteData; - { - QBuffer spriteBuffer(&spriteData); - if (!sprite.save(&spriteBuffer, "PNG")) { - common::logError(kErrorCouldNotSerialize, filepath) << "could not serialize sprite file"; - return false; - } - } - QFile file(filepath); - if (!forceReGenerate_ && file.open(QIODevice::ReadOnly)) { - if (file.readAll() == spriteData) { - continue; - } - file.close(); - } - if (!file.open(QIODevice::WriteOnly)) { - common::logError(kErrorCouldNotOpen, filepath) << "could not open sprite file for write"; - return false; - } - if (file.write(spriteData) != spriteData.size()) { - common::logError(kErrorCouldNotWrite, filepath) << "could not write sprite file"; - return false; - } - - // Touch resource file. - filepath = basePath_ + "/telegram.qrc"; - QFile qrc(filepath); - if (qrc.open(QIODevice::ReadOnly)) { - auto qrcContent = qrc.readAll(); - qrc.close(); - if (!qrc.open(QIODevice::WriteOnly)) { - common::logError(kErrorCouldNotOpen, filepath) << "could not open .qrc file for write"; - return false; - } - if (qrc.write(qrcContent) != qrcContent.size()) { - common::logError(kErrorCouldNotWrite, filepath) << "could not write .qrc file"; - return false; - } - } - } - - return true; -} - -bool SpriteGenerator::collectSprites() { - std::function collector = [this, &collector](const Variable &variable) { - auto value = variable.value; - if (value.type().tag == Tag::Sprite) { - auto v(value.Sprite()); - if (!v.width || !v.height) return true; - - QRect vRect(v.left, v.top, v.width, v.height); - bool found = false; - for (auto var : sprites_) { - auto sprite = var.value.Sprite(); - QRect spriteRect(sprite.left, sprite.top, sprite.width, sprite.height); - if (spriteRect == vRect) { - found = true; - } else if (spriteRect.intersects(vRect)) { - common::logError(kErrorSpritesIntersect, module_.filepath()) << "sprite '" << logFullName(variable.name) << "' intersects with '" << logFullName(var.name) << "'"; - return false; - } - } - if (!found) { - sprites_.push_back(variable); - } - } else if (value.type().tag == Tag::Struct) { - auto fields = variable.value.Fields(); - if (!fields) { - return false; - } - - for (auto field : *fields) { - if (!collector(field.variable)) { - return false; - } - } - } - return true; - }; - return module_.enumVariables(collector); -} - -QImage SpriteGenerator::generateSprite(int scale) { - auto convert = [scale](int value) -> int { return structure::data::pxAdjust(value, scale); }; - QImage result(convert(sprite2x_.width() / 2), convert(sprite2x_.height() / 2), sprite2x_.format()); - { - QPainter p(&result); - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(0, 0, result.width(), result.height(), QColor(0, 0, 0, 0)); - for (auto variable : sprites_) { - auto sprite = variable.value.Sprite(); - auto copy = sprite2x_.copy(sprite.left * 2, sprite.top * 2, sprite.width * 2, sprite.height * 2); - copy = copy.scaled(convert(sprite.width), convert(sprite.height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - p.drawImage(convert(sprite.left), convert(sprite.top), copy); - } - } - return result; -} - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/sprite_generator.h b/Telegram/SourceFiles/codegen/style/sprite_generator.h deleted file mode 100644 index fc1458db3..000000000 --- a/Telegram/SourceFiles/codegen/style/sprite_generator.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -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 -#include -#include -#include -#include "codegen/style/structure_types.h" - -namespace codegen { -namespace style { -namespace structure { -class Module; -} // namespace structure - -class SpriteGenerator { -public: - SpriteGenerator(const structure::Module &module, bool forceReGenerate); - SpriteGenerator(const SpriteGenerator &other) = delete; - SpriteGenerator &operator=(const SpriteGenerator &other) = delete; - - bool writeSprites(); - -private: - - bool collectSprites(); - QImage generateSprite(int scale); // scale = 5 for 125% and 6 for 150%. - - const structure::Module &module_; - bool forceReGenerate_; - QString basePath_; - QImage sprite2x_; - QList sprites_; - -}; - -} // namespace style -} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/structure_types.cpp b/Telegram/SourceFiles/codegen/style/structure_types.cpp index cec0f0c48..e11042a34 100644 --- a/Telegram/SourceFiles/codegen/style/structure_types.cpp +++ b/Telegram/SourceFiles/codegen/style/structure_types.cpp @@ -69,17 +69,6 @@ struct Value::DataTypes { }; - class TSprite : public DataBase { - public: - TSprite(data::sprite value) : value_(value) { - } - data::sprite Sprite() const override { return value_; } - - private: - data::sprite value_; - - }; - class TSize : public DataBase { public: TSize(data::size value) : value_(value) { @@ -154,9 +143,6 @@ Value::Value() : Value(TypeTag::Invalid, std::make_shared()) { Value::Value(data::point value) : Value(TypeTag::Point, std::make_shared(value)) { } -Value::Value(data::sprite value) : Value(TypeTag::Sprite, std::make_shared(value)) { -} - Value::Value(data::size value) : Value(TypeTag::Size, std::make_shared(value)) { } @@ -210,7 +196,6 @@ Value::Value(Type type, Qt::Initialization) : type_(type) { case TypeTag::String: data_ = std::make_shared(""); break; case TypeTag::Color: data_ = std::make_shared(data::color { 0, 0, 0, 255 }); break; case TypeTag::Point: data_ = std::make_shared(data::point { 0, 0 }); break; - case TypeTag::Sprite: data_ = std::make_shared(data::sprite { 0, 0, 0, 0 }); break; case TypeTag::Size: data_ = std::make_shared(data::size { 0, 0 }); break; case TypeTag::Transition: data_ = std::make_shared("linear"); break; case TypeTag::Cursor: data_ = std::make_shared("default"); break; diff --git a/Telegram/SourceFiles/codegen/style/structure_types.h b/Telegram/SourceFiles/codegen/style/structure_types.h index 848acb32a..b2c785809 100644 --- a/Telegram/SourceFiles/codegen/style/structure_types.h +++ b/Telegram/SourceFiles/codegen/style/structure_types.h @@ -47,7 +47,6 @@ enum class TypeTag { String, Color, Point, - Sprite, Size, Transition, Cursor, @@ -86,10 +85,6 @@ struct point { int x, y; }; -struct sprite { - int left, top, width, height; -}; - struct size { int width, height; }; @@ -127,7 +122,6 @@ class Value { public: Value(); Value(data::point value); - Value(data::sprite value); Value(data::size value); Value(data::color value); Value(data::margins value); @@ -152,7 +146,6 @@ public: double Double() const { return data_->Double(); } std::string String() const { return data_->String(); } data::point Point() const { return data_->Point(); } - data::sprite Sprite() const { return data_->Sprite(); }; data::size Size() const { return data_->Size(); }; data::color Color() const { return data_->Color(); }; data::margins Margins() const { return data_->Margins(); }; @@ -182,7 +175,6 @@ private: virtual double Double() const { return 0.; } virtual std::string String() const { return std::string(); } virtual data::point Point() const { return {}; }; - virtual data::sprite Sprite() const { return {}; }; virtual data::size Size() const { return {}; }; virtual data::color Color() const { return {}; }; virtual data::margins Margins() const { return {}; }; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index ec82cc590..ee44cb5a7 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -44,7 +44,7 @@ dialogsDateFont: font(13px); dialogsDateFgActive: #ffffff; dialogsDateFg: #a8a8a8; dialogsDateSkip: 5px; -dialogsNameFg: #000; +dialogsNameFg: #000000; dialogsNameTop: 2px; dialogsRowHeight: 62px; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index eb07e0fe1..1e6904521 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -26,7 +26,7 @@ historyPaddingBottom: 10px; historyToDown: icon { { "history_down_shadow", #00000040 }, - { "history_down_circle", #fff, point(2px, 1px) }, + { "history_down_circle", #ffffff, point(2px, 1px) }, }; historyToDownPosition: point(12px, 10px); historyToDownArrow: icon { @@ -189,7 +189,7 @@ historyRecordVoiceActive: icon {{ "send_control_record", #58b2ed }}; historyRecordSignalColor: #f17077; historyRecordSignalMin: 5px; historyRecordSignalMax: 12px; -historyRecordCancel: #aaa; +historyRecordCancel: #aaaaaa; historyRecordCancelActive: #ec6466; historyRecordFont: font(13px); historyRecordTextTop: 14px; diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 7171d2b12..d25f147d2 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -116,10 +116,10 @@ mediaviewMenu: Menu(defaultMenu) { itemBgOver: #505050; itemFg: white; itemFgOver: white; - itemFgDisabled: #999; - itemFgShortcut: #eee; - itemFgShortcutOver: #fff; - itemFgShortcutDisabled: #999; + itemFgDisabled: #999999; + itemFgShortcut: #eeeeee; + itemFgShortcutOver: #ffffff; + itemFgShortcutDisabled: #999999; separatorFg: #484848; } diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index 7250e6d15..bbf292fc6 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -42,7 +42,7 @@ OverviewFileLayout { fileDateTop: pixels; } -overviewCheckBg: #0006; +overviewCheckBg: #00000066; overviewCheckedBg: #2fa9e2; overviewPhotoSkip: 10px; @@ -50,11 +50,11 @@ overviewPhotoBg: #F1F1F1; overviewPhotoMinSize: minPhotoSize; overviewPhotoCheck: icon { { size(32px, 32px), overviewCheckBg }, - { "overview_photo_check", #fff, point(8px, 10px) }, + { "overview_photo_check", #ffffff, point(8px, 10px) }, }; overviewPhotoChecked: icon { { size(32px, 32px), overviewCheckedBg }, - { "overview_photo_check", #fff, point(8px, 10px) }, + { "overview_photo_check", #ffffff, point(8px, 10px) }, }; overviewPhotoSelectOverlay: #0a7bb03f; @@ -104,9 +104,9 @@ linksPhotoSize: 46px; linksPhotoPadding: 12px; overviewLinksCheck: icon { { "overview_links_check_bg", overviewCheckBg }, - { "overview_links_check", #fff, point(4px, 5px) }, + { "overview_links_check", #ffffff, point(4px, 5px) }, }; overviewLinksChecked: icon { { "overview_links_check_bg", overviewCheckedBg }, - { "overview_links_check", #fff, point(4px, 5px) }, + { "overview_links_check", #ffffff, point(4px, 5px) }, }; diff --git a/Telegram/SourceFiles/profile/profile_settings_widget.cpp b/Telegram/SourceFiles/profile/profile_settings_widget.cpp index 4c6d254c8..c93ee264c 100644 --- a/Telegram/SourceFiles/profile/profile_settings_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_settings_widget.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_profile.h" #include "ui/buttons/left_outline_button.h" -#include "ui/flatcheckbox.h" +#include "ui/buttons/checkbox.h" #include "boxes/confirmbox.h" #include "boxes/contactsbox.h" #include "observer_peer.h" @@ -114,7 +114,7 @@ void SettingsWidget::refreshEnableNotifications() { } else { auto ¬ifySettings = peer()->notify; bool enabled = (notifySettings == EmptyNotifySettings || notifySettings->mute < unixtime()); - _enableNotifications->setChecked(enabled, Checkbox::NotifyAboutChange::DontNotify); + _enableNotifications->setChecked(enabled, Ui::Checkbox::NotifyAboutChange::DontNotify); } } diff --git a/Telegram/SourceFiles/profile/profile_settings_widget.h b/Telegram/SourceFiles/profile/profile_settings_widget.h index 4b02ee17c..ba727cb57 100644 --- a/Telegram/SourceFiles/profile/profile_settings_widget.h +++ b/Telegram/SourceFiles/profile/profile_settings_widget.h @@ -22,9 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_block_widget.h" -class Checkbox; - namespace Ui { +class Checkbox; class LeftOutlineButton; } // namespace Ui @@ -59,7 +58,7 @@ private: void refreshManageAdminsButton(); void refreshInviteLinkButton(); - ChildWidget _enableNotifications; + ChildWidget _enableNotifications; // In groups: creator of non-deactivated groups can see this link. // In channels: creator of supergroup can see this link. diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index 27a53b28b..35e910c24 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "boxes/backgroundbox.h" #include "ui/effects/widget_slide_wrap.h" +#include "ui/buttons/checkbox.h" #include "localstorage.h" #include "mainwindow.h" #include "window/chat_background.h" diff --git a/Telegram/SourceFiles/settings/settings_background_widget.h b/Telegram/SourceFiles/settings/settings_background_widget.h index 4a61b69b1..6b7d458f2 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.h +++ b/Telegram/SourceFiles/settings/settings_background_widget.h @@ -25,8 +25,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" class LinkButton; -class Checkbox; + namespace Ui { +class Checkbox; template class WidgetSlideWrap; } // namespace Ui; @@ -84,8 +85,8 @@ private: void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update); ChildWidget _background = { nullptr }; - ChildWidget _tile = { nullptr }; - ChildWidget> _adaptive = { nullptr }; + ChildWidget _tile = { nullptr }; + ChildWidget> _adaptive = { nullptr }; FileDialog::QueryId _chooseFromFileQueryId = 0; diff --git a/Telegram/SourceFiles/settings/settings_block_widget.cpp b/Telegram/SourceFiles/settings/settings_block_widget.cpp index 8a188de02..edfe760a4 100644 --- a/Telegram/SourceFiles/settings/settings_block_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_block_widget.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" #include "styles/style_settings.h" -#include "ui/flatcheckbox.h" +#include "ui/buttons/checkbox.h" namespace Settings { @@ -84,13 +84,13 @@ void BlockWidget::rowHeightUpdated() { } } -void BlockWidget::createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, bool checked) { - child = new Checkbox(this, text, checked, st::defaultBoxCheckbox); +void BlockWidget::createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, bool checked) { + child = new Ui::Checkbox(this, text, checked, st::defaultBoxCheckbox); connect(child, SIGNAL(changed()), this, slot); } -void BlockWidget::createChildRow(ChildWidget &child, style::margins &margin, const QString &group, int value, const QString &text, const char *slot, bool checked) { - child = new Radiobutton(this, group, value, text, checked, st::defaultRadiobutton); +void BlockWidget::createChildRow(ChildWidget &child, style::margins &margin, const QString &group, int value, const QString &text, const char *slot, bool checked) { + child = new Ui::Radiobutton(this, group, value, text, checked, st::defaultRadiobutton); connect(child, SIGNAL(changed()), this, slot); } diff --git a/Telegram/SourceFiles/settings/settings_block_widget.h b/Telegram/SourceFiles/settings/settings_block_widget.h index 059973d04..92e28a5ab 100644 --- a/Telegram/SourceFiles/settings/settings_block_widget.h +++ b/Telegram/SourceFiles/settings/settings_block_widget.h @@ -22,10 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/observer.h" +namespace Ui { class Checkbox; class Radiobutton; - -namespace Ui { template class WidgetSlideWrap; } // namespace Ui @@ -87,8 +86,8 @@ private: margin.setRight(margin.right() - padding.right()); margin.setBottom(margin.bottom() - padding.bottom()); } - void createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, bool checked); - void createChildRow(ChildWidget &child, style::margins &margin, const QString &group, int value, const QString &text, const char *slot, bool checked); + void createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, bool checked); + void createChildRow(ChildWidget &child, style::margins &margin, const QString &group, int value, const QString &text, const char *slot, bool checked); void createChildRow(ChildWidget &child, style::margins &margin, const QString &text, const char *slot, const style::linkButton &st = st::defaultBoxLinkButton); void addCreatedRow(TWidget *child, const style::margins &margin); @@ -106,8 +105,8 @@ private: template using NotImplementedYet = std_::enable_if_t< !IsWidgetSlideWrap::value && - !std_::is_same::value && - !std_::is_same::value && + !std_::is_same::value && + !std_::is_same::value && !std_::is_same::value>; template > diff --git a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp index 00167763e..c9527e6c8 100644 --- a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_settings.h" #include "lang.h" #include "ui/effects/widget_slide_wrap.h" +#include "ui/buttons/checkbox.h" #include "ui/flatlabel.h" #include "localstorage.h" #include "mainwidget.h" diff --git a/Telegram/SourceFiles/settings/settings_chat_settings_widget.h b/Telegram/SourceFiles/settings/settings_chat_settings_widget.h index f04ac7f53..2c1da6c74 100644 --- a/Telegram/SourceFiles/settings/settings_chat_settings_widget.h +++ b/Telegram/SourceFiles/settings/settings_chat_settings_widget.h @@ -105,12 +105,12 @@ private slots: private: void createControls(); - ChildWidget _replaceEmoji = { nullptr }; + ChildWidget _replaceEmoji = { nullptr }; ChildWidget> _viewList = { nullptr }; - ChildWidget _dontAskDownloadPath = { nullptr }; + ChildWidget _dontAskDownloadPath = { nullptr }; ChildWidget> _downloadPath = { nullptr }; - ChildWidget _sendByEnter = { nullptr }; - ChildWidget _sendByCtrlEnter = { nullptr }; + ChildWidget _sendByEnter = { nullptr }; + ChildWidget _sendByCtrlEnter = { nullptr }; ChildWidget _automaticMediaDownloadSettings = { nullptr }; ChildWidget _manageStickerSets = { nullptr }; diff --git a/Telegram/SourceFiles/settings/settings_general_widget.cpp b/Telegram/SourceFiles/settings/settings_general_widget.cpp index 06790fe07..454a25232 100644 --- a/Telegram/SourceFiles/settings/settings_general_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_general_widget.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "ui/effects/widget_slide_wrap.h" #include "ui/flatbutton.h" -#include "ui/flatcheckbox.h" +#include "ui/buttons/checkbox.h" #include "localstorage.h" #include "pspecific.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/settings/settings_general_widget.h b/Telegram/SourceFiles/settings/settings_general_widget.h index 018dba991..228cf182a 100644 --- a/Telegram/SourceFiles/settings/settings_general_widget.h +++ b/Telegram/SourceFiles/settings/settings_general_widget.h @@ -23,10 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" #include "ui/filedialog.h" -class Checkbox; class LinkButton; namespace Ui { +class Checkbox; template class WidgetSlideWrap; } // namespace Ui @@ -116,14 +116,14 @@ private: ChildWidget _changeLanguage; #ifndef TDESKTOP_DISABLE_AUTOUPDATE - ChildWidget _updateAutomatically = { nullptr }; + ChildWidget _updateAutomatically = { nullptr }; ChildWidget> _updateRow = { nullptr }; #endif // !TDESKTOP_DISABLE_AUTOUPDATE - ChildWidget _enableTrayIcon = { nullptr }; - ChildWidget _enableTaskbarIcon = { nullptr }; - ChildWidget _autoStart = { nullptr }; - ChildWidget> _startMinimized = { nullptr }; - ChildWidget _addInSendTo = { nullptr }; + ChildWidget _enableTrayIcon = { nullptr }; + ChildWidget _enableTaskbarIcon = { nullptr }; + ChildWidget _autoStart = { nullptr }; + ChildWidget> _startMinimized = { nullptr }; + ChildWidget _addInSendTo = { nullptr }; FileDialog::QueryId _chooseLangFileQueryId = 0; QString _testLanguage; diff --git a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp index 6068f7787..2bd3b7ce8 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "localstorage.h" #include "ui/effects/widget_slide_wrap.h" -#include "ui/flatcheckbox.h" +#include "ui/buttons/checkbox.h" #include "mainwindow.h" #include "window/notifications_manager.h" #include "boxes/notifications_box.h" diff --git a/Telegram/SourceFiles/settings/settings_notifications_widget.h b/Telegram/SourceFiles/settings/settings_notifications_widget.h index 5bc3c28d9..404457c5c 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_widget.h +++ b/Telegram/SourceFiles/settings/settings_notifications_widget.h @@ -45,12 +45,12 @@ private: void desktopEnabledUpdated(); void viewParamUpdated(); - ChildWidget _desktopNotifications = { nullptr }; - ChildWidget> _showSenderName = { nullptr }; - ChildWidget> _showMessagePreview = { nullptr }; - ChildWidget _nativeNotifications = { nullptr }; - ChildWidget _playSound = { nullptr }; - ChildWidget _includeMuted = { nullptr }; + ChildWidget _desktopNotifications = { nullptr }; + ChildWidget> _showSenderName = { nullptr }; + ChildWidget> _showMessagePreview = { nullptr }; + ChildWidget _nativeNotifications = { nullptr }; + ChildWidget _playSound = { nullptr }; + ChildWidget _includeMuted = { nullptr }; ChildWidget> _advanced = { nullptr }; }; diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp index a621e832d..c4ab37279 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_scale_widget.h" #include "styles/style_settings.h" +#include "ui/buttons/checkbox.h" #include "lang.h" #include "localstorage.h" #include "mainwindow.h" diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.h b/Telegram/SourceFiles/settings/settings_scale_widget.h index 39684d754..1f975a726 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.h +++ b/Telegram/SourceFiles/settings/settings_scale_widget.h @@ -22,9 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_block_widget.h" -class Checkbox; - namespace Ui { +class Checkbox; class DiscreteSlider; } // namespace Ui @@ -45,7 +44,7 @@ private: void createControls(); void setScale(DBIScale newScale); - ChildWidget _auto = { nullptr }; + ChildWidget _auto = { nullptr }; ChildWidget _scale = { nullptr }; }; diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index e1252378c..fb97c37c9 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -2784,14 +2784,7 @@ void EmojiPan::paintEvent(QPaintEvent *e) { p.drawPixmapLeft(x + (st::emojiCategory.width - s.pixw) / 2, _iconsTop + (st::emojiCategory.height - s.pixh) / 2, width(), pix); x += st::emojiCategory.width; } else { - if (true || selxrel != x) { - getSpecialSetIcon(s.setId, false)->paint(p, x + st::emojiCategory.iconPosition.x(), _iconsTop + st::emojiCategory.iconPosition.y(), width()); - } - //if (selxrel < x + st::emojiCategory.width && selxrel > x - st::emojiCategory.width) { - // p.setOpacity(1 - (qAbs(selxrel - x) / float64(st::emojiCategory.width))); - // p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), getSpecialSetIcon(s.setId, true)); - // p.setOpacity(1); - //} + getSpecialSetIcon(s.setId, false)->paint(p, x + st::emojiCategory.iconPosition.x(), _iconsTop + st::emojiCategory.iconPosition.y(), width()); if (s.setId == Stickers::FeaturedSetId) { paintFeaturedStickerSetsBadge(p, x); } diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 2d2c67324..4e28e6f4d 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -30,7 +30,7 @@ stickersTrendingHeaderFont: semiboldFont; stickersTrendingHeaderFg: windowTextFg; stickersTrendingHeaderTop: 0px; stickersTrendingSubheaderFont: normalFont; -stickersTrendingSubheaderFg: #777; +stickersTrendingSubheaderFg: #777777; stickersTrendingSubheaderTop: 20px; stickersTrendingAddTop: 3px; @@ -68,7 +68,7 @@ stickersScroll: flatScroll(boxScroll) { deltab: 9px; } stickersReorderPadding: margins(0px, 12px, 0px, 12px); -stickersReorderFg: #777; +stickersReorderFg: #777777; stickersRowDisabledOpacity: 0.4; stickersRowDuration: 200; @@ -131,7 +131,7 @@ emojiPanHover: #f0f4f7; emojiPanHeader: 42px; emojiPanHeaderFont: semiboldFont; -emojiPanHeaderColor: #999; +emojiPanHeaderColor: #999999; emojiPanHeaderLeft: 22px; emojiPanHeaderTop: 12px; emojiPanHeaderBg: #fffffff2; diff --git a/Telegram/SourceFiles/ui/flatcheckbox.cpp b/Telegram/SourceFiles/ui/buttons/checkbox.cpp similarity index 69% rename from Telegram/SourceFiles/ui/flatcheckbox.cpp rename to Telegram/SourceFiles/ui/buttons/checkbox.cpp index ba03450a7..36aeedbb4 100644 --- a/Telegram/SourceFiles/ui/flatcheckbox.cpp +++ b/Telegram/SourceFiles/ui/buttons/checkbox.cpp @@ -19,126 +19,21 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "ui/flatcheckbox.h" +#include "ui/buttons/checkbox.h" #include "lang.h" -FlatCheckbox::FlatCheckbox(QWidget *parent, const QString &text, bool checked, const style::flatCheckbox &st) : Button(parent) -, _st(st) -, a_over(0, 0) -, _a_appearance(animation(this, &FlatCheckbox::step_appearance)) -, _text(text) -, _opacity(1) -, _checked(checked) { - connect(this, SIGNAL(clicked()), this, SLOT(onClicked())); - connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); - setCursor(_st.cursor); - int32 w = _st.width, h = _st.height; - if (w <= 0) w = _st.textLeft + _st.font->width(_text) + 2; - if (h <= 0) h = qMax(_st.font->height, _st.imageRect.pxHeight()); - resize(QSize(w, h)); -} +namespace Ui { +namespace { -bool FlatCheckbox::checked() const { - return _checked; -} - -void FlatCheckbox::setChecked(bool checked) { - if (_checked != checked) { - _checked = checked; - emit changed(); - update(); - } -} - -void FlatCheckbox::setOpacity(float64 o) { - _opacity = o; - update(); -} - -void FlatCheckbox::onClicked() { - if (_state & StateDisabled) return; - setChecked(!checked()); -} - -void FlatCheckbox::onStateChange(int oldState, ButtonStateChangeSource source) { - if ((_state & StateOver) && !(oldState & StateOver)) { - a_over.start(1); - _a_appearance.start(); - } else if (!(_state & StateOver) && (oldState & StateOver)) { - a_over.start(0); - _a_appearance.start(); - } - if ((_state & StateDisabled) && !(oldState & StateDisabled)) { - setCursor(_st.disabledCursor); - _a_appearance.start(); - } else if (!(_state & StateDisabled) && (oldState & StateDisabled)) { - setCursor(_st.cursor); - _a_appearance.start(); - } -} - -void FlatCheckbox::paintEvent(QPaintEvent *e) { - Painter p(this); - - p.setOpacity(_opacity); - if (_st.bgColor != st::transparent) { - p.fillRect(rect(), _st.bgColor->b); - } - - if (!_text.isEmpty()) { - p.setFont(_st.font->f); - p.setRenderHint(QPainter::TextAntialiasing); - p.setPen((_state & StateDisabled ? _st.disColor : _st.textColor)->p); - - QRect tRect(rect()); - tRect.setTop(_st.textTop); - tRect.setLeft(_st.textLeft); -// p.drawText(_st.textLeft, _st.textTop + _st.font->ascent, _text); - p.drawText(tRect, _text, QTextOption(style::al_topleft)); - } - - if (_state & StateDisabled) { - const style::sprite &sRect(_checked ? _st.chkDisImageRect : _st.disImageRect); - p.drawSprite(_st.imagePos, sRect); - } else if ((_checked && _st.chkImageRect == _st.chkOverImageRect) || (!_checked && _st.imageRect == _st.overImageRect)) { - p.setOpacity(_opacity); - const style::sprite &sRect(_checked ? _st.chkImageRect : _st.imageRect); - p.drawSprite(_st.imagePos, sRect); - } else { - if (a_over.current() < 1) { - const style::sprite &sRect(_checked ? _st.chkImageRect : _st.imageRect); - p.drawSprite(_st.imagePos, sRect); - } - if (a_over.current() > 0) { - p.setOpacity(_opacity * a_over.current()); - const style::sprite &sRect(_checked ? _st.chkOverImageRect : _st.overImageRect); - p.drawSprite(_st.imagePos, sRect); - } - } -} - -void FlatCheckbox::step_appearance(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_appearance.stop(); - a_over.finish(); - } else { - a_over.update(dt, _st.bgFunc); - } - if (timer) update(); -} - -template -class TemplateRadiobuttonsGroup : public QMap { - typedef QMap Parent; +class RadiobuttonGroup : public QMap { + using Parent = QMap; public: - TemplateRadiobuttonsGroup(const QString &name) : _name(name), _val(0) { + RadiobuttonGroup(const QString &name) : _name(name) { } - void remove(Type * const &radio) { - } + void remove(Radiobutton * const &radio); int32 val() const { return _val; } @@ -148,23 +43,18 @@ public: private: QString _name; - int32 _val; + int _val = 0; }; -typedef TemplateRadiobuttonsGroup FlatRadiobuttonGroup; -typedef TemplateRadiobuttonsGroup RadiobuttonGroup; - -template -class Radiobuttons : public QMap *> { - typedef QMap *> Parent; +class Radiobuttons : public QMap { + using Parent = QMap; public: - - TemplateRadiobuttonsGroup *reg(const QString &group) { + RadiobuttonGroup *reg(const QString &group) { typename Parent::const_iterator i = Parent::constFind(group); if (i == Parent::cend()) { - i = Parent::insert(group, new TemplateRadiobuttonsGroup(group)); + i = Parent::insert(group, new RadiobuttonGroup(group)); } return i.value(); } @@ -186,55 +76,17 @@ public: } }; -namespace { - Radiobuttons flatRadiobuttons; - Radiobuttons radiobuttons; -} +Radiobuttons radiobuttons; -template <> -void TemplateRadiobuttonsGroup::remove(FlatRadiobutton * const &radio) { - Parent::remove(radio); - if (isEmpty()) { - flatRadiobuttons.remove(_name); - } -} +} // namespace -template <> -void TemplateRadiobuttonsGroup::remove(Radiobutton * const &radio) { +void RadiobuttonGroup::remove(Radiobutton * const &radio) { Parent::remove(radio); if (isEmpty()) { radiobuttons.remove(_name); } } -FlatRadiobutton::FlatRadiobutton(QWidget *parent, const QString &group, int32 value, const QString &text, bool checked, const style::flatCheckbox &st) : - FlatCheckbox(parent, text, checked, st), _group(flatRadiobuttons.reg(group)), _value(value) { - reinterpret_cast(_group)->insert(this, true); - connect(this, SIGNAL(changed()), this, SLOT(onChanged())); - if (this->checked()) onChanged(); -} - -void FlatRadiobutton::onChanged() { - FlatRadiobuttonGroup *group = reinterpret_cast(_group); - if (checked()) { - int32 uncheck = group->val(); - if (uncheck != _value) { - group->setVal(_value); - for (FlatRadiobuttonGroup::const_iterator i = group->cbegin(), e = group->cend(); i != e; ++i) { - if (i.key()->val() == uncheck) { - i.key()->setChecked(false); - } - } - } - } else if (group->val() == _value) { - setChecked(true); - } -} - -FlatRadiobutton::~FlatRadiobutton() { - reinterpret_cast(_group)->remove(this); -} - Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st) : Button(parent) , _st(st) , a_over(0) @@ -564,3 +416,5 @@ void Radiobutton::onChanged() { Radiobutton::~Radiobutton() { reinterpret_cast(_group)->remove(this); } + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/flatcheckbox.h b/Telegram/SourceFiles/ui/buttons/checkbox.h similarity index 72% rename from Telegram/SourceFiles/ui/flatcheckbox.h rename to Telegram/SourceFiles/ui/buttons/checkbox.h index ea43ce09d..8b7ccf367 100644 --- a/Telegram/SourceFiles/ui/flatcheckbox.h +++ b/Telegram/SourceFiles/ui/buttons/checkbox.h @@ -22,62 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/button.h" -class FlatCheckbox : public Button { - Q_OBJECT - -public: - FlatCheckbox(QWidget *parent, const QString &text, bool checked, const style::flatCheckbox &st); - - bool checked() const; - void setChecked(bool checked); - - void step_appearance(float64 ms, bool timer); - void paintEvent(QPaintEvent *e); - - void setOpacity(float64 o); - -public slots: - void onClicked(); - void onStateChange(int oldState, ButtonStateChangeSource source); - -signals: - void changed(); - -private: - style::flatCheckbox _st; - anim::fvalue a_over; - Animation _a_appearance; - - QString _text; - style::font _font; - - float64 _opacity; - - bool _checked; - -}; - -class FlatRadiobutton : public FlatCheckbox { - Q_OBJECT - -public: - - FlatRadiobutton(QWidget *parent, const QString &group, int32 value, const QString &text, bool checked, const style::flatCheckbox &st); - int32 val() const { - return _value; - } - ~FlatRadiobutton(); - -public slots: - - void onChanged(); - -private: - - void *_group; - int32 _value; - -}; +namespace Ui { class Checkbox : public Button { Q_OBJECT @@ -99,7 +44,7 @@ public: protected: void paintEvent(QPaintEvent *e) override; -public slots: + public slots: void onClicked(); void onStateChange(int oldState, ButtonStateChangeSource source); @@ -142,7 +87,7 @@ public: ~Radiobutton(); -public slots: + public slots: void onClicked(); void onStateChange(int oldState, ButtonStateChangeSource source); @@ -166,3 +111,5 @@ private: int32 _value; }; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 7fcc8d8b3..15573f26a 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -163,109 +163,6 @@ void LinkButton::onStateChange(int oldState, ButtonStateChangeSource source) { LinkButton::~LinkButton() { } -IconedButton::IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text) : Button(parent) -, _text(text) -, _st(st) -, _width(_st.width) -, a_opacity(_st.opacity) -, a_bg(_st.bgColor->c) -, _a_appearance(animation(this, &IconedButton::step_appearance)) -, _opacity(1) { - - if (_width < 0) { - _width = _st.font->width(text) - _width; - } else if (!_width) { - _width = _st.font->width(text) + _st.height - _st.font->height; - } - connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); - resize(_width, _st.height); - setCursor(_st.cursor); -} - -void IconedButton::setOpacity(float64 opacity) { - _opacity = opacity; - update(); -} - -void IconedButton::setText(const QString &text) { - if (_text != text) { - _text = text; - if (_st.width < 0) { - _width = _st.font->width(text) - _st.width; - } else if (!_st.width) { - _width = _st.font->width(text) + _st.height - _st.font->height; - } - resize(_width, _st.height); - update(); - } -} - -QString IconedButton::getText() const { - return _text; -} - -void IconedButton::step_appearance(float64 ms, bool timer) { - if (_st.duration <= 1) { - _a_appearance.stop(); - a_opacity.finish(); - a_bg.finish(); - } else { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_appearance.stop(); - a_opacity.finish(); - a_bg.finish(); - } else { - a_opacity.update(dt, anim::linear); - a_bg.update(dt, anim::linear); - } - } - if (timer) update(); -} - -void IconedButton::onStateChange(int oldState, ButtonStateChangeSource source) { - a_opacity.start((_state & (StateOver | StateDown)) ? _st.overOpacity : _st.opacity); - a_bg.start(((_state & (StateOver | StateDown)) ? _st.overBgColor : _st.bgColor)->c); - - if (source == ButtonByUser || source == ButtonByPress) { - _a_appearance.stop(); - a_opacity.finish(); - a_bg.finish(); - update(); - } else { - _a_appearance.start(); - } -} - -void IconedButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - p.setOpacity(_opacity); - - p.fillRect(e->rect(), a_bg.current()); - - p.setOpacity(a_opacity.current() * _opacity); - - if (!_text.isEmpty()) { - p.setFont(_st.font->f); - p.setRenderHint(QPainter::TextAntialiasing); - p.setPen(_st.color->p); - const QPoint &t((_state & StateDown) ? _st.downTextPos : _st.textPos); - p.drawText(t.x(), t.y() + _st.font->ascent, _text); - } - const style::sprite &i((_state & StateDown) ? _st.downIcon : _st.icon); - if (i.pxWidth()) { - QPoint t((_state & StateDown) ? _st.downIconPos : _st.iconPos); - if (t.x() < 0) { - t.setX((width() - i.pxWidth()) / 2); - } - if (t.y() < 0) { - t.setY((height() - i.pxHeight()) / 2); - } - p.drawSprite(t, i); - } -} - BoxButton::BoxButton(QWidget *parent, const QString &text, const style::RoundButton &st) : Button(parent) , _text(text.toUpper()) , _fullText(text.toUpper()) diff --git a/Telegram/SourceFiles/ui/flatbutton.h b/Telegram/SourceFiles/ui/flatbutton.h index 643836c66..6b1c2576f 100644 --- a/Telegram/SourceFiles/ui/flatbutton.h +++ b/Telegram/SourceFiles/ui/flatbutton.h @@ -21,7 +21,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/button.h" -#include "ui/flatcheckbox.h" #include "ui/animation.h" class FlatButton : public Button { @@ -84,36 +83,6 @@ private: }; -class IconedButton : public Button { - Q_OBJECT - -public: - IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text = QString()); - - void step_appearance(float64 ms, bool timer); - void paintEvent(QPaintEvent *e); - - void setOpacity(float64 o); - - void setText(const QString &text); - QString getText() const; - -public slots: - void onStateChange(int oldState, ButtonStateChangeSource source); - -protected: - QString _text; - - style::iconedButton _st; - int32 _width; - - anim::fvalue a_opacity; - anim::cvalue a_bg; - Animation _a_appearance; - - float64 _opacity; -}; - class BoxButton : public Button { Q_OBJECT diff --git a/Telegram/SourceFiles/ui/style/style_core.cpp b/Telegram/SourceFiles/ui/style/style_core.cpp index 846394ef8..736c1fe67 100644 --- a/Telegram/SourceFiles/ui/style/style_core.cpp +++ b/Telegram/SourceFiles/ui/style/style_core.cpp @@ -65,7 +65,6 @@ void startManager() { } internal::registerFontFamily(qsl("Open Sans")); - internal::loadSprite(); internal::startModules(); } @@ -74,7 +73,6 @@ void stopManager() { internal::destroyFonts(); internal::destroyColors(); internal::destroyIcons(); - internal::destroySprite(); } QImage colorizeImage(const QImage &src, const color &c, const QRect &r) { diff --git a/Telegram/SourceFiles/ui/style/style_core.h b/Telegram/SourceFiles/ui/style/style_core.h index f3848978a..ea61f8ce7 100644 --- a/Telegram/SourceFiles/ui/style/style_core.h +++ b/Telegram/SourceFiles/ui/style/style_core.h @@ -77,7 +77,3 @@ inline QImage createInvertedCircleMask(int size) { } } // namespace style - -inline QRect centersprite(const QRect &inRect, const style::sprite &sprite) { - return centerrect(inRect, QRect(QPoint(0, 0), sprite.pxSize())); -} diff --git a/Telegram/SourceFiles/ui/style/style_core_types.cpp b/Telegram/SourceFiles/ui/style/style_core_types.cpp index 2f0444307..d26a173da 100644 --- a/Telegram/SourceFiles/ui/style/style_core_types.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_types.cpp @@ -22,46 +22,4 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/style/style_core_types.h" namespace style { -namespace internal { -namespace { - -int spriteWidthValue = 0; -QPixmap *spriteData = nullptr; - -} // namespace - -void loadSprite() { - QString spriteFilePostfix; - if (cRetina() || cScale() == dbisTwo) { - spriteFilePostfix = qsl("_200x"); - } else if (cScale() == dbisOneAndQuarter) { - spriteFilePostfix = qsl("_125x"); - } else if (cScale() == dbisOneAndHalf) { - spriteFilePostfix = qsl("_150x"); - } - QString spriteFile = qsl(":/gui/art/sprite") + spriteFilePostfix + qsl(".png"); - if (rtl()) { - spriteData = new QPixmap(App::pixmapFromImageInPlace(QImage(spriteFile).mirrored(true, false))); - } else { - spriteData = new QPixmap(spriteFile); - } - if (cRetina()) spriteData->setDevicePixelRatio(cRetinaFactor()); - spriteWidthValue = spriteData->width(); -} - -int spriteWidth() { - return spriteWidthValue; -} - -void destroySprite() { - delete spriteData; - spriteData = nullptr; -} - -} // namespace internal - -const QPixmap &spritePixmap() { - return *internal::spriteData; -} - } // namespace style diff --git a/Telegram/SourceFiles/ui/style/style_core_types.h b/Telegram/SourceFiles/ui/style/style_core_types.h index aa2fa782c..89194a264 100644 --- a/Telegram/SourceFiles/ui/style/style_core_types.h +++ b/Telegram/SourceFiles/ui/style/style_core_types.h @@ -33,50 +33,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/style/style_core_icon.h" namespace style { -namespace internal { - -void loadSprite(); -int spriteWidth(); -void destroySprite(); - -class Sprite { -public: - Sprite() { - } - Sprite(int left, int top, int width, int height) - : _rect(rtl() ? (spriteWidth() - left * cIntRetinaFactor() - width * cIntRetinaFactor()) : left * cIntRetinaFactor(), - top * cIntRetinaFactor(), - width * cIntRetinaFactor(), - height * cIntRetinaFactor()) { - } - int pxWidth() const { - return _rect.width() / cIntRetinaFactor(); - } - int pxHeight() const { - return _rect.height() / cIntRetinaFactor(); - } - QSize pxSize() const { - return _rect.size() / cIntRetinaFactor(); - } - QRect rect() const { - return _rect; - } - bool isEmpty() const { - return _rect.isEmpty(); - } - -private: - QRect _rect; - -}; -inline bool operator==(const Sprite &a, const Sprite &b) { - return a.rect() == b.rect(); -} -inline bool operator!=(const Sprite &a, const Sprite &b) { - return !(a == b); -} - -} // namespace internal using string = QString; using rect = QRect; @@ -88,7 +44,6 @@ using align = Qt::Alignment; using margins = QMargins; using font = internal::Font; using color = internal::Color; -using sprite = internal::Sprite; using icon = internal::Icon; static constexpr cursor cur_default = Qt::ArrowCursor; @@ -111,6 +66,4 @@ static const align al_bottomleft = (Qt::AlignBottom | Qt::AlignLeft); static const align al_left = (Qt::AlignVCenter | Qt::AlignLeft); static const align al_center = (Qt::AlignVCenter | Qt::AlignHCenter); -const QPixmap &spritePixmap(); - } // namespace style diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h index 8c9c6f964..b8bb58a7c 100644 --- a/Telegram/SourceFiles/ui/twidget.h +++ b/Telegram/SourceFiles/ui/twidget.h @@ -20,10 +20,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -namespace App { - const QPixmap &sprite(); -} - namespace Fonts { void start(); } @@ -79,45 +75,6 @@ public: void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix) { return drawPixmapRight(p.x(), p.y(), outerw, pix); } - void drawSprite(int x, int y, const style::sprite &sprite) { - return drawPixmap(QPoint(x, y), App::sprite(), sprite.rect()); - } - void drawSprite(const QPoint &p, const style::sprite &sprite) { - return drawPixmap(p, App::sprite(), sprite.rect()); - } - void drawSpriteLeft(int x, int y, int outerw, const style::sprite &sprite) { - return drawPixmapLeft(x, y, outerw, App::sprite(), sprite.rect()); - } - void drawSpriteLeft(const QPoint &p, int outerw, const style::sprite &sprite) { - return drawPixmapLeft(p, outerw, App::sprite(), sprite.rect()); - } - void drawSpriteLeft(int x, int y, int w, int h, int outerw, const style::sprite &sprite) { - return drawPixmapLeft(x, y, w, h, outerw, App::sprite(), sprite.rect()); - } - void drawSpriteLeft(const QRect &r, int outerw, const style::sprite &sprite) { - return drawPixmapLeft(r, outerw, App::sprite(), sprite.rect()); - } - void drawSpriteRight(int x, int y, int outerw, const style::sprite &sprite) { - return drawPixmapRight(x, y, outerw, App::sprite(), sprite.rect()); - } - void drawSpriteRight(const QPoint &p, int outerw, const style::sprite &sprite) { - return drawPixmapRight(p, outerw, App::sprite(), sprite.rect()); - } - void drawSpriteRight(int x, int y, int w, int h, int outerw, const style::sprite &sprite) { - return drawPixmapRight(x, y, w, h, outerw, App::sprite(), sprite.rect()); - } - void drawSpriteRight(const QRect &r, int outerw, const style::sprite &sprite) { - return drawPixmapRight(r, outerw, App::sprite(), sprite.rect()); - } - void drawSpriteCenter(const QRect &in, const style::sprite &sprite) { - return drawPixmap(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), App::sprite(), sprite.rect()); - } - void drawSpriteCenterLeft(const QRect &in, int outerw, const style::sprite &sprite) { - return drawPixmapLeft(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), outerw, App::sprite(), sprite.rect()); - } - void drawSpriteCenterRight(const QRect &in, int outerw, const style::sprite &sprite) { - return drawPixmapRight(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), outerw, App::sprite(), sprite.rect()); - } }; #define T_WIDGET \ diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 48c61dd98..2f6f99509 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -195,10 +195,10 @@ defaultMenu: Menu { itemBgOver: overBg; itemFg: black; itemFgOver: black; - itemFgDisabled: #ccc; - itemFgShortcut: #999; + itemFgDisabled: #cccccc; + itemFgShortcut: #999999; itemFgShortcutOver: #7c99b2; - itemFgShortcutDisabled: #ccc; + itemFgShortcutDisabled: #cccccc; itemIconPosition: point(0px, 0px); itemIconOpacity: 1.; itemIconOverOpacity: 1.; diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 9ffc4d0f8..e7a05c460 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -444,6 +444,8 @@ '<(src_loc)/stickers/emoji_pan.h', '<(src_loc)/stickers/stickers.cpp', '<(src_loc)/stickers/stickers.h', + '<(src_loc)/ui/buttons/checkbox.cpp', + '<(src_loc)/ui/buttons/checkbox.h', '<(src_loc)/ui/buttons/history_down_button.cpp', '<(src_loc)/ui/buttons/history_down_button.h', '<(src_loc)/ui/buttons/icon_button.cpp', @@ -522,8 +524,6 @@ '<(src_loc)/ui/filedialog.h', '<(src_loc)/ui/flatbutton.cpp', '<(src_loc)/ui/flatbutton.h', - '<(src_loc)/ui/flatcheckbox.cpp', - '<(src_loc)/ui/flatcheckbox.h', '<(src_loc)/ui/flatinput.cpp', '<(src_loc)/ui/flatinput.h', '<(src_loc)/ui/flatlabel.cpp', diff --git a/Telegram/gyp/codegen.gyp b/Telegram/gyp/codegen.gyp index b48dcb970..04f62494a 100644 --- a/Telegram/gyp/codegen.gyp +++ b/Telegram/gyp/codegen.gyp @@ -83,8 +83,6 @@ '<(src_loc)/codegen/style/parsed_file.h', '<(src_loc)/codegen/style/processor.cpp', '<(src_loc)/codegen/style/processor.h', - '<(src_loc)/codegen/style/sprite_generator.cpp', - '<(src_loc)/codegen/style/sprite_generator.h', '<(src_loc)/codegen/style/structure_types.cpp', '<(src_loc)/codegen/style/structure_types.h', ], diff --git a/Telegram/gyp/codegen_rules.gypi b/Telegram/gyp/codegen_rules.gypi index 058098b9d..825a09f4e 100644 --- a/Telegram/gyp/codegen_rules.gypi +++ b/Telegram/gyp/codegen_rules.gypi @@ -19,25 +19,6 @@ { 'actions': [{ - 'action_name': 'update_sprites', - 'inputs': [ - '<(PRODUCT_DIR)/codegen_style<(exe_ext)', - '<(res_loc)/basic.style', - '<(res_loc)/art/sprite.png', - '<(res_loc)/art/sprite_200x.png', - ], - 'outputs': [ - '<(res_loc)/art/sprite_125x.png', - '<(res_loc)/art/sprite_150x.png', - ], - 'action': [ - '<(PRODUCT_DIR)/codegen_style<(exe_ext)', - '-I<(res_loc)', '-I<(src_loc)', - '-w<(PRODUCT_DIR)/../..', - '--skip-styles', '<(res_loc)/basic.style', - ], - 'message': 'Updating sprites..', - }, { 'action_name': 'update_dependent_styles', 'inputs': [ '<(DEPTH)/update_dependent.py', @@ -116,7 +97,7 @@ ], 'action': [ '<(PRODUCT_DIR)/codegen_style<(exe_ext)', - '-I<(res_loc)', '-I<(src_loc)', '--skip-sprites', + '-I<(res_loc)', '-I<(src_loc)', '-o<(SHARED_INTERMEDIATE_DIR)/styles', '-w<(PRODUCT_DIR)/../..', From dbb6371e67ad6b27606b690b08522937a6c18633 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 28 Oct 2016 15:44:28 +0300 Subject: [PATCH 006/100] First version on loading themes is ready. --- .../Resources/art/{bg0.png => bg_old.png} | Bin Telegram/Resources/basic.style | 67 +- Telegram/Resources/basic_types.style | 28 - .../colors.palette} | 47 +- .../Resources/icons/playlist_download.png | Bin 135 -> 148 bytes .../Resources/icons/playlist_download@2x.png | Bin 177 -> 197 bytes Telegram/Resources/icons/playlist_play.png | Bin 265 -> 272 bytes Telegram/Resources/icons/playlist_play@2x.png | Bin 352 -> 365 bytes Telegram/Resources/sample.tdesktop-theme | 33 + Telegram/Resources/telegram.qrc | 2 +- Telegram/SourceFiles/_other/genlang.cpp | 2 +- Telegram/SourceFiles/apiwrap.cpp | 5 +- Telegram/SourceFiles/app.cpp | 70 +- Telegram/SourceFiles/app.h | 5 +- Telegram/SourceFiles/application.cpp | 77 +- Telegram/SourceFiles/application.h | 9 +- Telegram/SourceFiles/boxes/backgroundbox.cpp | 11 +- Telegram/SourceFiles/boxes/boxes.style | 6 +- Telegram/SourceFiles/boxes/connectionbox.cpp | 4 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 2 +- Telegram/SourceFiles/boxes/languagebox.cpp | 12 +- Telegram/SourceFiles/boxes/sharebox.cpp | 2 +- .../codegen/common/basic_tokenized_file.h | 4 + .../SourceFiles/codegen/style/generator.cpp | 437 +++- .../SourceFiles/codegen/style/generator.h | 7 +- .../SourceFiles/codegen/style/options.cpp | 11 +- Telegram/SourceFiles/codegen/style/options.h | 2 +- .../SourceFiles/codegen/style/parsed_file.cpp | 51 +- .../SourceFiles/codegen/style/processor.cpp | 17 +- .../codegen/style/structure_types.h | 1 + Telegram/SourceFiles/config.h | 2 - Telegram/SourceFiles/core/parse_helper.cpp | 115 + Telegram/SourceFiles/core/parse_helper.h | 55 + Telegram/SourceFiles/core/utils.h | 4 + Telegram/SourceFiles/core/zlib_help.h | 390 +++ Telegram/SourceFiles/dialogs/dialogs.style | 2 +- .../SourceFiles/dialogs/dialogs_layout.cpp | 2 +- Telegram/SourceFiles/facades.cpp | 4 + Telegram/SourceFiles/facades.h | 2 + .../history/history_media_types.cpp | 2 +- .../SourceFiles/history/history_message.cpp | 4 +- Telegram/SourceFiles/historywidget.cpp | 6 +- Telegram/SourceFiles/intro/intro.style | 22 + Telegram/SourceFiles/intro/introcode.cpp | 1 + Telegram/SourceFiles/intro/intropwdcheck.cpp | 1 + Telegram/SourceFiles/intro/introsignup.cpp | 1 + Telegram/SourceFiles/intro/introstart.cpp | 2 +- Telegram/SourceFiles/intro/introwidget.cpp | 4 +- Telegram/SourceFiles/lang.cpp | 2 +- Telegram/SourceFiles/lang.h | 2 +- Telegram/SourceFiles/langloaderplain.cpp | 63 +- Telegram/SourceFiles/langloaderplain.h | 38 +- Telegram/SourceFiles/localstorage.cpp | 253 +- Telegram/SourceFiles/localstorage.h | 8 + Telegram/SourceFiles/mainwidget.cpp | 20 +- Telegram/SourceFiles/mainwindow.cpp | 140 +- Telegram/SourceFiles/mainwindow.h | 4 +- .../SourceFiles/overview/overview_layout.cpp | 4 +- Telegram/SourceFiles/overviewwidget.cpp | 2 +- .../platform/linux/main_window_linux.cpp | 4 +- .../platform/linux/main_window_linux.h | 2 +- .../platform/mac/main_window_mac.h | 4 +- .../platform/mac/main_window_mac.mm | 6 +- .../platform/win/main_window_win.cpp | 2 +- .../platform/win/main_window_win.h | 2 +- Telegram/SourceFiles/profile/profile.style | 4 +- Telegram/SourceFiles/pspecific_winrt.h | 2 +- .../settings/settings_background_widget.cpp | 43 +- .../settings/settings_general_widget.cpp | 15 +- .../settings/settings_scale_widget.cpp | 13 +- Telegram/SourceFiles/shortcuts.cpp | 75 +- Telegram/SourceFiles/shortcuts.h | 20 +- Telegram/SourceFiles/stdafx.h | 1 + Telegram/SourceFiles/sysbuttons.cpp | 9 +- Telegram/SourceFiles/title.cpp | 2 +- Telegram/SourceFiles/ui/animation.cpp | 1 - Telegram/SourceFiles/ui/countryinput.h | 5 +- Telegram/SourceFiles/ui/flatbutton.cpp | 4 +- Telegram/SourceFiles/ui/style/style_core.h | 3 + Telegram/SourceFiles/ui/widgets/shadow.cpp | 2 +- Telegram/SourceFiles/ui/widgets/widgets.style | 2 +- .../SourceFiles/window/chat_background.cpp | 84 - Telegram/SourceFiles/window/window.style | 2 +- Telegram/SourceFiles/window/window_theme.cpp | 410 ++++ Telegram/SourceFiles/window/window_theme.h | 99 + Telegram/ThirdParty/minizip/unzip.c | 2125 +++++++++++++++++ Telegram/ThirdParty/minizip/unzip.h | 437 ++++ Telegram/gyp/Telegram.gyp | 9 +- Telegram/gyp/codegen_rules.gypi | 27 + 89 files changed, 4686 insertions(+), 795 deletions(-) rename Telegram/Resources/art/{bg0.png => bg_old.png} (100%) rename Telegram/{SourceFiles/window/chat_background.h => Resources/colors.palette} (59%) create mode 100644 Telegram/Resources/sample.tdesktop-theme create mode 100644 Telegram/SourceFiles/core/parse_helper.cpp create mode 100644 Telegram/SourceFiles/core/parse_helper.h create mode 100644 Telegram/SourceFiles/core/zlib_help.h delete mode 100644 Telegram/SourceFiles/window/chat_background.cpp create mode 100644 Telegram/SourceFiles/window/window_theme.cpp create mode 100644 Telegram/SourceFiles/window/window_theme.h create mode 100644 Telegram/ThirdParty/minizip/unzip.c create mode 100644 Telegram/ThirdParty/minizip/unzip.h diff --git a/Telegram/Resources/art/bg0.png b/Telegram/Resources/art/bg_old.png similarity index 100% rename from Telegram/Resources/art/bg0.png rename to Telegram/Resources/art/bg_old.png diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index f7558960a..dbd7d6804 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -18,6 +18,8 @@ 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 "colors.palette"; + using "basic_types.style"; semibold: "Open Sans Semibold"; @@ -30,10 +32,10 @@ emojiImgSize: 18px; // exceptional value for retina emojiSize: 18px; emojiPadding: 0px; -counterBG: #f23c34; -counterMuteBG: #888888; -counterColor: #ffffff; -counterMacInvColor: #ffffff01; +counterBg: #f23c34; +counterMuteBg: #888888; +counterFg: #ffffff; +counterMacInvFg: #ffffff01; lineWidth: 1px; @@ -46,14 +48,6 @@ wndMinWidth: 380px; adaptiveNormalWidth: 640px; adaptiveWideWidth: 1366px; -windowBg: #ffffff; // fallback for background: white -windowActiveBg: #40ace3; // fallback for blue filled active areas -windowTextFg: #000000; // fallback for text color: black -windowSubTextFg: #8a8a8a; // fallback for subtext color: gray -windowSubTextFgOver: #7c99b2; // fallback for subtext over color: gray over blue bg -windowActiveTextFg: #1485c2; // fallback for active color: blue online -windowShadowFg: #000000; // fallback for shadow color - semiboldButtonBlueText: #2b99d5; wndMinHeight: 480px; @@ -65,8 +59,6 @@ wndShadowShift: 1px; layerAlpha: 0.5; layerBg: black; -overBg: #edf2f5; - labelDefFlat: flatLabel { font: font(fsize); width: 0px; @@ -75,7 +67,6 @@ labelDefFlat: flatLabel { textFg: windowTextFg; } -boxBg: white; boxVerticalMargin: 10px; boxWidth: 320px; boxWideWidth: 364px; @@ -86,7 +77,6 @@ boxTextFont: font(boxFontSize); boxLittleSkip: 10px; boxMediumSkip: 20px; -boxTitleFg: #444444; boxTitleFont: font(boxFontSize bold); boxTitlePosition: point(26px, 28px); boxTitleHeight: 54px; @@ -97,7 +87,7 @@ defaultBoxButton: RoundButton { textFgOver: #2f9fea; secondaryTextFg: #2f9fea; secondaryTextFgOver: #2f9fea; - textBg: white; + textBg: boxBg; textBgOver: #edf7ff; width: -24px; @@ -139,7 +129,7 @@ boxLabel: flatLabel(labelDefFlat) { defaultLeftOutlineButton: OutlineButton { outlineWidth: 3px; outlineFg: windowBg; - outlineFgOver: windowActiveBg; + outlineFgOver: windowActiveFill; textBg: windowBg; textBgOver: #f2f7fa; @@ -533,24 +523,6 @@ introLabel: flatLabel(labelDefFlat) { align: align(center); } -introPointsTop: -30px; // intro steps bottom points -introPointWidth: 4px; -introPointHeight: 4px; -introPointHoverWidth: 10px; -introPointHoverHeight: 10px; -introPointLeft: 3px; -introPointTop: 3px; -introPointDelta: 10px; -introPointColor: #000000; -introPointAlpha: 0.5; -introPointHoverColor: #86b4e3; -introPointStepT: transition(sineInOut); -introPointAlphaT: transition(linear); -introPointShowStepT: transition(easeOutCirc); -introPointHideStepT: transition(easeInCirc); -introPointShowAlphaT: transition(easeInCirc); -introPointHideAlphaT: transition(easeOutCirc); - introStepSize: size(400px, 200px); introSize: size(400px, 400px); introSlideShift: 500px; // intro hiding animation @@ -581,17 +553,6 @@ btnIntroNext: flatButton(btnDefNext, btnDefBig) { boxShadow: icon {{ "box_shadow", windowShadowFg }}; boxShadowShift: 2px; -introCountry: countryInput { - width: 300px; - height: 41px; - top: 33px; - bgColor: #f2f2f2; - ptrSize: size(15px, 8px); - textMrg: margins(16px, 5px, 16px, 15px); - font: inpDefFont; - align: align(left); -} - introPhoneTop: 8px; inpIntroCountryCode: flatInput(inpDefGray) { width: 70px; @@ -632,7 +593,7 @@ countryRowHeight: 36px; countryRowNameFont: semiboldFont; countryRowPadding: margins(22px, 9px, 8px, 0px); countryRowCodeFont: font(fsize); -countryRowBgOver: overBg; +countryRowBgOver: windowOverBg; countryRowCodeFg: #808080; countryRowCodeFgOver: #7c99b2; countriesSkip: 12px; @@ -711,7 +672,7 @@ defaultActiveButton: RoundButton { textFgOver: #ffffff; secondaryTextFg: #cceeff; secondaryTextFgOver: #cceeff; - textBg: windowActiveBg; + textBg: windowActiveFill; textBgOver: #46b4eb; secondarySkip: 7px; @@ -807,8 +768,6 @@ msgLinkColor: #2a6dc2; msgPressedLinkColor: #004bad; msgSkip: 40px; msgPtr: 8px; -msgBG: ":/gui/art/bg.jpg"; -msgBG0: ":/gui/art/bg0.png"; msgDateSpace: 12px; msgDateDelta: point(2px, 5px); @@ -960,7 +919,7 @@ msgFileThumbLinkOutFgSelected: #31a298; msgFileNameTop: 16px; msgFileStatusTop: 37px; msgFileMinWidth: 294px; -msgFileInBg: windowActiveBg; +msgFileInBg: windowActiveFill; msgFileInBgOver: #4eade3; msgFileInBgSelected: #51a3d3; msgFileOutBg: #78c67f; @@ -976,7 +935,7 @@ msgWaveformBar: 2px; msgWaveformSkip: 1px; msgWaveformMin: 2px; msgWaveformMax: 20px; -msgWaveformInActive: windowActiveBg; +msgWaveformInActive: windowActiveFill; msgWaveformInActiveSelected: #51a3d3; msgWaveformInInactive: #d4dee6; msgWaveformInInactiveSelected: #9cc1e1; @@ -1100,7 +1059,7 @@ contactsStatusFont: font(fsize); contactsStatusFg: #999999; contactsStatusFgOver: #7c99b2; contactsStatusFgOnline: #3b8dcc; -contactsBgOver: overBg; +contactsBgOver: windowOverBg; contactsCheckPosition: point(8px, 16px); contactsAboutBg: #f7f7f7; contactsAboutShadow: #0000001F; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index ee9cdd023..1fe528fc0 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -141,17 +141,6 @@ flatScroll { hiding: int; } -countryInput { - width: pixels; - height: pixels; - top: pixels; - bgColor: color; - ptrSize: size; - textMrg: margins; - font: font; - align: align; -} - flatLabel { font: font; margin: margins; @@ -161,23 +150,6 @@ flatLabel { maxHeight: pixels; } -switcher { - border: pixels; - borderColor: color; - - bgColor: color; - bgHovered: color; - bgActive: color; - - height: pixels; - - font: font; - textColor: color; - activeColor: color; - - duration: int; -} - Tooltip { textBg: color; textFg: color; diff --git a/Telegram/SourceFiles/window/chat_background.h b/Telegram/Resources/colors.palette similarity index 59% rename from Telegram/SourceFiles/window/chat_background.h rename to Telegram/Resources/colors.palette index 6840dbc2f..68ce47b90 100644 --- a/Telegram/SourceFiles/window/chat_background.h +++ b/Telegram/Resources/colors.palette @@ -18,42 +18,15 @@ 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 Window { +windowBg: #ffffff; // white: fallback for background +windowTextFg: #000000; // black: fallback for text color +windowSubTextFg: #8a8a8a; // gray: fallback for subtext color +windowActiveFill: #40ace3; // bright blue: fallback for blue filled active areas +windowOverBg: #edf2f5; // light blue: fallback for over background +windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over color +windowActiveTextFg: #1485c2; // online blue: fallback for active color +windowShadowFg: #000000; // black: fallback for shadow color -struct ChatBackgroundUpdate { - enum class Type { - New, - Changed, - Start, - }; - - ChatBackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) { - } - Type type; - bool tiled; -}; - -class ChatBackground : public base::Observable { -public: - bool empty() const; - void initIfEmpty(); - void init(int32 id, QPixmap &&image); - void reset(); - - int32 id() const; - const QPixmap &image() const; - bool tile() const; - void setTile(bool tile); - -private: - int32 _id = 0; - QPixmap _image; - bool _tile = false; - -}; - -ChatBackground *chatBackground(); - -} // namespace Window +boxBg: windowBg; +boxTitleFg: #444444 | windowTextFg; diff --git a/Telegram/Resources/icons/playlist_download.png b/Telegram/Resources/icons/playlist_download.png index 2e645d911d76dd2897194ca1a35a4e88d96b773c..f02cb662acd73251ee8d003302dfdc052e2e985f 100644 GIT binary patch delta 132 zcmV-~0DJ$30h9rd7kv;21^@s6B4M5f00016NklJQ3F^axBz>ggkE{^@0000<1^@s6(BUpg0000^Nkl=TxZd8FIR^Dh7Z002ovPDHLkV1kmkF(v>2 diff --git a/Telegram/Resources/icons/playlist_download@2x.png b/Telegram/Resources/icons/playlist_download@2x.png index 6dad4371804925453f6af2274bd24f349abaff75..b340ffe3ad126102a49f926c3556a7a792f679ae 100644 GIT binary patch delta 182 zcmV;n07?I`0mT837k?lK1^@s62wu220001tNkl5sxHZVdQ@iEXtoEvc;&>|CYZrBG+GH*;J QmjD0&07*qoM6N<$f?O#;iU0rr diff --git a/Telegram/Resources/icons/playlist_play.png b/Telegram/Resources/icons/playlist_play.png index 780df063d0eb4bd59f74f8aed3bb1e4b8c6b4b73..9e314e48dfec0dc8b6f2bb74bc468842d51b70e6 100644 GIT binary patch delta 257 zcmV+c0sj7p0+0fb7k>~41^@s68wM|20002jNkl;Ac4lXU5Q4u^6a{e{V;IKe z>?W?sldkIsg5bFbKq*C4RXC3GOmwm=3rUjPmHV0KWEcj*FnWA00yvI?x~_3u_nGKq zU02dH#WYP`r|mazN!^m?x%m3mjW^8mEc(6|+NCL_D9aMtw)K0|wk^Kz>;Kv`O+-RVr?+eSzB7&^Qp#uBFQxqS6+iI}NgVq($1%T{00000NkvXX Hu0mjf_ziV` delta 250 zcmV{31^@s6>4u*Y0002cNkl0C!!Rzj zo46(wecuxV!E+ISl#;rxaUAEFNU^RfX`0@R`;g xUWmk?sI`XHn$9Ra3V+0^kCW7k?fI1^@s6MB%(s0003eNklp+chT}LqgbY$j6h*;D$S_S4qJJpjBLo0iYX~6_1OX2r z!?tZG%M!M2^AIvfDUoFvA0fjyjtIk$j}Y)(UtQP1^E@6x2Bj3rvV>(>JcJDMJR?n0 zK0=0J7=Eg+_k;l8I1aRJ3)gi)MEEdPzX4oIyUnsJ)HDr!K6=VcSY6qxLTrkf&9hfZ zp{lC)KSoxqgG)_wWix)P>x!zX-XGA;^MnxMYMneQ#Ae8sQXy#N3J07*qoM6N<$f`bT>^#A|> diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme new file mode 100644 index 000000000..3244f2460 --- /dev/null +++ b/Telegram/Resources/sample.tdesktop-theme @@ -0,0 +1,33 @@ +// +// This is a sample Telegram Desktop theme file. +// It was generated from the 'colors.palette' style file. +// +// To create a theme with a background image included you should +// put two files in a .zip archive: +// +// First one is the color scheme like the one you're viewing +// right now, this file should be named 'colors.tdesktop-theme'. +// +// Second one should be the background image and it can be named +// 'background.jpg', 'background.png', 'tiled.jpg' or 'tiled.png'. +// You should name it 'background' (if you'd like it not to be tiled), +// or it can be named 'tiled' (if you'd like it to be tiled). +// +// After that you need to change the extension of your .zip archive +// to 'tdesktop-theme', so you'll have: +// +// mytheme.tdesktop-theme +// |-colors.tdesktop-theme +// |-background.jpg (or tiled.jpg, background.png, tiled.png) +// + +windowBg: #ffffff; +windowTextFg: #000000; +windowSubTextFg: #8a8a8a; +windowActiveFill: #40ace3; +windowOverBg: #edf2f5; +windowSubTextFgOver: #7c99b2; +windowActiveTextFg: #1485c2; +windowShadowFg: #000000; +boxBg: #ffffff; +boxTitleFg: #444444; // windowTextFg; diff --git a/Telegram/Resources/telegram.qrc b/Telegram/Resources/telegram.qrc index 0c60957e2..53c94d871 100644 --- a/Telegram/Resources/telegram.qrc +++ b/Telegram/Resources/telegram.qrc @@ -5,7 +5,7 @@ art/fonts/OpenSans-Semibold.ttf art/newmsg.wav art/bg.jpg - art/bg0.png + art/bg_old.png art/icon256.png art/iconbig256.png diff --git a/Telegram/SourceFiles/_other/genlang.cpp b/Telegram/SourceFiles/_other/genlang.cpp index 17cb66065..ea0f80706 100644 --- a/Telegram/SourceFiles/_other/genlang.cpp +++ b/Telegram/SourceFiles/_other/genlang.cpp @@ -542,7 +542,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\ tcpp << "\t\tfor (const char *v = key.constData() + from, *e = v + len; v != e; ++v, ++value) {\n"; tcpp << "\t\t\tif (*v != *value) return false;\n"; tcpp << "\t\t}\n"; - tcpp << "\t\treturn true; \n"; + tcpp << "\t\treturn true;\n"; tcpp << "\t}\n"; tcpp << "}\n\n"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 05c4cdef8..adda378bb 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -30,10 +30,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "historywidget.h" #include "localstorage.h" #include "boxes/confirmbox.h" +#include "window/window_theme.h" ApiWrap::ApiWrap(QObject *parent) : QObject(parent) , _messageDataResolveDelayed(new SingleDelayedCall(this, "resolveMessageDatas")) { - App::initBackground(); + if (!Local::readBackground()) { + Window::Theme::Background()->start(); + } connect(&_webPagesTimer, SIGNAL(timeout()), this, SLOT(resolveWebPages())); connect(&_draftsSaveTimer, SIGNAL(timeout()), this, SLOT(saveDraftsToCloud())); diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 475d302a6..4a16b3f29 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -42,7 +42,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "apiwrap.h" #include "numbers.h" #include "observer_peer.h" -#include "window/chat_background.h" +#include "window/window_theme.h" #include "window/notifications_manager.h" #include "platform/platform_notifications_manager.h" @@ -134,7 +134,6 @@ namespace { style::color _historyScrollBgColor; style::color _historyScrollBarOverColor; style::color _historyScrollBgOverColor; - style::color _introPointHoverColor; } namespace App { @@ -211,6 +210,7 @@ namespace { } MTP::setAuthedId(0); Local::reset(); + Window::Theme::Background()->reset(); cSetOtherOnline(0); histories().clear(); @@ -2456,6 +2456,21 @@ namespace { _launchState = state; } + void restart() { +#ifndef TDESKTOP_DISABLE_AUTOUPDATE + bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady); +#else // !TDESKTOP_DISABLE_AUTOUPDATE + bool updateReady = false; +#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE + if (updateReady) { + cSetRestartingUpdate(true); + } else { + cSetRestarting(true); + cSetRestartingToSettings(true); + } + App::quit(); + } + QImage readImage(QByteArray data, QByteArray *format, bool opaque, bool *animated) { QByteArray tmpFormat; QImage result; @@ -2761,22 +2776,19 @@ namespace { void initBackground(int32 id, const QImage &p, bool nowrite) { if (Local::readBackground()) return; - uint64 components[3] = { 0 }, componentsScroll[3] = { 0 }, componentsPoint[3] = { 0 }; - int size = 0; - QImage img(p); - bool remove = false; + bool remove = (id == Window::Theme::kThemeBackground); if (p.isNull()) { - if (id == DefaultChatBackground) { - img.load(st::msgBG); + if (id == Window::Theme::kDefaultBackground) { + img.load(qsl(":/gui/art/bg.jpg")); } else { - img.load(st::msgBG0); + img.load(qsl(":/gui/art/bg_old.png")); if (cRetina()) { img = img.scaledToWidth(img.width() * 2, Qt::SmoothTransformation); } else if (cScale() != dbisOne) { img = img.scaledToWidth(convertScale(img.width()), Qt::SmoothTransformation); } - id = 0; + id = Window::Theme::kOldBackground; } remove = true; } @@ -2789,11 +2801,16 @@ namespace { Local::writeBackground(id, remove ? QImage() : img); } - int w = img.width(), h = img.height(); - size = w * h; - const uchar *pix = img.constBits(); - if (pix) { - for (int32 i = 0, l = size * 4; i < l; i += 4) { + initColorsFromBackground(img); + } + + void initColorsFromBackground(const QImage &img) { + uint64 components[3] = { 0 }, componentsScroll[3] = { 0 }; + auto w = img.width(); + auto h = img.height(); + auto size = w * h; + if (auto pix = img.constBits()) { + for (int i = 0, l = size * 4; i != l; i += 4) { components[2] += pix[i + 0]; components[1] += pix[i + 1]; components[0] += pix[i + 2]; @@ -2801,7 +2818,9 @@ namespace { } if (size) { - for (int32 i = 0; i < 3; ++i) components[i] /= size; + for (int i = 0; i != 3; ++i) { + components[i] /= size; + } } int maxtomin[3] = { 0, 1, 2 }; if (components[maxtomin[0]] < components[maxtomin[1]]) { @@ -2816,10 +2835,7 @@ namespace { uint64 max = qMax(1ULL, components[maxtomin[0]]), mid = qMax(1ULL, components[maxtomin[1]]), min = qMax(1ULL, components[maxtomin[2]]); - Window::chatBackground()->init(id, pixmapFromImageInPlace(std_::move(img))); - memcpy(componentsScroll, components, sizeof(components)); - memcpy(componentsPoint, components, sizeof(components)); if (max != min) { if (min > uint64(qRound(0.77 * max))) { @@ -2832,15 +2848,6 @@ namespace { uint64 newmid = max - ((max - mid) * (max - newmin)) / (max - min); componentsScroll[maxtomin[1]] = newmid; componentsScroll[maxtomin[2]] = newmin; - - uint64 pmax = 227; // 89% brightness - uint64 pmin = qRound(0.75 * pmax); // 41% saturation - uint64 pmid = pmax - ((max - mid) * (pmax - pmin)) / (max - min); - componentsPoint[maxtomin[0]] = pmax; - componentsPoint[maxtomin[1]] = pmid; - componentsPoint[maxtomin[2]] = pmin; - } else { - componentsPoint[0] = componentsPoint[1] = componentsPoint[2] = 227; // 89% brightness } float64 luminance = 0.299 * componentsScroll[0] + 0.587 * componentsScroll[1] + 0.114 * componentsScroll[2]; @@ -2886,9 +2893,6 @@ namespace { _historyScrollBarOverColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.barOverColor->c.alphaF() * 0xFF)); _historyScrollBgOverColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.bgOverColor->c.alphaF() * 0xFF)); - uchar rPoint = uchar(componentsPoint[0]), gPoint = uchar(componentsPoint[1]), bPoint = uchar(componentsPoint[2]); - _introPointHoverColor = style::color(rPoint, gPoint, bPoint); - if (App::main()) { App::main()->updateScrollColors(); } @@ -2919,10 +2923,6 @@ namespace { return _historyScrollBgOverColor; } - const style::color &introPointHoverColor() { - return _introPointHoverColor; - } - WallPapers gServerBackgrounds; } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 0181a1660..e7b9fd858 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -236,6 +236,7 @@ namespace App { void allDraftsSaved(); LaunchState launchState(); void setLaunchState(LaunchState state); + void restart(); QImage readImage(QByteArray data, QByteArray *format = 0, bool opaque = true, bool *animated = 0); QImage readImage(const QString &file, QByteArray *format = 0, bool opaque = true, bool *animated = 0, QByteArray *content = 0); @@ -292,7 +293,8 @@ namespace App { return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius); } - void initBackground(int32 id = DefaultChatBackground, const QImage &p = QImage(), bool nowrite = false); + void initColorsFromBackground(const QImage &image); + void initBackground(int32 id, const QImage &p = QImage(), bool nowrite = false); const style::color &msgServiceBg(); const style::color &msgServiceSelectBg(); @@ -300,7 +302,6 @@ namespace App { const style::color &historyScrollBgColor(); const style::color &historyScrollBarOverColor(); const style::color &historyScrollBgOverColor(); - const style::color &introPointHoverColor(); struct WallPaper { WallPaper(int32 id, ImagePtr thumb, ImagePtr full) : id(id), thumb(thumb), full(full) { diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index e6f3b9c05..0a50e4629 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -34,7 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "autoupdater.h" #include "core/observer.h" #include "observer_peer.h" -#include "window/chat_background.h" +#include "window/window_theme.h" #include "media/player/media_player_instance.h" #include "window/notifications_manager.h" #include "history/history_location_manager.h" @@ -676,11 +676,7 @@ namespace Sandbox { } -AppClass::AppClass() : QObject() -, _lastActionTime(0) -, _window(0) -, _uploader(0) -, _translator(0) { +AppClass::AppClass() : QObject() { AppObject = this; Fonts::start(); @@ -702,33 +698,7 @@ AppClass::AppClass() : QObject() cSetConfigScale(dbisOne); cSetRealScale(dbisOne); } - - if (cLang() < languageTest) { - cSetLang(Sandbox::LangSystem()); - } - if (cLang() == languageTest) { - if (QFileInfo(cLangFile()).exists()) { - LangLoaderPlain loader(cLangFile()); - cSetLangErrors(loader.errors()); - if (!cLangErrors().isEmpty()) { - LOG(("Lang load errors: %1").arg(cLangErrors())); - } else if (!loader.warnings().isEmpty()) { - LOG(("Lang load warnings: %1").arg(loader.warnings())); - } - } else { - cSetLang(languageDefault); - } - } else if (cLang() > languageDefault && cLang() < languageCount) { - LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[cLang()].c_str() + qsl(".strings")); - if (!loader.errors().isEmpty()) { - LOG(("Lang load errors: %1").arg(loader.errors())); - } else if (!loader.warnings().isEmpty()) { - LOG(("Lang load warnings: %1").arg(loader.warnings())); - } - } - - application()->installTranslator(_translator = new Translator()); - + loadLanguage(); style::startManager(); anim::startManager(); historyInit(); @@ -812,6 +782,33 @@ AppClass::AppClass() : QObject() } } +void AppClass::loadLanguage() { + if (cLang() < languageTest) { + cSetLang(Sandbox::LangSystem()); + } + if (cLang() == languageTest) { + if (QFileInfo(cLangFile()).exists()) { + LangLoaderPlain loader(cLangFile()); + cSetLangErrors(loader.errors()); + if (!cLangErrors().isEmpty()) { + LOG(("Lang load errors: %1").arg(cLangErrors())); + } else if (!loader.warnings().isEmpty()) { + LOG(("Lang load warnings: %1").arg(loader.warnings())); + } + } else { + cSetLang(languageDefault); + } + } else if (cLang() > languageDefault && cLang() < languageCount) { + LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[cLang()].c_str() + qsl(".strings")); + if (!loader.errors().isEmpty()) { + LOG(("Lang load errors: %1").arg(loader.errors())); + } else if (!loader.warnings().isEmpty()) { + LOG(("Lang load warnings: %1").arg(loader.warnings())); + } + } + application()->installTranslator(_translator = new Translator()); +} + void AppClass::regPhotoUpdate(const PeerId &peer, const FullMsgId &msgId) { photoUpdates.insert(msgId, peer); } @@ -998,9 +995,7 @@ void AppClass::onSwitchDebugMode() { if (cDebug()) { QFile(cWorkingDir() + qsl("tdata/withdebug")).remove(); cSetDebug(false); - cSetRestarting(true); - cSetRestartingToSettings(true); - App::quit(); + App::restart(); } else { cSetDebug(true); DEBUG_LOG(("Debug logs started.")); @@ -1017,9 +1012,7 @@ void AppClass::onSwitchWorkMode() { Global::SetDialogsModeEnabled(!Global::DialogsModeEnabled()); Global::SetDialogsMode(Dialogs::Mode::All); Local::writeUserSettings(); - cSetRestarting(true); - cSetRestartingToSettings(true); - App::quit(); + App::restart(); } void AppClass::onSwitchTestMode() { @@ -1034,9 +1027,7 @@ void AppClass::onSwitchTestMode() { } cSetTestMode(true); } - cSetRestarting(true); - cSetRestartingToSettings(true); - App::quit(); + App::restart(); } FileUploader *AppClass::uploader() { @@ -1120,7 +1111,7 @@ AppClass::~AppClass() { delete base::take(_uploader); delete base::take(_translator); - Window::chatBackground()->reset(); + Window::Theme::Unload(); Media::Player::finish(); style::stopManager(); diff --git a/Telegram/SourceFiles/application.h b/Telegram/SourceFiles/application.h index a18b4d9fa..a8e9627db 100644 --- a/Telegram/SourceFiles/application.h +++ b/Telegram/SourceFiles/application.h @@ -204,17 +204,18 @@ public slots: void call_handleObservables(); private: + void loadLanguage(); QMap photoUpdates; QMap killDownloadSessionTimes; SingleTimer killDownloadSessionsTimer; - uint64 _lastActionTime; + uint64 _lastActionTime = 0; - MainWindow *_window; - FileUploader *_uploader; - Translator *_translator; + MainWindow *_window = nullptr; + FileUploader *_uploader = nullptr; + Translator *_translator = nullptr; SingleTimer _mtpUnpauseTimer; diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index ae9d61c94..a31418997 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "backgroundbox.h" #include "mainwidget.h" #include "mainwindow.h" -#include "window/chat_background.h" +#include "window/window_theme.h" #include "styles/style_overview.h" BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll) @@ -48,8 +48,8 @@ void BackgroundBox::onBackgroundChosen(int index) { const App::WallPaper &paper(App::cServerBackgrounds().at(index)); if (App::main()) App::main()->setChatBackground(paper); - using Update = Window::ChatBackgroundUpdate; - Window::chatBackground()->notify(Update(Update::Type::Start, !paper.id)); + using Update = Window::Theme::BackgroundUpdate; + Window::Theme::Background()->notify(Update(Update::Type::Start, !paper.id)); } onClose(); } @@ -73,7 +73,8 @@ BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent) void BackgroundBox::Inner::gotWallpapers(const MTPVector &result) { App::WallPapers wallpapers; - wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG0), ImagePtr(st::msgBG0))); + auto oldBackground = ImagePtr(qsl(":/gui/art/bg_old.png")); + wallpapers.push_back(App::WallPaper(Window::Theme::kOldBackground, oldBackground, oldBackground)); const auto &v(result.c_vector().v); for (int i = 0, l = v.size(); i < l; ++i) { const auto &w(v.at(i)); @@ -161,7 +162,7 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) { const QPixmap &pix(paper.thumb->pix(st::backgroundSize.width(), st::backgroundSize.height())); p.drawPixmap(x, y, pix); - if (paper.id == Window::chatBackground()->id()) { + if (paper.id == Window::Theme::Background()->id()) { int checkPosX = x + st::backgroundSize.width() - st::overviewPhotoChecked.width(); int checkPosY = y + st::backgroundSize.height() - st::overviewPhotoChecked.height(); st::overviewPhotoChecked.paint(p, QPoint(checkPosX, checkPosY), width()); diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index c055abfbf..a3afb5ebc 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -106,7 +106,7 @@ contactsMultiSelect: MultiSelect { font: normalFont; textBg: contactsBgOver; textFg: windowTextFg; - textActiveBg: windowActiveBg; + textActiveBg: windowActiveFill; textActiveFg: white; deleteFg: white; deleteLeft: 10px; @@ -153,10 +153,10 @@ contactsPhotoCheckbox: RoundImageCheckbox { imageRadius: 21px; imageSmallRadius: 18px; selectWidth: 2px; - selectFg: windowActiveBg; + selectFg: windowActiveFill; selectDuration: 150; checkBorder: windowBg; - checkBg: windowActiveBg; + checkBg: windowActiveFill; checkRadius: 10px; checkSmallRadius: 3px; checkIcon: icon {{ "default_checkbox_check", windowBg, point(3px, 6px) }}; diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index dfaf522b5..7f02db63d 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -200,9 +200,7 @@ void ConnectionBox::onSave() { Local::writeSettings(); Global::RefConnectionTypeChanged().notify(); - cSetRestarting(true); - cSetRestartingToSettings(true); - App::quit(); + App::restart(); } else { Global::SetTryIPv6(_tryIPv6->checked()); Local::writeSettings(); diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index e50d15116..f945c9d7e 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -356,7 +356,7 @@ void ContactsBox::onFilterUpdate(const QString &filter) { void ContactsBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) { using AddItemWay = Ui::MultiSelect::AddItemWay; auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default; - _select->entity()->addItem(peer->id, peer->shortName(), st::windowActiveBg, PaintUserpicCallback(peer), addItemWay); + _select->entity()->addItem(peer->id, peer->shortName(), st::windowActiveFill, PaintUserpicCallback(peer), addItemWay); } void ContactsBox::onPeerSelectedChanged(PeerData *peer, bool checked) { diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp index 15fafe9ba..44674bf0b 100644 --- a/Telegram/SourceFiles/boxes/languagebox.cpp +++ b/Telegram/SourceFiles/boxes/languagebox.cpp @@ -45,7 +45,7 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) { for (int32 i = 0; i < languageCount; ++i) { LangLoaderResult result; if (i) { - LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), LangLoaderRequest(lng_language_name)); + LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), langLoaderRequest(lng_language_name)); result = loader.found(); } else { result.insert(lng_language_name, langOriginal(lng_language_name)); @@ -74,7 +74,7 @@ void LanguageBox::showAll() { void LanguageBox::mousePressEvent(QMouseEvent *e) { if ((e->modifiers() & Qt::CTRL) && (e->modifiers() & Qt::ALT) && (e->modifiers() & Qt::SHIFT)) { for (int32 i = 1; i < languageCount; ++i) { - LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), LangLoaderRequest(lngkeys_cnt)); + LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), langLoaderRequest(lngkeys_cnt)); if (!loader.errors().isEmpty()) { Ui::showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" error :(\n\nError: ") + loader.errors())); return; @@ -104,10 +104,10 @@ void LanguageBox::onChange() { if (_langs[i]->checked() && langId != cLang()) { LangLoaderResult result; if (langId > 0) { - LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok)); + LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), langLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok)); result = loader.found(); } else if (langId == languageTest) { - LangLoaderPlain loader(cLangFile(), LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok)); + LangLoaderPlain loader(cLangFile(), langLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok)); result = loader.found(); } QString text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)), @@ -134,9 +134,7 @@ void LanguageBox::onSave() { if (_langs[i]->checked()) { cSetLang(_langs[i]->val()); Local::writeSettings(); - cSetRestarting(true); - cSetRestartingToSettings(true); - App::quit(); + App::restart(); } } } diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index aa65bdc6c..1d5cb8315 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -236,7 +236,7 @@ void ShareBox::onFilterUpdate(const QString &query) { void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) { using AddItemWay = Ui::MultiSelect::AddItemWay; auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default; - _select->addItem(peer->id, peer->shortName(), st::windowActiveBg, PaintUserpicCallback(peer), addItemWay); + _select->addItem(peer->id, peer->shortName(), st::windowActiveFill, PaintUserpicCallback(peer), addItemWay); } void ShareBox::onPeerSelectedChanged(PeerData *peer, bool checked) { diff --git a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h index fcfcd27a4..d19ad1868 100644 --- a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h +++ b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h @@ -62,6 +62,8 @@ public: Plus, Minus, Equals, + And, + Or, Name, // [0-9a-zA-Z_]+ with at least one letter. }; Type type; @@ -144,6 +146,8 @@ private: { '+', Type::Plus }, { '-', Type::Minus }, { '=', Type::Equals }, + { '&', Type::And }, + { '|', Type::Or }, }; }; diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 62db76caf..39b4019bb 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -41,6 +41,54 @@ namespace { constexpr int kErrorBadIconSize = 861; constexpr int kErrorBadIconFormat = 862; +// crc32 hash, taken somewhere from the internet + +class Crc32Table { +public: + Crc32Table() { + quint32 poly = 0x04c11db7; + for (auto i = 0; i != 256; ++i) { + _data[i] = reflect(i, 8) << 24; + for (auto j = 0; j != 8; ++j) { + _data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0); + } + _data[i] = reflect(_data[i], 32); + } + } + + inline quint32 operator[](int index) const { + return _data[index]; + } + +private: + quint32 reflect(quint32 val, char ch) { + quint32 result = 0; + for (int i = 1; i < (ch + 1); ++i) { + if (val & 1) { + result |= 1 << (ch - i); + } + val >>= 1; + } + return result; + } + + quint32 _data[256]; + +}; + +qint32 hashCrc32(const void *data, int len) { + static Crc32Table table; + + const uchar *buffer = static_cast(data); + + quint32 crc = 0xffffffff; + for (int i = 0; i != len; ++i) { + crc = (crc >> 8) ^ table[(crc & 0xFF) ^ buffer[i]]; + } + + return static_cast(crc ^ 0xffffffff); +} + char hexChar(uchar ch) { if (ch < 10) { return '0' + ch; @@ -118,13 +166,38 @@ QString pxValueName(int value) { return result + QString::number(value); } +QString moduleBaseName(const structure::Module &module) { + auto moduleInfo = QFileInfo(module.filepath()); + auto moduleIsPalette = (moduleInfo.suffix() == "palette"); + return moduleIsPalette ? "palette" : "style_" + moduleInfo.baseName(); +} + +QChar paletteColorPart(uchar part) { + part = (part & 0x0F); + if (part >= 10) { + return 'a' + (part - 10); + } + return '0' + part; +} + +QString paletteColorComponent(uchar value) { + return QString() + paletteColorPart(value >> 4) + paletteColorPart(value); +} + +QString paletteColorValue(const structure::data::color &value) { + auto result = paletteColorComponent(value.red) + paletteColorComponent(value.green) + paletteColorComponent(value.blue); + if (value.alpha != 255) result += paletteColorComponent(value.alpha); + return result; +} + } // namespace -Generator::Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project) +Generator::Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project, bool isPalette) : module_(module) , basePath_(destBasePath) , baseName_(QFileInfo(basePath_).baseName()) -, project_(project) { +, project_(project) +, isPalette_(isPalette) { } bool Generator::writeHeader() { @@ -164,8 +237,13 @@ public:\n\ }\n\ };\n\ Module_" << baseName_ << " registrator;\n"; - if (!writeVariableDefinitions()) { - return false; + if (isPalette_) { + source_->newline(); + source_->stream() << "style::palette _palette;\n"; + } else { + if (!writeVariableDefinitions()) { + return false; + } } source_->newline().popNamespace(); @@ -174,8 +252,11 @@ Module_" << baseName_ << " registrator;\n"; return false; } - source_->popNamespace().newline(); - source_->newline().pushNamespace("style").pushNamespace("internal").newline(); + source_->popNamespace().newline().pushNamespace("style"); + if (isPalette_) { + writeSetPaletteColor(); + } + source_->pushNamespace("internal").newline(); if (!writeVariableInit()) { return false; } @@ -329,12 +410,84 @@ bool Generator::writeHeaderStyleNamespace() { if (!writeStructsDefinitions()) { return false; } + } else if (isPalette_) { + if (!wroteForwardDeclarations) { + header_->newline(); + } + if (!writePaletteDefinition()) { + return false; + } } header_->popNamespace().newline(); return true; } +bool Generator::writePaletteDefinition() { + header_->stream() << "\ +class palette {\n\ +public:\n\ + QByteArray save() const;\n\ + bool load(const QByteArray &cache);\n\ + bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\ + bool setColor(QLatin1String name, QLatin1String from);\n\ +\n\ + // Created not inited, should be finalized before usage.\n\ + void finalize();\n\ +\n"; + + QByteArray checksumString; + int indexInPalette = 0; + if (!module_.enumVariables([this, &indexInPalette, &checksumString](const Variable &value) -> bool { + auto name = value.name.back(); + if (value.value.type().tag != structure::TypeTag::Color) { + return false; + } + + auto type = typeToString(value.value.type()); + auto index = (indexInPalette++); + header_->stream() << "\tinline const " << type << " &" << name << "() const { return _colors[" << index << "]; };\n"; + checksumString.append(':' + name); + return true; + })) return false; + + auto checksum = hashCrc32(checksumString.constData(), checksumString.size()); + auto count = indexInPalette; + auto type = typeToString({ structure::TypeTag::Color }); + header_->stream() << "\ +\n\ + static constexpr int32 kChecksum = " << checksum << ";\n\ +\n\ +private:\n\ + struct TempColorData { uchar r, g, b, a; };\n\ + void compute(int index, int fallbackIndex, TempColorData data);\n\ +\n\ + enum class Status {\n\ + Initial,\n\ + Loaded,\n\ + Fallback,\n\ + };\n\ +\n\ + " << type << " _colors[" << count << "] = { Qt::Uninitialized };\n\ + Status _status[" << count << "] = { Status::Initial };\n\ +\n\ +};\n\ +\n\ +namespace main_palette {\n\ +\n\ +QByteArray save();\n\ +bool load(const QByteArray &cache);\n\ +bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\ +bool setColor(QLatin1String name, QLatin1String from);\n\ +\n\ +} // namespace main_palette\n\ +\n"; + + header_->newline(); + + return true; +} + bool Generator::writeStructsForwardDeclarations() { bool hasNoExternalStructs = module_.enumVariables([this](const Variable &value) -> bool { if (value.value.type().tag == structure::TypeTag::Struct) { @@ -411,7 +564,7 @@ bool Generator::writeIncludesInSource() { } bool result = module_.enumIncludes([this](const Module &module) -> bool { - source_->include("style_" + QFileInfo(module.filepath()).baseName() + ".h"); + source_->include(moduleBaseName(module) + ".h"); return true; }); source_->newline(); @@ -441,19 +594,189 @@ bool Generator::writeRefsDefinition() { return true; } - source_->newline(); bool result = module_.enumVariables([this](const Variable &variable) -> bool { auto name = variable.name.back(); auto type = typeToString(variable.value.type()); if (type.isEmpty()) { return false; } - source_->stream() << "const " << type << " &" << name << "(_" << name << ");\n"; + source_->stream() << "const " << type << " &" << name << "("; + if (isPalette_) { + source_->stream() << "_palette." << name << "()"; + } else { + source_->stream() << "_" << name; + } + source_->stream() << ");\n"; return true; }); return result; } +bool Generator::writeSetPaletteColor() { + source_->newline(); + source_->stream() << "void palette::finalize() {\n"; + + int indexInPalette = 0; + bool result = module_.enumVariables([this, &indexInPalette](const Variable &variable) -> bool { + auto name = variable.name.back(); + auto index = indexInPalette++; + paletteIndices_[name] = index; + if (variable.value.type().tag != structure::TypeTag::Color) { + return false; + } + auto color = variable.value.Color(); + auto fallbackIndex = paletteIndices_.value(variable.value.Color().fallback, -1); + source_->stream() << "\tcompute(" << index << ", " << fallbackIndex << ", {" << color.red << ", " << color.green << ", " << color.blue << ", " << color.alpha << "});\n"; + return true; + }); + auto count = indexInPalette; + + source_->stream() << "\ +}\n\ +\n\ +void palette::compute(int index, int fallbackIndex, TempColorData data) {\n\ + if (_status[index] == Status::Initial) {\n\ + if (fallbackIndex >= 0 && _status[fallbackIndex] != Status::Initial) {\n\ + _status[index] = Status::Fallback;\n\ + _colors[index] = _colors[fallbackIndex];\n\ + } else {\n\ + _colors[index] = { data.r, data.g, data.b, data.a };\n\ + }\n\ + }\n\ +}\n"; + + source_->newline().pushNamespace().newline(); + source_->stream() << "\ +int getPaletteIndex(QLatin1String name) {\n\ + auto size = name.size();\n\ + auto data = name.data();\n"; + + int already = 0; + QString prefix; + QString tabs; + for (auto i = paletteIndices_.end(), b = paletteIndices_.begin(); i != b;) { + --i; + auto name = i.key(); + auto index = i.value(); + auto prev = i; + auto next = (i == b) ? QString() : (--prev).key(); + while ((prefix.size() > name.size()) || (!prefix.isEmpty() && prefix.mid(0, already - 1) != name.mid(0, already - 1))) { + source_->stream() << "\n" << tabs << "};"; + prefix.chop(1); + tabs.chop(1); + --already; + } + if (!prefix.isEmpty() && prefix[already - 1] != name[already - 1]) { + source_->stream() << "\n" << tabs << "case '" << name[already - 1] << "':"; + prefix[already - 1] = name[already - 1]; + } + while (name.size() > already) { + if (name.mid(0, already) != next.mid(0, already)) { + break; + } else if (next.size() <= already) { + source_->stream() << "\n" << tabs << "\tif (size == " << name.size() << ")"; + break; + } + source_->stream() << "\n" << tabs << "\tif (size > " << already << ") switch (data[" << already << "]) {\n"; + prefix.append(name[already]); + tabs.append('\t'); + ++already; + source_->stream() << tabs << "case '" << name[already - 1] << "':"; + } + if (name.size() == already || name.mid(0, already) != next.mid(0, already)) { + source_->stream() << " return (size == " << name.size(); + if (name.size() != already) { + source_->stream() << " && "; + } + } else { + source_->stream() << " return ("; + } + if (already != name.size()) { + source_->stream() << "!memcmp(data + " << already << ", \"" << name.mid(already) << "\", " << (name.size() - already) << ")"; + } + source_->stream() << ") ? " << index << " : -1;"; + } + while (!prefix.isEmpty()) { + source_->stream() << "\n" << tabs << "};"; + prefix.chop(1); + tabs.chop(1); + --already; + } + + source_->stream() << "\ +\n\ + return -1;\n\ +}\n"; + + source_->newline().popNamespace().newline(); + source_->stream() << "\ +QByteArray palette::save() const {\n\ + auto result = QByteArray(" << (count * 4) << ", Qt::Uninitialized);\n\ + for (auto i = 0, index = 0; i != " << count << "; ++i) {\n\ + result[index++] = static_cast(_colors[i]->c.red());\n\ + result[index++] = static_cast(_colors[i]->c.green());\n\ + result[index++] = static_cast(_colors[i]->c.blue());\n\ + result[index++] = static_cast(_colors[i]->c.alpha());\n\ + }\n\ + return result;\n\ +}\n\ +\n\ +bool palette::load(const QByteArray &cache) {\n\ + if (cache.size() != " << (count * 4) << ") return false;\n\ +\n\ + auto p = reinterpret_cast(cache.constData());\n\ + for (auto i = 0; i != " << count << "; ++i) {\n\ + _colors[i] = { p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3] };\n\ + _status[i] = Status::Loaded;\n\ + }\n\ + return true;\n\ +}\n\ +\n\ +bool palette::setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\ + auto index = getPaletteIndex(name);\n\ + if (index >= 0) {\n\ + _colors[index] = { r, g, b, a };\n\ + _status[index] = Status::Loaded;\n\ + return true;\n\ + }\n\ + return false;\n\ +}\n\ +\n\ +bool palette::setColor(QLatin1String name, QLatin1String from) {\n\ + auto nameIndex = getPaletteIndex(name);\n\ + auto fromIndex = getPaletteIndex(from);\n\ + if (nameIndex >= 0 && fromIndex >= 0 && _status[fromIndex] == Status::Loaded) {\n\ + _colors[nameIndex] = _colors[fromIndex];\n\ + _status[nameIndex] = Status::Loaded;\n\ + return true;\n\ + }\n\ + return false;\n\ +}\n\ +\n\ +namespace main_palette {\n\ +\n\ +QByteArray save() {\n\ + return _palette.save();\n\ +}\n\ +\n\ +bool load(const QByteArray &cache) {\n\ + return _palette.load(cache);\n\ +}\n\ +\n\ +bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\ + return _palette.setColor(name, r, g, b, a);\n\ +}\n\ +\n\ +bool setColor(QLatin1String name, QLatin1String from) {\n\ + return _palette.setColor(name, from);\n\ +}\n\ +\n\ +} // namespace main_palette\n\ +\n"; + + return result; +} + bool Generator::writeVariableInit() { if (!module_.hasVariables()) { return true; @@ -486,7 +809,7 @@ void init_" << baseName_ << "() {\n\ bool writtenAtLeastOne = false; bool result = module_.enumIncludes([this,&writtenAtLeastOne](const Module &module) -> bool { if (module.hasVariables()) { - source_->stream() << "\tinit_style_" + QFileInfo(module.filepath()).baseName() + "();\n"; + source_->stream() << "\tinit_" + moduleBaseName(module) + "();\n"; writtenAtLeastOne = true; } return true; @@ -509,7 +832,9 @@ void init_" << baseName_ << "() {\n\ source_->newline(); } - bool result = module_.enumVariables([this](const Variable &variable) -> bool { + if (isPalette_) { + source_->stream() << "\t_palette.finalize();\n"; + } else if (!module_.enumVariables([this](const Variable &variable) -> bool { auto name = variable.name.back(); auto value = valueAssignmentCode(variable.value); if (value.isEmpty()) { @@ -517,11 +842,12 @@ void init_" << baseName_ << "() {\n\ } source_->stream() << "\t_" << name << " = " << value << ";\n"; return true; - }); - + })) { + return false; + } source_->stream() << "\ }\n\n"; - return result; + return true; } bool Generator::writePxValuesInit() { @@ -629,7 +955,6 @@ QByteArray iconMaskValuePng(const QString &filepath) { { QBuffer buffer(&result); composed.save(&buffer, "PNG"); -// composed.save(filePath + "@final.png", "PNG"); } return result; } @@ -735,5 +1060,87 @@ bool Generator::collectUniqueValues() { return module_.enumVariables(collector); } +bool Generator::writeSampleTheme(const QString &filepath) { + QByteArray content; + QTextStream stream(&content); + + stream << "\ +//\n\ +// This is a sample Telegram Desktop theme file.\n\ +// It was generated from the 'colors.palette' style file.\n\ +//\n\ +// To create a theme with a background image included you should\n\ +// put two files in a .zip archive:\n\ +//\n\ +// First one is the color scheme like the one you're viewing\n\ +// right now, this file should be named 'colors.tdesktop-theme'.\n\ +//\n\ +// Second one should be the background image and it can be named\n\ +// 'background.jpg', 'background.png', 'tiled.jpg' or 'tiled.png'.\n\ +// You should name it 'background' (if you'd like it not to be tiled),\n\ +// or it can be named 'tiled' (if you'd like it to be tiled).\n\ +//\n\ +// After that you need to change the extension of your .zip archive\n\ +// to 'tdesktop-theme', so you'll have:\n\ +//\n\ +// mytheme.tdesktop-theme\n\ +// |-colors.tdesktop-theme\n\ +// |-background.jpg (or tiled.jpg, background.png, tiled.png)\n\ +//\n\n"; + + QList names; + module_.enumVariables([this, &names](const Variable &variable) -> bool { + names.push_back(variable.name); + return true; + }); + bool result = module_.enumVariables([this, &names, &stream](const Variable &variable) -> bool { + auto name = variable.name.back(); + if (variable.value.type().tag != structure::TypeTag::Color) { + return false; + } + auto color = variable.value.Color(); + auto colorString = paletteColorValue(color); + auto fallbackIndex = paletteIndices_.value(variable.value.Color().fallback, -1); + if (fallbackIndex >= 0) { + auto fallbackVariable = module_.findVariableInModule(names[fallbackIndex], module_); + if (!fallbackVariable || fallbackVariable->value.type().tag != structure::TypeTag::Color) { + return false; + } + auto fallbackName = fallbackVariable->name.back(); + auto fallbackColor = fallbackVariable->value.Color(); + if (colorString == paletteColorValue(fallbackColor)) { + stream << name << ": " << fallbackName << ";\n"; + } else { + stream << name << ": #" << colorString << "; // " << fallbackName << ";\n"; + } + } else { + stream << name << ": #" << colorString << ";\n"; + } + return true; + }); + if (!result) { + return result; + } + + stream.flush(); + + QFile file(filepath); + if (file.open(QIODevice::ReadOnly)) { + if (file.readAll() == content) { + file.close(); + return true; + } + file.close(); + } + + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + if (file.write(content) != content.size()) { + return false; + } + return true; +} + } // namespace style } // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/generator.h b/Telegram/SourceFiles/codegen/style/generator.h index 4d7319dae..746be4213 100644 --- a/Telegram/SourceFiles/codegen/style/generator.h +++ b/Telegram/SourceFiles/codegen/style/generator.h @@ -34,12 +34,13 @@ class Module; class Generator { public: - Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project); + Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project, bool isPalette); Generator(const Generator &other) = delete; Generator &operator=(const Generator &other) = delete; bool writeHeader(); bool writeSource(); + bool writeSampleTheme(const QString &filepath); private: QString typeToString(structure::Type type) const; @@ -49,11 +50,13 @@ private: bool writeHeaderStyleNamespace(); bool writeStructsForwardDeclarations(); bool writeStructsDefinitions(); + bool writePaletteDefinition(); bool writeRefsDeclarations(); bool writeIncludesInSource(); bool writeVariableDefinitions(); bool writeRefsDefinition(); + bool writeSetPaletteColor(); bool writeVariableInit(); bool writePxValuesInit(); bool writeFontFamiliesInit(); @@ -66,10 +69,12 @@ private: QString basePath_, baseName_; const common::ProjectInfo &project_; std::unique_ptr source_, header_; + bool isPalette_ = false; QMap pxValues_; QMap fontFamilies_; QMap iconMasks_; // icon file -> index + QMap paletteIndices_; std::vector scales = { 4, 5, 6, 8 }; // scale / 4 gives our 1.00, 1.25, 1.50, 2.00 std::vectorscaleNames = { "dbisOne", "dbisOneAndQuarter", "dbisOneAndHalf", "dbisTwo" }; diff --git a/Telegram/SourceFiles/codegen/style/options.cpp b/Telegram/SourceFiles/codegen/style/options.cpp index 1b4d08a2c..705818df7 100644 --- a/Telegram/SourceFiles/codegen/style/options.cpp +++ b/Telegram/SourceFiles/codegen/style/options.cpp @@ -41,16 +41,12 @@ using common::logError; Options parseOptions() { Options result; - auto args(QCoreApplication::instance()->arguments()); + auto args = QCoreApplication::instance()->arguments(); for (int i = 1, count = args.size(); i < count; ++i) { // skip first - const auto &arg(args.at(i)); - - // Rebuild all dependencies - if (arg == "--rebuild") { - result.rebuildDependencies = true; + auto &arg = args.at(i); // Include paths - } else if (arg == "-I") { + if (arg == "-I") { if (++i == count) { logError(kErrorIncludePathExpected, "Command Line") << "include path expected after -I"; return Options(); @@ -96,6 +92,7 @@ Options parseOptions() { logError(kErrorInputPathExpected, "Command Line") << "input path expected"; return Options(); } + result.isPalette = (QFileInfo(result.inputPath).suffix() == "palette"); return result; } diff --git a/Telegram/SourceFiles/codegen/style/options.h b/Telegram/SourceFiles/codegen/style/options.h index 34746ee2d..93af08ace 100644 --- a/Telegram/SourceFiles/codegen/style/options.h +++ b/Telegram/SourceFiles/codegen/style/options.h @@ -30,7 +30,7 @@ struct Options { QStringList includePaths = { "." }; QString outputPath = "."; QString inputPath; - bool rebuildDependencies = false; + bool isPalette = false; }; // Parsing failed if inputPath is empty in the result. diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index 7f9cc0cc3..69ea7aa50 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -77,6 +77,10 @@ bool isValidColor(const QString &str) { return true; } +uchar toGray(uchar r, uchar g, uchar b) { + return qMax(qMin(int(0.21 * r + 0.72 * g + 0.07 * b), 255), 0); +} + uchar readHexUchar(QChar ch) { auto code = ch.unicode(); return (code >= '0' && code <= '9') ? ((code - '0') & 0xFF) : ((code + 10 - 'a') & 0xFF); @@ -86,7 +90,7 @@ uchar readHexUchar(QChar char1, QChar char2) { return ((readHexUchar(char1) & 0x0F) << 4) | (readHexUchar(char2) & 0x0F); } -structure::data::color convertWebColor(const QString &str) { +structure::data::color convertWebColor(const QString &str, const QString &fallback = QString()) { uchar r = 0, g = 0, b = 0, a = 255; if (isValidColor(str)) { r = readHexUchar(str.at(0), str.at(1)); @@ -96,7 +100,7 @@ structure::data::color convertWebColor(const QString &str) { a = readHexUchar(str.at(6), str.at(7)); } } - return { r, g, b, a }; + return { r, g, b, a, fallback }; } structure::data::color convertIntColor(int r, int g, int b, int a) { @@ -216,6 +220,11 @@ ParsedFile::ModulePtr ParsedFile::readIncluded() { } structure::Struct ParsedFile::readStruct(const QString &name) { + if (options_.isPalette) { + logErrorUnexpectedToken() << "unique color variable for the palette"; + return {}; + } + structure::Struct result = { composeFullName(name) }; do { if (auto fieldName = file_.getToken(BasicType::Name)) { @@ -236,6 +245,10 @@ structure::Variable ParsedFile::readVariable(const QString &name) { structure::Variable result = { composeFullName(name) }; if (auto value = readValue()) { result.value = value; + if (options_.isPalette && value.type().tag != structure::TypeTag::Color) { + logErrorUnexpectedToken() << "unique color variable for the palette"; + return {}; + } if (value.type().tag != structure::TypeTag::Struct || !value.copyOf().empty()) { assertNextToken(BasicType::Semicolon); } @@ -491,14 +504,35 @@ structure::Value ParsedFile::readStringValue() { structure::Value ParsedFile::readColorValue() { if (auto numberSign = file_.getToken(BasicType::Number)) { - auto color = file_.getAnyToken(); - if (color.type == BasicType::Int || color.type == BasicType::Name) { - auto chars = tokenValue(color).toLower(); - if (isValidColor(chars)) { - return { convertWebColor(chars) }; + if (options_.isPalette || true) { // enable for now + auto color = file_.getAnyToken(); + if (color.type == BasicType::Int || color.type == BasicType::Name) { + auto chars = tokenValue(color).toLower(); + if (isValidColor(chars)) { + if (auto fallbackSeparator = file_.getToken(BasicType::Or)) { + if (options_.isPalette) { + if (auto fallbackName = file_.getToken(BasicType::Name)) { + structure::FullName name = { tokenValue(fallbackName) }; + if (auto variable = module_->findVariableInModule(name, *module_)) { + return { convertWebColor(chars, tokenValue(fallbackName)) }; + } else { + logError(kErrorIdentifierNotFound) << "fallback color name"; + } + } else { + logErrorUnexpectedToken() << "fallback color name"; + } + } else { + logErrorUnexpectedToken() << "';', color fallbacks are only allowed in palette module"; + } + } else { + return { convertWebColor(chars) }; + } + } + } else { + logErrorUnexpectedToken() << "color value in #ccc, #ccca, #cccccc or #ccccccaa format"; } } else { - logErrorUnexpectedToken() << "color value in #ccc, #ccca, #cccccc or #ccccccaa format"; + logErrorUnexpectedToken() << "color value alias, unique color values are only allowed in palette module"; } } return {}; @@ -815,6 +849,7 @@ Options ParsedFile::includedOptions(const QString &filepath) { auto result = options_; result.inputPath = filepath; result.includePaths[0] = QFileInfo(filePath_).dir().absolutePath(); + result.isPalette = (QFileInfo(filepath).suffix() == "palette"); return result; } diff --git a/Telegram/SourceFiles/codegen/style/processor.cpp b/Telegram/SourceFiles/codegen/style/processor.cpp index b73bc503f..395644e3e 100644 --- a/Telegram/SourceFiles/codegen/style/processor.cpp +++ b/Telegram/SourceFiles/codegen/style/processor.cpp @@ -49,14 +49,7 @@ int Processor::launch() { } auto module = parser_->getResult(); - if (options_.rebuildDependencies) { - bool result = module->enumIncludes([this](const structure::Module &included) -> bool { - return write(included); - }); - if (!result) { - return -1; - } - } else if (!write(*module)) { + if (!write(*module)) { return -1; } @@ -72,7 +65,7 @@ bool Processor::write(const structure::Module &module) const { } QFileInfo srcFile(module.filepath()); - QString dstFilePath = dir.absolutePath() + '/' + destFileBaseName(module); + QString dstFilePath = dir.absolutePath() + '/' + (options_.isPalette ? "palette" : destFileBaseName(module)); common::ProjectInfo project = { "codegen_style", @@ -81,13 +74,17 @@ bool Processor::write(const structure::Module &module) const { forceReGenerate }; - Generator generator(module, dstFilePath, project); + Generator generator(module, dstFilePath, project, options_.isPalette); if (!generator.writeHeader()) { return false; } if (!generator.writeSource()) { return false; } + auto themePath = srcFile.absoluteDir().absolutePath() + "/sample.tdesktop-theme"; + if (options_.isPalette && !generator.writeSampleTheme(themePath)) { + return false; + } return true; } diff --git a/Telegram/SourceFiles/codegen/style/structure_types.h b/Telegram/SourceFiles/codegen/style/structure_types.h index b2c785809..53c5b595a 100644 --- a/Telegram/SourceFiles/codegen/style/structure_types.h +++ b/Telegram/SourceFiles/codegen/style/structure_types.h @@ -91,6 +91,7 @@ struct size { struct color { uchar red, green, blue, alpha; + QString fallback; }; struct margins { diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index a9ef7f033..9ed902b86 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -341,8 +341,6 @@ static const char *DefaultCountry = "US"; static const char *DefaultLanguage = "en"; enum { - DefaultChatBackground = 21, - DialogsFirstLoad = 20, // first dialogs part size requested DialogsPerPage = 500, // next dialogs part size diff --git a/Telegram/SourceFiles/core/parse_helper.cpp b/Telegram/SourceFiles/core/parse_helper.cpp new file mode 100644 index 000000000..7457d1311 --- /dev/null +++ b/Telegram/SourceFiles/core/parse_helper.cpp @@ -0,0 +1,115 @@ +/* +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 "core/parse_helper.h" + +namespace base { +namespace parse { + +// inspired by https://github.com/sindresorhus/strip-json-comments +QByteArray stripComments(const QByteArray &content) { + enum class InsideComment { + None, + SingleLine, + MultiLine, + }; + auto insideComment = InsideComment::None; + auto insideString = false; + + QByteArray result; + auto begin = content.cbegin(), end = content.cend(), offset = begin; + auto feedContent = [&result, &offset, end](const char *ch) { + if (ch > offset) { + if (result.isEmpty()) result.reserve(end - offset - 2); + result.append(offset, ch - offset); + offset = ch; + } + }; + auto feedComment = [&result, &offset, end](const char *ch) { + if (ch > offset) { + if (result.isEmpty()) result.reserve(end - offset - 2); + result.append(' '); + offset = ch; + } + }; + for (auto ch = offset; ch != end;) { + auto currentChar = *ch; + auto nextChar = (ch + 1 == end) ? 0 : *(ch + 1); + + if (insideComment == InsideComment::None && currentChar == '"') { + auto escaped = ((ch > begin) && *(ch - 1) == '\\') && ((ch - 1 < begin) || *(ch - 2) != '\\'); + if (!escaped) { + insideString = !insideString; + } + } + if (insideString) { + ++ch; + continue; + } + + if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '/') { + feedContent(ch); + insideComment = InsideComment::SingleLine; + ch += 2; + } else if (insideComment == InsideComment::SingleLine && currentChar == '\r' && nextChar == '\n') { + feedComment(ch); + ch += 2; + insideComment = InsideComment::None; + } else if (insideComment == InsideComment::SingleLine && currentChar == '\n') { + feedComment(ch); + ++ch; + insideComment = InsideComment::None; + } else if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '*') { + feedContent(ch); + ch += 2; + insideComment = InsideComment::MultiLine; + } else if (insideComment == InsideComment::MultiLine && currentChar == '*' && nextChar == '/') { + ch += 2; + feedComment(ch); + insideComment = InsideComment::None; + } else if (insideComment == InsideComment::MultiLine && currentChar == '\r' && nextChar == '\n') { + feedComment(ch); + ch += 2; + feedContent(ch); + } else if (insideComment == InsideComment::MultiLine && currentChar == '\n') { + feedComment(ch); + ++ch; + feedContent(ch); + } else { + ++ch; + } + } + + if (insideComment == InsideComment::MultiLine) { + // unexpected end of content + } + if (insideComment == InsideComment::None && end > offset) { + if (result.isEmpty()) { + return content; + } else { + result.append(offset, end - offset); + } + } + return result; +} + +} // namespace parse +} // namespace base diff --git a/Telegram/SourceFiles/core/parse_helper.h b/Telegram/SourceFiles/core/parse_helper.h new file mode 100644 index 000000000..6284baaf6 --- /dev/null +++ b/Telegram/SourceFiles/core/parse_helper.h @@ -0,0 +1,55 @@ +/* +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 base { +namespace parse { + +// Strip all C-style comments. +QByteArray stripComments(const QByteArray &content); + +inline bool skipWhitespaces(const char *&from, const char *end) { + t_assert(from <= end); + while (from != end && ( + (*from == ' ') || + (*from == '\n') || + (*from == '\t') || + (*from == '\r'))) { + ++from; + } + return (from != end); +} + +inline QByteArray readName(const char *&from, const char *end) { + t_assert(from <= end); + auto start = from; + while (from != end && ( + (*from >= 'a' && *from <= 'z') || + (*from >= 'A' && *from <= 'Z') || + (*from >= '0' && *from <= '9') || + (*from == '_'))) { + ++from; + } + return QByteArray::fromRawData(start, from - start); +} + +} // namespace parse +} // namespace base diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index f14625ddf..60a3527b4 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -78,6 +78,10 @@ inline QString str_const_toString(const str_const &str) { return QString::fromUtf8(str.c_str(), str.size()); } +inline QByteArray str_const_toByteArray(const str_const &str) { + return QByteArray::fromRawData(str.c_str(), str.size()); +} + template inline void accumulate_max(T &a, const T &b) { if (a < b) a = b; } diff --git a/Telegram/SourceFiles/core/zlib_help.h b/Telegram/SourceFiles/core/zlib_help.h new file mode 100644 index 000000000..9e0604515 --- /dev/null +++ b/Telegram/SourceFiles/core/zlib_help.h @@ -0,0 +1,390 @@ +/* +WARNING! All changes made in this file will be lost! +Created from 'colors.palette' by 'codegen_style' + +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 "zip.h" +#include "unzip.h" + +namespace zlib { +namespace internal { + +class InMemoryFile { +public: + InMemoryFile(const QByteArray &data = QByteArray()) : _data(data) { + } + + zlib_filefunc_def funcs() { + zlib_filefunc_def result; + result.opaque = this; + result.zopen_file = &InMemoryFile::Open; + result.zerror_file = &InMemoryFile::Error; + result.zread_file = &InMemoryFile::Read; + result.zwrite_file = &InMemoryFile::Write; + result.zclose_file = &InMemoryFile::Close; + result.zseek_file = &InMemoryFile::Seek; + result.ztell_file = &InMemoryFile::Tell; + return result; + } + + int error() const { + return _error; + } + + QByteArray result() const { + return _data; + } + +private: + voidpf open(const char *filename, int mode) { + if (mode & ZLIB_FILEFUNC_MODE_WRITE) { + if (mode & ZLIB_FILEFUNC_MODE_CREATE) { + _data.clear(); + } + _position = _data.size(); + _data.reserve(2 * 1024 * 1024); + } else if (mode & ZLIB_FILEFUNC_MODE_READ) { + _position = 0; + } + _error = 0; + return this; + } + + uLong read(voidpf stream, void* buf, uLong size) { + uLong toRead = 0; + if (!_error) { + if (_data.size() > int(_position)) { + toRead = qMin(size, uLong(_data.size() - _position)); + memcpy(buf, _data.constData() + _position, toRead); + _position += toRead; + } + if (toRead < size) { + _error = -1; + } + } + return toRead; + } + + uLong write(voidpf stream, const void* buf, uLong size) { + if (_data.size() < int(_position + size)) { + _data.resize(_position + size); + } + memcpy(_data.data() + _position, buf, size); + _position += size; + return size; + } + + int close(voidpf stream) { + auto result = _error; + _position = 0; + _error = 0; + return result; + } + + int error(voidpf stream) const { + return _error; + } + + long tell(voidpf stream) const { + return _position; + } + + long seek(voidpf stream, uLong offset, int origin) { + if (!_error) { + switch (origin) { + case ZLIB_FILEFUNC_SEEK_SET: _position = offset; break; + case ZLIB_FILEFUNC_SEEK_CUR: _position += offset; break; + case ZLIB_FILEFUNC_SEEK_END: _position = _data.size() + offset; break; + } + if (int(_position) > _data.size()) { + _error = -1; + } + } + return _error; + } + + static voidpf Open(voidpf opaque, const char* filename, int mode) { + return static_cast(opaque)->open(filename, mode); + } + + static uLong Read(voidpf opaque, voidpf stream, void* buf, uLong size) { + return static_cast(opaque)->read(stream, buf, size); + } + + static uLong Write(voidpf opaque, voidpf stream, const void* buf, uLong size) { + return static_cast(opaque)->write(stream, buf, size); + } + + static int Close(voidpf opaque, voidpf stream) { + return static_cast(opaque)->close(stream); + } + + static int Error(voidpf opaque, voidpf stream) { + return static_cast(opaque)->error(stream); + } + + static long Tell(voidpf opaque, voidpf stream) { + return static_cast(opaque)->tell(stream); + } + + static long Seek(voidpf opaque, voidpf stream, uLong offset, int origin) { + return static_cast(opaque)->seek(stream, offset, origin); + } + + uLong _position = 0; + int _error = 0; + QByteArray _data; + +}; + +} // namespace internal + +constexpr int kCaseSensitive = 1; +constexpr int kCaseInsensitive = 2; + +class FileToRead { +public: + FileToRead(const QByteArray &content) : _data(content) { + auto funcs = _data.funcs(); + if (!(_handle = unzOpen2(nullptr, &funcs))) { + _error = -1; + } + } + + int getGlobalInfo(unz_global_info *pglobal_info) { + if (error() == UNZ_OK) { + _error = _handle ? unzGetGlobalInfo(_handle, pglobal_info) : -1; + } + return _error; + } + + int locateFile(const char *szFileName, int iCaseSensitivity) { + if (error() == UNZ_OK) { + _error = _handle ? unzLocateFile(_handle, szFileName, iCaseSensitivity) : -1; + } + return error(); + } + + int getCurrentFileInfo( + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize + ) { + if (error() == UNZ_OK) { + _error = _handle ? unzGetCurrentFileInfo( + _handle, + pfile_info, + szFileName, + fileNameBufferSize, + extraField, + extraFieldBufferSize, + szComment, + commentBufferSize + ) : -1; + } + return error(); + } + + int openCurrentFile() { + if (error() == UNZ_OK) { + _error = _handle ? unzOpenCurrentFile(_handle) : -1; + } + return error(); + } + + int readCurrentFile(voidp buf, unsigned len) { + if (error() == UNZ_OK) { + auto result = _handle ? unzReadCurrentFile(_handle, buf, len) : -1; + if (result >= 0) { + return result; + } else { + _error = result; + } + } + return error(); + } + + int closeCurrentFile() { + if (error() == UNZ_OK) { + _error = _handle ? unzCloseCurrentFile(_handle) : -1; + } + return error(); + } + + QByteArray readCurrentFileContent(int fileSizeLimit) { + unz_file_info fileInfo = { 0 }; + if (getCurrentFileInfo(&fileInfo, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK) { + LOG(("Error: could not get current file info in a zip file.")); + return QByteArray(); + } + + auto size = fileInfo.uncompressed_size; + if (size > fileSizeLimit) { + if (_error == UNZ_OK) _error = -1; + LOG(("Error: current file is too large (should be less than %1, got %2) in a zip file.").arg(fileSizeLimit).arg(size)); + return QByteArray(); + } + if (openCurrentFile() != UNZ_OK) { + LOG(("Error: could not open current file in a zip file.")); + return QByteArray(); + } + + QByteArray result; + result.resize(size); + + auto couldRead = readCurrentFile(result.data(), size); + if (couldRead != size) { + LOG(("Error: could not read current file in a zip file, got %1.").arg(couldRead)); + return QByteArray(); + } + + if (closeCurrentFile() != UNZ_OK) { + LOG(("Error: could not close current file in a zip file.")); + return QByteArray(); + } + + return result; + } + + QByteArray readFileContent(const char *szFileName, int iCaseSensitivity, int fileSizeLimit) { + if (locateFile(szFileName, iCaseSensitivity) != UNZ_OK) { + LOG(("Error: could not locate '%1' in a zip file.").arg(szFileName)); + return false; + } + return readCurrentFileContent(fileSizeLimit); + } + + void close() { + if (_handle && unzClose(_handle) != UNZ_OK && _error == UNZ_OK) { + _error = -1; + } + _handle = nullptr; + } + + int error() const { + if (auto dataError = _data.error()) { + return dataError; + } + return _error; + } + + void clearError() { + _error = UNZ_OK; + } + + ~FileToRead() { + close(); + } + +private: + internal::InMemoryFile _data; + unzFile _handle = nullptr; + int _error = 0; + +}; + +class FileToWrite { +public: + FileToWrite() { + auto funcs = _data.funcs(); + if (!(_handle = zipOpen2(nullptr, APPEND_STATUS_CREATE, nullptr, &funcs))) { + _error = -1; + } + } + + int openNewFile( + const char *filename, + const zip_fileinfo *zipfi, + const void *extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level + ) { + if (error() == ZIP_OK) { + _error = _handle ? zipOpenNewFileInZip( + _handle, + filename, + zipfi, + extrafield_local, + size_extrafield_local, + extrafield_global, + size_extrafield_global, + comment, + method, + level + ) : -1; + } + return error(); + } + + int writeInFile(const void* buf, unsigned len) { + if (error() == ZIP_OK) { + _error = _handle ? zipWriteInFileInZip(_handle, buf, len) : -1; + } + return error(); + } + + int closeFile() { + if (error() == ZIP_OK) { + _error = _handle ? zipCloseFileInZip(_handle) : -1; + } + return error(); + } + + void close() { + if (_handle && zipClose(_handle, nullptr) != ZIP_OK && _error == ZIP_OK) { + _error = -1; + } + _handle = nullptr; + } + + int error() const { + if (auto dataError = _data.error()) { + return dataError; + } + return _error; + } + + QByteArray result() const { + return _data.result(); + } + + ~FileToWrite() { + close(); + } + +private: + internal::InMemoryFile _data; + zipFile _handle = nullptr; + int _error = 0; + +}; + +} // namespace zlib diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index ee44cb5a7..4ad5a4543 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -25,7 +25,7 @@ using "ui/widgets/widgets.style"; dialogsUnreadFg: #ffffff; dialogsUnreadFgActive: #5b94bf; -dialogsUnreadBg: windowActiveBg; +dialogsUnreadBg: windowActiveFill; dialogsUnreadBgMuted: #bbbbbb; dialogsUnreadBgActive: #ffffff; dialogsUnreadBgMutedActive: #d3e2ee; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index dd6d7c230..d303f5b04 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -183,7 +183,7 @@ void paintUnreadBadge(Painter &p, const QRect &rect, const UnreadBadgeStyle &st) t_assert(st.sizeId < UnreadBadgeSizesCount); badgeData = &unreadBadgeStyle->sizes[st.sizeId]; } - style::color bg = unreadBadgeStyle->bg[index]; + auto &bg = unreadBadgeStyle->bg[index]; if (badgeData->left[index].isNull()) { int imgsize = size * cIntRetinaFactor(), imgsizehalf = sizehalf * cIntRetinaFactor(); createCircleMask(badgeData, size); diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 8457a0775..17493cb6f 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -667,6 +667,8 @@ struct Data { base::Observable ItemRemoved; + bool ApplyingTheme = false; + }; } // namespace internal @@ -782,4 +784,6 @@ DefineRefVar(Global, base::Observable, LocalPasscodeChanged); DefineRefVar(Global, base::Observable, ItemRemoved); +DefineVar(Global, bool, ApplyingTheme); + } // namespace Global diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index dc3eaed73..d049c6a9f 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -356,6 +356,8 @@ DeclareRefVar(base::Observable, LocalPasscodeChanged); DeclareRefVar(base::Observable, ItemRemoved); +DeclareVar(bool, ApplyingTheme); + } // namespace Global namespace Adaptive { diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 0281440e0..a0ade85f4 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -1172,7 +1172,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, if (radial) { QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); - style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg)); + auto &bg = outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg); _animation->radial.draw(p, rinner, st::msgFileRadialLine, bg); } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 80c9c2d0d..e39e437d3 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1291,8 +1291,8 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u auto top = marginTop(); QRect r(left, top, width, height - top - marginBottom()); - style::color bg(selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg)); - style::color sh(selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow)); + auto &bg = selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg); + auto &sh = selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow); RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); App::roundRect(p, r, bg, cors, &sh); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 9cd79baef..1fa4ecea5 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -51,7 +51,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "apiwrap.h" #include "window/top_bar_widget.h" -#include "window/chat_background.h" +#include "window/window_theme.h" #include "observer_peer.h" #include "core/qthelp_regex.h" #include "ui/widgets/popup_menu.h" @@ -8746,8 +8746,8 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { int fromy = App::main()->backgroundFromY(), x = 0, y = 0; QPixmap cached = App::main()->cachedBackground(fill, x, y); if (cached.isNull()) { - auto &pix = Window::chatBackground()->image(); - if (Window::chatBackground()->tile()) { + auto &pix = Window::Theme::Background()->image(); + if (Window::Theme::Background()->tile()) { int left = r.left(), top = r.top(), right = r.left() + r.width(), bottom = r.top() + r.height(); float64 w = pix.width() / cRetinaFactor(), h = pix.height() / cRetinaFactor(); int sx = qFloor(left / w), sy = qFloor((top - fromy) / h), cx = qCeil(right / w), cy = qCeil((bottom - fromy) / h); diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index a6d8e2077..3005ac39d 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -21,6 +21,28 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org using "basic.style"; using "ui/widgets/widgets.style"; +countryInput { + width: pixels; + height: pixels; + top: pixels; + bgColor: color; + ptrSize: size; + textMrg: margins; + font: font; + align: align; +} + +introCountry: countryInput { + width: 300px; + height: 41px; + top: 33px; + bgColor: #f2f2f2; + ptrSize: size(15px, 8px); + textMrg: margins(16px, 5px, 16px, 15px); + font: inpDefFont; + align: align(left); +} + introErrLabel: flatLabel(labelDefFlat) { font: introErrFont; align: align(center); diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index 74c927daf..20507a12a 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "intro/introsignup.h" #include "intro/intropwdcheck.h" +#include "styles/style_intro.h" CodeInput::CodeInput(QWidget *parent, const style::flatInput &st, const QString &ph) : FlatInput(parent, st, ph) { } diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index c7da076c9..0b7295444 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "intro/intropwdcheck.h" +#include "styles/style_intro.h" #include "ui/filedialog.h" #include "boxes/confirmbox.h" #include "lang.h" diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index 153e470a4..885d0a499 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "intro/introsignup.h" +#include "styles/style_intro.h" #include "ui/filedialog.h" #include "boxes/photocropbox.h" #include "lang.h" diff --git a/Telegram/SourceFiles/intro/introstart.cpp b/Telegram/SourceFiles/intro/introstart.cpp index cdced013d..ac9b04b64 100644 --- a/Telegram/SourceFiles/intro/introstart.cpp +++ b/Telegram/SourceFiles/intro/introstart.cpp @@ -34,7 +34,7 @@ IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent) if (cLang() == languageDefault) { int32 l = Sandbox::LangSystem(); if (l != languageDefault) { - LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[l].c_str() + qsl(".strings"), LangLoaderRequest(lng_switch_to_this)); + LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[l].c_str() + qsl(".strings"), langLoaderRequest(lng_switch_to_this)); QString text = loader.found().value(lng_switch_to_this); if (!text.isEmpty()) { _changeLang.setText(text); diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 678a0e355..014964e9b 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -73,9 +73,7 @@ void IntroWidget::langChangeTo(int32 langId) { void IntroWidget::onChangeLang() { cSetLang(_langChangeTo); Local::writeSettings(); - cSetRestarting(true); - cSetRestartingToSettings(false); - App::quit(); + App::restart(); } void IntroWidget::onParentResize(const QSize &newSize) { diff --git a/Telegram/SourceFiles/lang.cpp b/Telegram/SourceFiles/lang.cpp index 59b648bf6..cee53a251 100644 --- a/Telegram/SourceFiles/lang.cpp +++ b/Telegram/SourceFiles/lang.cpp @@ -68,7 +68,7 @@ QString langNewVersionText() { QString langNewVersionTextForLang(int langId) { LangLoaderResult result; if (langId) { - LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), LangLoaderRequest(lng_language_name, NEW_VER_KEY)); + LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), langLoaderRequest(lng_language_name, NEW_VER_KEY)); result = loader.found(); } else { result.insert(lng_language_name, langOriginal(lng_language_name)); diff --git a/Telegram/SourceFiles/lang.h b/Telegram/SourceFiles/lang.h index e8e551900..c6b20bcdc 100644 --- a/Telegram/SourceFiles/lang.h +++ b/Telegram/SourceFiles/lang.h @@ -178,11 +178,11 @@ private: LangLoader(const LangLoader &); LangLoader &operator=(const LangLoader &); + }; class Translator : public QTranslator { public: - QString translate(const char *context, const char *sourceText, const char *disambiguation = 0, int n = -1) const override; }; diff --git a/Telegram/SourceFiles/langloaderplain.cpp b/Telegram/SourceFiles/langloaderplain.cpp index cc36e7d21..81fe342d8 100644 --- a/Telegram/SourceFiles/langloaderplain.cpp +++ b/Telegram/SourceFiles/langloaderplain.cpp @@ -21,52 +21,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "langloaderplain.h" -namespace { - - bool skipWhitespaces(const char *&from, const char *end) { - while (from < end && (*from == ' ' || *from == '\n' || *from == '\t' || *from == '\r')) { - ++from; - } - return (from < end); - } - - bool skipComment(const char *&from, const char *end) { - if (from >= end) return false; - if (*from == '/') { - if (from + 1 >= end) return true; - if (*(from + 1) == '*') { - from += 2; - while (from + 1 < end && (*from != '*' || *(from + 1) != '/')) { - ++from; - } - from += 2; - return (from < end); - } else if (*(from + 1) == '/') { - from += 2; - while (from < end && *from != '\n' && *from != '\r') { - ++from; - } - return (from < end); - } else { - return true; - } - } - return true; - } - - bool skipJunk(const char *&from, const char *end) { - const char *start; - do { - start = from; - if (!skipWhitespaces(from, end)) return false; - if (!skipComment(from, end)) throw Exception("Unexpected end of comment!"); - } while (start != from); - return true; - } -} +#include "core/parse_helper.h" bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) { - if (!skipJunk(from, end)) return false; + using base::parse::skipWhitespaces; + if (!skipWhitespaces(from, end)) return false; if (*from != '"') throw Exception(QString("Expected quote before key name!")); ++from; @@ -77,13 +36,13 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) { QByteArray varName = QByteArray(nameStart, from - nameStart); - if (*from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(QLatin1String(varName))); + if (from == end || *from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(QLatin1String(varName))); ++from; - if (!skipJunk(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName))); + if (!skipWhitespaces(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName))); if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(QLatin1String(varName))); - if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName))); + if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName))); if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(QLatin1String(varName))); LangKey varKey = keyIndex(varName); @@ -231,10 +190,10 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) { if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName))); if (readingValue && from > start) varValue.append(start, from - start); - if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName))); + if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName))); if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(QLatin1String(varName))); - skipJunk(++from, end); + skipWhitespaces(++from, end); if (readingValue) { if (feedingValue) { @@ -299,9 +258,11 @@ LangLoaderPlain::LangLoaderPlain(const QString &file, const LangLoaderRequest &r } } - const char *text = data.constData() + skip, *end = text + data.size() - skip; + data = base::parse::stripComments(data); + + auto text = data.constData() + skip, end = text + data.size() - skip; try { - while (true) { + while (text != end) { if (!readKeyValue(text, end)) { break; } diff --git a/Telegram/SourceFiles/langloaderplain.h b/Telegram/SourceFiles/langloaderplain.h index b8a6e4418..f4893dc21 100644 --- a/Telegram/SourceFiles/langloaderplain.h +++ b/Telegram/SourceFiles/langloaderplain.h @@ -22,24 +22,32 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" -class LangLoaderRequest : public QMap { -public: - LangLoaderRequest() { - } - LangLoaderRequest(LangKey key1) { - insert(key1, true); - } - LangLoaderRequest(LangKey key1, LangKey key2) { - insert(key1, true); - insert(key2, true); - } - LangLoaderRequest(LangKey key1, LangKey key2, LangKey key3) { - insert(key1, true); - insert(key2, true); - insert(key3, true); +using LangLoaderRequest = OrderedSet; + +template +struct LangLoaderRequestHelper; + +template <> +struct LangLoaderRequestHelper<> { + static inline void fill(LangLoaderRequest &result) { } }; +template +struct LangLoaderRequestHelper { + static inline void fill(LangLoaderRequest &result, Arg arg, Args ...args) { + result.insert(arg); + LangLoaderRequestHelper::fill(result, args...); + } +}; + +template +inline LangLoaderRequest langLoaderRequest(Args ...args) { + LangLoaderRequest result; + LangLoaderRequestHelper::fill(result, args...); + return result; +} + using LangLoaderResult = QMap; class LangLoaderPlain : public LangLoader { public: diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index b2ff027da..690c4ca6e 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "serialize/serialize_document.h" #include "serialize/serialize_common.h" #include "data/data_drafts.h" -#include "window/chat_background.h" +#include "window/window_theme.h" #include "observer_peer.h" #include "mainwidget.h" #include "mainwindow.h" @@ -38,10 +38,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Local { namespace { +constexpr int kThemeFileSizeLimit = 5 * 1024 * 1024; + using FileKey = quint64; -static const char tdfMagic[] = { 'T', 'D', 'F', '$' }; -static const int32 tdfMagicLen = sizeof(tdfMagic); +constexpr char tdfMagic[] = { 'T', 'D', 'F', '$' }; +constexpr int tdfMagicLen = sizeof(tdfMagic); QString toFilePart(FileKey val) { QString result; @@ -68,30 +70,32 @@ bool _userWorking() { return _manager && !_basePath.isEmpty() && !_userBasePath.isEmpty(); } -enum FileOptions { - UserPath = 0x01, - SafePath = 0x02, +enum class FileOption { + User = 0x01, + Safe = 0x02, }; +Q_DECLARE_FLAGS(FileOptions, FileOption); +Q_DECLARE_OPERATORS_FOR_FLAGS(FileOptions); -bool keyAlreadyUsed(QString &name, int options = UserPath | SafePath) { +bool keyAlreadyUsed(QString &name, FileOptions options = FileOption::User | FileOption::Safe) { name += '0'; if (QFileInfo(name).exists()) return true; - if (options & SafePath) { + if (options & (FileOption::Safe)) { name[name.size() - 1] = '1'; return QFileInfo(name).exists(); } return false; } -FileKey genKey(int options = UserPath | SafePath) { - if (options & UserPath) { +FileKey genKey(FileOptions options = FileOption::User | FileOption::Safe) { + if (options & FileOption::User) { if (!_userWorking()) return 0; } else { if (!_working()) return 0; } FileKey result; - QString base = (options & UserPath) ? _userBasePath : _basePath, path; + QString base = (options & FileOption::User) ? _userBasePath : _basePath, path; path.reserve(base.size() + 0x11); path += base; do { @@ -103,18 +107,18 @@ FileKey genKey(int options = UserPath | SafePath) { return result; } -void clearKey(const FileKey &key, int options = UserPath | SafePath) { - if (options & UserPath) { +void clearKey(const FileKey &key, FileOptions options = FileOption::User | FileOption::Safe) { + if (options & FileOption::User) { if (!_userWorking()) return; } else { if (!_working()) return; } - QString base = (options & UserPath) ? _userBasePath : _basePath, name; + QString base = (options & FileOption::User) ? _userBasePath : _basePath, name; name.reserve(base.size() + 0x11); name.append(base).append(toFilePart(key)).append('0'); QFile::remove(name); - if (options & SafePath) { + if (options & FileOption::Safe) { name[name.size() - 1] = '1'; QFile::remove(name); } @@ -193,14 +197,14 @@ struct EncryptedDescriptor { }; struct FileWriteDescriptor { - FileWriteDescriptor(const FileKey &key, int options = UserPath | SafePath) : dataSize(0) { + FileWriteDescriptor(const FileKey &key, FileOptions options = FileOption::User | FileOption::Safe) { init(toFilePart(key), options); } - FileWriteDescriptor(const QString &name, int options = UserPath | SafePath) : dataSize(0) { + FileWriteDescriptor(const QString &name, FileOptions options = FileOption::User | FileOption::Safe) { init(name, options); } - void init(const QString &name, int options) { - if (options & UserPath) { + void init(const QString &name, FileOptions options) { + if (options & FileOption::User) { if (!_userWorking()) return; } else { if (!_working()) return; @@ -208,9 +212,9 @@ struct FileWriteDescriptor { // detect order of read attempts and file version QString toTry[2]; - toTry[0] = ((options & UserPath) ? _userBasePath : _basePath) + name + '0'; - if (options & SafePath) { - toTry[1] = ((options & UserPath) ? _userBasePath : _basePath) + name + '1'; + toTry[0] = ((options & FileOption::User) ? _userBasePath : _basePath) + name + '0'; + if (options & FileOption::Safe) { + toTry[1] = ((options & FileOption::User) ? _userBasePath : _basePath) + name + '1'; QFileInfo toTry0(toTry[0]); QFileInfo toTry1(toTry[1]); if (toTry0.exists()) { @@ -295,15 +299,15 @@ struct FileWriteDescriptor { QString toDelete; HashMd5 md5; - int32 dataSize; + int32 dataSize = 0; ~FileWriteDescriptor() { finish(); } }; -bool readFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath) { - if (options & UserPath) { +bool readFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe) { + if (options & FileOption::User) { if (!_userWorking()) return false; } else { if (!_working()) return false; @@ -311,11 +315,11 @@ bool readFile(FileReadDescriptor &result, const QString &name, int options = Use // detect order of read attempts QString toTry[2]; - toTry[0] = ((options & UserPath) ? _userBasePath : _basePath) + name + '0'; - if (options & SafePath) { + toTry[0] = ((options & FileOption::User) ? _userBasePath : _basePath) + name + '0'; + if (options & FileOption::Safe) { QFileInfo toTry0(toTry[0]); if (toTry0.exists()) { - toTry[1] = ((options & UserPath) ? _userBasePath : _basePath) + name + '1'; + toTry[1] = ((options & FileOption::User) ? _userBasePath : _basePath) + name + '1'; QFileInfo toTry1(toTry[1]); if (toTry1.exists()) { QDateTime mod0 = toTry0.lastModified(), mod1 = toTry1.lastModified(); @@ -435,7 +439,7 @@ bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, cons return true; } -bool readEncryptedFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath, const MTP::AuthKey &key = _localKey) { +bool readEncryptedFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe, const MTP::AuthKey &key = _localKey) { if (!readFile(result, name, options)) { return false; } @@ -465,7 +469,7 @@ bool readEncryptedFile(FileReadDescriptor &result, const QString &name, int opti return true; } -bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, int options = UserPath | SafePath, const MTP::AuthKey &key = _localKey) { +bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, FileOptions options = FileOption::User | FileOption::Safe, const MTP::AuthKey &key = _localKey) { return readEncryptedFile(result, toFilePart(fkey), options, key); } @@ -552,6 +556,7 @@ enum { dbiNativeNotifications = 0x44, dbiNotificationsCount = 0x45, dbiNotificationsCorner = 0x46, + dbiTheme = 0x47, dbiEncryptedWithSalt = 333, dbiEncrypted = 444, @@ -591,6 +596,9 @@ FileKey _savedGifsKey = 0; FileKey _backgroundKey = 0; bool _backgroundWasRead = false; +bool _backgroundCanWrite = true; + +FileKey _themeKey = 0, _applyingThemeKey = 0; bool _readingUserSettings = false; FileKey _userSettingsKey = 0; @@ -1057,6 +1065,15 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) { }; } break; + case dbiTheme: { + quint64 themeKey = 0, applyingThemeKey = 0; + stream >> themeKey >> applyingThemeKey; + if (!_checkStreamStatus(stream)) return false; + + _themeKey = themeKey; + _applyingThemeKey = applyingThemeKey; + } break; + case dbiTryIPv6: { qint32 v; stream >> v; @@ -1185,7 +1202,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) { if (!_checkStreamStatus(stream)) return false; bool tile = (version < 8005 && !_backgroundKey) ? false : (v == 1); - Window::chatBackground()->setTile(tile); + Window::Theme::Background()->setTile(tile); } break; case dbiAdaptiveForWide: { @@ -1610,7 +1627,7 @@ void _writeUserSettings() { EncryptedDescriptor data(size); data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter); - data.stream << quint32(dbiTileBackground) << qint32(Window::chatBackground()->tile() ? 1 : 0); + data.stream << quint32(dbiTileBackground) << qint32(Window::Theme::Background()->tile() ? 1 : 0); data.stream << quint32(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0); data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock()); data.stream << quint32(dbiReplaceEmojis) << qint32(cReplaceEmojis() ? 1 : 0); @@ -1691,7 +1708,7 @@ void _readUserSettings() { } void _writeMtpData() { - FileWriteDescriptor mtp(toFilePart(_dataNameKey), SafePath); + FileWriteDescriptor mtp(toFilePart(_dataNameKey), FileOption::Safe); if (!_localKey.created()) { LOG(("App Error: localkey not created in _writeMtpData()")); return; @@ -1714,7 +1731,7 @@ void _writeMtpData() { void _readMtpData() { FileReadDescriptor mtp; - if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), SafePath)) { + if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), FileOption::Safe)) { if (_localKey.created()) { _readOldMtpData(); _writeMtpData(); @@ -2074,11 +2091,12 @@ void finish() { _manager->finish(); _manager->deleteLater(); _manager = 0; - delete _localLoader; - _localLoader = 0; + delete base::take(_localLoader); } } +void readTheme(); + void start() { t_assert(_manager == 0); @@ -2089,7 +2107,7 @@ void start() { if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); FileReadDescriptor settingsData; - if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), SafePath)) { + if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), FileOption::Safe)) { _readOldSettings(); _readOldUserSettings(false); // needed further in _readUserSettings _readOldMtpData(false); // needed further in _readMtpData @@ -2156,6 +2174,8 @@ void start() { _oldSettingsVersion = settingsData.version; _settingsSalt = salt; + + readTheme(); } void writeSettings() { @@ -2166,7 +2186,7 @@ void writeSettings() { if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); - FileWriteDescriptor settings(cTestMode() ? qsl("settings_test") : qsl("settings"), SafePath); + FileWriteDescriptor settings(cTestMode() ? qsl("settings_test") : qsl("settings"), FileOption::Safe); if (_settingsSalt.isEmpty() || !_settingsKey.created()) { _settingsSalt.resize(LocalEncryptSaltSize); memset_rand(_settingsSalt.data(), _settingsSalt.size()); @@ -2212,7 +2232,9 @@ void writeSettings() { auto &proxy = Global::ConnectionProxy(); size += Serialize::stringSize(proxy.host) + sizeof(qint32) + Serialize::stringSize(proxy.user) + Serialize::stringSize(proxy.password); } - + if (_themeKey || _applyingThemeKey) { + size += sizeof(quint32) + 2 * sizeof(quint64); + } size += sizeof(quint32) + sizeof(qint32) * 7; EncryptedDescriptor data(size); @@ -2242,6 +2264,9 @@ void writeSettings() { data.stream << proxy.host << qint32(proxy.port) << proxy.user << proxy.password; } data.stream << quint32(dbiTryIPv6) << qint32(Global::TryIPv6()); + if (_themeKey || _applyingThemeKey) { + data.stream << quint32(dbiTheme) << quint64(_themeKey) << quint64(_applyingThemeKey); + } TWindowPos pos(cWindowPos()); data.stream << quint32(dbiWindowPosition) << qint32(pos.x) << qint32(pos.y) << qint32(pos.w) << qint32(pos.h) << qint32(pos.moncrc) << qint32(pos.maximized); @@ -2604,7 +2629,7 @@ void writeImage(const StorageKey &location, const StorageImageSaved &image, bool qint32 size = _storageImageSize(image.data.size()); StorageMap::const_iterator i = _imagesMap.constFind(location); if (i == _imagesMap.cend()) { - i = _imagesMap.insert(location, FileDesc(genKey(UserPath), size)); + i = _imagesMap.insert(location, FileDesc(genKey(FileOption::User), size)); _storageImagesSize += size; _mapChanged = true; _writeMap(); @@ -2613,7 +2638,7 @@ void writeImage(const StorageKey &location, const StorageImageSaved &image, bool } EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + image.data.size()); data.stream << quint64(location.first) << quint64(location.second) << quint32(image.type) << image.data; - FileWriteDescriptor file(i.value().first, UserPath); + FileWriteDescriptor file(i.value().first, FileOption::User); file.writeEncrypted(data); if (i.value().second != size) { _storageImagesSize += size; @@ -2630,7 +2655,7 @@ public: } void process() { FileReadDescriptor image; - if (!readEncryptedFile(image, _key, UserPath)) { + if (!readEncryptedFile(image, _key, FileOption::User)) { return; } @@ -2701,7 +2726,7 @@ public: void clearInMap() { StorageMap::iterator j = _imagesMap.find(_location); if (j != _imagesMap.cend() && j->first == _key) { - clearKey(_key, UserPath); + clearKey(_key, FileOption::User); _storageImagesSize -= j->second; _imagesMap.erase(j); } @@ -2730,7 +2755,7 @@ void writeStickerImage(const StorageKey &location, const QByteArray &sticker, bo qint32 size = _storageStickerSize(sticker.size()); StorageMap::const_iterator i = _stickerImagesMap.constFind(location); if (i == _stickerImagesMap.cend()) { - i = _stickerImagesMap.insert(location, FileDesc(genKey(UserPath), size)); + i = _stickerImagesMap.insert(location, FileDesc(genKey(FileOption::User), size)); _storageStickersSize += size; _mapChanged = true; _writeMap(); @@ -2739,7 +2764,7 @@ void writeStickerImage(const StorageKey &location, const QByteArray &sticker, bo } EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + sticker.size()); data.stream << quint64(location.first) << quint64(location.second) << sticker; - FileWriteDescriptor file(i.value().first, UserPath); + FileWriteDescriptor file(i.value().first, FileOption::User); file.writeEncrypted(data); if (i.value().second != size) { _storageStickersSize += size; @@ -2760,7 +2785,7 @@ public: void clearInMap() { auto j = _stickerImagesMap.find(_location); if (j != _stickerImagesMap.cend() && j->first == _key) { - clearKey(j.value().first, UserPath); + clearKey(j.value().first, FileOption::User); _storageStickersSize -= j.value().second; _stickerImagesMap.erase(j); } @@ -2804,7 +2829,7 @@ void writeAudio(const StorageKey &location, const QByteArray &audio, bool overwr qint32 size = _storageAudioSize(audio.size()); StorageMap::const_iterator i = _audiosMap.constFind(location); if (i == _audiosMap.cend()) { - i = _audiosMap.insert(location, FileDesc(genKey(UserPath), size)); + i = _audiosMap.insert(location, FileDesc(genKey(FileOption::User), size)); _storageAudiosSize += size; _mapChanged = true; _writeMap(); @@ -2813,7 +2838,7 @@ void writeAudio(const StorageKey &location, const QByteArray &audio, bool overwr } EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + audio.size()); data.stream << quint64(location.first) << quint64(location.second) << audio; - FileWriteDescriptor file(i.value().first, UserPath); + FileWriteDescriptor file(i.value().first, FileOption::User); file.writeEncrypted(data); if (i.value().second != size) { _storageAudiosSize += size; @@ -2834,7 +2859,7 @@ public: void clearInMap() { auto j = _audiosMap.find(_location); if (j != _audiosMap.cend() && j->first == _key) { - clearKey(j.value().first, UserPath); + clearKey(j.value().first, FileOption::User); _storageAudiosSize -= j.value().second; _audiosMap.erase(j); } @@ -2882,7 +2907,7 @@ void writeWebFile(const QString &url, const QByteArray &content, bool overwrite) qint32 size = _storageWebFileSize(url, content.size()); WebFilesMap::const_iterator i = _webFilesMap.constFind(url); if (i == _webFilesMap.cend()) { - i = _webFilesMap.insert(url, FileDesc(genKey(UserPath), size)); + i = _webFilesMap.insert(url, FileDesc(genKey(FileOption::User), size)); _storageWebFilesSize += size; _writeLocations(); } else if (!overwrite) { @@ -2890,7 +2915,7 @@ void writeWebFile(const QString &url, const QByteArray &content, bool overwrite) } EncryptedDescriptor data(Serialize::stringSize(url) + sizeof(quint32) + sizeof(quint32) + content.size()); data.stream << url << content; - FileWriteDescriptor file(i.value().first, UserPath); + FileWriteDescriptor file(i.value().first, FileOption::User); file.writeEncrypted(data); if (i.value().second != size) { _storageWebFilesSize += size; @@ -2909,7 +2934,7 @@ public: } void process() { FileReadDescriptor image; - if (!readEncryptedFile(image, _key, UserPath)) { + if (!readEncryptedFile(image, _key, FileOption::User)) { return; } @@ -2925,7 +2950,7 @@ public: } else { WebFilesMap::iterator j = _webFilesMap.find(_url); if (j != _webFilesMap.cend() && j->first == _key) { - clearKey(j.value().first, UserPath); + clearKey(j.value().first, FileOption::User); _storageWebFilesSize -= j.value().second; _webFilesMap.erase(j); } @@ -3592,11 +3617,11 @@ void readSavedGifs() { } void writeBackground(int32 id, const QImage &img) { - if (!_working()) return; + if (!_working() || !_backgroundCanWrite) return; - QByteArray png; + QByteArray bmp; if (!img.isNull()) { - QBuffer buf(&png); + QBuffer buf(&bmp); if (!img.save(&buf, "BMP")) return; } if (!_backgroundKey) { @@ -3604,17 +3629,19 @@ void writeBackground(int32 id, const QImage &img) { _mapChanged = true; _writeMap(WriteMapFast); } - quint32 size = sizeof(qint32) + sizeof(quint32) + (png.isEmpty() ? 0 : (sizeof(quint32) + png.size())); + quint32 size = sizeof(qint32) + sizeof(quint32) + (bmp.isEmpty() ? 0 : (sizeof(quint32) + bmp.size())); EncryptedDescriptor data(size); data.stream << qint32(id); - if (!png.isEmpty()) data.stream << png; + if (!bmp.isEmpty()) data.stream << bmp; FileWriteDescriptor file(_backgroundKey); file.writeEncrypted(data); } bool readBackground() { - if (_backgroundWasRead) return false; + if (_backgroundWasRead || Global::ApplyingTheme()) { + return false; + } _backgroundWasRead = true; FileReadDescriptor bg; @@ -3628,30 +3655,116 @@ bool readBackground() { QByteArray pngData; qint32 id; bg.stream >> id; - if (!id || id == DefaultChatBackground) { + if (id == Window::Theme::kOldBackground || id == Window::Theme::kDefaultBackground) { + _backgroundCanWrite = false; if (bg.version < 8005) { - App::initBackground(DefaultChatBackground, QImage(), true); - if (!id) Window::chatBackground()->setTile(!DefaultChatBackground); + Window::Theme::Background()->setImage(Window::Theme::kDefaultBackground); + if (id == Window::Theme::kOldBackground) { + Window::Theme::Background()->setTile(false); + } } else { - App::initBackground(id, QImage(), true); + Window::Theme::Background()->setImage(id); } + _backgroundCanWrite = true; return true; } bg.stream >> pngData; - QImage img; + QImage image; QBuffer buf(&pngData); QImageReader reader(&buf); #ifndef OS_MAC_OLD reader.setAutoTransform(true); #endif // OS_MAC_OLD - if (reader.read(&img)) { - App::initBackground(id, img, true); + if (reader.read(&image)) { + _backgroundCanWrite = false; + Window::Theme::Background()->setImage(id, std_::move(image)); + _backgroundCanWrite = true; return true; } return false; } +bool readThemeUsingKey(FileKey key) { + FileReadDescriptor theme; + if (!readEncryptedFile(theme, key, FileOption::Safe, _settingsKey)) { + return false; + } + + QByteArray themeContent; + QString pathRelative, pathAbsolute; + Window::Theme::Cached cache; + theme.stream >> themeContent; + theme.stream >> pathRelative >> pathAbsolute; + if (theme.stream.status() != QDataStream::Ok) { + return false; + } + QFile file(pathRelative); + if (pathRelative.isEmpty() || !file.exists()) { + file.setFileName(pathAbsolute); + } + + auto changed = false; + if (!file.fileName().isEmpty() && file.exists() && file.open(QIODevice::ReadOnly)) { + if (file.size() > kThemeFileSizeLimit) { + LOG(("Error: theme file too large: %1 (should be less than 5 MB, got %2)").arg(file.fileName()).arg(file.size())); + return false; + } + auto fileContent = file.readAll(); + file.close(); + if (themeContent != fileContent) { + themeContent = fileContent; + changed = true; + } + } + if (!changed) { + quint32 backgroundIsTiled = 0; + theme.stream >> cache.paletteChecksum >> cache.contentChecksum >> cache.colors >> cache.background >> backgroundIsTiled; + cache.tiled = (backgroundIsTiled == 1); + if (theme.stream.status() != QDataStream::Ok) { + return false; + } + } + return Window::Theme::Load(pathRelative, pathAbsolute, themeContent, cache); +} + +void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache) { + auto key = _applyingThemeKey ? _applyingThemeKey : _themeKey; + if (!key) { + key = _themeKey = genKey(); + writeSettings(); + } + + auto backgroundTiled = static_cast(cache.tiled ? 1 : 0); + quint32 size = Serialize::bytearraySize(content); + size += Serialize::stringSize(pathRelative) + Serialize::stringSize(pathAbsolute); + size += sizeof(int32) * 2 + Serialize::bytearraySize(cache.colors) + Serialize::bytearraySize(cache.background) + sizeof(quint32); + EncryptedDescriptor data(size); + data.stream << content; + data.stream << pathRelative << pathAbsolute; + data.stream << cache.paletteChecksum << cache.contentChecksum << cache.colors << cache.background << backgroundTiled; + + FileWriteDescriptor file(key, FileOption::Safe); + file.writeEncrypted(data, _settingsKey); +} + +void readTheme() { + if (_applyingThemeKey) { + if (readThemeUsingKey(_applyingThemeKey)) { + Global::SetApplyingTheme(true); + } else { + clearKey(_applyingThemeKey); + _applyingThemeKey = 0; + writeSettings(); + readTheme(); + } + } else if (_themeKey && !readThemeUsingKey(_themeKey)) { + clearKey(_themeKey); + _themeKey = 0; + writeSettings(); + } +} + uint32 _peerSize(PeerData *peer) { uint32 result = sizeof(quint64) + sizeof(quint64) + Serialize::storageImageLocationSize(); if (peer->isUser()) { @@ -4330,16 +4443,16 @@ void ClearManager::onStart() { break; case ClearManagerStorage: for (StorageMap::const_iterator i = images.cbegin(), e = images.cend(); i != e; ++i) { - clearKey(i.value().first, UserPath); + clearKey(i.value().first, FileOption::User); } for (StorageMap::const_iterator i = stickers.cbegin(), e = stickers.cend(); i != e; ++i) { - clearKey(i.value().first, UserPath); + clearKey(i.value().first, FileOption::User); } for (StorageMap::const_iterator i = audios.cbegin(), e = audios.cend(); i != e; ++i) { - clearKey(i.value().first, UserPath); + clearKey(i.value().first, FileOption::User); } for (WebFilesMap::const_iterator i = webFiles.cbegin(), e = webFiles.cend(); i != e; ++i) { - clearKey(i.value().first, UserPath); + clearKey(i.value().first, FileOption::User); } result = true; break; diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index d7b7e718d..ddcddf5a3 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -22,6 +22,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/basic_types.h" +namespace Window { +namespace Theme { +struct Cached; +} // namespace Theme +} // namespace Window + namespace Local { void start(); @@ -145,6 +151,8 @@ int32 countSavedGifsHash(); void writeBackground(int32 id, const QImage &img); bool readBackground(); +void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache); + void writeRecentHashtagsAndBots(); void readRecentHashtagsAndBots(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 0e3aa208f..06427faef 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -57,7 +57,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "media/player/media_player_instance.h" #include "core/qthelp_regex.h" #include "core/qthelp_url.h" -#include "window/chat_background.h" +#include "window/window_theme.h" #include "window/player_wrap_widget.h" StackItemSection::StackItemSection(std_::unique_ptr &&memento) : StackItem(nullptr) @@ -115,8 +115,8 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window) _webPageOrGameUpdater.setSingleShot(true); connect(&_webPageOrGameUpdater, SIGNAL(timeout()), this, SLOT(webPagesOrGamesUpdate())); - subscribe(Window::chatBackground(), [this](const Window::ChatBackgroundUpdate &update) { - using Update = Window::ChatBackgroundUpdate; + using Update = Window::Theme::BackgroundUpdate; + subscribe(Window::Theme::Background(), [this](const Update &update) { if (update.type == Update::Type::New || update.type == Update::Type::Changed) { clearCachedBackground(); } @@ -1068,8 +1068,8 @@ bool MainWidget::sendMessageFail(const RPCError &error) { } void MainWidget::onCacheBackground() { - auto &bg = Window::chatBackground()->image(); - if (Window::chatBackground()->tile()) { + auto &bg = Window::Theme::Background()->image(); + if (Window::Theme::Background()->tile()) { QImage result(_willCacheFor.width() * cIntRetinaFactor(), _willCacheFor.height() * cIntRetinaFactor(), QImage::Format_RGB32); result.setDevicePixelRatio(cRetinaFactor()); { @@ -1879,7 +1879,7 @@ QPixmap MainWidget::cachedBackground(const QRect &forRect, int &x, int &y) { } void MainWidget::backgroundParams(const QRect &forRect, QRect &to, QRect &from) const { - auto bg = Window::chatBackground()->image().size(); + auto bg = Window::Theme::Background()->image().size(); if (uint64(bg.width()) * forRect.height() > uint64(bg.height()) * forRect.width()) { float64 pxsize = forRect.height() / float64(bg.height()); int takewidth = qCeil(forRect.width() / pxsize); @@ -1929,11 +1929,11 @@ void MainWidget::checkChatBackground() { if (_background) { if (_background->full->loaded()) { if (_background->full->isNull()) { - App::initBackground(); - } else if (_background->id == 0 || _background->id == DefaultChatBackground) { - App::initBackground(_background->id); + Window::Theme::Background()->setImage(Window::Theme::kDefaultBackground); + } else if (_background->id == Window::Theme::kOldBackground || _background->id == Window::Theme::kDefaultBackground) { + Window::Theme::Background()->setImage(_background->id); } else { - App::initBackground(_background->id, _background->full->pix().toImage()); + Window::Theme::Background()->setImage(_background->id, _background->full->pix().toImage()); } _background = nullptr; QTimer::singleShot(0, this, SLOT(update())); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 2472c2b32..05b2b43c3 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" #include "ui/widgets/popup_menu.h" -#include "zip.h" +#include "core/zlib_help.h" #include "lang.h" #include "shortcuts.h" #include "application.h" @@ -1396,7 +1396,7 @@ QImage MainWindow::iconLarge() const { return iconbig256; } -void MainWindow::placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) { +void MainWindow::placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) { QPainter p(&img); QString cnt = (count < 100) ? QString("%1").arg(count) : QString("..%1").arg(count % 10, 1, 10, QChar('0')); @@ -1434,7 +1434,7 @@ void MainWindow::placeSmallCounter(QImage &img, int size, int count, style::colo } -QImage MainWindow::iconWithCounter(int size, int count, style::color bg, bool smallIcon) { +QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) { bool layer = false; if (size < 0) { size = -size; @@ -1449,7 +1449,7 @@ QImage MainWindow::iconWithCounter(int size, int count, style::color bg, bool sm result.fill(st::transparent->c); { QPainter p(&result); - p.setBrush(bg->b); + p.setBrush(bg); p.setPen(Qt::NoPen); p.setRenderHint(QPainter::Antialiasing); int32 fontSize; @@ -1478,9 +1478,9 @@ QImage MainWindow::iconWithCounter(int size, int count, style::color bg, bool sm r = (cntSize < 2) ? 16 : ((cntSize < 3) ? 14 : 8); } p.drawRoundedRect(QRect(size - w - d * 2, size - f->height, w + d * 2, f->height), r, r); - p.setFont(f->f); + p.setFont(f); - p.setPen(st::counterColor->p); + p.setPen(st::counterFg); p.drawText(size - w - d, size - f->height + f->ascent, cnt); } @@ -1493,7 +1493,7 @@ QImage MainWindow::iconWithCounter(int size, int count, style::color bg, bool sm if (!count) return img; if (smallIcon) { - placeSmallCounter(img, size, count, bg, QPoint(), st::counterColor); + placeSmallCounter(img, size, count, bg, QPoint(), st::counterFg); } else { QPainter p(&img); p.drawPixmap(size / 2, size / 2, App::pixmapFromImageInPlace(iconWithCounter(-size / 2, count, bg, false))); @@ -1997,91 +1997,6 @@ void LastCrashedWindow::onSendReport() { updateControls(); } -namespace { - struct zByteArray { - zByteArray() : pos(0), err(0) { - } - uLong pos; - int err; - QByteArray data; - }; - - voidpf zByteArrayOpenFile(voidpf opaque, const char* filename, int mode) { - zByteArray *ba = (zByteArray*)opaque; - if (mode & ZLIB_FILEFUNC_MODE_WRITE) { - if (mode & ZLIB_FILEFUNC_MODE_CREATE) { - ba->data.clear(); - } - ba->pos = ba->data.size(); - ba->data.reserve(2 * 1024 * 1024); - } else if (mode & ZLIB_FILEFUNC_MODE_READ) { - ba->pos = 0; - } - ba->err = 0; - return opaque; - } - - uLong zByteArrayReadFile(voidpf opaque, voidpf stream, void* buf, uLong size) { - zByteArray *ba = (zByteArray*)opaque; - uLong toRead = 0; - if (!ba->err) { - if (ba->data.size() > int(ba->pos)) { - toRead = qMin(size, uLong(ba->data.size() - ba->pos)); - memcpy(buf, ba->data.constData() + ba->pos, toRead); - ba->pos += toRead; - } - if (toRead < size) { - ba->err = -1; - } - } - return toRead; - } - - uLong zByteArrayWriteFile(voidpf opaque, voidpf stream, const void* buf, uLong size) { - zByteArray *ba = (zByteArray*)opaque; - if (ba->data.size() < int(ba->pos + size)) { - ba->data.resize(ba->pos + size); - } - memcpy(ba->data.data() + ba->pos, buf, size); - ba->pos += size; - return size; - } - - int zByteArrayCloseFile(voidpf opaque, voidpf stream) { - zByteArray *ba = (zByteArray*)opaque; - int result = ba->err; - ba->pos = 0; - ba->err = 0; - return result; - } - - int zByteArrayErrorFile(voidpf opaque, voidpf stream) { - zByteArray *ba = (zByteArray*)opaque; - return ba->err; - } - - long zByteArrayTellFile(voidpf opaque, voidpf stream) { - zByteArray *ba = (zByteArray*)opaque; - return ba->pos; - } - - long zByteArraySeekFile(voidpf opaque, voidpf stream, uLong offset, int origin) { - zByteArray *ba = (zByteArray*)opaque; - if (!ba->err) { - switch (origin) { - case ZLIB_FILEFUNC_SEEK_SET: ba->pos = offset; break; - case ZLIB_FILEFUNC_SEEK_CUR: ba->pos += offset; break; - case ZLIB_FILEFUNC_SEEK_END: ba->pos = ba->data.size() + offset; break; - } - if (int(ba->pos) > ba->data.size()) { - ba->err = -1; - } - } - return ba->err; - } - -} - QString LastCrashedWindow::minidumpFileName() { QFileInfo dmpFile(_minidumpFull); if (dmpFile.exists() && dmpFile.size() > 0 && dmpFile.size() < 20 * 1024 * 1024 && @@ -2138,45 +2053,24 @@ void LastCrashedWindow::onCheckingFinished() { file.close(); QString zipName = QString(dmpName).replace(qstr(".dmp"), qstr(".zip")); - zByteArray minidumpZip; - bool failed = false; - zlib_filefunc_def zfuncs; - zfuncs.opaque = &minidumpZip; - zfuncs.zopen_file = zByteArrayOpenFile; - zfuncs.zerror_file = zByteArrayErrorFile; - zfuncs.zread_file = zByteArrayReadFile; - zfuncs.zwrite_file = zByteArrayWriteFile; - zfuncs.zclose_file = zByteArrayCloseFile; - zfuncs.zseek_file = zByteArraySeekFile; - zfuncs.ztell_file = zByteArrayTellFile; + zlib::FileToWrite minidumpZip; - if (zipFile zf = zipOpen2(0, APPEND_STATUS_CREATE, 0, &zfuncs)) { - zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }; - QByteArray dmpNameUtf = dmpName.toUtf8(); - if (zipOpenNewFileInZip(zf, dmpNameUtf.constData(), &zfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION) != ZIP_OK) { - failed = true; - } else if (zipWriteInFileInZip(zf, minidump.constData(), minidump.size()) != 0) { - failed = true; - } else if (zipCloseFileInZip(zf) != 0) { - failed = true; - } - if (zipClose(zf, NULL) != 0) { - failed = true; - } - if (failed) { - minidumpZip.err = -1; - } - } + zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }; + QByteArray dmpNameUtf = dmpName.toUtf8(); + minidumpZip.openNewFile(dmpNameUtf.constData(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + minidumpZip.writeInFile(minidump.constData(), minidump.size()); + minidumpZip.closeFile(); + minidumpZip.close(); - if (!minidumpZip.err) { + if (minidumpZip.error() == ZIP_OK) { QHttpPart dumpPart; dumpPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); dumpPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(qsl("form-data; name=\"dump\"; filename=\"%1\"").arg(zipName))); - dumpPart.setBody(minidumpZip.data); + dumpPart.setBody(minidumpZip.result()); multipart->append(dumpPart); - _minidump.setText(qsl("+ %1 (%2 KB)").arg(zipName).arg(minidumpZip.data.size() / 1024)); + _minidump.setText(qsl("+ %1 (%2 KB)").arg(zipName).arg(minidumpZip.result().size() / 1024)); } } } diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 10031cc83..97b965764 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -158,7 +158,7 @@ public: void updateUnreadCounter(); - QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon); + QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon); bool contentOverlapped(const QRect &globalRect); bool contentOverlapped(QWidget *w, QPaintEvent *e) { @@ -232,7 +232,7 @@ private: QPixmap grabInner(); - void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color); + void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color); QImage icon16, icon32, icon64, iconbig16, iconbig32, iconbig64; QWidget *centralwidget; diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index e8d8f4ce7..4f790c22d 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -468,7 +468,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const if (radial) { QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); - style::color bg(selected ? st::msgInBgSelected : st::msgInBg); + auto &bg = selected ? st::msgInBgSelected : st::msgInBg; _radial->draw(p, rinner, st::msgFileRadialLine, bg); } @@ -679,7 +679,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con if (radial) { auto rinner = inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)); - style::color bg(selected ? st::msgInBgSelected : st::msgInBg); + auto &bg = selected ? st::msgInBgSelected : st::msgInBg; _radial->draw(p, rinner, st::msgFileRadialLine, bg); } diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 4bf11a7b8..274916235 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -30,7 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/widgets/tooltip.h" #include "ui/buttons/icon_button.h" #include "window/top_bar_widget.h" -#include "window/chat_background.h" +#include "window/window_theme.h" #include "lang.h" #include "mainwindow.h" #include "mainwidget.h" diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 7f251d44d..9dede149f 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -103,7 +103,7 @@ QImage _trayIconImageGen() { } else if (_trayIconSize >= 32) { layerSize = -20; } - QImage layer = App::wnd()->iconWithCounter(layerSize, counter, (muted ? st::counterMuteBG : st::counterBG), false); + QImage layer = App::wnd()->iconWithCounter(layerSize, counter, (muted ? st::counterMuteBg : st::counterBg), false); p.drawImage(_trayIconImage.width() - layer.width() - 1, _trayIconImage.height() - layer.height() - 1, layer); } } @@ -355,7 +355,7 @@ void MainWindow::psUpdateCounter() { int32 counter = App::histories().unreadBadge(); bool muted = App::histories().unreadOnlyMuted(); - style::color bg = muted ? st::counterMuteBG : st::counterBG; + auto &bg = (muted ? st::counterMuteBg : st::counterBg); icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, true))); icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, true))); } diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index f5b2aa2e6..85fb55633 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -60,7 +60,7 @@ public: bool psHasNativeNotifications(); - virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0; static void LibsLoaded(); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index ddbd582a7..007cb07aa 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -73,7 +73,7 @@ public: return !(QSysInfo::macVersion() < QSysInfo::MV_10_8); } - virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0; void closeWithoutDestroy() override; @@ -114,7 +114,7 @@ protected: void psTrayMenuUpdated(); void psSetupTrayIcon(); - virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0; + virtual void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) = 0; QTimer psUpdatedPositionTimer; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index e22b0b609..965a5e510 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -128,7 +128,7 @@ void MainWindow::psUpdateWorkmode() { setWindowIcon(wndIcon); } -void _placeCounter(QImage &img, int size, int count, style::color bg, style::color color) { +void _placeCounter(QImage &img, int size, int count, const style::color &bg, const style::color &color) { if (!count) return; QPainter p(&img); @@ -176,13 +176,13 @@ void MainWindow::psUpdateCounter() { bool muted = App::histories().unreadOnlyMuted(); bool dm = objc_darkMode(); - style::color bg = muted ? st::counterMuteBG : st::counterBG; + auto &bg = (muted ? st::counterMuteBg : st::counterBg); QIcon icon; QImage img(psTrayIcon(dm)), imgsel(psTrayIcon(true)); img.detach(); imgsel.detach(); int32 size = cRetina() ? 44 : 22; - _placeCounter(img, size, counter, bg, (dm && muted) ? st::counterMacInvColor : st::counterColor); + _placeCounter(img, size, counter, bg, (dm && muted) ? st::counterMacInvFg : st::counterFg); _placeCounter(imgsel, size, counter, st::white, st::counterMacInvColor); icon.addPixmap(App::pixmapFromImageInPlace(std_::move(img))); icon.addPixmap(App::pixmapFromImageInPlace(std_::move(imgsel)), QIcon::Selected); diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index dfb2b1198..9d02f428f 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -718,7 +718,7 @@ void MainWindow::psUpdateCounter() { auto iconSizeSmall = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); auto iconSizeBig = QSize(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); - style::color bg = muted ? st::counterMuteBG : st::counterBG; + auto bg = (muted ? st::counterMuteBg : st::counterBg); auto iconSmallPixmap16 = App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, true)); auto iconSmallPixmap32 = App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, true)); QIcon iconSmall, iconBig; diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index aca3eda28..decc8bfa2 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -67,7 +67,7 @@ public: bool psHasNativeNotifications(); - virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0; static UINT TaskbarCreatedMsgId() { return _taskbarCreatedMsgId; diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index b9f140959..0c473abf5 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -65,14 +65,14 @@ profileSecondaryButton: RoundButton(profilePrimaryButton) { textBg: #ffffff; textBgOver: #f2f7fa; } -profileAddMemberIcon: icon {{ "profile_add_member", windowActiveBg, point(20px, 10px) }}; +profileAddMemberIcon: icon {{ "profile_add_member", windowActiveFill, point(20px, 10px) }}; profileAddMemberButton: RoundButton(profileSecondaryButton) { width: 62px; icon: profileAddMemberIcon; } profileDropAreaBg: profileBg; -profileDropAreaFg: windowActiveBg; +profileDropAreaFg: windowActiveFill; profileDropAreaPadding: margins(25px, 3px, 25px, 20px); profileDropAreaTitleFont: font(24px); profileDropAreaTitleTop: 30px; diff --git a/Telegram/SourceFiles/pspecific_winrt.h b/Telegram/SourceFiles/pspecific_winrt.h index 30ab755d5..e95594237 100644 --- a/Telegram/SourceFiles/pspecific_winrt.h +++ b/Telegram/SourceFiles/pspecific_winrt.h @@ -77,7 +77,7 @@ public: bool psHasNativeNotifications(); void psCleanNotifyPhotosIn(int32 dt); - virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0; ~PsMainWindow(); diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index 35e910c24..77eed91a6 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/buttons/checkbox.h" #include "localstorage.h" #include "mainwindow.h" -#include "window/chat_background.h" +#include "window/window_theme.h" namespace Settings { @@ -37,8 +37,6 @@ BackgroundRow::BackgroundRow(QWidget *parent) : TWidget(parent) , _chooseFromGallery(this, lang(lng_settings_bg_from_gallery), st::defaultBoxLinkButton) , _chooseFromFile(this, lang(lng_settings_bg_from_file), st::defaultBoxLinkButton) , _radial(animation(this, &BackgroundRow::step_radial)) { - Window::chatBackground()->initIfEmpty(); - updateImage(); connect(_chooseFromGallery, SIGNAL(clicked()), this, SIGNAL(chooseFromGallery())); @@ -145,7 +143,7 @@ void BackgroundRow::updateImage() { back.setDevicePixelRatio(cRetinaFactor()); { QPainter p(&back); - auto &pix = Window::chatBackground()->image(); + auto &pix = Window::Theme::Background()->image(); int sx = (pix.width() > pix.height()) ? ((pix.width() - pix.height()) / 2) : 0; int sy = (pix.height() > pix.width()) ? ((pix.height() - pix.width()) / 2) : 0; int s = (pix.width() > pix.height()) ? pix.height() : pix.width(); @@ -169,8 +167,8 @@ BackgroundWidget::BackgroundWidget(QWidget *parent, UserData *self) : BlockWidge subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) { notifyFileQueryUpdated(update); }); - subscribe(Window::chatBackground(), [this](const Window::ChatBackgroundUpdate &update) { - using Update = Window::ChatBackgroundUpdate; + using Update = Window::Theme::BackgroundUpdate; + subscribe(Window::Theme::Background(), [this](const Update &update) { if (update.type == Update::Type::New) { _background->updateImage(); } else if (update.type == Update::Type::Start) { @@ -194,7 +192,7 @@ void BackgroundWidget::createControls() { connect(_background, SIGNAL(chooseFromGallery()), this, SLOT(onChooseFromGallery())); connect(_background, SIGNAL(chooseFromFile()), this, SLOT(onChooseFromFile())); - addChildRow(_tile, margin, lang(lng_settings_bg_tile), SLOT(onTile()), Window::chatBackground()->tile()); + addChildRow(_tile, margin, lang(lng_settings_bg_tile), SLOT(onTile()), Window::Theme::Background()->tile()); addChildRow(_adaptive, margin, slidedPadding, lang(lng_settings_adaptive_wide), SLOT(onAdaptive()), Global::AdaptiveForWide()); if (Global::AdaptiveLayout() != Adaptive::WideLayout) { _adaptive->hideFast(); @@ -211,10 +209,12 @@ void BackgroundWidget::needBackgroundUpdate(bool tile) { } void BackgroundWidget::onChooseFromFile() { - QStringList imgExtensions(cImgExtensions()); - QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter()); + auto imgExtensions = cImgExtensions(); + auto filters = QStringList(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(")")); + filters.push_back(qsl("Theme files (*.tdesktop-theme)")); + filters.push_back(filedialogAllFilesFilter()); - _chooseFromFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter); + _chooseFromFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filters.join(qsl(";;"))); } void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) { @@ -227,11 +227,28 @@ void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &upd return; } + auto filePath = update.filePaths.front(); + if (filePath.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)) { + QByteArray themeContent; + Window::Theme::Instance theme; + if (Window::Theme::LoadFromFile(filePath, &theme, &themeContent)) { + Local::writeTheme(QDir().relativeFilePath(filePath), QFileInfo(filePath).absoluteFilePath(), themeContent, theme.cached); + if (Window::Theme::Background()->tile() != theme.cached.tiled) { + Window::Theme::Background()->setTile(theme.cached.tiled); + } + if (!theme.cached.background.isEmpty()) { + Local::writeBackground(Window::Theme::kThemeBackground, QImage()); + } + App::restart(); + } + return; + } + QImage img; if (!update.remoteContent.isEmpty()) { img = App::readImage(update.remoteContent); } else { - img = App::readImage(update.filePaths.front()); + img = App::readImage(filePath); } if (img.isNull() || img.width() <= 0 || img.height() <= 0) return; @@ -242,13 +259,13 @@ void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &upd img = img.copy(0, (img.height() - 4096 * img.width()) / 2, img.width(), 4096 * img.width()); } - App::initBackground(-1, img); + Window::Theme::Background()->setImage(Window::Theme::kCustomBackground, std_::move(img)); _tile->setChecked(false); _background->updateImage(); } void BackgroundWidget::onTile() { - Window::chatBackground()->setTile(_tile->checked()); + Window::Theme::Background()->setTile(_tile->checked()); } void BackgroundWidget::onAdaptive() { diff --git a/Telegram/SourceFiles/settings/settings_general_widget.cpp b/Telegram/SourceFiles/settings/settings_general_widget.cpp index 454a25232..2b38960a7 100644 --- a/Telegram/SourceFiles/settings/settings_general_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_general_widget.cpp @@ -231,7 +231,7 @@ void GeneralWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update } _testLanguage = QFileInfo(update.filePaths.front()).absoluteFilePath(); - LangLoaderPlain loader(_testLanguage, LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok)); + LangLoaderPlain loader(_testLanguage, langLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok)); if (loader.errors().isEmpty()) { LangLoaderResult result = loader.found(); QString text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)), @@ -263,17 +263,8 @@ void GeneralWidget::onSaveTestLanguage() { void GeneralWidget::onRestart() { #ifndef TDESKTOP_DISABLE_AUTOUPDATE checkReadyUpdate(); - if (_updateRow->entity()->isUpdateReady()) { - cSetRestartingUpdate(true); - } else { - cSetRestarting(true); - cSetRestartingToSettings(true); - } -#else // !TDESKTOP_DISABLE_AUTOUPDATE - cSetRestarting(true); - cSetRestartingToSettings(true); -#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE - App::quit(); +#endif // !TDESKTOP_DISABLE_AUTOUPDATE + App::restart(); } #ifndef TDESKTOP_DISABLE_AUTOUPDATE diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp index c4ab37279..c85be6440 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp @@ -117,18 +117,7 @@ void ScaleWidget::scaleChanged() { } void ScaleWidget::onRestartNow() { -#ifndef TDESKTOP_DISABLE_AUTOUPDATE - bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady); -#else // !TDESKTOP_DISABLE_AUTOUPDATE - bool updateReady = false; -#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE - if (updateReady) { - cSetRestartingUpdate(true); - } else { - cSetRestarting(true); - cSetRestartingToSettings(true); - } - App::quit(); + App::restart(); } } // namespace Settings diff --git a/Telegram/SourceFiles/shortcuts.cpp b/Telegram/SourceFiles/shortcuts.cpp index 336f9cd63..643f0634d 100644 --- a/Telegram/SourceFiles/shortcuts.cpp +++ b/Telegram/SourceFiles/shortcuts.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "media/player/media_player_instance.h" #include "pspecific.h" +#include "core/parse_helper.h" namespace ShortcutCommands { @@ -156,76 +157,6 @@ inline bool qMapLessThanKey(const ShortcutCommands::Handler &a, const ShortcutCo namespace Shortcuts { -// inspired by https://github.com/sindresorhus/strip-json-comments -QByteArray _stripJsonComments(const QByteArray &json) { - enum InsideComment { - InsideCommentNone, - InsideCommentSingleLine, - InsideCommentMultiLine, - }; - InsideComment insideComment = InsideCommentNone; - bool insideString = false; - - QByteArray result; - - const char *b = json.cbegin(), *e = json.cend(), *offset = b; - for (const char *ch = offset; ch != e; ++ch) { - char currentChar = *ch; - char nextChar = (ch + 1 == e) ? 0 : *(ch + 1); - - if (insideComment == InsideCommentNone && currentChar == '"') { - bool escaped = ((ch > b) && *(ch - 1) == '\\') && ((ch - 1 < b) || *(ch - 2) != '\\'); - if (!escaped) { - insideString = !insideString; - } - } - - if (insideString) { - continue; - } - - if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '/') { - if (ch > offset) { - if (result.isEmpty()) result.reserve(json.size() - 2); - result.append(offset, ch - offset); - offset = ch; - } - insideComment = InsideCommentSingleLine; - ++ch; - } else if (insideComment == InsideCommentSingleLine && currentChar == '\r' && nextChar == '\n') { - if (ch > offset) { - offset = ch; - } - ++ch; - insideComment = InsideCommentNone; - } else if (insideComment == InsideCommentSingleLine && currentChar == '\n') { - if (ch > offset) { - offset = ch; - } - insideComment = InsideCommentNone; - } else if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '*') { - if (ch > offset) { - if (result.isEmpty()) result.reserve(json.size() - 2); - result.append(offset, ch - offset); - offset = ch; - } - insideComment = InsideCommentMultiLine; - ++ch; - } else if (insideComment == InsideCommentMultiLine && currentChar == '*' && nextChar == '/') { - if (ch > offset) { - offset = ch; - } - ++ch; - insideComment = InsideCommentNone; - } - } - - if (insideComment == InsideCommentNone && e > offset && !result.isEmpty()) { - result.append(offset, e - offset); - } - return result.isEmpty() ? json : result; -} - struct DataStruct; DataStruct *DataPtr = nullptr; @@ -406,7 +337,7 @@ void start() { QFile defaultFile(cWorkingDir() + qsl("tdata/shortcuts-default.json")); if (defaultFile.open(QIODevice::ReadOnly)) { QJsonParseError error = { 0, QJsonParseError::NoError }; - QJsonDocument doc = QJsonDocument::fromJson(_stripJsonComments(defaultFile.readAll()), &error); + QJsonDocument doc = QJsonDocument::fromJson(base::parse::stripComments(defaultFile.readAll()), &error); defaultFile.close(); if (error.error == QJsonParseError::NoError && doc.isArray()) { @@ -457,7 +388,7 @@ void start() { if (customFile.exists()) { if (customFile.open(QIODevice::ReadOnly)) { QJsonParseError error = { 0, QJsonParseError::NoError }; - QJsonDocument doc = QJsonDocument::fromJson(_stripJsonComments(customFile.readAll()), &error); + QJsonDocument doc = QJsonDocument::fromJson(base::parse::stripComments(customFile.readAll()), &error); customFile.close(); if (error.error != QJsonParseError::NoError) { diff --git a/Telegram/SourceFiles/shortcuts.h b/Telegram/SourceFiles/shortcuts.h index bfb665e90..35ca61b92 100644 --- a/Telegram/SourceFiles/shortcuts.h +++ b/Telegram/SourceFiles/shortcuts.h @@ -22,18 +22,18 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Shortcuts { - void start(); - const QStringList &errors(); +void start(); +const QStringList &errors(); - bool launch(int shortcutId); - bool launch(const QString &command); +bool launch(int shortcutId); +bool launch(const QString &command); - // Media shortcuts are not enabled by default, because other - // applications also use them. They are enabled only when - // the in-app player is active and disabled back after. - void enableMediaShortcuts(); - void disableMediaShortcuts(); +// Media shortcuts are not enabled by default, because other +// applications also use them. They are enabled only when +// the in-app player is active and disabled back after. +void enableMediaShortcuts(); +void disableMediaShortcuts(); - void finish(); +void finish(); } diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index 5a62d1128..624599163 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -64,6 +64,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mtproto/facade.h" #include "ui/style/style_core.h" +#include "styles/palette.h" #include "styles/style_basic_types.h" #include "styles/style_basic.h" diff --git a/Telegram/SourceFiles/sysbuttons.cpp b/Telegram/SourceFiles/sysbuttons.cpp index 8821619ef..a61564afe 100644 --- a/Telegram/SourceFiles/sysbuttons.cpp +++ b/Telegram/SourceFiles/sysbuttons.cpp @@ -135,15 +135,8 @@ UpdateBtn::UpdateBtn(QWidget *parent) : SysBtn(parent, st::sysUpd, lang(lng_menu setClickedCallback([]() { #ifndef TDESKTOP_DISABLE_AUTOUPDATE checkReadyUpdate(); - if (Sandbox::updatingState() == Application::UpdatingReady) { - cSetRestartingUpdate(true); - } else #endif // !TDESKTOP_DISABLE_AUTOUPDATE - { - cSetRestarting(true); - cSetRestartingToSettings(false); - } - App::quit(); + App::restart(); }); } diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index 9722ac9d4..e6da02fbe 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -325,7 +325,7 @@ void TitleWidget::updateCounter() { int32 counter = App::histories().unreadBadge(); bool muted = App::histories().unreadOnlyMuted(); - style::color bg = muted ? st::counterMuteBG : st::counterBG; + auto &bg = (muted ? st::counterMuteBg : st::counterBg); if (counter > 0) { int32 size = cRetina() ? -32 : -16; diff --git a/Telegram/SourceFiles/ui/animation.cpp b/Telegram/SourceFiles/ui/animation.cpp index 88d79b43f..d404f7cf1 100644 --- a/Telegram/SourceFiles/ui/animation.cpp +++ b/Telegram/SourceFiles/ui/animation.cpp @@ -97,7 +97,6 @@ void startManager() { stopManager(); _manager = new AnimationManager(); - } void stopManager() { diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index e9a72d257..2514a9e7f 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -25,11 +25,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/flatbutton.h" #include "ui/effects/rect_shadow.h" #include "boxes/abstractbox.h" +#include "styles/style_intro.h" QString findValidCode(QString fullCode); -class CountrySelect; - namespace Ui { class MultiSelect; } // namespace Ui @@ -63,8 +62,6 @@ private: bool _active; QString _text; - CountrySelect *_select; - }; namespace internal { diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 15573f26a..434d6f1e0 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -82,8 +82,8 @@ void FlatButton::step_appearance(float64 ms, bool timer) { } void FlatButton::onStateChange(int oldState, ButtonStateChangeSource source) { - style::color bgColorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downBgColor : _st.overBgColor) : _st.bgColor; - style::color colorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downColor : _st.overColor) : _st.color; + auto &bgColorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downBgColor : _st.overBgColor) : _st.bgColor; + auto &colorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downColor : _st.overColor) : _st.color; a_bg.start(bgColorTo->c); a_text.start(colorTo->c); diff --git a/Telegram/SourceFiles/ui/style/style_core.h b/Telegram/SourceFiles/ui/style/style_core.h index ea61f8ce7..cf96db24b 100644 --- a/Telegram/SourceFiles/ui/style/style_core.h +++ b/Telegram/SourceFiles/ui/style/style_core.h @@ -55,6 +55,9 @@ public: void registerModule(ModuleBase *module); void unregisterModule(ModuleBase *module); +// This method is implemented in palette.cpp (codegen). +bool setPaletteColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a); + } // namespace internal void startManager(); diff --git a/Telegram/SourceFiles/ui/widgets/shadow.cpp b/Telegram/SourceFiles/ui/widgets/shadow.cpp index c6bbf0df9..38c49e1fe 100644 --- a/Telegram/SourceFiles/ui/widgets/shadow.cpp +++ b/Telegram/SourceFiles/ui/widgets/shadow.cpp @@ -25,7 +25,7 @@ namespace Ui { void ToggleableShadow::setMode(Mode mode) { if (mode == Mode::ShownFast || mode == Mode::HiddenFast) { - if (!_a_opacity.animating()) { + if (_a_opacity.animating()) { _a_opacity.finish(); update(); } diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 2f6f99509..563cef7f7 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -192,7 +192,7 @@ defaultMenu: Menu { skip: 5px; itemBg: white; - itemBgOver: overBg; + itemBgOver: windowOverBg; itemFg: black; itemFgOver: black; itemFgDisabled: #cccccc; diff --git a/Telegram/SourceFiles/window/chat_background.cpp b/Telegram/SourceFiles/window/chat_background.cpp deleted file mode 100644 index 315ac129f..000000000 --- a/Telegram/SourceFiles/window/chat_background.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* -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 "window/chat_background.h" - -#include "mainwidget.h" -#include "localstorage.h" - -namespace Window { -namespace { - -NeverFreedPointer instance; - -} // namespace - -bool ChatBackground::empty() const { - return _image.isNull(); -} - -void ChatBackground::initIfEmpty() { - if (empty()) { - App::initBackground(); - } -} - -void ChatBackground::init(int32 id, QPixmap &&image) { - _id = id; - _image = std_::move(image); - - notify(ChatBackgroundUpdate(ChatBackgroundUpdate::Type::New, _tile)); -} - -void ChatBackground::reset() { - _id = 0; - _image = QPixmap(); - _tile = false; - - notify(ChatBackgroundUpdate(ChatBackgroundUpdate::Type::New, _tile)); -} - -int32 ChatBackground::id() const { - return _id; -} - -const QPixmap &ChatBackground::image() const { - return _image; -} - -bool ChatBackground::tile() const { - return _tile; -} - -void ChatBackground::setTile(bool tile) { - if (_tile != tile) { - _tile = tile; - Local::writeUserSettings(); - notify(ChatBackgroundUpdate(ChatBackgroundUpdate::Type::Changed, _tile)); - } -} - -ChatBackground *chatBackground() { - instance.createIfNull(); - return instance.data(); -} - -} // namespace Window diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 378c81bae..90ca2ce53 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -72,7 +72,7 @@ notifySendReply: IconButton(defaultIconButton) { width: 36px; height: 36px; - icon: icon {{ "notification_send", windowActiveBg, point(3px, 9px) }}; + icon: icon {{ "notification_send", windowActiveFill, point(3px, 9px) }}; iconPosition: point(0px, 0px); downIconPosition: point(0px, 1px); } diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp new file mode 100644 index 000000000..2034628bb --- /dev/null +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -0,0 +1,410 @@ +/* +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 "window/window_theme.h" + +#include "mainwidget.h" +#include "localstorage.h" +#include "core/parse_helper.h" +#include "core/zlib_help.h" + +namespace Window { +namespace Theme { +namespace { + +constexpr int kThemeFileSizeLimit = 5 * 1024 * 1024; +constexpr int kThemeBackgroundSizeLimit = 4 * 1024 * 1024; +constexpr int kThemeSchemeSizeLimit = 1024 * 1024; + +struct Data { + ChatBackground background; +}; +NeverFreedPointer instance; + +QByteArray readThemeContent(const QString &path) { + QFile file(path); + if (!file.exists()) { + LOG(("Error: theme file not found: %1").arg(path)); + return QByteArray(); + } + + if (file.size() > kThemeFileSizeLimit) { + LOG(("Error: theme file too large: %1 (should be less than 5 MB, got %2)").arg(path).arg(file.size())); + return QByteArray(); + } + if (!file.open(QIODevice::ReadOnly)) { + LOG(("Warning: could not open theme file: %1").arg(path)); + return QByteArray(); + } + + return file.readAll(); +} + +inline uchar readHexUchar(char code, bool &error) { + if (code >= '0' && code <= '9') { + return ((code - '0') & 0xFF); + } else if (code >= 'a' && code <= 'f') { + return ((code + 10 - 'a') & 0xFF); + } else if (code >= 'A' && code <= 'F') { + return ((code + 10 - 'A') & 0xFF); + } + error = true; + return 0xFF; +} + +inline uchar readHexUchar(char char1, char char2, bool &error) { + return ((readHexUchar(char1, error) & 0x0F) << 4) | (readHexUchar(char2, error) & 0x0F); +} + +bool readNameAndValue(const char *&from, const char *end, QByteArray *outName, QByteArray *outValue) { + using base::parse::skipWhitespaces; + using base::parse::readName; + + if (!skipWhitespaces(from, end)) return true; + + *outName = readName(from, end); + if (outName->isEmpty()) { + LOG(("Error: Could not read name in the color scheme.")); + return false; + } + if (!skipWhitespaces(from, end)) { + LOG(("Error: Unexpected end of the color scheme.")); + return false; + } + if (*from != ':') { + LOG(("Error: Expected ':' between each name and value in the color scheme.")); + return false; + } + if (!skipWhitespaces(++from, end)) { + LOG(("Error: Unexpected end of the color scheme.")); + return false; + } + auto valueStart = from; + if (*from == '#') ++from; + + if (readName(from, end).isEmpty()) { + LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme.")); + return false; + } + *outValue = QByteArray::fromRawData(valueStart, from - valueStart); + + if (!skipWhitespaces(from, end)) { + LOG(("Error: Unexpected end of the color scheme.")); + return false; + } + if (*from != ';') { + LOG(("Error: Expected ';' after each value in the color scheme.")); + return false; + } + ++from; + return true; +} + +bool loadColorScheme(const QByteArray &content, Instance *out = nullptr) { + if (content.size() > kThemeSchemeSizeLimit) { + LOG(("Error: color scheme file too large (should be less than 1 MB, got %2)").arg(content.size())); + return false; + } + + auto data = base::parse::stripComments(content); + auto from = data.constData(), end = from + data.size(); + while (from != end) { + QByteArray name, value; + if (!readNameAndValue(from, end, &name, &value)) { + return false; + } + if (name.isEmpty()) { // End of content reached. + return true; + } + + auto size = value.size(); + auto error = false; + if (value[0] == '#' && (size == 7 || size == 8)) { + auto r = readHexUchar(value[1], value[2], error); + auto g = readHexUchar(value[3], value[4], error); + auto b = readHexUchar(value[5], value[6], error); + auto a = (size == 8) ? readHexUchar(value[7], value[8], error) : uchar(255); + if (!error) { + if (out) { + error = !out->palette.setColor(QLatin1String(name), r, g, b, a); + } else { + error = !style::main_palette::setColor(QLatin1String(name), r, g, b, a); + } + } + } else { + if (out) { + error = !out->palette.setColor(QLatin1String(name), QLatin1String(value)); + } else { + error = !style::main_palette::setColor(QLatin1String(name), QLatin1String(value)); + } + } + if (error) { + LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme.")); + return false; + } + } + return true; +} + +void applyBackground(QImage &&background, bool tiled, Instance *out) { + if (out) { + out->background = std_::move(background); + out->tiled = tiled; + } else { + Background()->setThemeData(std_::move(background), tiled); + } +} + +bool loadThemeFromCache(const QByteArray &content, Cached &cache) { + if (cache.paletteChecksum != style::palette::kChecksum) { + return false; + } + if (cache.contentChecksum != hashCrc32(content.constData(), content.size())) { + return false; + } + + QImage background; + if (!cache.background.isEmpty()) { + QBuffer buffer(&cache.background); + QImageReader reader(&buffer); +#ifndef OS_MAC_OLD + reader.setAutoTransform(true); +#endif // OS_MAC_OLD + if (!reader.read(&background) || background.isNull()) { + return false; + } + } + + if (!style::main_palette::load(cache.colors)) { + return false; + } + if (!background.isNull()) { + applyBackground(std_::move(background), cache.tiled, nullptr); + } + + return true; +} + +enum class LoadResult { + Loaded, + Failed, + NotFound, +}; + +LoadResult loadBackgroundFromFile(zlib::FileToRead &file, const char *filename, QByteArray *outBackground) { + *outBackground = file.readFileContent(filename, zlib::kCaseInsensitive, kThemeBackgroundSizeLimit); + if (file.error() == UNZ_OK) { + return LoadResult::Loaded; + } else if (file.error() == UNZ_END_OF_LIST_OF_FILE) { + file.clearError(); + return LoadResult::NotFound; + } + LOG(("Error: could not read '%1' in the theme file.").arg(filename)); + return LoadResult::Failed; +} + +bool loadBackground(zlib::FileToRead &file, QByteArray *outBackground, bool *outTiled) { + auto result = loadBackgroundFromFile(file, "background.jpg", outBackground); + if (result != LoadResult::NotFound) return (result == LoadResult::Loaded); + + result = loadBackgroundFromFile(file, "background.png", outBackground); + if (result != LoadResult::NotFound) return (result == LoadResult::Loaded); + + *outTiled = true; + result = loadBackgroundFromFile(file, "tiled.jpg", outBackground); + if (result != LoadResult::NotFound) return (result == LoadResult::Loaded); + + result = loadBackgroundFromFile(file, "tiled.png", outBackground); + if (result != LoadResult::NotFound) return (result == LoadResult::Loaded); + return true; +} + +bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr) { + cache = Cached(); + zlib::FileToRead file(content); + + unz_global_info globalInfo = { 0 }; + file.getGlobalInfo(&globalInfo); + if (file.error() == UNZ_OK) { + auto schemeContent = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit); + if (file.error() != UNZ_OK) { + LOG(("Error: could not read 'colors.tdesktop-theme' in the theme file.")); + return false; + } + if (!loadColorScheme(schemeContent, out)) { + return false; + } + + auto backgroundTiled = false; + auto backgroundContent = QByteArray(); + if (!loadBackground(file, &backgroundContent, &backgroundTiled)) { + return false; + } + + if (!backgroundContent.isEmpty()) { + auto background = App::readImage(backgroundContent); + if (background.isNull()) { + LOG(("Error: could not read background image in the theme file.")); + return false; + } + QBuffer buffer(&cache.background); + if (!background.save(&buffer, "BMP")) { + LOG(("Error: could not write background image as a BMP to cache.")); + return false; + } + cache.tiled = backgroundTiled; + + applyBackground(std_::move(background), cache.tiled, out); + } + } else { + // Looks like it is not a .zip theme. + if (!loadColorScheme(content, out)) { + return false; + } + } + if (out) { + cache.colors = out->palette.save(); + } else { + cache.colors = style::main_palette::save(); + } + cache.paletteChecksum = style::palette::kChecksum; + cache.contentChecksum = hashCrc32(content.constData(), content.size()); + + return true; +} + +QImage prepareBackgroundImage(QImage &&image) { + if (image.format() != QImage::Format_ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied && image.format() != QImage::Format_RGB32) { + image = std_::move(image).convertToFormat(QImage::Format_RGB32); + } + image.setDevicePixelRatio(cRetinaFactor()); + return std_::move(image); +} + +} // namespace + +void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) { + _themeImage = prepareBackgroundImage(std_::move(themeImage)); + _themeTile = themeTile; +} + +void ChatBackground::start() { + if (_id == internal::kUninitializedBackground) { + setImage(kThemeBackground); + } +} + +void ChatBackground::setImage(int32 id, QImage &&image) { + if (id == kThemeBackground && _themeImage.isNull()) { + id = kDefaultBackground; + } + _id = id; + if (_id == kThemeBackground) { + _tile = _themeTile; + setPreparedImage(QImage(_themeImage)); + } else { + if (_id == kDefaultBackground) { + image.load(qsl(":/gui/art/bg.jpg")); + } else if (_id == kOldBackground || image.isNull()) { + _id = kOldBackground; + image.load(qsl(":/gui/art/bg_old.png")); + if (cRetina()) { + image = image.scaledToWidth(image.width() * 2, Qt::SmoothTransformation); + } else if (cScale() != dbisOne) { + image = image.scaledToWidth(convertScale(image.width()), Qt::SmoothTransformation); + } + } + Local::writeBackground(_id, (_id == kDefaultBackground || _id == kOldBackground) ? QImage() : image); + setPreparedImage(prepareBackgroundImage(std_::move(image))); + } + t_assert(!_image.isNull()); + notify(BackgroundUpdate(BackgroundUpdate::Type::New, _tile)); +} + +void ChatBackground::setPreparedImage(QImage &&image) { + App::initColorsFromBackground(image); + _image = App::pixmapFromImageInPlace(std_::move(image)); +} + +int32 ChatBackground::id() const { + return _id; +} + +const QPixmap &ChatBackground::image() const { + return _image; +} + +bool ChatBackground::tile() const { + return _tile; +} + +void ChatBackground::setTile(bool tile) { + if (_image.isNull()) { + // We should start first, otherwise the default call + // to start() will reset this value to _themeTile. + start(); + } + if (_tile != tile) { + _tile = tile; + Local::writeUserSettings(); + notify(BackgroundUpdate(BackgroundUpdate::Type::Changed, _tile)); + } +} + +ChatBackground *Background() { + instance.createIfNull(); + return &instance->background; +} + +bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache) { + if (content.size() < 4) { + LOG(("Error: Could not load theme from '%1' (%2)").arg(pathRelative).arg(pathAbsolute)); + return false; + } + + instance.createIfNull(); + if (loadThemeFromCache(content, cache)) { + return true; + } + + if (!loadTheme(content, cache)) { + return false; + } + Local::writeTheme(pathRelative, pathAbsolute, content, cache); + return true; +} + +void Unload() { + instance.clear(); +} + +bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) { + *outContent = readThemeContent(path); + if (outContent->size() < 4) { + LOG(("Error: Could not load theme from %1").arg(path)); + return false; + } + + return loadTheme(*outContent, out->cached, out); +} + +} // namespace Theme +} // namespace Window diff --git a/Telegram/SourceFiles/window/window_theme.h b/Telegram/SourceFiles/window/window_theme.h new file mode 100644 index 000000000..caa5925cc --- /dev/null +++ b/Telegram/SourceFiles/window/window_theme.h @@ -0,0 +1,99 @@ +/* +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 Window { +namespace Theme { +namespace internal { + +constexpr int32 kUninitializedBackground = -3; + +} // namespace internal + +constexpr int32 kThemeBackground = -2; +constexpr int32 kCustomBackground = -1; +constexpr int32 kOldBackground = 0; +constexpr int32 kDefaultBackground = 21; + +struct Cached { + QByteArray colors; + QByteArray background; + bool tiled = false; + int32 paletteChecksum = 0; + int32 contentChecksum = 0; +}; +bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache); +void Unload(); + +struct Instance { + style::palette palette; + QImage background; + Cached cached; + bool tiled = false; +}; +bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent); + +struct BackgroundUpdate { + enum class Type { + New, + Changed, + Start, + }; + + BackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) { + } + Type type; + bool tiled; +}; + +class ChatBackground : public base::Observable { +public: + // This method is allowed to (and should) be called before start(). + void setThemeData(QImage &&themeImage, bool themeTile); + + // This method is setting the default (themed) image if none was set yet. + void start(); + void setImage(int32 id, QImage &&image = QImage()); + void setTile(bool tile); + void reset() { + setImage(kThemeBackground); + } + + int32 id() const; + const QPixmap &image() const; + bool tile() const; + +private: + void setPreparedImage(QImage &&image); + + int32 _id = internal::kUninitializedBackground; + QPixmap _image; + bool _tile = false; + + QImage _themeImage; + bool _themeTile = false; + +}; + +ChatBackground *Background(); + +} // namespace Theme +} // namespace Window diff --git a/Telegram/ThirdParty/minizip/unzip.c b/Telegram/ThirdParty/minizip/unzip.c new file mode 100644 index 000000000..909350435 --- /dev/null +++ b/Telegram/ThirdParty/minizip/unzip.c @@ -0,0 +1,2125 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == MAXU32) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == MAXU32) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if ((err==UNZ_OK) && (pfile_info != NULL)) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/Telegram/ThirdParty/minizip/unzip.h b/Telegram/ThirdParty/minizip/unzip.h new file mode 100644 index 000000000..2104e3915 --- /dev/null +++ b/Telegram/ThirdParty/minizip/unzip.h @@ -0,0 +1,437 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzCloseCurrentFile before call unzClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index e7a05c460..22c622fe5 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -34,6 +34,7 @@ 'minizip_loc': '<(third_party_loc)/minizip', 'sp_media_key_tap_loc': '<(third_party_loc)/SPMediaKeyTap', 'style_files': [ + '<(res_loc)/colors.palette', '<(res_loc)/basic.style', '<(res_loc)/basic_types.style', '<(src_loc)/boxes/boxes.style', @@ -213,6 +214,9 @@ '<(src_loc)/core/observer.cpp', '<(src_loc)/core/observer.h', '<(src_loc)/core/ordered_set.h', + '<(src_loc)/core/parse_helper.cpp', + '<(src_loc)/core/parse_helper.h', + '<(src_loc)/core/qthelp_regex.h', '<(src_loc)/core/qthelp_url.cpp', '<(src_loc)/core/qthelp_url.h', '<(src_loc)/core/runtime_composer.cpp', @@ -226,6 +230,7 @@ '<(src_loc)/core/vector_of_moveable.h', '<(src_loc)/core/version.h', '<(src_loc)/core/virtual_method.h', + '<(src_loc)/core/zlib_help.h', '<(src_loc)/data/data_abstract_structure.cpp', '<(src_loc)/data/data_abstract_structure.h', '<(src_loc)/data/data_drafts.cpp', @@ -536,8 +541,6 @@ '<(src_loc)/ui/scrollarea.h', '<(src_loc)/ui/twidget.cpp', '<(src_loc)/ui/twidget.h', - '<(src_loc)/window/chat_background.cpp', - '<(src_loc)/window/chat_background.h', '<(src_loc)/window/main_window.cpp', '<(src_loc)/window/main_window.h', '<(src_loc)/window/notifications_manager.cpp', @@ -554,6 +557,8 @@ '<(src_loc)/window/slide_animation.h', '<(src_loc)/window/top_bar_widget.cpp', '<(src_loc)/window/top_bar_widget.h', + '<(src_loc)/window/window_theme.cpp', + '<(src_loc)/window/window_theme.h', '<(sp_media_key_tap_loc)/SPMediaKeyTap.m', '<(sp_media_key_tap_loc)/SPMediaKeyTap.h', diff --git a/Telegram/gyp/codegen_rules.gypi b/Telegram/gyp/codegen_rules.gypi index 825a09f4e..11d802f8e 100644 --- a/Telegram/gyp/codegen_rules.gypi +++ b/Telegram/gyp/codegen_rules.gypi @@ -50,6 +50,33 @@ '<@(qrc_files)', ], 'message': 'Updating dependent qrc files..', + }, { + 'action_name': 'codegen_palette', + 'inputs': [ + '<(PRODUCT_DIR)/codegen_style<(exe_ext)', + '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp', + '<(res_loc)/colors.palette', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/styles/palette.h', + '<(SHARED_INTERMEDIATE_DIR)/styles/palette.cpp', + ], + 'action': [ + '<(PRODUCT_DIR)/codegen_style<(exe_ext)', + '-I<(res_loc)', '-I<(src_loc)', + '-o<(SHARED_INTERMEDIATE_DIR)/styles', + '-w<(PRODUCT_DIR)/../..', + + # GYP/Ninja bug workaround: if we specify just <(RULE_INPUT_PATH) + # the <(RULE_INPUT_ROOT) variables won't be available in Ninja, + # and the 'message' will be just 'codegen_style-ing .style..' + # Looks like the using the <(RULE_INPUT_ROOT) here "exports" it + # for using in the 'message' field. + + '<(res_loc)/colors.palette', + ], + 'message': 'codegen_palette-ing colors..', + 'process_outputs_as_sources': 1, }, { 'action_name': 'codegen_lang', 'inputs': [ From e08f5437a63ad577da4a836182853d0f5d0849e1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 31 Oct 2016 15:29:26 +0300 Subject: [PATCH 007/100] Palette usage improvements. --- Telegram/Resources/basic.style | 377 ++++-------------- Telegram/Resources/basic_types.style | 4 +- Telegram/Resources/colors.palette | 109 ++++- Telegram/Resources/icons/box_cancel.png | Bin 187 -> 0 bytes Telegram/Resources/icons/box_cancel@2x.png | Bin 296 -> 0 bytes Telegram/Resources/langs/lang.strings | 2 + Telegram/Resources/sample.tdesktop-theme | 79 +++- Telegram/SourceFiles/app.cpp | 13 +- Telegram/SourceFiles/boxes/abstractbox.cpp | 58 +-- Telegram/SourceFiles/boxes/abstractbox.h | 28 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 16 +- Telegram/SourceFiles/boxes/boxes.style | 95 ++++- Telegram/SourceFiles/boxes/confirmbox.cpp | 8 +- .../SourceFiles/boxes/confirmphonebox.cpp | 2 +- Telegram/SourceFiles/boxes/connectionbox.cpp | 2 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 14 +- Telegram/SourceFiles/boxes/emojibox.cpp | 6 +- Telegram/SourceFiles/boxes/members_box.cpp | 18 +- .../SourceFiles/boxes/notifications_box.cpp | 4 +- Telegram/SourceFiles/boxes/passcodebox.cpp | 19 +- Telegram/SourceFiles/boxes/photocropbox.cpp | 20 +- Telegram/SourceFiles/boxes/photosendbox.cpp | 8 +- Telegram/SourceFiles/boxes/report_box.cpp | 3 +- Telegram/SourceFiles/boxes/sessionsbox.cpp | 38 +- Telegram/SourceFiles/boxes/sharebox.cpp | 2 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 6 +- Telegram/SourceFiles/boxes/usernamebox.cpp | 6 +- .../SourceFiles/codegen/style/generator.cpp | 42 +- .../SourceFiles/codegen/style/parsed_file.cpp | 6 + Telegram/SourceFiles/dialogs/dialogs.style | 5 +- .../SourceFiles/dialogs/dialogs_layout.cpp | 6 +- Telegram/SourceFiles/dialogswidget.cpp | 15 +- .../history/field_autocomplete.cpp | 13 +- Telegram/SourceFiles/history/history.style | 36 +- .../SourceFiles/history/history_drag_area.cpp | 6 +- .../history/history_location_manager.cpp | 2 +- .../history/history_media_types.cpp | 57 ++- .../SourceFiles/history/history_message.cpp | 15 +- Telegram/SourceFiles/historywidget.cpp | 60 +-- Telegram/SourceFiles/historywidget.h | 7 +- .../inline_bot_layout_internal.cpp | 22 +- Telegram/SourceFiles/intro/intro.style | 102 ++++- Telegram/SourceFiles/intro/introcode.cpp | 16 +- Telegram/SourceFiles/intro/introphone.cpp | 18 +- Telegram/SourceFiles/intro/intropwdcheck.cpp | 12 +- Telegram/SourceFiles/intro/introsignup.cpp | 17 +- Telegram/SourceFiles/intro/introstart.cpp | 6 +- Telegram/SourceFiles/intro/introwidget.cpp | 6 +- Telegram/SourceFiles/layerwidget.cpp | 3 +- Telegram/SourceFiles/layout.h | 2 +- Telegram/SourceFiles/localimageloader.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 4 +- Telegram/SourceFiles/mainwindow.cpp | 10 +- Telegram/SourceFiles/mainwindow.h | 2 +- .../SourceFiles/media/media_clip_reader.cpp | 10 +- .../SourceFiles/media/view/mediaview.style | 75 ++-- Telegram/SourceFiles/mediaview.cpp | 109 +++-- Telegram/SourceFiles/overview/overview.style | 6 + .../SourceFiles/overview/overview_layout.cpp | 40 +- Telegram/SourceFiles/overviewwidget.cpp | 10 +- Telegram/SourceFiles/passcodewidget.cpp | 12 +- .../platform/linux/main_window_linux.cpp | 11 +- .../platform/linux/main_window_linux.h | 2 +- .../platform/mac/main_window_mac.h | 2 +- .../platform/mac/main_window_mac.mm | 6 +- .../platform/win/main_window_win.cpp | 15 +- .../platform/win/main_window_win.h | 2 +- Telegram/SourceFiles/profile/profile.style | 16 +- Telegram/SourceFiles/settings/settings.style | 37 +- .../settings/settings_background_widget.cpp | 6 +- .../SourceFiles/settings/settings_cover.cpp | 4 +- .../SourceFiles/settings/settings_cover.h | 3 +- .../settings/settings_fixed_bar.cpp | 4 + .../SourceFiles/settings/settings_fixed_bar.h | 4 +- .../SourceFiles/settings/settings_widget.cpp | 3 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 19 +- Telegram/SourceFiles/stickers/stickers.style | 2 +- Telegram/SourceFiles/title.cpp | 10 +- Telegram/SourceFiles/ui/buttons/checkbox.cpp | 12 +- .../ui/buttons/history_down_button.cpp | 4 +- .../SourceFiles/ui/buttons/icon_button.cpp | 41 ++ Telegram/SourceFiles/ui/buttons/icon_button.h | 16 + .../SourceFiles/ui/buttons/round_button.cpp | 56 +-- .../SourceFiles/ui/buttons/round_button.h | 6 - Telegram/SourceFiles/ui/countryinput.cpp | 17 +- Telegram/SourceFiles/ui/flatbutton.cpp | 44 +- Telegram/SourceFiles/ui/flatbutton.h | 4 - Telegram/SourceFiles/ui/flatinput.cpp | 6 +- Telegram/SourceFiles/ui/flattextarea.cpp | 14 +- Telegram/SourceFiles/ui/images.cpp | 20 +- Telegram/SourceFiles/ui/scrollarea.cpp | 4 +- .../SourceFiles/ui/style/style_core_color.h | 6 +- Telegram/SourceFiles/ui/text/text_block.h | 5 +- .../SourceFiles/ui/widgets/multi_select.cpp | 2 +- .../SourceFiles/ui/widgets/multi_select.h | 4 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 3 +- Telegram/SourceFiles/ui/widgets/widgets.style | 31 +- .../window/notifications_manager_default.cpp | 8 +- .../SourceFiles/window/slide_animation.cpp | 4 +- .../SourceFiles/window/top_bar_widget.cpp | 2 +- Telegram/SourceFiles/window/window_theme.cpp | 4 +- 101 files changed, 1105 insertions(+), 1045 deletions(-) delete mode 100644 Telegram/Resources/icons/box_cancel.png delete mode 100644 Telegram/Resources/icons/box_cancel@2x.png diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index dbd7d6804..d882b86eb 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -32,33 +32,19 @@ emojiImgSize: 18px; // exceptional value for retina emojiSize: 18px; emojiPadding: 0px; -counterBg: #f23c34; -counterMuteBg: #888888; -counterFg: #ffffff; -counterMacInvFg: #ffffff01; - lineWidth: 1px; -transparent: #ffffff00; -white: #ffffff; -black: #000000; - wndMinWidth: 380px; adaptiveNormalWidth: 640px; adaptiveWideWidth: 1366px; -semiboldButtonBlueText: #2b99d5; - wndMinHeight: 480px; wndDefWidth: 800px; wndDefHeight: 600px; wndShadow: icon {{ "window_shadow", windowShadowFg }}; wndShadowShift: 1px; -layerAlpha: 0.5; -layerBg: black; - labelDefFlat: flatLabel { font: font(fsize); width: 0px; @@ -98,7 +84,6 @@ defaultBoxButton: RoundButton { downTextTop: 9px; font: boxButtonFont; - duration: 200; } cancelBoxButton: RoundButton(defaultBoxButton) { textFg: #aeaeae; @@ -150,7 +135,8 @@ attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) { } defaultInputArea: InputArea { - textFg: black; + textBg: windowBg; + textFg: windowTextFg; textMargins: margins(5px, 6px, 5px, 4px); placeholderFg: #999999; @@ -174,8 +160,8 @@ defaultInputArea: InputArea { heightMax: 128px; } defaultInputField: InputField { - textBg: white; - textFg: black; + textBg: windowBg; + textFg: windowTextFg; textMargins: margins(0px, 6px, 0px, 4px); textAlign: align(topleft); @@ -199,12 +185,13 @@ defaultInputField: InputField { height: 32px; } defaultCheckbox: Checkbox { - textFg: black; - textBg: white; + textFg: windowTextFg; + textBg: windowBg; + checkBg: #ffffff; checkFg: #b3b3b3; checkFgOver: #b3b3b3; - checkFgActive: #40ace3; + checkFgActive: windowActiveFill; width: -44px; height: 22px; @@ -223,9 +210,10 @@ defaultBoxCheckbox: Checkbox(defaultCheckbox) { font: boxTextFont; } defaultRadiobutton: Radiobutton { - textFg: black; - textBg: white; + textFg: windowTextFg; + textBg: windowBg; + checkBg: #ffffff; checkFg: #b3b3b3; checkFgOver: #bfbfbf; checkFgActive: #4eb3ee; @@ -281,7 +269,6 @@ defaultTooltip: Tooltip { linesMax: 12; } -almostTransparent: #ffffff0d; boxScroll: flatScroll(solidScroll) { round: 3px; width: 18px; @@ -312,7 +299,7 @@ shadowToggleDuration: 200; slideDuration: 240; slideShift: 100px; -slideFadeOut: 0.3; +slideFadeOutBg: #0000003c; slideShadow: icon {{ "slide_shadow", #000000 }}; slideFunction: transition(easeOutCirc); @@ -347,8 +334,6 @@ sysUnlock: sysButton(sysUpd) { icon: icon {{ "title_button_unlock", titleBg }}; } -btnWhiteHover: #f5f5f5; -btnBoxWhiteHover: #fafafa; btnYesColor: #0080c0; btnYesHover: #0073ad; btnNoColor: #8b8b8b; @@ -375,37 +360,6 @@ titleTextButton: flatButton { cursor: cursor(default); } -btnDefFlat: flatButton { - duration: 200; - cursor: cursor(pointer); -} -btnDefBig: flatButton(btnDefFlat) { - textTop: 11px; - overTextTop: 11px; - downTextTop: 12px; - - font: font(23px); - overFont: font(23px); - height: 56px; -} -btnNextBG: #2fa9e2; -btnDefNext: flatButton(btnDefFlat) { - color: white; - overColor: white; - downColor: white; - bgColor: btnNextBG; - overBgColor: #279ad0; - downBgColor: #279ad0; -} -btnDefBack: flatButton(btnDefFlat) { - color: white; - overColor: white; - downColor: white; - bgColor: #c7c7c7; - overBgColor: #b9b9b9; - downBgColor: #b9b9b9; -} - linkCropLimit: 360px; linkFont: normalFont; linkOverFont: font(fsize underline); @@ -430,9 +384,9 @@ inpDefFlat: flatInput { cursor: cursor(text); borderWidth: 0px; - borderColor: black; - borderActive: black; - borderError: black; + borderColor: transparent; + borderActive: transparent; + borderError: transparent; phColor: #949494; phFocusColor: #aaaaaa; @@ -454,25 +408,6 @@ inpDefGray: flatInput(inpDefFlat) { phColor: #808080; } -taDefFlat: flatTextarea { - textColor: #000000; - bgColor: #ffffff; - align: align(left); - textMrg: margins(5px, 5px, 5px, 5px); - font: inpDefFont; - cursor: cursor(text); - - phColor: #999999; - phFocusColor: #aaaaaa; - phAlign: align(topleft); - phPos: point(2px, 0px); - phShift: 50px; - phDuration: 100; - phLeftFunc: transition(linear); - phAlphaFunc: transition(linear); - phColorFunc: transition(linear); -} - scrollDef: flatScroll { barColor: #00000053; bgColor: #0000001a; @@ -505,83 +440,9 @@ scrollCountries: flatScroll(scrollDef) { lnkText: #0f7dc7; -introBtnTop: 288px; -introSkip: 45px; -introFinishSkip: 15px; -introPhotoSize: 98px; -introHeaderFont: font(24px); -introHeaderSkip: 14px; -introIconSkip: 54px; -introFont: font(16px); -introLink: linkButton(btnDefLink) { - font: introFont; - overFont: font(16px underline); -} -introColor: black; -introLabel: flatLabel(labelDefFlat) { - font: introFont; - align: align(center); -} - -introStepSize: size(400px, 200px); -introSize: size(400px, 400px); -introSlideShift: 500px; // intro hiding animation -introSlideDuration: 200; -introSlideDelta: 0; // between hide start and show start -introHideFunc: transition(easeInCirc); -introShowFunc: transition(easeOutCirc); -introAlphaHideFunc: transition(easeOutCirc); -introAlphaShowFunc: transition(easeInCirc); -introTextTop: 22px; -introTextSize: size(400px, 93px); -introCallSkip: 15px; -introPwdTextSize: size(400px, 73px); - -btnIntroSep: 12px; -btnIntroNext: flatButton(btnDefNext, btnDefBig) { - textTop: 16px; - overTextTop: 16px; - downTextTop: 17px; - - font: font(17px); - overFont: font(17px); - - width: 300px; - radius: buttonRadius; -} - boxShadow: icon {{ "box_shadow", windowShadowFg }}; boxShadowShift: 2px; -introPhoneTop: 8px; -inpIntroCountryCode: flatInput(inpDefGray) { - width: 70px; - height: 41px; - align: align(center); -} -inpIntroPhone: flatInput(inpDefGray) { - textMrg: margins(12px, 5px, 12px, 6px); - width: 225px; - height: 41px; -} -inpIntroCode: flatInput(inpDefGray) { - textMrg: margins(12px, 5px, 12px, 6px); - width: 106px; - height: 41px; - align: align(center); - - phPos: point(0px, 0px); - phAlign: align(center); - phShift: 0px; -} -inpIntroName: flatInput(inpIntroPhone) { - width: 192px; -} -inpIntroPassword: flatInput(inpIntroPhone) { - width: 300px; -} - -introSelectDelta: 30px; btnSelectSep: #e0e0e0; btnRedLink: linkButton(btnDefLink) { color: #d15948; @@ -591,34 +452,24 @@ btnRedLink: linkButton(btnDefLink) { countryRowHeight: 36px; countryRowNameFont: semiboldFont; +countryRowNameFg: boxTextFg; countryRowPadding: margins(22px, 9px, 8px, 0px); countryRowCodeFont: font(fsize); +countryRowBg: windowBg; countryRowBgOver: windowOverBg; -countryRowCodeFg: #808080; -countryRowCodeFgOver: #7c99b2; +countryRowCodeFg: windowSubTextFg; +countryRowCodeFgOver: windowSubTextFgOver; countriesSkip: 12px; countriesScroll: flatScroll(boxScroll) { deltat: 9px; deltab: 3px; } -introErrWidth: 450px; -introErrDuration: 200; -introErrFunc: transition(linear); -introErrColor: black; -introErrTop: 15px; -introErrHeight: 40px; -introErrFont: font(16px); - setLittleSkip: 9px; setScroll: flatScroll(scrollDef) { bottomsh: 0px; topsh: 0px; } -setPhotoDuration: 150; - -setErrColor: #d84d4d; -setGoodColor: #4ab44a; noContactsHeight: 100px; noContactsFont: font(fsize); @@ -638,7 +489,6 @@ dlgFilter: flatInput(inpDefGray) { } topBarHeight: 54px; -topBarBG: white; topBarDuration: 200; topBarForward: icon {{ "title_next", #a3a3a3 }}; topBarBackward: icon {{ "title_previous", #a3a3a3 }}; @@ -650,10 +500,10 @@ topBarBackFont: font(16px); topBarArrowPadding: margins(39px, 8px, 17px, 8px); topBarMinPadding: 5px; topBarButton: RoundButton { - textFg: #0084c4; - textFgOver: #0084c4; - secondaryTextFg: #0084c4; - secondaryTextFgOver: #0084c4; + textFg: btnYesColor; + textFgOver: btnYesColor; + secondaryTextFg: btnYesColor; + secondaryTextFgOver: btnYesColor; textBg: windowBg; textBgOver: #edf4f7; @@ -665,15 +515,14 @@ topBarButton: RoundButton { downTextTop: 7px; font: font(fsize); - duration: 200; } defaultActiveButton: RoundButton { - textFg: #ffffff; - textFgOver: #ffffff; - secondaryTextFg: #cceeff; - secondaryTextFgOver: #cceeff; - textBg: windowActiveFill; - textBgOver: #46b4eb; + textFg: activeButtonFg; + textFgOver: activeButtonFgOver; + secondaryTextFg: activeButtonSecondaryFg; + secondaryTextFgOver: activeButtonSecondaryFgOver; + textBg: activeButtonBg; + textBgOver: activeButtonBgOver; secondarySkip: 7px; @@ -685,14 +534,14 @@ defaultActiveButton: RoundButton { downTextTop: 9px; font: semiboldFont; - duration: 200; } -topBarClearButton: RoundButton(defaultActiveButton) { - textFg: semiboldButtonBlueText; - textFgOver: semiboldButtonBlueText; - textBg: #ffffff; - textBgOver: #f2f7fa; - +defaultLightButton: RoundButton(defaultActiveButton) { + textFg: lightButtonFg; + textFgOver: lightButtonFgOver; + textBg: lightButtonBg; + textBgOver: lightButtonBgOver; +} +topBarClearButton: RoundButton(defaultLightButton) { width: -18px; } topBarActionSkip: 10px; @@ -741,10 +590,11 @@ msgReplyBarSize: size(2px, 36px); msgReplyBarSkip: 10px; msgInReplyBarColor: #2fa9e2; msgInReplyBarSelColor: #2fa9e2; +msgImgReplyBarColor: #ffffff; msgBotKbDuration: 200; msgBotKbFont: semiboldFont; -msgBotKbOverOpacity: 0.1; +msgBotKbOverBg: #fffffffc; msgBotKbIconPadding: 2px; msgBotKbUrlIcon: icon {{ "inline_button_url", #ffffff }}; msgBotKbSwitchPmIcon: icon {{ "inline_button_switch", #ffffff }}; @@ -780,24 +630,6 @@ msgDateImgBgSelected: #1c4a7187; msgDateImgPadding: point(8px, 2px); msgDateImgCheckSpace: 4px; -collapseButton: flatButton(btnDefFlat) { - font: msgServiceFont; - overFont: msgServiceFont; - width: -24px; - bgColor: transparent; - downBgColor: transparent; - overBgColor: transparent; - color: white; - overColor: white; - downColor: white; - textTop: 3px; - overTextTop: 3px; - downTextTop: 3px; - height: 25px; -} -collapseHideDuration: 200; -collapseShowDuration: 200; - defaultTextStyle: textStyle { linkFlags: font(fsize); linkFlagsOver: font(fsize underline); @@ -853,13 +685,6 @@ medviewSaveAsTextStyle: textStyle(defaultTextStyle) { linkFgDown: #91d9ff; } -introLabelTextStyle: textStyle(defaultTextStyle) { - lineHeight: 30px; -} -introErrLabelTextStyle: textStyle(defaultTextStyle) { - lineHeight: 27px; -} - mediaPadding: margins(0px, 0px, 0px, 0px);//1px, 1px, 1px, 1px);//2px, 2px, 2px, 2px); mediaCaptionSkip: 5px; mediaInBubbleSkip: 5px; @@ -912,8 +737,8 @@ msgFileThumbPadding: margins(10px, 10px, 14px, 10px); msgFileThumbNameTop: 12px; msgFileThumbStatusTop: 32px; msgFileThumbLinkTop: 60px; -msgFileThumbLinkInFg: semiboldButtonBlueText; -msgFileThumbLinkInFgSelected: semiboldButtonBlueText; +msgFileThumbLinkInFg: lightButtonFg; +msgFileThumbLinkInFgSelected: lightButtonFgOver; msgFileThumbLinkOutFg: #5eba5b; msgFileThumbLinkOutFgSelected: #31a298; msgFileNameTop: 16px; @@ -945,14 +770,17 @@ msgWaveformOutInactive: #b3e2b4; msgWaveformOutInactiveSelected: #91c3c3; sendPadding: 9px; -btnSend: flatButton(btnDefFlat) { +btnSend: flatButton { + duration: 200; + cursor: cursor(pointer); + color: btnYesColor; overColor: btnYesHover; downColor: btnYesHover; - bgColor: white; - overBgColor: btnWhiteHover; - downBgColor: btnWhiteHover; + bgColor: historySendBg; + overBgColor: historySendBgOver; + downBgColor: historySendBgOver; width: -32px; height: 46px; @@ -987,13 +815,11 @@ historyScroll: flatScroll(scrollDef) { bottomsh: -1px; } textRectMargins: margins(-2px, -1px, -2px, -1px); -taMsgField: flatTextarea(taDefFlat) { - font: msgFont; -} -maxFieldHeight: 224px; -// historyMinHeight: 56px; -reportSpamHide: flatButton(btnDefFlat) { +reportSpamHide: flatButton { + duration: 200; + cursor: cursor(pointer); + color: btnYesColor; overColor: btnYesHover; downColor: btnYesHover; @@ -1026,6 +852,7 @@ reportSpamButton: flatButton(reportSpamHide) { } reportSpamSeparator: 30px; reportSpamBg: #fffffff0; +reportSpamFg: #000000; newMsgSound: ":/gui/art/newmsg.wav"; @@ -1059,7 +886,6 @@ contactsStatusFont: font(fsize); contactsStatusFg: #999999; contactsStatusFgOver: #7c99b2; contactsStatusFgOnline: #3b8dcc; -contactsBgOver: windowOverBg; contactsCheckPosition: point(8px, 16px); contactsAboutBg: #f7f7f7; contactsAboutShadow: #0000001F; @@ -1101,32 +927,7 @@ membersPadding: margins(0px, 10px, 0px, 10px); forwardMargins: margins(30px, 10px, 30px, 10px); forwardFont: font(16px); forwardBg: #0000004c; - -newGroupAboutFg: #808080; -newGroupPadding: margins(4px, 6px, 4px, 3px); -newGroupSkip: 17px; -newGroupInfoPadding: margins(0px, -4px, 0px, 1px); - -newGroupLinkPadding: margins(4px, 27px, 4px, 12px); -newGroupLinkTop: 3px; -newGroupLinkFont: font(16px); - -newGroupPhotoSize: 76px; -newGroupPhotoBg: #4eb5f0; -newGroupPhotoBgOver: #3fa9e7; -newGroupPhotoIcon: icon {{ "new_chat_photo", #ffffff }}; -newGroupPhotoIconPosition: point(23px, 25px); - -newGroupNamePosition: point(27px, 20px); - -newGroupDescriptionPadding: margins(0px, 23px, 0px, 14px); -newGroupDescription: InputArea(defaultInputArea) { - textMargins: margins(1px, 6px, 1px, 4px); - heightMax: 115px; -} - -newGroupPublicLinkPadding: margins(0px, 20px, 0px, 5px); -newGroupLinkFadeDuration: 5000; +forwardFg: #ffffff; connectionHostInputField: InputField(defaultInputField) { width: 160px; @@ -1171,6 +972,7 @@ connectingBG: #ffffffee; connectingColor: #777777; connectingPadding: margins(5px, 5px, 5px, 5px); +dragBg: #ffffff; dragFont: font(28px semibold); dragSubfont: font(20px semibold); dragColor: #777777; @@ -1181,13 +983,6 @@ dragPadding: margins(20px, 10px, 20px, 10px); dragHeight: 72px; -dpiActive: black; -dpiInactive: #999999; -dpiFont1: linkFont; -dpiFont2: linkFont; -dpiFont3: linkFont; -dpiFont4: linkFont; - botKbDuration: 200; botKbBg: #edf1f5; botKbOverBg: #d8e2ec; @@ -1225,8 +1020,6 @@ maxStickerSize: 256px; maxGifSize: 320px; maxSignatureSize: 144px; -mvBgColor: #222222; -mvBgOpacity: 0.92; mvThickFont: semiboldFont; mvFont: font(fsize); @@ -1234,13 +1027,13 @@ mvTextLeft: 16px; mvTextSkip: 10px; mvHeaderTop: 48px; mvTextTop: 24px; -mvTextColor: white; +mvTextColor: #ffffff; mvTextOpacity: 0.5; mvTextOverOpacity: 1; mvIconOpacity: 0.45; mvIconOverOpacity: 1; -mvControlBgColor: black; +mvControlFg: #ffffff; mvControlBgOpacity: 0.3; mvControlMargin: 0px; mvControlSize: 90px; @@ -1251,33 +1044,13 @@ mvHideDuration: 1000; mvShowDuration: 200; mvFadeDuration: 150; -mvDocPadding: 18px; -mvDocSize: size(340px, 116px); -mvDocBg: white; -mvDocNameTop: 4px; -mvDocNameFont: font(semibold 14px); -mvDocNameColor: black; -mvDocSizeTop: 29px; -mvDocSizeColor: #808080; -mvDocExtTop: 35px; -mvDocExtFont: font(semibold 18px); -mvDocExtColor: white; -mvDocExtPadding: 10px; -mvDocLinksTop: 57px; -mvDocIconSize: 80px; - -mvDocLink: linkButton(btnDefLink) { - color: #4595d3; - overColor: #4595d3; - downColor: #4595d3; -} - mvDeltaFromLastAction: 5px; mvSwipeDistance: 80px; mvCaptionPadding: margins(18px, 10px, 18px, 10px); mvCaptionMargin: size(11px, 11px); mvCaptionRadius: 2px; +mvCaptionFg: #ffffff; mvCaptionBg: #11111180; mvCaptionFont: font(fsize); @@ -1288,6 +1061,7 @@ medviewSaveMsgCheckPos: point(23px, 21px); medviewSaveMsgShowing: 200; medviewSaveMsgShown: 2000; medviewSaveMsgHiding: 2500; +medviewSaveMsgFg: #ffffff; medviewSaveMsg: #000000b2; // Mac specific @@ -1305,7 +1079,8 @@ radialSize: size(50px, 50px); radialLine: 3px; radialDuration: 350; radialPeriod: 3000; -radialBgOpacity: 0.4; +radialFg: #ffffff; +radialBg: #00000056; downloadPathSkip: 10px; @@ -1343,22 +1118,6 @@ backgroundScroll: flatScroll(boxScroll) { deltab: 0px; } -passcodeHeaderFont: font(19px); -passcodeHeaderHeight: 80px; -passcodeInput: flatInput(inpIntroPhone) { -} -passcodeSubmit: flatButton(btnIntroNext) { - textTop: 15px; - overTextTop: 15px; - downTextTop: 16px; - width: 225px; - font: font(19px); - overFont: font(19px); -} -passcodeSubmitSkip: 40px; -passcodePadding: margins(0px, 22px, 0px, 3px); -passcodeSkip: 31px; - mentionHeight: 40px; mentionScroll: flatScroll(scrollDef) { topsh: 0px; @@ -1367,7 +1126,9 @@ mentionScroll: flatScroll(scrollDef) { mentionPadding: margins(8px, 5px, 8px, 5px); mentionTop: 11px; mentionFont: linkFont; +mentionNameFg: windowTextFg; mentionPhotoSize: msgPhotoSize; +mentionBg: #ffffff; mentionBgOver: #f5f5f5; mentionFg: #777777; mentionFgOver: #707070; @@ -1377,6 +1138,10 @@ mentionFgOverActive: #0077b3; webPageLeft: 10px; webPageBar: 2px; webPageTitleFont: semiboldFont; +webPageTitleOutFg: historyTextOutFg; +webPageTitleInFg: historyTextInFg; +webPageDescriptionOutFg: historyTextOutFg; +webPageDescriptionInFg: historyTextInFg; webPageDescriptionFont: normalFont; webPagePhotoSize: 100px; webPagePhotoDelta: 8px; @@ -1393,6 +1158,7 @@ inlineResultsSkip: 3px; inlineMediaHeight: 96px; inlineThumbSize: 64px; inlineThumbSkip: 10px; +inlineTitleFg: windowTextFg; inlineDescriptionFg: windowSubTextFg; inlineRowMargin: 6px; inlineRowBorder: 1px; @@ -1425,12 +1191,13 @@ infoButton: PeerAvatarButton { profileTopBarBackIconFg: #0290d7; profileTopBarBackIcon: icon {{ "title_previous", profileTopBarBackIconFg }}; -boxCancelIcon: icon {{ "box_cancel", #000000 }}; -historyReplyCancelIcon: icon {{ "box_cancel", #adadad }}; +historyReplyCancelIcon: icon {{ "box_button_close", historyReplyBg }}; +boxSearchCancelIcon: icon {{ "box_button_close", boxSearchBg }}; +settingsFixedBarCloseIcon: icon {{ "box_button_close", settingsFixedBarBg }}; -notifyBg: white; -notifyFadeRight: icon {{ "fade_horizontal_right", notifyBg }}; +notifyFadeRight: icon {{ "fade_horizontal_right", notificationBg }}; +emojiPanBg: #ffffff; emojiPanCategories: #f7f7f7; stickerIconLeft: icon {{ "fade_horizontal_left", emojiPanCategories }}; stickerIconRight: icon {{ "fade_horizontal_right", emojiPanCategories }}; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index 1fe528fc0..c0d17c4c4 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -192,13 +192,13 @@ RoundButton { icon: icon; font: font; - duration: int; } Checkbox { textFg: color; textBg: color; + checkBg: color; checkFg: color; checkFgOver: color; checkFgActive: color; @@ -219,6 +219,7 @@ Radiobutton { textFg: color; textBg: color; + checkBg: color; checkFg: color; checkFgOver: color; checkFgActive: color; @@ -236,6 +237,7 @@ Radiobutton { } InputArea { + textBg: color; textFg: color; textMargins: margins; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 68ce47b90..cf3f2d4af 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -19,6 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ +// basic windowBg: #ffffff; // white: fallback for background windowTextFg: #000000; // black: fallback for text color windowSubTextFg: #8a8a8a; // gray: fallback for subtext color @@ -28,5 +29,111 @@ windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over windowActiveTextFg: #1485c2; // online blue: fallback for active color windowShadowFg: #000000; // black: fallback for shadow color +imageBg: #000000; +imageBgTransparent: #ffffff; + +// tray icon +trayCounterBg: #f23c34; +trayCounterBgMute: #888888; +trayCounterFg: #ffffff; +trayCounterBgMacInvert: #ffffff; +trayCounterFgMacInvert: #ffffff01; + +cancelIconFg: #a2a2a2; +cancelIconFgOver: #808080; + +activeButtonBg: windowActiveFill; +activeButtonBgOver: #46b4eb | activeButtonBg; +activeButtonFg: #ffffff; +activeButtonFgOver: activeButtonFg; +activeButtonSecondaryFg: #cceeff; +activeButtonSecondaryFgOver: activeButtonSecondaryFg; + +lightButtonBg: windowBg; +lightButtonBgOver: #f2f7fa | lightButtonBg; +lightButtonFg: #2b99d5; +lightButtonFgOver: lightButtonFg; + +// window title +titleCounterBg: trayCounterBg; +titleCounterBgMute: trayCounterBgMute; +titleCounterFg: trayCounterFg; + +// layers +layerBg: #0000007f; + +// boxes boxBg: windowBg; -boxTitleFg: #444444 | windowTextFg; +boxTextFg: windowTextFg; +boxTextFgGood: #4ab44a; +boxTextFgError: #d84d4d; +boxTitleFg: #404040 | windowTextFg; +boxSearchBg: boxBg; +boxSearchCancelIconFg: cancelIconFg; +boxSearchCancelIconFgOver: cancelIconFgOver; + +membersAboutLimitFg: windowSubTextFg; + +contactsBg: windowBg; +contactsBgOver: windowOverBg; +contactsNameFg: boxTextFg; + +photoCropFadeBg: #0000007f; +photoCropPointFg: #ffffff7f; + +// settings +settingsBg: windowBg; +settingsFixedBarBg: settingsBg; +settingsCloseFg: cancelIconFg; +settingsCloseFgOver: cancelIconFgOver; + +notificationsBoxMonitorFg: windowTextFg; + +notificationSampleUserpicFg: windowActiveFill; +notificationSampleCloseFg: #d7d7d7 | windowSubTextFg; +notificationSampleTextFg: #d7d7d7 | windowSubTextFg; +notificationSampleNameFg: #939393 | windowSubTextFg; + +// intro +introHeaderFg: windowTextFg; +introErrorFg: windowTextFg; + +// history +topBarBg: windowBg; + +historyComposeAreaBg: windowBg; +historyPinnedBg: historyComposeAreaBg; +historyReplyBg: historyComposeAreaBg; +historyReplyCancelIconFg: cancelIconFg; +historyReplyCancelIconFgOver: cancelIconFgOver; + +historySendBg: historyComposeAreaBg; +historySendBgOver: #f5f5f5 | historySendBg; + +historyTextInFg: windowTextFg; +historyTextOutFg: windowTextFg; +historyCaptionInFg: historyTextInFg; +historyCaptionOutFg: historyTextOutFg; +historyFileNameInFg: historyTextInFg; +historyFileNameOutFg: historyTextOutFg; + +// mediaview +mediaviewFileBg: windowBg; +mediaviewFileNameFg: windowTextFg; +mediaviewFileSizeFg: windowSubTextFg; +mediaviewFileRedCornerFg: #d55959; +mediaviewFileYellowCornerFg: #e8a659; +mediaviewFileGreenCornerFg: #49a957; +mediaviewFileBlueCornerFg: #599dcf; +mediaviewFileExtFg: activeButtonFg; + +mediaviewMenuBg: #383838; +mediaviewMenuBgOver: #505050; +mediaviewMenuFg: #ffffff; + +mediaviewBg: #222222eb; +mediaviewVideoBg: #000000; +mediaviewControlBg: #0000003c; + +// notification +notificationBg: windowBg; diff --git a/Telegram/Resources/icons/box_cancel.png b/Telegram/Resources/icons/box_cancel.png deleted file mode 100644 index 7fa112a33b2e9cef41f273abc62eb07b4a1839b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a)4CQlc~5RHkYlP+?y8gj5qzuvy= zzucZ6AMIlK<9me-ofKa1om^E^uEb>Qe5vA0Psf3~7fWLqQl4?ll$^=w;G$5T{@&Ip zbk5B4+>>T3X#T+%w}OlB5&w$|j%WN;8NNysPd-?ce|Proi?)9k#;vHATWGZ-Z-cX7 m-C&U>J17(8A5T-G@yGywp}X-3=t diff --git a/Telegram/Resources/icons/box_cancel@2x.png b/Telegram/Resources/icons/box_cancel@2x.png deleted file mode 100644 index 40d3def3d2c56edb57dc818be15c1add75a32652..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 296 zcmV+@0oVSCP)CU0sydW8>EyV#t0+joP&9uVc+-f zo-niILPYsU&C5CWy6=MV< us`fKmy!Tx`KwVn9@0000b); + p.fillRect(QRect(0, 0, rect.width(), rect.height()), Qt::transparent); p.setCompositionMode(QPainter::CompositionMode_SourceOver); p.setRenderHint(QPainter::HighQualityAntialiasing); p.setPen(Qt::NoPen); @@ -2231,18 +2231,19 @@ namespace { if (cRetina()) ::emojiLarge->setDevicePixelRatio(cRetinaFactor()); } + style::color white = { 255, 255, 255, 255 }; QImage mask[4]; - prepareCorners(LargeMaskCorners, msgRadius(), st::white, nullptr, mask); + prepareCorners(LargeMaskCorners, msgRadius(), white, nullptr, mask); for (int i = 0; i < 4; ++i) { ::cornersMaskLarge[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied)); ::cornersMaskLarge[i]->setDevicePixelRatio(cRetinaFactor()); } - prepareCorners(SmallMaskCorners, st::buttonRadius, st::white, nullptr, mask); + prepareCorners(SmallMaskCorners, st::buttonRadius, white, nullptr, mask); for (int i = 0; i < 4; ++i) { ::cornersMaskSmall[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied)); ::cornersMaskSmall[i]->setDevicePixelRatio(cRetinaFactor()); } - prepareCorners(WhiteCorners, st::dateRadius, st::white); + prepareCorners(BotKbOverCorners, st::dateRadius, st::msgBotKbOverBg); prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg); prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceSelectBg); prepareCorners(SelectedOverlaySmallCorners, st::buttonRadius, st::msgSelectOverlay); @@ -2379,7 +2380,7 @@ namespace { } const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight) { - EmojiMap *map = &(fontHeight == st::taDefFlat.font->height ? mainEmojiMap : otherEmojiMap[fontHeight]); + EmojiMap *map = &(fontHeight == st::msgFont->height ? mainEmojiMap : otherEmojiMap[fontHeight]); EmojiMap::const_iterator i = map->constFind(emojiKey(emoji)); if (i == map->cend()) { QImage img(ESize + st::emojiPadding * cIntRetinaFactor() * 2, fontHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); @@ -2519,7 +2520,7 @@ namespace { #endif // OS_MAC_OLD } else if (opaque && result.hasAlphaChannel()) { QImage solid(result.width(), result.height(), QImage::Format_ARGB32_Premultiplied); - solid.fill(st::white->c); + solid.fill(st::imageBgTransparent->c); { QPainter(&solid).drawImage(0, 0, result); } diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index 106b79d70..bf2cd382b 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -19,11 +19,11 @@ 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/abstractbox.h" #include "localstorage.h" - -#include "abstractbox.h" +#include "lang.h" +#include "ui/buttons/icon_button.h" #include "mainwidget.h" #include "mainwindow.h" #include "styles/style_boxes.h" @@ -35,45 +35,6 @@ void BlueTitleShadow::paintEvent(QPaintEvent *e) { st::boxBlueTitleShadow.fill(p, QRect(r.left(), 0, r.width(), height())); } -BlueTitleClose::BlueTitleClose(QWidget *parent) : Button(parent) -, a_iconFg(st::boxBlueCloseFg->c) -, _a_over(animation(this, &BlueTitleClose::step_over)) { - resize(st::boxTitleHeight, st::boxTitleHeight); - setCursor(style::cur_pointer); - connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); -} - -void BlueTitleClose::onStateChange(int oldState, ButtonStateChangeSource source) { - if ((oldState & StateOver) != (_state & StateOver)) { - a_iconFg.start(((_state & StateOver) ? st::boxBlueCloseOverFg : st::boxBlueCloseFg)->c); - _a_over.start(); - } -} - -void BlueTitleClose::step_over(float64 ms, bool timer) { - float64 dt = ms / st::boxBlueCloseDuration; - if (dt >= 1) { - _a_over.stop(); - a_iconFg.finish(); - } else { - a_iconFg.update(dt, anim::linear); - } - if (timer) update((st::boxTitleHeight - st::boxBlueCloseIcon.width()) / 2, (st::boxTitleHeight - st::boxBlueCloseIcon.height()) / 2, st::boxBlueCloseIcon.width(), st::boxBlueCloseIcon.height()); -} - -void BlueTitleClose::paintEvent(QPaintEvent *e) { - Painter p(this); - - QRect r(e->rect()), s((st::boxTitleHeight - st::boxBlueCloseIcon.width()) / 2, (st::boxTitleHeight - st::boxBlueCloseIcon.height()) / 2, st::boxBlueCloseIcon.width(), st::boxBlueCloseIcon.height()); - if (!s.contains(r)) { - p.fillRect(r, st::boxBlueTitleBg); - } - if (s.intersects(r)) { - p.fillRect(s.intersected(r), a_iconFg.current()); - st::boxBlueCloseIcon.paint(p, s.topLeft(), width()); - } -} - AbstractBox::AbstractBox(int w) : LayerWidget() { setAttribute(Qt::WA_OpaquePaintEvent); resize(w, 0); @@ -116,8 +77,8 @@ bool AbstractBox::paint(QPainter &p) { void AbstractBox::paintTitle(Painter &p, const QString &title, const QString &additional) { p.setFont(st::boxTitleFont); if (_blueTitle) { - p.fillRect(0, 0, width(), st::boxTitleHeight, st::boxBlueTitleBg->b); - p.setPen(st::white); + p.fillRect(0, 0, width(), st::boxTitleHeight, st::boxBlueTitleBg); + p.setPen(st::boxBlueTitleFg); int32 titleWidth = st::boxTitleFont->width(title); p.drawTextLeft(st::boxBlueTitlePosition.x(), st::boxBlueTitlePosition.y(), width(), title, titleWidth); @@ -175,12 +136,9 @@ void AbstractBox::onClose() { void AbstractBox::setBlueTitle(bool blue) { _blueTitle = blue; - delete _blueShadow; - _blueShadow = new BlueTitleShadow(this); - delete _blueClose; - _blueClose = new BlueTitleClose(this); - _blueClose->setAttribute(Qt::WA_OpaquePaintEvent); - connect(_blueClose, SIGNAL(clicked()), this, SLOT(onClose())); + _blueShadow.create(this); + _blueClose.create(this, st::boxBlueClose); + _blueClose->setClickedCallback([this] { onClose(); }); } void AbstractBox::raiseShadow() { diff --git a/Telegram/SourceFiles/boxes/abstractbox.h b/Telegram/SourceFiles/boxes/abstractbox.h index 2f80e07ec..e140267c9 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.h +++ b/Telegram/SourceFiles/boxes/abstractbox.h @@ -23,6 +23,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "layerwidget.h" #include "ui/widgets/shadow.h" +namespace Ui { +class MaskButton; +} // namespace Ui + class BlueTitleShadow : public TWidget { public: BlueTitleShadow(QWidget *parent) : TWidget(parent) { @@ -30,24 +34,6 @@ public: void paintEvent(QPaintEvent *e); }; -class BlueTitleClose : public Button { - Q_OBJECT - -public: - BlueTitleClose(QWidget *parent); - void paintEvent(QPaintEvent *e); - -public slots: - - void onStateChange(int oldState, ButtonStateChangeSource source); - -private: - void step_over(float64 ms, bool timer); - anim::cvalue a_iconFg; - Animation _a_over; - -}; - class AbstractBox : public LayerWidget, protected base::Subscriber { Q_OBJECT @@ -78,8 +64,6 @@ protected: virtual void closePressed() { } virtual void showAll() { - if (_blueClose) _blueClose->show(); - if (_blueShadow) _blueShadow->show(); } private: @@ -89,8 +73,8 @@ private: bool _closed = false; bool _blueTitle = false; - BlueTitleClose *_blueClose = nullptr; - BlueTitleShadow *_blueShadow = nullptr; + ChildWidget _blueClose = { nullptr }; + ChildWidget _blueShadow = { nullptr }; }; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 305f20d93..d529be97c 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -113,8 +113,8 @@ void AddContactBox::paintEvent(QPaintEvent *e) { st::contactUserIcon.paint(p, st::boxPadding.left(), _first->y() + st::contactIconTop, width()); st::contactPhoneIcon.paint(p, st::boxPadding.left(), _phone->y() + st::contactIconTop, width()); } else { - p.setPen(st::black->p); - p.setFont(st::boxTextFont->f); + p.setPen(st::boxTextFg); + p.setFont(st::boxTextFont); int32 h = height() - st::boxTitleHeight - st::contactPadding.top() - st::contactPadding.bottom() - st::boxPadding.bottom() - st::boxButtonPadding.top() - _retry->height() - st::boxButtonPadding.bottom(); p.drawText(QRect(st::boxPadding.left(), st::boxTitleHeight + st::contactPadding.top(), width() - st::boxPadding.left() - st::boxPadding.right(), h), lng_contact_not_joined(lt_name, _sentName), style::al_topleft); } @@ -442,7 +442,7 @@ void GroupInfoBox::leaveEvent(QEvent *e) { } void GroupInfoBox::step_photoOver(float64 ms, bool timer) { - float64 dt = ms / st::setPhotoDuration; + float64 dt = ms / st::newGroupPhotoDuration; if (dt >= 1) { _a_photoOver.stop(); a_photoOver.finish(); @@ -675,7 +675,7 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) { _aboutPrivate.drawLeft(p, aboutPrivate.x(), aboutPrivate.y(), aboutPrivate.width(), width()); if (!_channel->isMegagroup() || !_link->isHidden()) { - p.setPen(st::black); + p.setPen(st::boxTextFg); p.setFont(st::newGroupLinkFont); p.drawTextLeft(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultInputField.textMargins.left(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop, width(), lang(_link->isHidden() ? lng_create_group_invite_link : lng_create_group_link)); } @@ -689,7 +689,7 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) { p.drawText(_invitationLink, _channel->inviteLink(), option); if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) { p.setOpacity(a_goodOpacity.current()); - p.setPen(st::setGoodColor); + p.setPen(st::boxTextFgGood); p.setFont(st::boxTextFont); p.drawTextRight(st::boxPadding.right(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _goodTextLink); p.setOpacity(1); @@ -697,11 +697,11 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) { } } else { if (!_errorText.isEmpty()) { - p.setPen(st::setErrColor); + p.setPen(st::boxTextFgError); p.setFont(st::boxTextFont); p.drawTextRight(st::boxPadding.right(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _errorText); } else if (!_goodText.isEmpty()) { - p.setPen(st::setGoodColor); + p.setPen(st::boxTextFgGood); p.setFont(st::boxTextFont); p.drawTextRight(st::boxPadding.right(), _link->y() - st::newGroupLinkPadding.top() + st::newGroupLinkTop + st::newGroupLinkFont->ascent - st::boxTextFont->ascent, width(), _goodText); } @@ -1454,7 +1454,7 @@ void RevokePublicLinkBox::paintChat(Painter &p, const ChatRow &row, bool selecte auto peer = row.peer; peer->paintUserpicLeft(p, st::contactsPhotoSize, st::contactsPadding.left(), st::contactsPadding.top(), width()); - p.setPen(st::black); + p.setPen(st::contactsNameFg); int32 namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); int32 namew = width() - namex - st::contactsPadding.right() - (_revokeWidth + st::contactsCheckPosition.x() * 2); diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index a3afb5ebc..3af7e9d24 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -20,16 +20,25 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "basic.style"; using "ui/widgets/widgets.style"; +using "intro/intro.style"; boxBlueTitleBg: #6393b5; +boxBlueTitleFg: #ffffff; boxBlueTitleAdditionalFg: #dae9f5; boxBlueTitleAdditionalSkip: 12px; boxBlueTitlePosition: point(23px, 18px); boxBlueTitleShadow: icon {{ "box_title_shadow", windowShadowFg }}; -boxBlueCloseFg: #c8e1f0; -boxBlueCloseOverFg: #ffffff; -boxBlueCloseIcon: icon {{ "box_button_close", boxBlueTitleBg }}; -boxBlueCloseDuration: 150; + +boxBlueClose: MaskButton(defaultMaskButton) { + width: boxTitleHeight; + height: boxTitleHeight; + + bg: boxBlueTitleBg; + iconBg: #c8e1f0; + iconBgOver: #ffffff; + + icon: icon {{ "box_button_close", boxBlueTitleBg }}; +} confirmInviteTitle: flatLabel(labelDefFlat) { font: font(16px semibold); @@ -88,6 +97,7 @@ contactsNewItemTop: 18px; contactsNewItemFg: #4b82af; contactsMultiSelect: MultiSelect { + bg: boxSearchBg; padding: margins(8px, 8px, 8px, 8px); maxHeight: 104px; scroll: flatScroll(solidScroll) { @@ -106,9 +116,9 @@ contactsMultiSelect: MultiSelect { font: normalFont; textBg: contactsBgOver; textFg: windowTextFg; - textActiveBg: windowActiveFill; - textActiveFg: white; - deleteFg: white; + textActiveBg: activeButtonBg; + textActiveFg: activeButtonFg; + deleteFg: activeButtonFg; deleteLeft: 10px; deleteStroke: 2px; duration: 150; @@ -136,16 +146,18 @@ contactsMultiSelect: MultiSelect { fieldIcon: fieldSearchIcon; fieldIconSkip: 36px; - fieldCancel: IconButton(defaultIconButton) { + fieldCancel: MaskButton(defaultMaskButton) { width: 41px; height: 48px; - opacity: 0.3; - overOpacity: 0.4; + bg: boxSearchBg; + icon: boxSearchCancelIcon; + + iconBg: boxSearchCancelIconFg; + iconBgOver: boxSearchCancelIconFgOver; - icon: boxCancelIcon; iconPosition: point(8px, 18px); - downIconPosition: point(8px, 19px); + downIconPosition: point(8px, 18px); } fieldCancelSkip: 34px; } @@ -183,7 +195,7 @@ shareScrollDuration: 300; notificationsBoxHeight: 450px; notificationsBoxMonitorTop: 63px; -notificationsBoxMonitor: icon {{ "monitor", #000000 }}; +notificationsBoxMonitor: icon {{ "monitor", notificationsBoxMonitorFg }}; notificationsBoxScreenTop: 10px; notificationsBoxScreenSize: size(280px, 160px); notificationsBoxScreenBg: titleBg; @@ -197,13 +209,8 @@ notificationsSampleMargin: 2px; notificationSampleOpacity: 0.5; notificationSampleSize: size(64px, 16px); -notificationSampleUserpicFg: #40ace3; -notificationSampleCloseFg: #d7d7d7; -notificationSampleTextFg: #d7d7d7; -notificationSampleNameFg: #939393; -membersAboutPadding: margins(0px, 12px, 0px, 12px); -membersAboutFg: #777777; +membersAboutLimitPadding: margins(0px, 12px, 0px, 12px); sessionsScroll: flatScroll(boxScroll) { round: 2px; @@ -216,10 +223,11 @@ sessionCurrentPadding: margins(0px, 7px, 0px, 4px); sessionCurrentHeight: 118px; sessionPadding: margins(21px, 10px, 21px, 0px); sessionNameFont: msgNameFont; -sessionActiveFont: msgDateFont; -sessionActiveColor: #aaaaaa; +sessionNameFg: boxTextFg; +sessionWhenFont: msgDateFont; +sessionWhenFg: #aaaaaa; sessionInfoFont: msgFont; -sessionInfoColor: #888888; +sessionInfoFg: #888888; sessionTerminateTop: 30px; sessionTerminateSkip: 18px; sessionTerminate: IconButton(defaultIconButton) { @@ -230,3 +238,46 @@ sessionTerminate: IconButton(defaultIconButton) { iconPosition: point(3px, 3px); downIconPosition: point(3px, 4px); } + +passcodeHeaderFont: font(19px); +passcodeHeaderHeight: 80px; +passcodeInput: flatInput(inpIntroPhone) { +} +passcodeSubmit: flatButton(introNextButton) { + textTop: 15px; + overTextTop: 15px; + downTextTop: 16px; + width: 225px; + font: font(19px); + overFont: font(19px); +} +passcodeSubmitSkip: 40px; +passcodePadding: margins(0px, 22px, 0px, 3px); +passcodeSkip: 31px; + +newGroupAboutFg: #808080; +newGroupPadding: margins(4px, 6px, 4px, 3px); +newGroupSkip: 17px; +newGroupInfoPadding: margins(0px, -4px, 0px, 1px); + +newGroupLinkPadding: margins(4px, 27px, 4px, 12px); +newGroupLinkTop: 3px; +newGroupLinkFont: font(16px); + +newGroupPhotoSize: 76px; +newGroupPhotoBg: #4eb5f0; +newGroupPhotoBgOver: #3fa9e7; +newGroupPhotoIcon: icon {{ "new_chat_photo", #ffffff }}; +newGroupPhotoIconPosition: point(23px, 25px); +newGroupPhotoDuration: 150; + +newGroupNamePosition: point(27px, 20px); + +newGroupDescriptionPadding: margins(0px, 23px, 0px, 14px); +newGroupDescription: InputArea(defaultInputArea) { + textMargins: margins(1px, 6px, 1px, 4px); + heightMax: 115px; +} + +newGroupPublicLinkPadding: margins(0px, 20px, 0px, 5px); +newGroupLinkFadeDuration: 5000; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 47d17bf6b..d907c745d 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -165,7 +165,7 @@ void ConfirmBox::paintEvent(QPaintEvent *e) { if (paint(p)) return; // draw box title / text - p.setPen(st::black->p); + p.setPen(st::boxTextFg); textstyleSet(&st::boxTextStyle); _text.drawLeftElided(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), 16, style::al_left); textstyleRestore(); @@ -276,7 +276,7 @@ void MaxInviteBox::paintEvent(QPaintEvent *e) { if (paint(p)) return; // draw box title / text - p.setPen(st::black->p); + p.setPen(st::boxTextFg); _text.drawLeftElided(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), 16, style::al_left); QTextOption option(style::al_left); @@ -286,7 +286,7 @@ void MaxInviteBox::paintEvent(QPaintEvent *e) { p.drawText(_invitationLink, _link, option); if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) { p.setOpacity(a_goodOpacity.current()); - p.setPen(st::setGoodColor); + p.setPen(st::boxTextFgGood); p.setFont(st::boxTextFont); p.drawTextLeft(st::boxPadding.left(), height() - st::boxButtonPadding.bottom() - _close->height() + st::defaultBoxButton.textTop + st::defaultBoxButton.font->ascent - st::boxTextFont->ascent, width(), _goodTextLink); p.setOpacity(1); @@ -380,7 +380,7 @@ void ConvertToSupergroupBox::paintEvent(QPaintEvent *e) { paintTitle(p, lang(lng_profile_convert_title)); // draw box title / text - p.setPen(st::black); + p.setPen(st::boxTextFg); textstyleSet(&st::boxTextStyle); _text.drawLeft(p, st::boxPadding.left(), st::boxTitleHeight, _textWidth, width()); _note.drawLeft(p, st::boxPadding.left(), st::boxTitleHeight + _textHeight + st::boxPadding.bottom(), _textWidth, width()); diff --git a/Telegram/SourceFiles/boxes/confirmphonebox.cpp b/Telegram/SourceFiles/boxes/confirmphonebox.cpp index af9c48d85..7dc63ab2d 100644 --- a/Telegram/SourceFiles/boxes/confirmphonebox.cpp +++ b/Telegram/SourceFiles/boxes/confirmphonebox.cpp @@ -264,7 +264,7 @@ void ConfirmPhoneBox::paintEvent(QPaintEvent *e) { p.setPen(st::usernameDefaultFg); errorText = lang(lng_confirm_phone_enter_code); } else { - p.setPen(st::setErrColor); + p.setPen(st::boxTextFgError); } auto errorTextRectLeft = st::usernamePadding.left(); auto errorTextRectTop = _code->y() + _code->height(); diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index 7f02db63d..f4f65460e 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -250,7 +250,7 @@ void AutoDownloadBox::paintEvent(QPaintEvent *e) { Painter p(this); if (paint(p)) return; - p.setPen(st::black); + p.setPen(st::boxTextFg); p.setFont(st::semiboldFont); p.drawTextLeft(st::boxTitlePosition.x(), st::boxTitlePosition.y(), width(), lang(lng_media_auto_photo)); p.drawTextLeft(st::boxTitlePosition.x(), _sectionHeight + st::boxTitlePosition.y(), width(), lang(lng_media_auto_audio)); diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index f945c9d7e..a59f7608b 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -951,7 +951,7 @@ void ContactsBox::Inner::paintDialog(Painter &p, uint64 ms, PeerData *peer, Cont } auto checkedRatio = 0.; - p.fillRect(0, 0, width(), _rowHeight, sel ? st::contactsBgOver : st::white); + p.fillRect(0, 0, width(), _rowHeight, sel ? st::contactsBgOver : st::contactsBg); if (paintDisabledCheck) { paintDisabledCheckUserpic(p, peer, st::contactsPadding.left(), st::contactsPadding.top(), width()); } else if (usingMultiSelect()) { @@ -970,12 +970,12 @@ void ContactsBox::Inner::paintDialog(Painter &p, uint64 ms, PeerData *peer, Cont } if (checkedRatio > 0) { if (checkedRatio < 1) { - p.setPen(style::interpolate(st::black, st::contactsNameCheckedFg, checkedRatio)); + p.setPen(style::interpolate(st::contactsNameFg, st::contactsNameCheckedFg, checkedRatio)); } else { p.setPen(st::contactsNameCheckedFg); } } else { - p.setPen(st::black); + p.setPen(st::contactsNameFg); } data->name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width()); @@ -1047,7 +1047,7 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { p.setClipRect(r); _time = unixtime(); - p.fillRect(r, st::white); + p.fillRect(r, st::contactsBg); uint64 ms = getms(); int32 yFrom = r.y(), yTo = r.y() + r.height(); @@ -1057,12 +1057,12 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { if (_chat) { p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); p.fillRect(0, _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, width(), st::lineWidth, st::shadowColor); - p.setPen(st::black); + p.setPen(st::boxTextFg); p.drawTextLeft(st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(lng_chat_all_members_admins)); int aboutw = width() - st::contactsPadding.left() - st::contactsPadding.right(); (_allAdmins->checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsNewItemHeight + st::contactsAboutTop, aboutw); } else { - p.fillRect(0, 0, width(), st::contactsNewItemHeight, (_newItemSel ? st::contactsBgOver : st::white)->b); + p.fillRect(0, 0, width(), st::contactsNewItemHeight, _newItemSel ? st::contactsBgOver : st::contactsBg); p.setFont(st::contactsNameFont); st::contactsNewItemIcon.paint(p, 0, 0, width()); p.setPen(st::contactsNewItemFg); @@ -1112,7 +1112,7 @@ void ContactsBox::Inner::paintEvent(QPaintEvent *e) { text = lang(lng_contacts_loading); p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); p.fillRect(0, _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, width(), st::lineWidth, st::shadowColor); - p.setPen(st::black); + p.setPen(st::boxTextFg); p.drawTextLeft(st::contactsPadding.left(), st::contactsNewItemTop, width(), lang(lng_chat_all_members_admins)); int aboutw = width() - st::contactsPadding.left() - st::contactsPadding.right(); (_allAdmins->checked() ? _aboutAllAdmins : _aboutAdmins).draw(p, st::contactsPadding.left(), st::contactsNewItemHeight + st::contactsAboutTop, aboutw); diff --git a/Telegram/SourceFiles/boxes/emojibox.cpp b/Telegram/SourceFiles/boxes/emojibox.cpp index 9aca04bc0..2300aa5ba 100644 --- a/Telegram/SourceFiles/boxes/emojibox.cpp +++ b/Telegram/SourceFiles/boxes/emojibox.cpp @@ -77,7 +77,7 @@ EmojiBox::EmojiBox() : _esize(EmojiSizes[EIndex + 1]) { fillBlocks(); _blockHeight = st::emojiReplaceInnerHeight; - + resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + 2 * st::emojiReplacePadding, st::boxTitleHeight + st::emojiReplacePadding + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + st::emojiReplacePadding); prepare(); @@ -127,8 +127,8 @@ void EmojiBox::paintEvent(QPaintEvent *e) { paintTitle(p, lang(lng_settings_emoji_list)); - p.setFont(st::emojiTextFont->f); - p.setPen(st::black->p); + p.setFont(st::emojiTextFont); + p.setPen(st::boxTextFg); int32 top = st::boxTitleHeight + st::emojiReplacePadding + (st::emojiReplaceHeight - _blockHeight) / 2; for (Blocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) { int32 rowSize = i->size(), left = (width() - rowSize * st::emojiReplaceWidth) / 2; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index cacffc59c..9292d671b 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -139,17 +139,17 @@ void MembersBox::Inner::paintEvent(QPaintEvent *e) { Painter p(this); _time = unixtime(); - p.fillRect(r, st::white->b); + p.fillRect(r, st::contactsBg); int32 yFrom = r.y() - st::membersPadding.top(), yTo = r.y() + r.height() - st::membersPadding.top(); p.translate(0, st::membersPadding.top()); if (_rows.isEmpty()) { - p.setFont(st::noContactsFont->f); - p.setPen(st::noContactsColor->p); + p.setFont(st::noContactsFont); + p.setPen(st::noContactsColor); p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); } else { if (_newItemHeight) { - p.fillRect(0, 0, width(), _newItemHeight, (_newItemSel ? st::contactsBgOver : st::white)->b); + p.fillRect(0, 0, width(), _newItemHeight, _newItemSel ? st::contactsBgOver : st::contactsBg); st::contactsNewItemIcon.paint(p, 0, 0, width()); p.setFont(st::contactsNameFont); p.setPen(st::contactsNewItemFg); @@ -170,8 +170,8 @@ void MembersBox::Inner::paintEvent(QPaintEvent *e) { p.translate(0, _rowHeight); } if (to == _rows.size() && _filter == MembersFilter::Recent && (_rows.size() < _channel->membersCount() || _rows.size() >= Global::ChatSizeMax())) { - p.setPen(st::membersAboutFg); - _about.draw(p, st::contactsPadding.left(), st::membersAboutPadding.top(), _aboutWidth, style::al_center); + p.setPen(st::membersAboutLimitFg); + _about.draw(p, st::contactsPadding.left(), st::membersAboutLimitPadding.top(), _aboutWidth, style::al_center); } } } @@ -237,10 +237,10 @@ void MembersBox::Inner::onKickConfirm() { void MembersBox::Inner::paintDialog(Painter &p, PeerData *peer, MemberData *data, bool sel, bool kickSel, bool kickDown) { UserData *user = peer->asUser(); - p.fillRect(0, 0, width(), _rowHeight, (sel ? st::contactsBgOver : st::white)->b); + p.fillRect(0, 0, width(), _rowHeight, sel ? st::contactsBgOver : st::contactsBg); peer->paintUserpicLeft(p, st::contactsPhotoSize, st::contactsPadding.left(), st::contactsPadding.top(), width()); - p.setPen(st::black); + p.setPen(st::contactsNameFg); int32 namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); int32 namew = width() - namex - st::contactsPadding.right() - (data->canKick ? (_kickWidth + st::contactsCheckPosition.x() * 2) : 0); @@ -348,7 +348,7 @@ void MembersBox::Inner::refresh() { _aboutHeight = 0; } else { _about.setText(st::boxTextFont, lng_channel_only_last_shown(lt_count, _rows.size())); - _aboutHeight = st::membersAboutPadding.top() + _about.countHeight(_aboutWidth) + st::membersAboutPadding.bottom(); + _aboutHeight = st::membersAboutLimitPadding.top() + _about.countHeight(_aboutWidth) + st::membersAboutLimitPadding.bottom(); if (_filter != MembersFilter::Recent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) { _aboutHeight = 0; } diff --git a/Telegram/SourceFiles/boxes/notifications_box.cpp b/Telegram/SourceFiles/boxes/notifications_box.cpp index dbd16898d..b63e365f3 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.cpp +++ b/Telegram/SourceFiles/boxes/notifications_box.cpp @@ -220,7 +220,7 @@ void NotificationsBox::prepareNotificationSampleSmall() { auto height = st::notificationSampleSize.height(); auto sampleImage = QImage(width * cIntRetinaFactor(), height * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); sampleImage.setDevicePixelRatio(cRetinaFactor()); - sampleImage.fill(st::notifyBg->c); + sampleImage.fill(st::notificationBg->c); { Painter p(&sampleImage); p.setPen(Qt::NoPen); @@ -263,7 +263,7 @@ void NotificationsBox::prepareNotificationSampleLarge() { int w = st::notifyWidth, h = st::notifyMinHeight; auto sampleImage = QImage(w * cIntRetinaFactor(), h * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); sampleImage.setDevicePixelRatio(cRetinaFactor()); - sampleImage.fill(st::notifyBg->c); + sampleImage.fill(st::notificationBg->c); { Painter p(&sampleImage); p.fillRect(0, 0, w - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder->b); diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp index d0ef9b754..8f8decb21 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.cpp +++ b/Telegram/SourceFiles/boxes/passcodebox.cpp @@ -19,13 +19,13 @@ 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/passcodebox.h" -#include "passcodebox.h" +#include "lang.h" #include "confirmbox.h" #include "mainwindow.h" - #include "localstorage.h" +#include "styles/style_boxes.h" PasscodeBox::PasscodeBox(bool turningOff) : AbstractBox(st::boxWidth) , _replacedBy(0) @@ -204,26 +204,25 @@ void PasscodeBox::paintEvent(QPaintEvent *e) { int32 w = st::boxWidth - st::boxPadding.left() * 1.5; int32 abouty = (_passwordHint.isHidden() ? (_reenterPasscode.isHidden() ? (_oldPasscode.y() + (_hasRecovery && !_hintText.isEmpty() ? st::passcodeSkip : 0)) : _reenterPasscode.y()) + st::passcodeSkip : _passwordHint.y() + st::contactSkip) + _oldPasscode.height(); - p.setPen(st::black); + p.setPen(st::boxTextFg); _about.drawLeft(p, st::boxPadding.left(), abouty, w, width()); if (!_hintText.isEmpty() && _oldError.isEmpty()) { - p.setPen(st::black->p); _hintText.drawLeftElided(p, st::boxPadding.left(), _oldPasscode.y() + _oldPasscode.height() + ((st::passcodeSkip - st::normalFont->height) / 2), w, width(), 1, style::al_topleft); } if (!_oldError.isEmpty()) { - p.setPen(st::setErrColor->p); + p.setPen(st::boxTextFgError); p.drawText(QRect(st::boxPadding.left(), _oldPasscode.y() + _oldPasscode.height(), w, st::passcodeSkip), _oldError, style::al_left); } if (!_newError.isEmpty()) { - p.setPen(st::setErrColor->p); + p.setPen(st::boxTextFgError); p.drawText(QRect(st::boxPadding.left(), _reenterPasscode.y() + _reenterPasscode.height(), w, st::passcodeSkip), _newError, style::al_left); } if (!_emailError.isEmpty()) { - p.setPen(st::setErrColor->p); + p.setPen(st::boxTextFgError); p.drawText(QRect(st::boxPadding.left(), _recoverEmail.y() + _recoverEmail.height(), w, st::passcodeSkip), _emailError, style::al_left); } @@ -525,12 +524,12 @@ void RecoverBox::paintEvent(QPaintEvent *e) { paintTitle(p, lang(lng_signin_recover_title)); p.setFont(st::normalFont); - p.setPen(st::black); + p.setPen(st::boxTextFg); int32 w = st::boxWidth - st::boxPadding.left() * 1.5; p.drawText(QRect(st::boxPadding.left(), _recoverCode.y() - st::passcodeSkip - st::passcodePadding.top(), w, st::passcodePadding.top() + st::passcodeSkip), _pattern, style::al_left); if (!_error.isEmpty()) { - p.setPen(st::setErrColor->p); + p.setPen(st::boxTextFgError); p.drawText(QRect(st::boxPadding.left(), _recoverCode.y() + _recoverCode.height(), w, st::passcodeSkip), _error, style::al_left); } } diff --git a/Telegram/SourceFiles/boxes/photocropbox.cpp b/Telegram/SourceFiles/boxes/photocropbox.cpp index 1e0e0de3e..34dc05bf1 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.cpp +++ b/Telegram/SourceFiles/boxes/photocropbox.cpp @@ -235,25 +235,25 @@ void PhotoCropBox::paintEvent(QPaintEvent *e) { p.translate(_thumbx, _thumby); p.drawPixmap(0, 0, _thumb); - p.setOpacity(0.5); if (_cropy > 0) { - p.fillRect(QRect(0, 0, _cropx + _cropw, _cropy), st::black->b); + p.fillRect(QRect(0, 0, _cropx + _cropw, _cropy), st::photoCropFadeBg); } if (_cropx + _cropw < _thumbw) { - p.fillRect(QRect(_cropx + _cropw, 0, _thumbw - _cropx - _cropw, _cropy + _cropw), st::black->b); + p.fillRect(QRect(_cropx + _cropw, 0, _thumbw - _cropx - _cropw, _cropy + _cropw), st::photoCropFadeBg); } if (_cropy + _cropw < _thumbh) { - p.fillRect(QRect(_cropx, _cropy + _cropw, _thumbw - _cropx, _thumbh - _cropy - _cropw), st::black->b); + p.fillRect(QRect(_cropx, _cropy + _cropw, _thumbw - _cropx, _thumbh - _cropy - _cropw), st::photoCropFadeBg); } if (_cropx > 0) { - p.fillRect(QRect(0, _cropy, _cropx, _thumbh - _cropy), st::black->b); + p.fillRect(QRect(0, _cropy, _cropx, _thumbh - _cropy), st::photoCropFadeBg); } - int32 delta = st::cropPointSize, mdelta(-delta / 2); - p.fillRect(QRect(_cropx + mdelta, _cropy + mdelta, delta, delta), st::white->b); - p.fillRect(QRect(_cropx + _cropw + mdelta, _cropy + mdelta, delta, delta), st::white->b); - p.fillRect(QRect(_cropx + _cropw + mdelta, _cropy + _cropw + mdelta, delta, delta), st::white->b); - p.fillRect(QRect(_cropx + mdelta, _cropy + _cropw + mdelta, delta, delta), st::white->b); + int delta = st::cropPointSize; + int mdelta = -delta / 2; + p.fillRect(QRect(_cropx + mdelta, _cropy + mdelta, delta, delta), st::photoCropPointFg); + p.fillRect(QRect(_cropx + _cropw + mdelta, _cropy + mdelta, delta, delta), st::photoCropPointFg); + p.fillRect(QRect(_cropx + _cropw + mdelta, _cropy + _cropw + mdelta, delta, delta), st::photoCropPointFg); + p.fillRect(QRect(_cropx + mdelta, _cropy + _cropw + mdelta, delta, delta), st::photoCropPointFg); } void PhotoCropBox::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 988e5b439..bec77968c 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -281,7 +281,7 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) { p.drawPixmapLeft(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), width(), userDefPhoto(1)->pixCircled(st::msgFileSize)); } p.setFont(st::semiboldFont); - p.setPen(st::black); + p.setPen(st::historyFileNameOutFg); _name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); style::color status(st::mediaOutFg); @@ -599,7 +599,7 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { icon->paintInCenter(p, inner); } p.setFont(st::semiboldFont); - p.setPen(st::black); + p.setPen(st::historyFileNameInFg); _name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); style::color status(st::mediaInFg); @@ -608,13 +608,13 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { p.drawTextLeft(x + nameleft, y + statustop, width(), _status); } else { p.setFont(st::boxTitleFont); - p.setPen(st::black); + p.setPen(st::boxTextFg); p.drawTextLeft(_field->x(), st::boxPhotoPadding.top(), width(), lang(lng_edit_message)); } if (!_error.isEmpty()) { p.setFont(st::normalFont); - p.setPen(st::setErrColor); + p.setPen(st::boxTextFgError); p.drawTextLeft(_field->x(), _field->y() + _field->height() + (st::boxButtonPadding.top() / 2), width(), _error); } } diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp index 8c93392f5..9d09e825f 100644 --- a/Telegram/SourceFiles/boxes/report_box.cpp +++ b/Telegram/SourceFiles/boxes/report_box.cpp @@ -19,9 +19,10 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "report_box.h" +#include "boxes/report_box.h" #include "lang.h" +#include "styles/style_boxes.h" #include "styles/style_profile.h" #include "boxes/confirmbox.h" #include "ui/buttons/checkbox.h" diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index cd3c10ad7..747a45dc5 100644 --- a/Telegram/SourceFiles/boxes/sessionsbox.cpp +++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp @@ -139,7 +139,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) { data.ip = qs(d.vip) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country); if (!data.hash || (d.vflags.v & 1)) { data.active = lang(lng_sessions_header); - data.activeWidth = st::sessionActiveFont->width(lang(lng_sessions_header)); + data.activeWidth = st::sessionWhenFont->width(lang(lng_sessions_header)); int32 availForName = availCurrent - st::sessionPadding.right() - data.activeWidth; if (data.nameWidth > availForName) { data.name = st::sessionNameFont->elided(data.name, availForName); @@ -167,7 +167,7 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) { } else { data.active = lastDate.toString(qsl("d.MM.yy")); } - data.activeWidth = st::sessionActiveFont->width(data.active); + data.activeWidth = st::sessionWhenFont->width(data.active); int32 availForName = availOther - st::sessionPadding.right() - data.activeWidth; if (data.nameWidth > availForName) { data.name = st::sessionNameFont->elided(data.name, availForName); @@ -257,7 +257,7 @@ void SessionsBox::Inner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); Painter p(this); - p.fillRect(r, st::white->b); + p.fillRect(r, st::boxBg); int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPosition.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip; int32 w = width(); @@ -270,24 +270,24 @@ void SessionsBox::Inner::paintEvent(QPaintEvent *e) { if (r.y() <= st::sessionCurrentHeight) { p.translate(0, st::sessionCurrentPadding.top()); - p.setFont(st::sessionNameFont->f); - p.setPen(st::black->p); + p.setFont(st::sessionNameFont); + p.setPen(st::sessionNameFg); p.drawTextLeft(x, st::sessionPadding.top(), w, _current->name, _current->nameWidth); - p.setFont(st::sessionActiveFont->f); - p.setPen(st::sessionActiveColor->p); + p.setFont(st::sessionWhenFont); + p.setPen(st::sessionWhenFg); p.drawTextRight(x, st::sessionPadding.top(), w, _current->active, _current->activeWidth); - p.setFont(st::sessionInfoFont->f); - p.setPen(st::black->p); + p.setFont(st::sessionInfoFont); + p.setPen(st::boxTextFg); p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, _current->info, _current->infoWidth); - p.setPen(st::sessionInfoColor->p); + p.setPen(st::sessionInfoFg); p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, _current->ip, _current->ipWidth); } p.translate(0, st::sessionCurrentHeight - st::sessionCurrentPadding.top()); if (_list->isEmpty()) { - p.setFont(st::sessionInfoFont->f); - p.setPen(st::sessionInfoColor->p); + p.setFont(st::sessionInfoFont); + p.setPen(st::sessionInfoFg); p.drawText(QRect(st::sessionPadding.left(), 0, width() - st::sessionPadding.left() - st::sessionPadding.right(), st::noContactsHeight), lang(lng_sessions_other_desc), style::al_topleft); return; } @@ -300,18 +300,18 @@ void SessionsBox::Inner::paintEvent(QPaintEvent *e) { for (int32 i = from; i < to; ++i) { const SessionsBox::Data &auth(_list->at(i)); - p.setFont(st::sessionNameFont->f); - p.setPen(st::black->p); + p.setFont(st::sessionNameFont); + p.setPen(st::sessionNameFg); p.drawTextLeft(x, st::sessionPadding.top(), w, auth.name, auth.nameWidth); - p.setFont(st::sessionActiveFont->f); - p.setPen(st::sessionActiveColor->p); + p.setFont(st::sessionWhenFont); + p.setPen(st::sessionWhenFg); p.drawTextRight(xact, st::sessionPadding.top(), w, auth.active, auth.activeWidth); - p.setFont(st::sessionInfoFont->f); - p.setPen(st::black->p); + p.setFont(st::sessionInfoFont); + p.setPen(st::boxTextFg); p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, auth.info, auth.infoWidth); - p.setPen(st::sessionInfoColor->p); + p.setPen(st::sessionInfoFg); p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, auth.ip, auth.ipWidth); p.translate(0, st::sessionHeight); diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 1d5cb8315..448abd9f9 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -532,7 +532,7 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) { auto ms = getms(); auto r = e->rect(); p.setClipRect(r); - p.fillRect(r, st::white); + p.fillRect(r, st::boxBg); auto yFrom = r.y(), yTo = r.y() + r.height(); auto rowFrom = yFrom / _rowHeight; auto rowTo = (yTo + _rowHeight - 1) / _rowHeight; diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index d83b2f360..dc5d29c3c 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -561,7 +561,7 @@ void StickersBox::Inner::paintEvent(QPaintEvent *e) { _a_shifting.step(); - p.fillRect(r, st::white); + p.fillRect(r, st::boxBg); p.setClipRect(r); int y = st::membersPadding.top(); @@ -632,7 +632,7 @@ void StickersBox::Inner::paintRow(Painter &p, int32 index) { p.setOpacity(current); QRect row(myrtlrect(_aboveShadow.getDimensions(st::boxShadowShift).left(), st::contactsPadding.top() / 2, width() - (st::contactsPadding.left() / 2) - _scrollbar - _aboveShadow.getDimensions(st::boxShadowShift).right(), _rowHeight - ((st::contactsPadding.top() + st::contactsPadding.bottom()) / 2))); _aboveShadow.paint(p, row, st::boxShadowShift); - p.fillRect(row, st::white); + p.fillRect(row, st::boxBg); p.setOpacity(1); } } else if (s->installed && !s->disabled) { @@ -669,7 +669,7 @@ void StickersBox::Inner::paintRow(Painter &p, int32 index) { int statusy = st::contactsPadding.top() + st::contactsStatusTop; p.setFont(st::contactsNameFont); - p.setPen(st::black); + p.setPen(st::contactsNameFg); p.drawTextLeft(namex, namey, width(), s->title, s->titleWidth); if (s->unread) { diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp index c4ae6190c..dea3b4056 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.cpp +++ b/Telegram/SourceFiles/boxes/usernamebox.cpp @@ -79,16 +79,16 @@ void UsernameBox::paintEvent(QPaintEvent *e) { p.setPen(st::usernameDefaultFg); p.drawTextLeft(st::usernamePadding.left(), _username.y() + _username.height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _copiedTextLink); } else if (!_errorText.isEmpty()) { - p.setPen(st::setErrColor); + p.setPen(st::boxTextFgError); p.drawTextLeft(st::usernamePadding.left(), _username.y() + _username.height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _errorText); } else if (!_goodText.isEmpty()) { - p.setPen(st::setGoodColor); + p.setPen(st::boxTextFgGood); p.drawTextLeft(st::usernamePadding.left(), _username.y() + _username.height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), _goodText); } else { p.setPen(st::usernameDefaultFg); p.drawTextLeft(st::usernamePadding.left(), _username.y() + _username.height() + ((st::usernameSkip - st::boxTextFont->height) / 2), width(), lang(lng_username_choose)); } - p.setPen(st::black); + p.setPen(st::boxTextFg); textstyleSet(&st::usernameTextStyle); int32 availw = st::boxWidth - st::usernamePadding.left(), h = _about.countHeight(availw); _about.drawLeft(p, st::usernamePadding.left(), _username.y() + _username.height() + st::usernameSkip, availw, width()); diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 39b4019bb..b01feaee1 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -172,6 +172,14 @@ QString moduleBaseName(const structure::Module &module) { return moduleIsPalette ? "palette" : "style_" + moduleInfo.baseName(); } +QString colorFallbackName(structure::Value value) { + auto copy = value.copyOf(); + if (!copy.isEmpty()) { + return copy.back(); + } + return value.Color().fallback; +} + QChar paletteColorPart(uchar part) { part = (part & 0x0F); if (part >= 10) { @@ -436,27 +444,24 @@ public:\n\ void finalize();\n\ \n"; - QByteArray checksumString; int indexInPalette = 0; - if (!module_.enumVariables([this, &indexInPalette, &checksumString](const Variable &value) -> bool { - auto name = value.name.back(); - if (value.value.type().tag != structure::TypeTag::Color) { + if (!module_.enumVariables([this, &indexInPalette](const Variable &variable) -> bool { + auto name = variable.name.back(); + if (variable.value.type().tag != structure::TypeTag::Color) { return false; } - auto type = typeToString(value.value.type()); + auto type = typeToString(variable.value.type()); auto index = (indexInPalette++); header_->stream() << "\tinline const " << type << " &" << name << "() const { return _colors[" << index << "]; };\n"; - checksumString.append(':' + name); return true; })) return false; - auto checksum = hashCrc32(checksumString.constData(), checksumString.size()); auto count = indexInPalette; auto type = typeToString({ structure::TypeTag::Color }); header_->stream() << "\ \n\ - static constexpr int32 kChecksum = " << checksum << ";\n\ + static int32 Checksum();\n\ \n\ private:\n\ struct TempColorData { uchar r, g, b, a; };\n\ @@ -470,6 +475,7 @@ private:\n\ \n\ " << type << " _colors[" << count << "] = { Qt::Uninitialized };\n\ Status _status[" << count << "] = { Status::Initial };\n\ + bool _ready = false;\n\ \n\ };\n\ \n\ @@ -614,10 +620,14 @@ bool Generator::writeRefsDefinition() { bool Generator::writeSetPaletteColor() { source_->newline(); - source_->stream() << "void palette::finalize() {\n"; + source_->stream() << "\ +void palette::finalize() {\n\ + if (_ready) return;\n\ + _ready = true;\n\n"; int indexInPalette = 0; - bool result = module_.enumVariables([this, &indexInPalette](const Variable &variable) -> bool { + QByteArray checksumString; + bool result = module_.enumVariables([this, &indexInPalette, &checksumString](const Variable &variable) -> bool { auto name = variable.name.back(); auto index = indexInPalette++; paletteIndices_[name] = index; @@ -625,15 +635,21 @@ bool Generator::writeSetPaletteColor() { return false; } auto color = variable.value.Color(); - auto fallbackIndex = paletteIndices_.value(variable.value.Color().fallback, -1); + auto fallbackIndex = paletteIndices_.value(colorFallbackName(variable.value), -1); source_->stream() << "\tcompute(" << index << ", " << fallbackIndex << ", {" << color.red << ", " << color.green << ", " << color.blue << ", " << color.alpha << "});\n"; + checksumString.append('&' + name + ':' + valueAssignmentCode(variable.value)); return true; }); auto count = indexInPalette; + auto checksum = hashCrc32(checksumString.constData(), checksumString.size()); source_->stream() << "\ }\n\ \n\ +int32 palette::Checksum() {\n\ + return " << checksum << ";\n\ +}\n\ +\n\ void palette::compute(int index, int fallbackIndex, TempColorData data) {\n\ if (_status[index] == Status::Initial) {\n\ if (fallbackIndex >= 0 && _status[fallbackIndex] != Status::Initial) {\n\ @@ -711,6 +727,8 @@ int getPaletteIndex(QLatin1String name) {\n\ source_->newline().popNamespace().newline(); source_->stream() << "\ QByteArray palette::save() const {\n\ + if (!_ready) const_cast(this)->finalize();\n\ +\n\ auto result = QByteArray(" << (count * 4) << ", Qt::Uninitialized);\n\ for (auto i = 0, index = 0; i != " << count << "; ++i) {\n\ result[index++] = static_cast(_colors[i]->c.red());\n\ @@ -1100,7 +1118,7 @@ bool Generator::writeSampleTheme(const QString &filepath) { } auto color = variable.value.Color(); auto colorString = paletteColorValue(color); - auto fallbackIndex = paletteIndices_.value(variable.value.Color().fallback, -1); + auto fallbackIndex = paletteIndices_.value(colorFallbackName(variable.value), -1); if (fallbackIndex >= 0) { auto fallbackVariable = module_.findVariableInModule(names[fallbackIndex], module_); if (!fallbackVariable || fallbackVariable->value.type().tag != structure::TypeTag::Color) { diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index 69ea7aa50..8a2fe4dea 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -534,7 +534,13 @@ structure::Value ParsedFile::readColorValue() { } else { logErrorUnexpectedToken() << "color value alias, unique color values are only allowed in palette module"; } + } else if (auto transparentName = file_.getToken(BasicType::Name)) { + if (tokenValue(transparentName) == "transparent") { + return { structure::data::color { 255, 255, 255, 0 } }; + } + file_.putBack(); } + return {}; } diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 4ad5a4543..f6a78f96a 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -83,11 +83,12 @@ dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) { linkFgDown: #c6e1f7; } +dialogsNewChatIcon: icon {{ "dialogs_new_chat", #b7b7b7 }}; dialogsNewChatButton: IconButton(defaultIconButton) { width: 36px; height: 36px; - icon: icon {{ "dialogs_new_chat", #b7b7b7 }}; + icon: dialogsNewChatIcon; iconPosition: point(9px, 10px); downIconPosition: point(9px, 11px); } @@ -128,5 +129,3 @@ historySendingIcon: icon {{ "dialogs_sending", #98d292, point(5px, 5px) }}; historySendingInvertedIcon: icon {{ "dialogs_sending", #ffffffc8, point(5px, 5px) }}; historyViewsSendingIcon: icon {{ "dialogs_sending", #a0adb5, point(3px, 0px) }}; historyViewsSendingInvertedIcon: icon {{ "dialogs_sending", #ffffffc8, point(3px, 0px) }}; - -settingsEditIcon: icon {{ "dialogs_new_chat", #b7b7b7, point(3px, 9px) }}; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index d303f5b04..312c3d7da 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -293,17 +293,17 @@ void RowPainter::paint(Painter &p, const FakeRow *row, int w, bool active, bool } void paintImportantSwitch(Painter &p, Mode current, int w, bool selected, bool onlyBackground) { - p.fillRect(0, 0, w, st::dialogsImportantBarHeight, selected ? st::dialogsBgOver : st::white); + p.fillRect(0, 0, w, st::dialogsImportantBarHeight, selected ? st::dialogsBgOver : st::dialogsBg); if (onlyBackground) { return; } p.setFont(st::semiboldFont); - p.setPen(st::black); + p.setPen(st::dialogsNameFg); int unreadTop = (st::dialogsImportantBarHeight - st::dialogsUnreadHeight) / 2; bool mutedHidden = (current == Dialogs::Mode::Important); - QString text = mutedHidden ? qsl("Show all chats") : qsl("Hide muted chats"); + QString text = lang(mutedHidden ? lng_dialogs_show_all_chats : lng_dialogs_hide_muted_chats); int textBaseline = unreadTop + (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2 + st::dialogsUnreadFont->ascent; p.drawText(st::dialogsPadding.x(), textBaseline, text); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index b6de91dbe..5fc8b3b10 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -105,7 +105,7 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO shownDialogs()->all().paint(p, fullWidth(), dialogsClip.top(), dialogsClip.top() + dialogsClip.height(), active, selected, paintingOther); } if (!otherStart) { - p.fillRect(dialogsClip, st::white); + p.fillRect(dialogsClip, st::dialogsBg); if (!paintingOther) { p.setFont(st::noContactsFont); p.setPen(st::noContactsColor); @@ -120,11 +120,10 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO if (from < _hashtagResults.size()) { int32 w = fullWidth(), htagwidth = w - st::dialogsPadding.x() * 2; - p.setFont(st::mentionFont->f); - p.setPen(st::black->p); + p.setFont(st::mentionFont); for (; from < to; ++from) { bool selected = (from == _hashtagSel); - p.fillRect(0, 0, w, st::mentionHeight, (selected ? st::mentionBgOver : st::white)->b); + p.fillRect(0, 0, w, st::mentionHeight, selected ? st::mentionBgOver : st::dialogsBg); if (!paintingOther) { if (selected) { int skip = (st::mentionHeight - st::simpleCloseIcon.height()) / 2; @@ -2501,8 +2500,8 @@ void DialogsWidget::paintEvent(QPaintEvent *e) { int retina = cIntRetinaFactor(); if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, _cacheUnder.height())); - p.setOpacity(a_progress.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina, st::black); + p.setOpacity(a_progress.current()); + p.fillRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina, st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, _cacheOver.height() / retina), _cacheOver, QRect(0, 0, _cacheOver.width(), _cacheOver.height())); @@ -2512,11 +2511,11 @@ void DialogsWidget::paintEvent(QPaintEvent *e) { } QRect above(0, 0, width(), _scroll.y()); if (above.intersects(r)) { - p.fillRect(above.intersected(r), st::white->b); + p.fillRect(above.intersected(r), st::dialogsBg); } QRect below(0, _scroll.y() + qMin(_scroll.height(), _inner.height()), width(), height()); if (below.intersects(r)) { - p.fillRect(below.intersected(r), st::white->b); + p.fillRect(below.intersected(r), st::dialogsBg); } } diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index d04c2ab2f..ada41bb66 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -65,7 +65,7 @@ void FieldAutocomplete::paintEvent(QPaintEvent *e) { return; } - p.fillRect(rect(), st::white); + p.fillRect(rect(), st::mentionBg); } void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool addInlineBots) { @@ -549,7 +549,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#'); int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize; int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right(); - int32 htagleft = st::historyAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; + int32 htagleft = st::historyAttachPhoto.width + st::historyComposeField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; if (!_srows->isEmpty()) { int32 rows = rowscount(_srows->size(), _stickersPerRow); @@ -604,13 +604,12 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { bool selected = (i == _sel); if (selected) { - p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b); + p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver); int skip = (st::mentionHeight - st::simpleCloseIcon.height()) / 2; if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) { st::simpleCloseIcon.paint(p, QPoint(width() - st::simpleCloseIcon.width() - skip, i * st::mentionHeight + skip), width()); } } - p.setPen(st::black->p); if (!_mrows->isEmpty()) { UserData *user = _mrows->at(i); QString first = (!filterIsEmpty && user->username.startsWith(filter, Qt::CaseInsensitive)) ? ('@' + user->username.mid(0, filterSize)) : QString(); @@ -632,9 +631,11 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { } user->loadUserpic(); user->paintUserpicLeft(p, st::mentionPhotoSize, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width()); + + p.setPen(st::mentionNameFg); user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); - p.setFont(st::mentionFont->f); + p.setFont(st::mentionFont); p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); if (!second.isEmpty()) { @@ -678,7 +679,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { auto commandText = '/' + toHighlight; - p.setPen(st::windowTextFg); + p.setPen(st::mentionNameFg); p.setFont(st::semiboldFont); p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop + st::semiboldFont->ascent, commandText); diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 1e6904521..2c0d2dea8 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -140,6 +140,27 @@ historyPeer8UserpicBg: #f7b37c; historyPeer8UserpicFg: #de8d62; historyPeer8UserpicPerson: icon {{ size(120px, 120px), historyPeer8UserpicBg }, { "userpic_person", historyPeer8UserpicFg }}; +historyComposeField: flatTextarea { + textColor: #000000; + bgColor: historyComposeAreaBg; + align: align(left); + textMrg: margins(5px, 5px, 5px, 5px); + font: msgFont; + cursor: cursor(text); + + phColor: #999999; + phFocusColor: #aaaaaa; + phAlign: align(topleft); + phPos: point(2px, 0px); + phShift: 50px; + phDuration: 100; + phLeftFunc: transition(linear); + phAlphaFunc: transition(linear); + phColorFunc: transition(linear); +} +historyComposeFieldMaxHeight: 224px; +// historyMinHeight: 56px; + historyMediaTypeFile: icon {{ "media_type_file", #b3b3b3, point(2px, 2px) }}; historyMediaTypePhoto: icon {{ "media_type_photo", #bebebe, point(2px, 2px) }}; historyMediaTypeVideo: icon {{ "media_type_video", #bebebe, point(2px, 2px) }}; @@ -192,13 +213,14 @@ historyRecordSignalMax: 12px; historyRecordCancel: #aaaaaa; historyRecordCancelActive: #ec6466; historyRecordFont: font(13px); +historyRecordDurationFg: #000000; historyRecordTextTop: 14px; historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { menu: Menu(defaultMenu) { skip: 5px; - itemBgOver: btnWhiteHover; + itemBgOver: historySendBgOver; itemIconPosition: point(12px, 6px); itemIconOpacity: 0.78; itemIconOverOpacity: 1.; @@ -220,18 +242,18 @@ historyReplyIconPosition: point(13px, 13px); historyReplyIcon: icon {{ "history_action_reply", #6fa1d2 }}; historyForwardIcon: icon {{ "history_action_forward", #6fa1d2 }}; historyEditIcon: icon {{ "history_action_edit", #6fa1d2 }}; -historyReplyCancel: IconButton(defaultIconButton) { +historyReplyCancel: MaskButton(defaultMaskButton) { width: 49px; height: 49px; + bg: historyReplyBg; icon: historyReplyCancelIcon; - iconPosition: point(17px, 17px); - downIconPosition: point(17px, 18px); + + iconBg: historyReplyCancelIconFg; + iconBgOver: historyReplyCancelIconFgOver; } -historyInlineBotCancel: IconButton(historyReplyCancel) { +historyInlineBotCancel: MaskButton(historyReplyCancel) { height: 46px; - iconPosition: point(-1px, 16px); // < 0 means draw in the center of the button - downIconPosition: point(-1px, 17px); } topBarSearch: IconButton(defaultIconButton) { diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp index d3aa25800..54a4e2861 100644 --- a/Telegram/SourceFiles/history/history_drag_area.cpp +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -88,14 +88,14 @@ void DragArea::paintEvent(QPaintEvent *e) { // draw shadow _shadow.paint(p, r, st::boxShadowShift); - p.fillRect(r, st::white->b); + p.fillRect(r, st::dragBg); p.setPen(a_color.current()); - p.setFont(st::dragFont->f); + p.setFont(st::dragFont); p.drawText(QRect(0, (height() - st::dragHeight) / 2, width(), st::dragFont->height), _text, QTextOption(style::al_top)); - p.setFont(st::dragSubfont->f); + p.setFont(st::dragSubfont); p.drawText(QRect(0, (height() + st::dragHeight) / 2 - st::dragSubfont->height, width(), st::dragSubfont->height * 2), _subtext, QTextOption(style::al_top)); } diff --git a/Telegram/SourceFiles/history/history_location_manager.cpp b/Telegram/SourceFiles/history/history_location_manager.cpp index 49633f7df..82f4b6be3 100644 --- a/Telegram/SourceFiles/history/history_location_manager.cpp +++ b/Telegram/SourceFiles/history/history_location_manager.cpp @@ -81,7 +81,7 @@ void LocationManager::init() { delete notLoadedPlaceholder; } auto data = QImage(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - data.fill(st::white->c); + data.fill(st::imageBgTransparent->c); data.setDevicePixelRatio(cRetinaFactor()); notLoadedPlaceholder = new ImagePtr(App::pixmapFromImageInPlace(std_::move(data)), "GIF"); } diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index a0ade85f4..80e320824 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -416,11 +416,10 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uin if (selected) { p.setBrush(st::msgDateImgBgSelected); } else if (isThumbAnimation(ms)) { - float64 over = _animation->a_thumbOver.current(); - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); - p.setBrush(st::black); + auto over = _animation->a_thumbOver.current(); + p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { - bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); + auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } @@ -460,7 +459,7 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uin _parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); } } else { - p.setPen(st::black); + p.setPen(outbg ? st::historyCaptionOutFg : st::historyCaptionInFg); _caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); } } @@ -755,9 +754,8 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, uin if (selected) { p.setBrush(st::msgDateImgBgSelected); } else if (isThumbAnimation(ms)) { - float64 over = _animation->a_thumbOver.current(); - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); - p.setBrush(st::black); + auto over = _animation->a_thumbOver.current(); + p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); @@ -790,7 +788,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, uin int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); p.setFont(st::normalFont); - p.setPen(st::white); + p.setPen(st::msgDateImgColor); p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x()); // date @@ -800,7 +798,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, uin _parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); } } else { - p.setPen(st::black); + p.setPen(outbg ? st::historyCaptionOutFg : st::historyCaptionInFg); _caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); } } @@ -1110,11 +1108,10 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, if (selected) { p.setBrush(st::msgDateImgBgSelected); } else if (isThumbAnimation(ms)) { - float64 over = _animation->a_thumbOver.current(); - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); - p.setBrush(st::black); + auto over = _animation->a_thumbOver.current(); + p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { - bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); + auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } p.setOpacity(radialOpacity * p.opacity()); @@ -1159,10 +1156,10 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, if (selected) { p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); } else if (isThumbAnimation(ms)) { - float64 over = _animation->a_thumbOver.current(); + auto over = _animation->a_thumbOver.current(); p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over)); } else { - bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); + auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(outbg ? (over ? st::msgFileOutBgOver : st::msgFileOutBg) : (over ? st::msgFileInBgOver : st::msgFileInBg)); } @@ -1256,7 +1253,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, } } else if (auto named = Get()) { p.setFont(st::semiboldFont); - p.setPen(st::black); + p.setPen(outbg ? st::historyFileNameOutFg : st::historyFileNameInFg); if (namewidth < named->_namew) { p.drawTextLeft(nameleft, nametop, _width, st::semiboldFont->elided(named->_name, namewidth)); } else { @@ -1282,7 +1279,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, } if (auto captioned = Get()) { - p.setPen(st::black); + p.setPen(outbg ? st::historyCaptionOutFg : st::historyCaptionInFg); captioned->_caption.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, selection); } } @@ -1733,11 +1730,10 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, uint6 if (selected) { p.setBrush(st::msgDateImgBgSelected); } else if (isThumbAnimation(ms)) { - float64 over = _animation->a_thumbOver.current(); - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); - p.setBrush(st::black); + auto over = _animation->a_thumbOver.current(); + p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { - bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); + auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } p.setOpacity(radialOpacity * p.opacity()); @@ -1773,13 +1769,13 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, uint6 int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); p.setFont(st::normalFont); - p.setPen(st::white); + p.setPen(st::msgDateImgColor); p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x()); } } if (!_caption.isEmpty()) { - p.setPen(st::black); + p.setPen(outbg ? st::historyCaptionOutFg : st::historyCaptionInFg); _caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); } else if (_parent->getMedia() == this && (_data->uploading() || App::hoveredItem() == _parent)) { int32 fullRight = skipx + width, fullBottom = skipy + height; @@ -2318,7 +2314,7 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, u int32 namewidth = width - nameleft - nameright; p.setFont(st::semiboldFont); - p.setPen(st::black); + p.setPen(outbg ? st::historyFileNameOutFg : st::historyFileNameInFg); _name.drawLeftElided(p, nameleft, nametop, namewidth, width); style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); @@ -2699,7 +2695,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u tshift += _lineHeight; } if (_titleLines) { - p.setPen(st::black); + p.setPen(outbg ? st::webPageTitleOutFg : st::webPageTitleInFg); int32 endskip = 0; if (_title.hasSkipBlock()) { endskip = _parent->skipBlockWidth(); @@ -2708,7 +2704,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u tshift += _titleLines * _lineHeight; } if (_descriptionLines) { - p.setPen(st::black); + p.setPen(outbg ? st::webPageDescriptionOutFg : st::webPageDescriptionInFg); int32 endskip = 0; if (_description.hasSkipBlock()) { endskip = _parent->skipBlockWidth(); @@ -3078,7 +3074,7 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint tshift += _titleLines * _lineHeight; } if (_descriptionLines) { - p.setPen(st::black); + p.setPen(outbg ? st::webPageDescriptionOutFg : st::webPageDescriptionInFg); int32 endskip = 0; if (_description.hasSkipBlock()) { endskip = _parent->skipBlockWidth(); @@ -3390,12 +3386,13 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection, width -= st::mediaPadding.left() + st::mediaPadding.right(); int32 textw = _width - st::msgPadding.left() - st::msgPadding.right(); - p.setPen(st::black); if (!_title.isEmpty()) { + p.setPen(outbg ? st::webPageTitleOutFg : st::webPageTitleInFg); _title.drawLeftElided(p, skipx + st::msgPadding.left(), skipy, textw, _width, 2, style::al_left, 0, -1, 0, false, selection); skipy += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height); } if (!_description.isEmpty()) { + p.setPen(outbg ? st::webPageDescriptionOutFg : st::webPageDescriptionInFg); _description.drawLeftElided(p, skipx + st::msgPadding.left(), skipy, textw, _width, 3, style::al_left, 0, -1, 0, false, toDescriptionSelection(selection)); skipy += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height); } @@ -3423,7 +3420,7 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection, } p.drawPixmap(QPoint(skipx, skipy), pix); } else { - App::roundRect(p, skipx, skipy, width, height, st::white, MessageInCorners); + App::roundRect(p, skipx, skipy, width, height, st::msgInBg, MessageInCorners); } if (selected) { App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners); diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index e39e437d3..dddced027 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -247,7 +247,7 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in if (flags & PaintInBubble) { bar = ((flags & PaintSelected) ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor)); } else { - bar = st::white; + bar = st::msgImgReplyBarColor; } QRect rbar(rtlrect(x + st::msgReplyBarPos.x(), y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), w + 2 * x)); p.fillRect(rbar, bar); @@ -271,7 +271,7 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in if (flags & PaintInBubble) { p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); } else { - p.setPen(st::white); + p.setPen(st::msgImgReplyBarColor); } replyToName.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip, w + 2 * x); if (_replyToVia && w > st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew) { @@ -292,7 +292,7 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in } else { p.setFont(st::msgDateFont); style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg)); - p.setPen((flags & PaintInBubble) ? date : st::white); + p.setPen((flags & PaintInBubble) ? date : st::msgDateImgColor); p.drawTextLeft(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2, w + 2 * x, st::msgDateFont->elided(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message), w - st::msgReplyBarSkip)); } } @@ -316,9 +316,9 @@ void HistoryMessage::KeyboardStyle::paintButtonBg(Painter &p, const QRect &rect, howMuchOver = 1.; } if (howMuchOver > 0) { - float64 o = p.opacity(); - p.setOpacity(o * (howMuchOver * st::msgBotKbOverOpacity)); - App::roundRect(p, rect, st::white, WhiteCorners); + auto o = p.opacity(); + p.setOpacity(o * howMuchOver); + App::roundRect(p, rect, st::msgBotKbOverBg, BotKbOverCorners); p.setOpacity(o); } } @@ -1412,7 +1412,8 @@ void HistoryMessage::paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) } void HistoryMessage::paintText(Painter &p, QRect &trect, TextSelection selection) const { - p.setPen(st::msgColor); + bool outbg = out() && !isPost(); + p.setPen(outbg ? st::historyTextOutFg : st::historyTextInFg); p.setFont(st::msgFont); _text.draw(p, trect.x(), trect.y(), trect.width(), style::al_left, 0, -1, selection); } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 1fa4ecea5..6179aceb3 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -371,9 +371,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) { App::roundRect(p, _botAbout->rect, st::msgInBg, MessageInCorners, &st::msgInShadow); p.setFont(st::msgNameFont); - p.setPen(st::black); + p.setPen(st::dialogsNameFg); p.drawText(_botAbout->rect.left() + st::msgPadding.left(), _botAbout->rect.top() + st::msgPadding.top() + st::msgNameFont->ascent, lang(lng_bot_description)); + p.setPen(st::historyTextInFg); _botAbout->info->text.draw(p, _botAbout->rect.left() + st::msgPadding.left(), _botAbout->rect.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip, _botAbout->width); textstyleRestore(); @@ -2303,7 +2304,7 @@ void HistoryInner::onParentGeometryChanged() { MessageField::MessageField(HistoryWidget *history, const style::flatTextarea &st, const QString &ph, const QString &val) : FlatTextarea(history, st, ph, val), history(history) { setMinHeight(st::btnSend.height - 2 * st::sendPadding); - setMaxHeight(st::maxFieldHeight); + setMaxHeight(st::historyComposeFieldMaxHeight); } bool MessageField::hasSendText() const { @@ -2397,11 +2398,11 @@ void ReportSpamPanel::resizeEvent(QResizeEvent *e) { void ReportSpamPanel::paintEvent(QPaintEvent *e) { Painter p(this); - p.fillRect(QRect(0, 0, width(), height() - st::lineWidth), st::reportSpamBg->b); + p.fillRect(QRect(0, 0, width(), height() - st::lineWidth), st::reportSpamBg); p.fillRect(Adaptive::OneColumn() ? 0 : st::lineWidth, height() - st::lineWidth, width() - (Adaptive::OneColumn() ? 0 : st::lineWidth), st::lineWidth, st::shadowColor->b); if (!_clear.isHidden()) { - p.setPen(st::black->p); - p.setFont(st::msgFont->f); + p.setPen(st::reportSpamFg); + p.setFont(st::msgFont); p.drawText(QRect(_report.x(), (_clear.y() - st::msgFont->height) / 2, _report.width(), st::msgFont->height), lang(lng_report_spam_thanks), style::al_top); } } @@ -2427,8 +2428,8 @@ BotKeyboard::BotKeyboard() { void BotKeyboard::paintEvent(QPaintEvent *e) { Painter p(this); - QRect clip(e->rect()); - p.fillRect(clip, st::white); + auto clip = e->rect(); + p.fillRect(clip, st::historyComposeAreaBg); if (_impl) { int x = rtl() ? st::botKbScroll.width : _st->margin; @@ -2729,10 +2730,9 @@ bool HistoryHider::withConfirm() const { void HistoryHider::paintEvent(QPaintEvent *e) { Painter p(this); + p.setOpacity(a_opacity.current()); if (!_hiding || !_cacheForAnim.isNull() || !_offered) { - p.setOpacity(a_opacity.current() * st::layerAlpha); - p.fillRect(rect(), st::layerBg->b); - p.setOpacity(a_opacity.current()); + p.fillRect(rect(), st::layerBg); } if (_cacheForAnim.isNull() || !_offered) { p.setFont(st::forwardFont); @@ -2742,13 +2742,13 @@ void HistoryHider::paintEvent(QPaintEvent *e) { // fill bg p.fillRect(_box, st::boxBg); - p.setPen(st::black); + p.setPen(st::boxTextFg); _toText.drawElided(p, _box.left() + st::boxPadding.left(), _box.top() + st::boxPadding.top(), _toTextWidth + 2); } else { int32 w = st::forwardMargins.left() + _chooseWidth + st::forwardMargins.right(), h = st::forwardMargins.top() + st::forwardFont->height + st::forwardMargins.bottom(); App::roundRect(p, (width() - w) / 2, (height() - st::titleHeight - h) / 2, w, h, st::forwardBg, ForwardCorners); - p.setPen(st::white); + p.setPen(st::forwardFg); p.drawText(_box, lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose), QTextOption(style::al_center)); } } else { @@ -3009,7 +3009,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _botKeyboardHide(this, st::historyBotKeyboardHide) , _botCommandStart(this, st::historyBotCommandStart) , _silent(this) -, _field(this, st::taMsgField, lang(lng_message_ph)) +, _field(this, st::historyComposeField, lang(lng_message_ph)) , _a_record(animation(this, &HistoryWidget::step_record)) , _a_recording(animation(this, &HistoryWidget::step_recording)) , a_recordCancel(st::historyRecordCancel->c, st::historyRecordCancel->c) @@ -6154,7 +6154,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbScroll.hide(); _kbShown = false; - _field.setMaxHeight(st::maxFieldHeight); + _field.setMaxHeight(st::historyComposeFieldMaxHeight); _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_editMsgId && !_replyToId) { @@ -6177,7 +6177,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbScroll.hide(); _kbShown = false; - _field.setMaxHeight(st::maxFieldHeight); + _field.setMaxHeight(st::historyComposeFieldMaxHeight); _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) { @@ -6195,8 +6195,8 @@ void HistoryWidget::onKbToggle(bool manual) { _kbScroll.show(); _kbShown = true; - int32 maxh = qMin(_keyboard.height(), int(st::maxFieldHeight) - (int(st::maxFieldHeight) / 2)); - _field.setMaxHeight(st::maxFieldHeight - maxh); + int32 maxh = qMin(_keyboard.height(), st::historyComposeFieldMaxHeight - (st::historyComposeFieldMaxHeight / 2)); + _field.setMaxHeight(st::historyComposeFieldMaxHeight - maxh); _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId) { @@ -6262,8 +6262,8 @@ void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { int retina = cIntRetinaFactor(); if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), st::topBarHeight), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, st::topBarHeight * retina)); - p.setOpacity(a_progress.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), st::topBarHeight, st::black); + p.setOpacity(a_progress.current()); + p.fillRect(0, 0, a_coordOver.current(), st::topBarHeight, st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina)); @@ -6439,7 +6439,7 @@ void HistoryWidget::updateOnlineDisplayTimer() { void HistoryWidget::moveFieldControls() { int w = width(), h = height(), right = w, bottom = h, keyboardHeight = 0; - int maxKeyboardHeight = int(st::maxFieldHeight) - _field.height(); + int maxKeyboardHeight = st::historyComposeFieldMaxHeight - _field.height(); _keyboard.resizeToWidth(width(), maxKeyboardHeight); if (_kbShown) { keyboardHeight = qMin(_keyboard.height(), maxKeyboardHeight); @@ -6505,7 +6505,7 @@ void HistoryWidget::clearInlineBot() { void HistoryWidget::inlineBotChanged() { bool isInlineBot = _inlineBot && (_inlineBot != LookingUpInlineBot); if (isInlineBot && !_inlineBotCancel) { - _inlineBotCancel = std_::make_unique(this, st::historyInlineBotCancel); + _inlineBotCancel = std_::make_unique(this, st::historyInlineBotCancel); connect(_inlineBotCancel.get(), SIGNAL(clicked()), this, SLOT(onInlineBotCancel())); _inlineBotCancel->setGeometry(_send.geometry()); _attachEmoji->raise(); @@ -7340,8 +7340,8 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _botKeyboardShow->hide(); _botCommandStart->hide(); } - int32 maxh = hasMarkup ? qMin(_keyboard.height(), int(st::maxFieldHeight) - (int(st::maxFieldHeight) / 2)) : 0; - _field.setMaxHeight(st::maxFieldHeight - maxh); + int32 maxh = hasMarkup ? qMin(_keyboard.height(), st::historyComposeFieldMaxHeight - (st::historyComposeFieldMaxHeight / 2)) : 0; + _field.setMaxHeight(st::historyComposeFieldMaxHeight - maxh); _kbShown = hasMarkup; _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { @@ -7358,7 +7358,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _botKeyboardShow->show(); _botCommandStart->hide(); } - _field.setMaxHeight(st::maxFieldHeight); + _field.setMaxHeight(st::historyComposeFieldMaxHeight); _kbShown = false; _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { @@ -7374,7 +7374,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _botKeyboardShow->hide(); _botCommandStart->show(); } - _field.setMaxHeight(st::maxFieldHeight); + _field.setMaxHeight(st::historyComposeFieldMaxHeight); _kbShown = false; _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId && !_editMsgId) { @@ -8524,7 +8524,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { backh += st::historyReplyHeight; } bool drawPreview = (_previewData && _previewData->pendingTill >= 0) && !_replyForwardPressed; - p.fillRect(0, backy, width(), backh, st::taMsgField.bgColor); + p.fillRect(0, backy, width(), backh, st::historyReplyBg); if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { int32 replyLeft = st::historyReplySkip; (_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); @@ -8668,7 +8668,7 @@ void HistoryWidget::drawRecording(Painter &p) { QString duration = formatDurationText(_recordingSamples / AudioVoiceMsgFrequency); p.setFont(st::historyRecordFont); - p.setPen(st::black); + p.setPen(st::historyRecordDurationFg); p.drawText(_attachPhoto->x() + _attachEmoji->width(), _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration); int32 left = _attachPhoto->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send.width() - st::historyRecordVoice.width()) / 2); @@ -8684,7 +8684,7 @@ void HistoryWidget::drawPinnedBar(Painter &p) { Text *from = 0, *text = 0; bool serviceColor = false, hasForward = readyToForward(); ImagePtr preview; - p.fillRect(0, 0, width(), st::historyReplyHeight, st::taMsgField.bgColor); + p.fillRect(0, 0, width(), st::historyReplyHeight, st::historyPinnedBg); QRect rbar(rtlrect(st::msgReplyBarSkip + st::msgReplyBarPos.x(), st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), width())); p.fillRect(rbar, st::msgInReplyBarColor); @@ -8732,8 +8732,8 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { int inCacheTop = hasTopBar ? st::topBarHeight : 0; if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * retina, inCacheTop * retina, a_coordOver.current() * retina, height() * retina)); - p.setOpacity(a_progress.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), height(), st::black); + p.setOpacity(a_progress.current()); + p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina)); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 87999b2bb..4b642c23e 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -42,6 +42,7 @@ class DropdownMenu; class PlainShadow; class PopupMenu; class IconButton; +class MaskButton; class HistoryDownButton; class EmojiButton; } // namespace Ui @@ -895,7 +896,7 @@ private: Text _replyEditMsgText; mutable SingleTimer _updateEditTimeLeftDisplay; - ChildWidget _fieldBarCancel; + ChildWidget _fieldBarCancel; void updateReplyEditTexts(bool force = false); struct PinnedBar { @@ -905,7 +906,7 @@ private: MsgId msgId = 0; HistoryItem *msg = nullptr; Text text; - ChildWidget cancel; + ChildWidget cancel; ChildWidget shadow; }; std_::unique_ptr _pinnedBar; @@ -1085,7 +1086,7 @@ private: UserData *_inlineBot = nullptr; QString _inlineBotUsername; mtpRequestId _inlineBotResolveRequestId = 0; - std_::unique_ptr _inlineBotCancel; + std_::unique_ptr _inlineBotCancel; void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result); bool inlineBotResolveFail(QString name, const RPCError &error); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index ed7b1e7db..3e852e96c 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -172,10 +172,10 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons auto radialOpacity = (radial && loaded) ? _animation->radial.opacity() : 1.; if (_animation && _animation->_a_over.animating(context->ms)) { auto over = _animation->_a_over.current(); - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); - p.fillRect(r, st::black); + p.fillRect(r, style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { - p.fillRect(r, (_state & StateFlag::Over) ? st::msgDateImgBgOver : st::msgDateImgBg); + auto over = (_state & StateFlag::Over); + p.fillRect(r, over ? st::msgDateImgBgOver : st::msgDateImgBg); } p.setOpacity(radialOpacity * p.opacity()); @@ -600,7 +600,7 @@ void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) co p.drawPixmapLeft(0, st::inlineRowMargin, _width, _thumb); } } else { - p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), st::black); + p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), st::overviewVideoBg); } if (!_duration.isEmpty()) { @@ -608,12 +608,12 @@ void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) co int durationW = _durationWidth + 2 * st::msgDateImgPadding.x(), durationH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); int durationX = (st::inlineThumbSize - durationW) / 2, durationY = st::inlineRowMargin + st::inlineThumbSize - durationH; App::roundRect(p, durationX, durationY - st::msgDateImgPadding.y(), durationW, durationH, st::msgDateImgBg, DateCorners); - p.setPen(st::white); + p.setPen(st::msgDateImgColor); p.setFont(st::normalFont); p.drawText(durationX + st::msgDateImgPadding.x(), durationTop + st::normalFont->ascent, _duration); } - p.setPen(st::black); + p.setPen(st::inlineTitleFg); _title.drawLeftElided(p, left, st::inlineRowMargin, _width - left, _width, 2); int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2); @@ -744,7 +744,7 @@ void File::paint(Painter &p, const QRect &clip, const PaintContext *context) con int titleTop = st::inlineRowMargin + st::inlineRowFileNameTop; int descriptionTop = st::inlineRowMargin + st::inlineRowFileDescriptionTop; - p.setPen(st::black); + p.setPen(st::inlineTitleFg); _title.drawLeftElided(p, left, titleTop, _width - left, _width); p.setPen(st::inlineDescriptionFg); @@ -939,7 +939,7 @@ void Contact::paint(Painter &p, const QRect &clip, const PaintContext *context) int titleTop = st::inlineRowMargin + st::inlineRowFileNameTop; int descriptionTop = st::inlineRowMargin + st::inlineRowFileDescriptionTop; - p.setPen(st::black); + p.setPen(st::inlineTitleFg); _title.drawLeftElided(p, left, titleTop, _width - left, _width); p.setPen(st::inlineDescriptionFg); @@ -1052,7 +1052,7 @@ void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) p.fillRect(rthumb, colors[index]); if (!_thumbLetter.isEmpty()) { p.setFont(st::linksLetterFont); - p.setPen(st::white); + p.setPen(st::linksLetterFg); p.drawText(rthumb, _thumbLetter, style::al_center); } } else { @@ -1063,7 +1063,7 @@ void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) } } - p.setPen(st::black); + p.setPen(st::inlineTitleFg); _title.drawLeftElided(p, left, st::inlineRowMargin, _width - left, _width, 2); int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2); @@ -1252,7 +1252,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con } } - p.setPen(st::black); + p.setPen(st::inlineTitleFg); _title.drawLeftElided(p, left, st::inlineRowMargin, _width - left, _width, 2); int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2); diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 3005ac39d..392159d3b 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -43,8 +43,106 @@ introCountry: countryInput { align: align(left); } -introErrLabel: flatLabel(labelDefFlat) { - font: introErrFont; +introBtnTop: 288px; +introSkip: 45px; +introFinishSkip: 15px; +introPhotoSize: 98px; +introHeaderFont: font(24px); +introHeaderSkip: 14px; +introIconSkip: 54px; +introFont: font(16px); +introLink: linkButton(btnDefLink) { + font: introFont; + overFont: font(16px underline); +} +introLabel: flatLabel(labelDefFlat) { + font: introFont; + align: align(center); +} + +introStepSize: size(400px, 200px); +introSize: size(400px, 400px); +introSlideShift: 500px; // intro hiding animation +introSlideDuration: 200; +introSlideDelta: 0; // between hide start and show start +introHideFunc: transition(easeInCirc); +introShowFunc: transition(easeOutCirc); +introAlphaHideFunc: transition(easeOutCirc); +introAlphaShowFunc: transition(easeInCirc); +introTextTop: 22px; +introTextSize: size(400px, 93px); +introCallSkip: 15px; +introPwdTextSize: size(400px, 73px); + +introNextButton: flatButton { + duration: 200; + cursor: cursor(pointer); + + color: #ffffff; + overColor: #ffffff; + downColor: #ffffff; + bgColor: #2fa9e2; + overBgColor: #279ad0; + downBgColor: #279ad0; + + textTop: 16px; + overTextTop: 16px; + downTextTop: 17px; + + font: font(17px); + overFont: font(17px); + + width: 300px; + height: 56px; + + radius: buttonRadius; +} + +introPhoneTop: 8px; +inpIntroCountryCode: flatInput(inpDefGray) { + width: 70px; + height: 41px; + align: align(center); +} +inpIntroPhone: flatInput(inpDefGray) { + textMrg: margins(12px, 5px, 12px, 6px); + width: 225px; + height: 41px; +} +inpIntroCode: flatInput(inpDefGray) { + textMrg: margins(12px, 5px, 12px, 6px); + width: 106px; + height: 41px; + align: align(center); + + phPos: point(0px, 0px); + phAlign: align(center); + phShift: 0px; +} +inpIntroName: flatInput(inpIntroPhone) { + width: 192px; +} +inpIntroPassword: flatInput(inpIntroPhone) { + width: 300px; +} + +introSelectDelta: 30px; + +introErrorWidth: 450px; +introErrorDuration: 200; +introErrorTop: 15px; +introErrorHeight: 40px; +introErrorFont: font(16px); + +introLabelTextStyle: textStyle(defaultTextStyle) { + lineHeight: 30px; +} +introErrorLabelTextStyle: textStyle(defaultTextStyle) { + lineHeight: 27px; +} + +introErrorLabel: flatLabel(labelDefFlat) { + font: introErrorFont; align: align(center); } introBackButton: IconButton(defaultIconButton) { diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index 20507a12a..b60211d97 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -75,7 +75,7 @@ void CodeInput::correctValue(const QString &was, QString &now) { IntroCode::IntroCode(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) , _a_error(animation(this, &IntroCode::step_error)) -, next(this, lang(lng_intro_next), st::btnIntroNext) +, next(this, lang(lng_intro_next), st::introNextButton) , _desc(st::introTextSize.width()) , _noTelegramCode(this, lang(lng_code_no_telegram), st::introLink) , _noTelegramCodeRequestId(0) @@ -149,14 +149,14 @@ void IntroCode::paintEvent(QPaintEvent *e) { } break; } if (!callText.isEmpty()) { - p.drawText(QRect(textRect.left(), code.y() + code.height() + st::introCallSkip, st::introTextSize.width(), st::introErrHeight), callText, style::al_center); + p.drawText(QRect(textRect.left(), code.y() + code.height() + st::introCallSkip, st::introTextSize.width(), st::introErrorHeight), callText, style::al_center); } } if (_a_error.animating() || error.length()) { p.setOpacity(a_errorAlpha.current()); - p.setFont(st::introErrFont->f); - p.setPen(st::introErrColor->p); - p.drawText(QRect(textRect.left(), next.y() + next.height() + st::introErrTop, st::introTextSize.width(), st::introErrHeight), error, style::al_center); + p.setFont(st::introErrorFont); + p.setPen(st::introErrorFg); + p.drawText(QRect(textRect.left(), next.y() + next.height() + st::introErrorTop, st::introTextSize.width(), st::introErrorHeight), error, style::al_center); } } @@ -166,7 +166,7 @@ void IntroCode::resizeEvent(QResizeEvent *e) { code.move((width() - code.width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); } textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); - _noTelegramCode.move(textRect.left() + (st::introTextSize.width() - _noTelegramCode.width()) / 2, code.y() + code.height() + st::introCallSkip + (st::introErrHeight - _noTelegramCode.height()) / 2); + _noTelegramCode.move(textRect.left() + (st::introTextSize.width() - _noTelegramCode.width()) / 2, code.y() + code.height() + st::introCallSkip + (st::introErrorHeight - _noTelegramCode.height()) / 2); } void IntroCode::showError(const QString &err) { @@ -183,7 +183,7 @@ void IntroCode::showError(const QString &err) { } void IntroCode::step_error(float64 ms, bool timer) { - float64 dt = ms / st::introErrDuration; + float64 dt = ms / st::introErrorDuration; if (dt >= 1) { _a_error.stop(); @@ -192,7 +192,7 @@ void IntroCode::step_error(float64 ms, bool timer) { error.clear(); } } else { - a_errorAlpha.update(dt, st::introErrFunc); + a_errorAlpha.update(dt, anim::linear); } if (timer) update(); } diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index 9bb01a378..b9aaf1f99 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -47,11 +47,11 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) , _a_error(animation(this, &IntroPhone::step_error)) , changed(false) -, next(this, lang(lng_intro_next), st::btnIntroNext) +, next(this, lang(lng_intro_next), st::introNextButton) , country(this, st::introCountry) , phone(this, st::inpIntroPhone) , code(this, st::inpIntroCountryCode) -, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), FlatLabel::InitType::Rich, st::introErrLabel, st::introErrLabelTextStyle) +, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), FlatLabel::InitType::Rich, st::introErrorLabel, st::introErrorLabelTextStyle) , _showSignup(false) , sentRequest(0) { setVisible(false); @@ -94,11 +94,11 @@ void IntroPhone::paintEvent(QPaintEvent *e) { p.drawText(textRect, lang(lng_phone_desc), style::al_bottom); } if (_a_error.animating() || error.length()) { - int32 errorY = _showSignup ? ((phone.y() + phone.height() + next.y() - st::introErrFont->height) / 2) : (next.y() + next.height() + st::introErrTop); + int32 errorY = _showSignup ? ((phone.y() + phone.height() + next.y() - st::introErrorFont->height) / 2) : (next.y() + next.height() + st::introErrorTop); p.setOpacity(a_errorAlpha.current()); - p.setFont(st::introErrFont->f); - p.setPen(st::introErrColor->p); - p.drawText(QRect(textRect.x(), errorY, textRect.width(), st::introErrFont->height), error, style::al_top); + p.setFont(st::introErrorFont); + p.setPen(st::introErrorFg); + p.drawText(QRect(textRect.x(), errorY, textRect.width(), st::introErrorFont->height), error, style::al_top); if (_signup.isHidden() && _showSignup) { p.drawPixmap(_signup.x(), _signup.y(), _signupCache); @@ -114,7 +114,7 @@ void IntroPhone::resizeEvent(QResizeEvent *e) { phone.move((width() - country.width()) / 2 + country.width() - st::inpIntroPhone.width, phoneTop); code.move((width() - country.width()) / 2, phoneTop); } - _signup.move((width() - _signup.width()) / 2, next.y() + next.height() + st::introErrTop - ((st::introErrLabelTextStyle.lineHeight - st::introErrFont->height) / 2)); + _signup.move((width() - _signup.width()) / 2, next.y() + next.height() + st::introErrorTop - ((st::introErrorLabelTextStyle.lineHeight - st::introErrorFont->height) / 2)); textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); } @@ -137,7 +137,7 @@ void IntroPhone::showError(const QString &err, bool signUp) { } void IntroPhone::step_error(float64 ms, bool timer) { - float64 dt = ms / st::introErrDuration; + float64 dt = ms / st::introErrorDuration; if (dt >= 1) { _a_error.stop(); @@ -149,7 +149,7 @@ void IntroPhone::step_error(float64 ms, bool timer) { _signup.show(); } } else { - a_errorAlpha.update(dt, st::introErrFunc); + a_errorAlpha.update(dt, anim::linear); } if (timer) update(); } diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index 0b7295444..c603206aa 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -31,7 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) , _a_error(animation(this, &IntroPwdCheck::step_error)) -, _next(this, lang(lng_intro_submit), st::btnIntroNext) +, _next(this, lang(lng_intro_submit), st::introNextButton) , _salt(parent->getPwdSalt()) , _hasRecovery(parent->getHasRecovery()) , _hint(parent->getPwdHint()) @@ -88,9 +88,9 @@ void IntroPwdCheck::paintEvent(QPaintEvent *e) { if (_a_error.animating() || error.length()) { p.setOpacity(a_errorAlpha.current()); - QRect errRect((width() - st::introErrWidth) / 2, (_pwdField.y() + _pwdField.height() + st::introFinishSkip + st::introFont->height + _next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight); - p.setFont(st::introErrFont->f); - p.setPen(st::introErrColor->p); + QRect errRect((width() - st::introErrorWidth) / 2, (_pwdField.y() + _pwdField.height() + st::introFinishSkip + st::introFont->height + _next.y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); + p.setFont(st::introErrorFont); + p.setPen(st::introErrorFg); p.drawText(errRect, error, QTextOption(style::al_center)); p.setOpacity(1); @@ -122,7 +122,7 @@ void IntroPwdCheck::showError(const QString &err) { } void IntroPwdCheck::step_error(float64 ms, bool timer) { - float64 dt = ms / st::introErrDuration; + float64 dt = ms / st::introErrorDuration; if (dt >= 1) { _a_error.stop(); @@ -131,7 +131,7 @@ void IntroPwdCheck::step_error(float64 ms, bool timer) { error.clear(); } } else { - a_errorAlpha.update(dt, st::introErrFunc); + a_errorAlpha.update(dt, anim::linear); } if (timer) update(); } diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index 885d0a499..4fa4f795b 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/introsignup.h" #include "styles/style_intro.h" +#include "styles/style_boxes.h" #include "ui/filedialog.h" #include "boxes/photocropbox.h" #include "lang.h" @@ -32,7 +33,7 @@ IntroSignup::IntroSignup(IntroWidget *parent) : IntroStep(parent) , a_photoOver(0) , _a_error(animation(this, &IntroSignup::step_error)) , _a_photo(animation(this, &IntroSignup::step_photo)) -, next(this, lang(lng_intro_finish), st::btnIntroNext) +, next(this, lang(lng_intro_finish), st::introNextButton) , first(this, st::inpIntroName, lang(lng_signup_firstname)) , last(this, st::inpIntroName, lang(lng_signup_lastname)) , sentRequest(0) @@ -112,12 +113,12 @@ void IntroSignup::paintEvent(QPaintEvent *e) { QRect errRect; if (_invertOrder) { - errRect = QRect((width() - st::introErrWidth) / 2, (first.y() + first.height() + next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight); + errRect = QRect((width() - st::introErrorWidth) / 2, (first.y() + first.height() + next.y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); } else { - errRect = QRect((width() - st::introErrWidth) / 2, (last.y() + last.height() + next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight); + errRect = QRect((width() - st::introErrorWidth) / 2, (last.y() + last.height() + next.y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); } - p.setFont(st::introErrFont->f); - p.setPen(st::introErrColor->p); + p.setFont(st::introErrorFont); + p.setPen(st::introErrorFg); p.drawText(errRect, error, QTextOption(style::al_center)); p.setOpacity(1); @@ -174,7 +175,7 @@ void IntroSignup::showError(const QString &err) { } void IntroSignup::step_error(float64 ms, bool timer) { - float64 dt = ms / st::introErrDuration; + float64 dt = ms / st::introErrorDuration; if (dt >= 1) { _a_error.stop(); @@ -183,13 +184,13 @@ void IntroSignup::step_error(float64 ms, bool timer) { error.clear(); } } else { - a_errorAlpha.update(dt, st::introErrFunc); + a_errorAlpha.update(dt, anim::linear); } if (timer) update(); } void IntroSignup::step_photo(float64 ms, bool timer) { - float64 dt = ms / st::introErrDuration; + float64 dt = ms / st::introErrorDuration; if (dt >= 1) { _a_photo.stop(); diff --git a/Telegram/SourceFiles/intro/introstart.cpp b/Telegram/SourceFiles/intro/introstart.cpp index ac9b04b64..60a7117b6 100644 --- a/Telegram/SourceFiles/intro/introstart.cpp +++ b/Telegram/SourceFiles/intro/introstart.cpp @@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent) , _intro(this, lang(lng_intro), FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle) , _changeLang(this, QString()) -, _next(this, lang(lng_start_msgs), st::btnIntroNext) { +, _next(this, lang(lng_start_msgs), st::introNextButton) { _changeLang.hide(); if (cLang() == languageDefault) { int32 l = Sandbox::LangSystem(); @@ -68,8 +68,8 @@ void IntroStart::paintEvent(QPaintEvent *e) { } int32 hy = _intro.y() - st::introHeaderFont->height - st::introHeaderSkip + st::introHeaderFont->ascent; - p.setFont(st::introHeaderFont->f); - p.setPen(st::introColor->p); + p.setFont(st::introHeaderFont); + p.setPen(st::introHeaderFg); p.drawText((width() - _headerWidth) / 2, hy, qsl("Telegram Desktop")); st::aboutIcon.paint(p, QPoint((width() - st::aboutIcon.width()) / 2, hy - st::introIconSkip - st::aboutIcon.height()), width()); diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 014964e9b..630fe9cb1 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -248,12 +248,12 @@ void IntroWidget::paintEvent(QPaintEvent *e) { if (!trivial) { p.setClipRect(e->rect()); } - p.fillRect(e->rect(), st::white->b); + p.fillRect(e->rect(), st::windowBg); if (_a_show.animating()) { if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor())); - p.setOpacity(a_shadow.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b); + p.setOpacity(a_shadow.current()); + p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(a_coordOver.current(), 0, _cacheOver); diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index ca803e083..f22513bcb 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -61,7 +61,7 @@ protected: void paintEvent(QPaintEvent *e) override { Painter p(this); - p.setOpacity(st::layerAlpha * _opacity); + p.setOpacity(_opacity); if (_box.isNull()) { p.fillRect(rect(), st::layerBg); } else { @@ -70,7 +70,6 @@ protected: p.fillRect(r, st::layerBg); } p.setClipRegion(clip); - p.setOpacity(_opacity); _shadow.paint(p, _box, st::boxShadowShift); if (!_hiddenSpecialBox.isNull()) { p.setClipRegion(QRegion(rect()) - _hiddenSpecialBox); diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index bb5ff4307..e80783a4b 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -36,7 +36,7 @@ enum RoundCorners { SmallMaskCorners = 0x00, // for images LargeMaskCorners, - WhiteCorners, + BotKbOverCorners, StickerCorners, StickerSelectedCorners, SelectedOverlaySmallCorners, diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index c3a36bd13..4dcf409f0 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -266,7 +266,7 @@ void FileLoadTask::process() { if (fullimage.hasAlphaChannel()) { QImage solid(fullimage.width(), fullimage.height(), QImage::Format_ARGB32_Premultiplied); - solid.fill(st::white->c); + solid.fill(st::imageBgTransparent->c); { QPainter(&solid).drawImage(0, 0, fullimage); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 06427faef..317217ee0 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2697,8 +2697,8 @@ void MainWidget::paintEvent(QPaintEvent *e) { if (_a_show.animating()) { if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor())); - p.setOpacity(a_shadow.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b); + p.setOpacity(a_shadow.current()); + p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(a_coordOver.current(), 0, _cacheOver); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 05b2b43c3..758afa19d 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -1434,7 +1434,7 @@ void MainWindow::placeSmallCounter(QImage &img, int size, int count, const style } -QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) { +QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) { bool layer = false; if (size < 0) { size = -size; @@ -1446,7 +1446,7 @@ QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, QString cnt = (count < 1000) ? QString("%1").arg(count) : QString("..%1").arg(count % 100, 2, 10, QChar('0')); QImage result(size, size, QImage::Format_ARGB32); int32 cntSize = cnt.size(); - result.fill(st::transparent->c); + result.fill(Qt::transparent); { QPainter p(&result); p.setBrush(bg); @@ -1480,7 +1480,7 @@ QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, p.drawRoundedRect(QRect(size - w - d * 2, size - f->height, w + d * 2, f->height), r, r); p.setFont(f); - p.setPen(st::counterFg); + p.setPen(fg); p.drawText(size - w - d, size - f->height + f->ascent, cnt); } @@ -1493,10 +1493,10 @@ QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, if (!count) return img; if (smallIcon) { - placeSmallCounter(img, size, count, bg, QPoint(), st::counterFg); + placeSmallCounter(img, size, count, bg, QPoint(), fg); } else { QPainter p(&img); - p.drawPixmap(size / 2, size / 2, App::pixmapFromImageInPlace(iconWithCounter(-size / 2, count, bg, false))); + p.drawPixmap(size / 2, size / 2, App::pixmapFromImageInPlace(iconWithCounter(-size / 2, count, bg, fg, false))); } return img; } diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 97b965764..aa87b5a08 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -158,7 +158,7 @@ public: void updateUnreadCounter(); - QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon); + QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon); bool contentOverlapped(const QRect &globalRect); bool contentOverlapped(QWidget *w, QPaintEvent *e) { diff --git a/Telegram/SourceFiles/media/media_clip_reader.cpp b/Telegram/SourceFiles/media/media_clip_reader.cpp index 792cefaa4..2c45b2084 100644 --- a/Telegram/SourceFiles/media/media_clip_reader.cpp +++ b/Telegram/SourceFiles/media/media_clip_reader.cpp @@ -54,16 +54,16 @@ QPixmap _prepareFrame(const FrameRequest &request, const QImage &original, bool Painter p(&cache); if (newcache) { if (request.framew < request.outerw) { - p.fillRect(0, 0, (request.outerw - request.framew) / (2 * factor), cache.height() / factor, st::black); - p.fillRect((request.outerw - request.framew) / (2 * factor) + (request.framew / factor), 0, (cache.width() / factor) - ((request.outerw - request.framew) / (2 * factor) + (request.framew / factor)), cache.height() / factor, st::black); + p.fillRect(0, 0, (request.outerw - request.framew) / (2 * factor), cache.height() / factor, st::imageBg); + p.fillRect((request.outerw - request.framew) / (2 * factor) + (request.framew / factor), 0, (cache.width() / factor) - ((request.outerw - request.framew) / (2 * factor) + (request.framew / factor)), cache.height() / factor, st::imageBg); } if (request.frameh < request.outerh) { - p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), 0, qMin(cache.width(), request.framew) / factor, (request.outerh - request.frameh) / (2 * factor), st::black); - p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), (request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor), qMin(cache.width(), request.framew) / factor, (cache.height() / factor) - ((request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor)), st::black); + p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), 0, qMin(cache.width(), request.framew) / factor, (request.outerh - request.frameh) / (2 * factor), st::imageBg); + p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), (request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor), qMin(cache.width(), request.framew) / factor, (cache.height() / factor) - ((request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor)), st::imageBg); } } if (hasAlpha) { - p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), qMax(0, (request.outerh - request.frameh) / (2 * factor)), qMin(cache.width(), request.framew) / factor, qMin(cache.height(), request.frameh) / factor, st::white); + p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), qMax(0, (request.outerh - request.frameh) / (2 * factor)), qMin(cache.width(), request.framew) / factor, qMin(cache.height(), request.frameh) / factor, st::imageBgTransparent); } QPoint position((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor)); if (badSize) { diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index d25f147d2..eba4b7774 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -85,43 +85,55 @@ mediaviewClose: icon {{ "mediaview_close", #ffffff }}; mediaviewSave: icon {{ "mediaview_download", #ffffff }}; mediaviewMore: icon {{ "mediaview_more", #ffffff }}; -mediaviewFileRedCornerFg: #d55959; -mediaviewFileYellowCornerFg: #e8a659; -mediaviewFileGreenCornerFg: #49a957; -mediaviewFileBlueCornerFg: #599dcf; - mediaviewFileRed: icon { - { size(25px, 25px), #ffffff }, + { size(25px, 25px), mediaviewFileBg }, { "mediaview_file_corner", mediaviewFileRedCornerFg }, }; mediaviewFileYellow: icon { - { size(25px, 25px), #ffffff }, + { size(25px, 25px), mediaviewFileBg }, { "mediaview_file_corner", mediaviewFileYellowCornerFg }, }; mediaviewFileGreen: icon { - { size(25px, 25px), #ffffff }, + { size(25px, 25px), mediaviewFileBg }, { "mediaview_file_corner", mediaviewFileGreenCornerFg }, }; mediaviewFileBlue: icon { - { size(25px, 25px), #ffffff }, + { size(25px, 25px), mediaviewFileBg }, { "mediaview_file_corner", mediaviewFileBlueCornerFg }, }; +mediaviewFilePadding: 18px; +mediaviewFileSize: size(340px, 116px); +mediaviewFileNameTop: 4px; +mediaviewFileNameFont: font(semibold 14px); +mediaviewFileSizeTop: 29px; +mediaviewFileExtTop: 35px; +mediaviewFileExtFont: font(semibold 18px); +mediaviewFileExtPadding: 10px; +mediaviewFileLinksTop: 57px; +mediaviewFileIconSize: 80px; + +mediaviewFileLink: linkButton(btnDefLink) { + color: #4595d3; + overColor: #4595d3; + downColor: #4595d3; +} + mediaviewTransparentBg: #ffffff; mediaviewTransparentFg: #cccccc; mediaviewTransparentSize: 4px; mediaviewMenu: Menu(defaultMenu) { - itemBg: #383838; - itemBgOver: #505050; - itemFg: white; - itemFgOver: white; - itemFgDisabled: #999999; - itemFgShortcut: #eeeeee; - itemFgShortcutOver: #ffffff; - itemFgShortcutDisabled: #999999; + itemBg: mediaviewMenuBg; + itemBgOver: mediaviewMenuBgOver; + itemFg: mediaviewMenuFg; + itemFgOver: mediaviewMenuFg; + itemFgDisabled: mediaviewMenuFg; + itemFgShortcut: mediaviewMenuFg; + itemFgShortcutOver: mediaviewMenuFg; + itemFgShortcutDisabled: mediaviewMenuFg; - separatorFg: #484848; + separatorFg: mediaviewMenuFg; } mediaviewPopupMenu: PopupMenu(defaultPopupMenu) { shadow: icon {}; @@ -130,30 +142,3 @@ mediaviewPopupMenu: PopupMenu(defaultPopupMenu) { mediaviewDropdownMenu: DropdownMenu(defaultDropdownMenu) { menu: mediaviewMenu; } -/* -mvDropdown: dropdown(dropdownDef) { - shadow: icon {}; - padding: margins(11px, 12px, 11px, 12px); - - border: 0px; - width: 182px; -} -mvButton: iconedButton(btnDefIconed) { - bgColor: #383838; - overBgColor: #505050; - font: font(fsize); - - opacity: 1.; - overOpacity: 1.; - - width: -32px; - height: 36px; - - color: white; - - textPos: point(16px, 9px); - downTextPos: point(16px, 10px); - - duration: 0; -} -*/ \ No newline at end of file diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index fd3538c89..1ddd03d19 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -83,9 +83,9 @@ bool typeHasMediaOverview(MediaOverviewType type) { MediaView::MediaView() : TWidget(App::wnd()) , _animStarted(getms()) -, _docDownload(this, lang(lng_media_download), st::mvDocLink) -, _docSaveAs(this, lang(lng_mediaview_save_as), st::mvDocLink) -, _docCancel(this, lang(lng_cancel), st::mvDocLink) +, _docDownload(this, lang(lng_media_download), st::mediaviewFileLink) +, _docSaveAs(this, lang(lng_mediaview_save_as), st::mediaviewFileLink) +, _docCancel(this, lang(lng_cancel), st::mediaviewFileLink) , _radial(animation(this, &MediaView::step_radial)) , _lastAction(-st::mvDeltaFromLastAction, -st::mvDeltaFromLastAction) , _a_state(animation(this, &MediaView::step_state)) @@ -279,7 +279,7 @@ void MediaView::updateDocSize() { _docSize = formatSizeText(_doc->size); } _docSizeWidth = st::mvFont->width(_docSize); - int32 maxw = st::mvDocSize.width() - st::mvDocIconSize - st::mvDocPadding * 3; + int32 maxw = st::mediaviewFileSize.width() - st::mediaviewFileIconSize - st::mediaviewFilePadding * 3; if (_docSizeWidth > maxw) { _docSize = st::mvFont->elided(_docSize, maxw); _docSizeWidth = st::mvFont->width(_docSize); @@ -291,18 +291,18 @@ void MediaView::updateControls() { if (_doc->loading()) { _docDownload.hide(); _docSaveAs.hide(); - _docCancel.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocIconSize, _docRect.y() + st::mvDocPadding + st::mvDocLinksTop); + _docCancel.moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); _docCancel.show(); } else { if (_doc->loaded(DocumentData::FilePathResolveChecked)) { _docDownload.hide(); - _docSaveAs.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocIconSize, _docRect.y() + st::mvDocPadding + st::mvDocLinksTop); + _docSaveAs.moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); _docSaveAs.show(); _docCancel.hide(); } else { - _docDownload.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocIconSize, _docRect.y() + st::mvDocPadding + st::mvDocLinksTop); + _docDownload.moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); _docDownload.show(); - _docSaveAs.moveToLeft(_docRect.x() + 2.5 * st::mvDocPadding + st::mvDocIconSize + _docDownload.width(), _docRect.y() + st::mvDocPadding + st::mvDocLinksTop); + _docSaveAs.moveToLeft(_docRect.x() + 2.5 * st::mediaviewFilePadding + st::mediaviewFileIconSize + _docDownload.width(), _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); _docSaveAs.show(); _docCancel.hide(); } @@ -1224,7 +1224,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty } } - _docIconRect = QRect((width() - st::mvDocIconSize) / 2, (height() - st::mvDocIconSize) / 2, st::mvDocIconSize, st::mvDocIconSize); + _docIconRect = QRect((width() - st::mediaviewFileIconSize) / 2, (height() - st::mediaviewFileIconSize) / 2, st::mediaviewFileIconSize, st::mediaviewFileIconSize); if (!fileShown()) { if (!_doc || _doc->thumb->isNull()) { int32 colorIndex = documentColorIndex(_doc, _docExt); @@ -1232,11 +1232,11 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty const style::icon *(thumbs[]) = { &st::mediaviewFileBlue, &st::mediaviewFileGreen, &st::mediaviewFileRed, &st::mediaviewFileYellow }; _docIcon = thumbs[colorIndex]; - int32 extmaxw = (st::mvDocIconSize - st::mvDocExtPadding * 2); - _docExtWidth = st::mvDocExtFont->width(_docExt); + int32 extmaxw = (st::mediaviewFileIconSize - st::mediaviewFileExtPadding * 2); + _docExtWidth = st::mediaviewFileExtFont->width(_docExt); if (_docExtWidth > extmaxw) { - _docExt = st::mvDocNameFont->elided(_docExt, extmaxw, Qt::ElideMiddle); - _docExtWidth = st::mvDocNameFont->width(_docExt); + _docExt = st::mediaviewFileNameFont->elided(_docExt, extmaxw, Qt::ElideMiddle); + _docExtWidth = st::mediaviewFileNameFont->width(_docExt); } } else { _doc->thumb->load(); @@ -1244,33 +1244,33 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty if (!tw || !th) { _docThumbx = _docThumby = _docThumbw = 0; } else if (tw > th) { - _docThumbw = (tw * st::mvDocIconSize) / th; - _docThumbx = (_docThumbw - st::mvDocIconSize) / 2; + _docThumbw = (tw * st::mediaviewFileIconSize) / th; + _docThumbx = (_docThumbw - st::mediaviewFileIconSize) / 2; _docThumby = 0; } else { - _docThumbw = st::mvDocIconSize; + _docThumbw = st::mediaviewFileIconSize; _docThumbx = 0; - _docThumby = ((th * _docThumbw) / tw - st::mvDocIconSize) / 2; + _docThumby = ((th * _docThumbw) / tw - st::mediaviewFileIconSize) / 2; } } - int32 maxw = st::mvDocSize.width() - st::mvDocIconSize - st::mvDocPadding * 3; + int32 maxw = st::mediaviewFileSize.width() - st::mediaviewFileIconSize - st::mediaviewFilePadding * 3; if (_doc) { _docName = (_doc->type == StickerDocument) ? lang(lng_in_dlg_sticker) : (_doc->type == AnimatedDocument ? qsl("GIF") : (_doc->name.isEmpty() ? lang(lng_mediaview_doc_image) : _doc->name)); } else { _docName = lang(lng_message_empty); } - _docNameWidth = st::mvDocNameFont->width(_docName); + _docNameWidth = st::mediaviewFileNameFont->width(_docName); if (_docNameWidth > maxw) { - _docName = st::mvDocNameFont->elided(_docName, maxw, Qt::ElideMiddle); - _docNameWidth = st::mvDocNameFont->width(_docName); + _docName = st::mediaviewFileNameFont->elided(_docName, maxw, Qt::ElideMiddle); + _docNameWidth = st::mediaviewFileNameFont->width(_docName); } // _docSize is updated in updateControls() - _docRect = QRect((width() - st::mvDocSize.width()) / 2, (height() - st::mvDocSize.height()) / 2, st::mvDocSize.width(), st::mvDocSize.height()); - _docIconRect = myrtlrect(_docRect.x() + st::mvDocPadding, _docRect.y() + st::mvDocPadding, st::mvDocIconSize, st::mvDocIconSize); + _docRect = QRect((width() - st::mediaviewFileSize.width()) / 2, (height() - st::mediaviewFileSize.height()) / 2, st::mediaviewFileSize.width(), st::mediaviewFileSize.height()); + _docIconRect = myrtlrect(_docRect.x() + st::mediaviewFilePadding, _docRect.y() + st::mediaviewFilePadding, st::mediaviewFileIconSize, st::mediaviewFileIconSize); } else if (!_current.isNull()) { _current.setDevicePixelRatio(cRetinaFactor()); _w = convertScale(_current.width()); @@ -1348,7 +1348,7 @@ void MediaView::initAnimation() { _current = _doc->thumb->pixNoCache(w, h, ImagePixSmooth | ImagePixBlurred, w / cIntRetinaFactor(), h / cIntRetinaFactor()); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); } else { - _current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), ImagePixSmooth | ImagePixBlurred, st::mvDocIconSize, st::mvDocIconSize); + _current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), ImagePixSmooth | ImagePixBlurred, st::mediaviewFileIconSize, st::mediaviewFileIconSize); } } @@ -1364,7 +1364,7 @@ void MediaView::createClipReader() { _current = _doc->thumb->pixNoCache(w, h, ImagePixSmooth | ImagePixBlurred, w / cIntRetinaFactor(), h / cIntRetinaFactor()); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); } else { - _current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), ImagePixSmooth | ImagePixBlurred, st::mvDocIconSize, st::mvDocIconSize); + _current = _doc->thumb->pixNoCache(_doc->thumb->width(), _doc->thumb->height(), ImagePixSmooth | ImagePixBlurred, st::mediaviewFileIconSize, st::mediaviewFileIconSize); } auto mode = _doc->isVideo() ? Media::Clip::Reader::Mode::Video : Media::Clip::Reader::Mode::Gif; _gif = std_::make_unique(_doc->location(), _doc->data(), [this](Media::Clip::Notification notification) { @@ -1545,12 +1545,11 @@ void MediaView::paintEvent(QPaintEvent *e) { p.setCompositionMode(QPainter::CompositionMode_Source); if (_fullScreenVideo) { for (int i = 0, l = region.rectCount(); i < l; ++i) { - p.fillRect(rs.at(i), st::black); + p.fillRect(rs.at(i), st::mediaviewVideoBg); } } else { - p.setOpacity(st::mvBgOpacity); for (int i = 0, l = region.rectCount(); i < l; ++i) { - p.fillRect(rs.at(i), st::mvBgColor); + p.fillRect(rs.at(i), st::mediaviewBg); } p.setCompositionMode(m); } @@ -1605,8 +1604,8 @@ void MediaView::paintEvent(QPaintEvent *e) { auto inner = radialRect(); p.setPen(Qt::NoPen); - p.setBrush(st::black); - p.setOpacity(radialOpacity * st::radialBgOpacity); + p.setOpacity(radialOpacity); + p.setBrush(st::radialBg); p.setRenderHint(QPainter::HighQualityAntialiasing); p.drawEllipse(inner); @@ -1614,7 +1613,7 @@ void MediaView::paintEvent(QPaintEvent *e) { p.setOpacity(1); QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine))); - _radial.draw(p, arc, st::radialLine, st::white); + _radial.draw(p, arc, st::radialLine, st::radialFg); } } else if (_doc) { paintDocRadialLoading(p, radial, radialOpacity); @@ -1634,7 +1633,7 @@ void MediaView::paintEvent(QPaintEvent *e) { App::roundRect(p, _saveMsg, st::medviewSaveMsg, MediaviewSaveCorners); st::medviewSaveMsgCheck.paint(p, _saveMsg.topLeft() + st::medviewSaveMsgCheckPos, width()); - p.setPen(st::white->p); + p.setPen(st::medviewSaveMsgFg); textstyleSet(&st::medviewSaveAsTextStyle); _saveMsgText.draw(p, _saveMsg.x() + st::medviewSaveMsgPadding.left(), _saveMsg.y() + st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right()); textstyleRestore(); @@ -1651,7 +1650,7 @@ void MediaView::paintEvent(QPaintEvent *e) { } } else { if (_docRect.intersects(r)) { - p.fillRect(_docRect, st::mvDocBg); + p.fillRect(_docRect, st::mediaviewFileBg); if (_docIconRect.intersects(r)) { bool radial = false; float64 radialOpacity = 0; @@ -1664,15 +1663,15 @@ void MediaView::paintEvent(QPaintEvent *e) { p.fillRect(_docIconRect, _docIconColor->b); if ((!_doc || _doc->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { _docIcon->paint(p, _docIconRect.x() + (_docIconRect.width() - _docIcon->width()), _docIconRect.y(), width()); - p.setPen(st::mvDocExtColor->p); - p.setFont(st::mvDocExtFont->f); + p.setPen(st::mediaviewFileExtFg); + p.setFont(st::mediaviewFileExtFont); if (!_docExt.isEmpty()) { - p.drawText(_docIconRect.x() + (_docIconRect.width() - _docExtWidth) / 2, _docIconRect.y() + st::mvDocExtTop + st::mvDocExtFont->ascent, _docExt); + p.drawText(_docIconRect.x() + (_docIconRect.width() - _docExtWidth) / 2, _docIconRect.y() + st::mediaviewFileExtTop + st::mediaviewFileExtFont->ascent, _docExt); } } } else { int32 rf(cIntRetinaFactor()); - p.drawPixmap(_docIconRect.topLeft(), _doc->thumb->pix(_docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mvDocIconSize * rf, st::mvDocIconSize * rf)); + p.drawPixmap(_docIconRect.topLeft(), _doc->thumb->pix(_docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mediaviewFileIconSize * rf, st::mediaviewFileIconSize * rf)); } paintDocRadialLoading(p, radial, radialOpacity); @@ -1680,13 +1679,13 @@ void MediaView::paintEvent(QPaintEvent *e) { if (!_docIconRect.contains(r)) { name = true; - p.setPen(st::mvDocNameColor); - p.setFont(st::mvDocNameFont); - p.drawTextLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocIconSize, _docRect.y() + st::mvDocPadding + st::mvDocNameTop, width(), _docName, _docNameWidth); + p.setPen(st::mediaviewFileNameFg); + p.setFont(st::mediaviewFileNameFont); + p.drawTextLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileNameTop, width(), _docName, _docNameWidth); - p.setPen(st::mvDocSizeColor); + p.setPen(st::mediaviewFileSizeFg); p.setFont(st::mvFont); - p.drawTextLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocIconSize, _docRect.y() + st::mvDocPadding + st::mvDocSizeTop, width(), _docSize, _docSizeWidth); + p.drawTextLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileSizeTop, width(), _docSize, _docSizeWidth); } } } @@ -1697,10 +1696,10 @@ void MediaView::paintEvent(QPaintEvent *e) { if (_leftNav.intersects(r) && _leftNavVisible) { auto o = overLevel(OverLeftNav); if (o > 0) { - p.setOpacity(o * st::mvControlBgOpacity * co); + p.setOpacity(o * co); for (int i = 0, l = region.rectCount(); i < l; ++i) { auto fill = _leftNav.intersected(rs.at(i)); - if (!fill.isEmpty()) p.fillRect(fill, st::black->b); + if (!fill.isEmpty()) p.fillRect(fill, st::mediaviewControlBg); } } if (_leftNavIcon.intersects(r)) { @@ -1713,10 +1712,10 @@ void MediaView::paintEvent(QPaintEvent *e) { if (_rightNav.intersects(r) && _rightNavVisible) { auto o = overLevel(OverRightNav); if (o > 0) { - p.setOpacity(o * st::mvControlBgOpacity * co); + p.setOpacity(o * co); for (int i = 0, l = region.rectCount(); i < l; ++i) { auto fill = _rightNav.intersected(rs.at(i)); - if (!fill.isEmpty()) p.fillRect(fill, st::black); + if (!fill.isEmpty()) p.fillRect(fill, st::mediaviewControlBg); } } if (_rightNavIcon.intersects(r)) { @@ -1729,10 +1728,10 @@ void MediaView::paintEvent(QPaintEvent *e) { if (_closeNav.intersects(r)) { auto o = overLevel(OverClose); if (o > 0) { - p.setOpacity(o * st::mvControlBgOpacity * co); + p.setOpacity(o * co); for (int i = 0, l = region.rectCount(); i < l; ++i) { auto fill = _closeNav.intersected(rs.at(i)); - if (!fill.isEmpty()) p.fillRect(fill, st::black); + if (!fill.isEmpty()) p.fillRect(fill, st::mediaviewControlBg); } } if (_closeNavIcon.intersects(r)) { @@ -1755,12 +1754,12 @@ void MediaView::paintEvent(QPaintEvent *e) { st::mediaviewMore.paintInCenter(p, _moreNavIcon); } - p.setPen(st::white); + p.setPen(st::mvControlFg); p.setFont(st::mvThickFont); // header if (_headerNav.intersects(r)) { - float64 o = _headerHasLink ? overLevel(OverHeader) : 0; + auto o = _headerHasLink ? overLevel(OverHeader) : 0; p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); p.drawText(_headerNav.left(), _headerNav.top() + st::mvThickFont->ascent, _headerText); @@ -1770,7 +1769,7 @@ void MediaView::paintEvent(QPaintEvent *e) { } } - p.setFont(st::mvFont->f); + p.setFont(st::mvFont); // name if (_from && _nameNav.intersects(r)) { @@ -1801,12 +1800,12 @@ void MediaView::paintEvent(QPaintEvent *e) { QRect outer(_captionRect.marginsAdded(st::mvCaptionPadding)); if (outer.intersects(r)) { p.setOpacity(co); - p.setBrush(st::mvCaptionBg->b); + p.setBrush(st::mvCaptionBg); p.setPen(Qt::NoPen); p.drawRoundedRect(outer, st::mvCaptionRadius, st::mvCaptionRadius); if (_captionRect.intersects(r)) { textstyleSet(&st::medviewSaveAsTextStyle); - p.setPen(st::white->p); + p.setPen(st::mvCaptionFg); _caption.drawElided(p, _captionRect.x(), _captionRect.y(), _captionRect.width(), _captionRect.height() / st::mvCaptionFont->height); textstyleRestore(); } @@ -1829,7 +1828,7 @@ void MediaView::paintDocRadialLoading(Painter &p, bool radial, float64 radialOpa p.setBrush(st::msgDateImgBgOver); } else { p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - o)) + (st::msgDateImgBgOver->c.alphaF() * o)); - p.setBrush(st::black); + p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, o)); } p.setRenderHint(QPainter::HighQualityAntialiasing); @@ -1849,7 +1848,7 @@ void MediaView::paintDocRadialLoading(Painter &p, bool radial, float64 radialOpa if (radial) { p.setOpacity(1); QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine))); - _radial.draw(p, arc, st::radialLine, st::white); + _radial.draw(p, arc, st::radialLine, st::radialFg); } } } diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index bbf292fc6..3c5ef8638 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -58,10 +58,14 @@ overviewPhotoChecked: icon { }; overviewPhotoSelectOverlay: #0a7bb03f; +overviewVideoBg: #000000; + +overviewFileThumbBg: #000000; overviewFileChecked: #2fa9e2; overviewFileCheck: #00000066; overviewFileExtPadding: 5px; overviewFileExtTop: 24px; +overviewFileExtFg: #ffffff; overviewFileExtFont: font(18px semibold); overviewFileLayout: OverviewFileLayout { @@ -93,8 +97,10 @@ playlistPadding: 10px; linksSearchMargin: margins(20px, 20px, 20px, 0px); linksMaxWidth: 520px; +linksLetterFg: #ffffff; linksLetterFont: font(24px); linksMargin: margins(0px, 7px, 0px, 5px); +linksTextFg: #000000; linksTextTop: 6px; linksBorder: 1px; linksBorderFg: #eaeaea; diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 4f790c22d..d1946aac7 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -310,7 +310,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const statusX = _width - statusW + statusX; p.fillRect(rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg); p.setFont(st::normalFont); - p.setPen(st::white); + p.setPen(st::msgDateImgColor); p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x()); } } @@ -320,7 +320,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); p.fillRect(rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg); p.setFont(st::normalFont); - p.setPen(st::white); + p.setPen(st::msgDateImgColor); p.drawTextLeft(statusX, statusY, _width, _duration, statusW - 2 * st::msgDateImgPadding.x()); } @@ -331,11 +331,10 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const p.setBrush(st::msgDateImgBgSelected); } else if (_a_iconOver.animating()) { _a_iconOver.step(context->ms); - float64 over = a_iconOver.current(); - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); - p.setBrush(st::black); + auto over = a_iconOver.current(); + p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { - bool over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _savel)); + auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _savel)); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } @@ -488,7 +487,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const int32 namewidth = _width - nameleft - nameright; if (clip.intersects(rtlrect(nameleft, nametop, namewidth, st::semiboldFont->height, _width))) { - p.setPen(st::black); + p.setPen(st::historyFileNameInFg); _name.drawLeftElided(p, nameleft, nametop, namewidth, _width); } @@ -718,13 +717,13 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con } p.drawPixmap(rthumb.topLeft(), _thumb); } else { - p.fillRect(rthumb, st::black); + p.fillRect(rthumb, st::overviewFileThumbBg); } } else { p.fillRect(rthumb, documentColor(_colorIndex)); if (!radial && loaded && !_ext.isEmpty()) { p.setFont(st::overviewFileExtFont); - p.setPen(st::white); + p.setPen(st::overviewFileExtFg); p.drawText(rthumb.left() + (rthumb.width() - _extw) / 2, rthumb.top() + st::overviewFileExtTop + st::overviewFileExtFont->ascent, _ext); } } @@ -735,21 +734,16 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con if (radial || (!loaded && !_data->loading())) { QRect inner(rthumb.x() + (rthumb.width() - _st.songThumbSize) / 2, rthumb.y() + (rthumb.height() - _st.songThumbSize) / 2, _st.songThumbSize, _st.songThumbSize); if (clip.intersects(inner)) { - float64 radialOpacity = (radial && loaded && !_data->uploading()) ? _radial->opacity() : 1; + auto radialOpacity = (radial && loaded && !_data->uploading()) ? _radial->opacity() : 1; p.setPen(Qt::NoPen); if (selected) { p.setBrush(wthumb ? st::msgDateImgBgSelected : documentSelectedColor(_colorIndex)); } else if (_a_iconOver.animating()) { _a_iconOver.step(context->ms); - float64 over = a_iconOver.current(); - if (wthumb) { - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); - p.setBrush(st::black); - } else { - p.setBrush(style::interpolate(documentDarkColor(_colorIndex), documentOverColor(_colorIndex), over)); - } + auto over = a_iconOver.current(); + p.setBrush(style::interpolate(wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex), wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex), over)); } else { - bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); + auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? (wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex)) : (wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex))); } p.setOpacity(radialOpacity * p.opacity()); @@ -785,7 +779,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con int availwidth = _width - nameleft - nameright; int namewidth = qMin(availwidth, _name.maxWidth()); if (clip.intersects(rtlrect(nameleft, nametop, namewidth, st::semiboldFont->height, _width))) { - p.setPen(st::black); + p.setPen(st::historyFileNameInFg); _name.drawLeftElided(p, nameleft, nametop, namewidth, _width); } @@ -1072,8 +1066,8 @@ void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const P } if (!_letter.isEmpty()) { - p.setFont(st::linksLetterFont->f); - p.setPen(st::white->p); + p.setFont(st::linksLetterFont); + p.setPen(st::linksLetterFg); p.drawText(rtlrect(0, top, st::linksPhotoSize, st::linksPhotoSize, _width), _letter, style::al_center); } } @@ -1092,7 +1086,7 @@ void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const P top = st::linksTextTop; } - p.setPen(st::black); + p.setPen(st::linksTextFg); p.setFont(st::semiboldFont); if (!_title.isEmpty()) { if (clip.intersects(rtlrect(left, top, qMin(w, _titlew), st::semiboldFont->height, _width))) { @@ -1100,7 +1094,7 @@ void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const P } top += st::semiboldFont->height; } - p.setFont(st::msgFont->f); + p.setFont(st::msgFont); if (!_text.isEmpty()) { int32 h = qMin(st::normalFont->height * 3, _text.countHeight(w)); if (clip.intersects(rtlrect(left, top, w, h, _width))) { diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 274916235..05c3498f6 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1942,8 +1942,8 @@ void OverviewWidget::paintEvent(QPaintEvent *e) { int inCacheTop = st::topBarHeight; if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * retina, inCacheTop * retina, a_coordOver.current() * retina, height() * retina)); - p.setOpacity(a_progress.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), height(), st::black); + p.setOpacity(a_progress.current()); + p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina)); @@ -1952,7 +1952,7 @@ void OverviewWidget::paintEvent(QPaintEvent *e) { return; } - p.fillRect(e->rect(), st::white); + p.fillRect(e->rect(), st::windowBg); } void OverviewWidget::contextMenuEvent(QContextMenuEvent *e) { @@ -1976,8 +1976,8 @@ void OverviewWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) int retina = cIntRetinaFactor(); if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), st::topBarHeight), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, st::topBarHeight * retina)); - p.setOpacity(a_progress.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), st::topBarHeight, st::black); + p.setOpacity(a_progress.current()); + p.fillRect(0, 0, a_coordOver.current(), st::topBarHeight, st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina)); diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 7e218d6e2..275857ad6 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "application.h" #include "ui/text/text.h" +#include "styles/style_boxes.h" PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) , _a_show(animation(this, &PasscodeWidget::step_show)) @@ -180,8 +181,8 @@ void PasscodeWidget::paintEvent(QPaintEvent *e) { if (_a_show.animating()) { if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor())); - p.setOpacity(a_shadow.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b); + p.setOpacity(a_shadow.current()); + p.fillRect(0, 0, a_coordOver.current(), height(), st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(a_coordOver.current(), 0, _cacheOver); @@ -190,12 +191,13 @@ void PasscodeWidget::paintEvent(QPaintEvent *e) { } else { p.fillRect(rect(), st::windowBg); - p.setFont(st::passcodeHeaderFont->f); + p.setFont(st::passcodeHeaderFont); + p.setPen(st::windowTextFg); p.drawText(QRect(0, _passcode.y() - st::passcodeHeaderHeight, width(), st::passcodeHeaderHeight), lang(lng_passcode_enter), style::al_center); if (!_error.isEmpty()) { - p.setFont(st::boxTextFont->f); - p.setPen(st::setErrColor->p); + p.setFont(st::boxTextFont); + p.setPen(st::boxTextFgError); p.drawText(QRect(0, _passcode.y() + _passcode.height(), width(), st::passcodeSubmitSkip), _error, style::al_center); } } diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 9dede149f..5d3916610 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -103,7 +103,9 @@ QImage _trayIconImageGen() { } else if (_trayIconSize >= 32) { layerSize = -20; } - QImage layer = App::wnd()->iconWithCounter(layerSize, counter, (muted ? st::counterMuteBg : st::counterBg), false); + auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg); + auto &fg = st::trayCounterFg; + auto layer = App::wnd()->iconWithCounter(layerSize, counter, bg, fg, false); p.drawImage(_trayIconImage.width() - layer.width() - 1, _trayIconImage.height() - layer.height() - 1, layer); } } @@ -355,9 +357,10 @@ void MainWindow::psUpdateCounter() { int32 counter = App::histories().unreadBadge(); bool muted = App::histories().unreadOnlyMuted(); - auto &bg = (muted ? st::counterMuteBg : st::counterBg); - icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, true))); - icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, true))); + auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg); + auto &fg = st::trayCounterFg; + icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, fg, true))); + icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, fg, true))); } trayIcon->setIcon(icon); } diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 85fb55633..57f788115 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -60,7 +60,7 @@ public: bool psHasNativeNotifications(); - virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) = 0; static void LibsLoaded(); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index 007cb07aa..fdbb7bd01 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -73,7 +73,7 @@ public: return !(QSysInfo::macVersion() < QSysInfo::MV_10_8); } - virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) = 0; void closeWithoutDestroy() override; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 965a5e510..64229f4bb 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -176,14 +176,14 @@ void MainWindow::psUpdateCounter() { bool muted = App::histories().unreadOnlyMuted(); bool dm = objc_darkMode(); - auto &bg = (muted ? st::counterMuteBg : st::counterBg); + auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg); QIcon icon; QImage img(psTrayIcon(dm)), imgsel(psTrayIcon(true)); img.detach(); imgsel.detach(); int32 size = cRetina() ? 44 : 22; - _placeCounter(img, size, counter, bg, (dm && muted) ? st::counterMacInvFg : st::counterFg); - _placeCounter(imgsel, size, counter, st::white, st::counterMacInvColor); + _placeCounter(img, size, counter, bg, (dm && muted) ? st::trayCounterFgMacInvert : st::trayCounterFg); + _placeCounter(imgsel, size, counter, st::trayCounterBgMacInvert, st::trayCounterFgMacInvert); icon.addPixmap(App::pixmapFromImageInPlace(std_::move(img))); icon.addPixmap(App::pixmapFromImageInPlace(std_::move(imgsel)), QIcon::Selected); trayIcon->setIcon(icon); diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 9d02f428f..bf500168a 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -718,14 +718,15 @@ void MainWindow::psUpdateCounter() { auto iconSizeSmall = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); auto iconSizeBig = QSize(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); - auto bg = (muted ? st::counterMuteBg : st::counterBg); - auto iconSmallPixmap16 = App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, true)); - auto iconSmallPixmap32 = App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, true)); + auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg); + auto &fg = st::trayCounterFg; + auto iconSmallPixmap16 = App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, fg, true)); + auto iconSmallPixmap32 = App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, fg, true)); QIcon iconSmall, iconBig; iconSmall.addPixmap(iconSmallPixmap16); iconSmall.addPixmap(iconSmallPixmap32); - iconBig.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(32, taskbarList.Get() ? 0 : counter, bg, false))); - iconBig.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(64, taskbarList.Get() ? 0 : counter, bg, false))); + iconBig.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(32, taskbarList.Get() ? 0 : counter, bg, fg, false))); + iconBig.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(64, taskbarList.Get() ? 0 : counter, bg, fg, false))); if (trayIcon) { // Force Qt to use right icon size, not the larger one. QIcon forTrayIcon; @@ -742,8 +743,8 @@ void MainWindow::psUpdateCounter() { if (taskbarList.Get()) { if (counter > 0) { QIcon iconOverlay; - iconOverlay.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(-16, counter, bg, false))); - iconOverlay.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(-32, counter, bg, false))); + iconOverlay.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(-16, counter, bg, fg, false))); + iconOverlay.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(-32, counter, bg, fg, false))); ps_iconOverlay = createHIconFromQIcon(iconOverlay, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); } auto description = (counter > 0) ? lng_unread_bar(lt_count, counter) : LangString(); diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index decc8bfa2..73658bc50 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -67,7 +67,7 @@ public: bool psHasNativeNotifications(); - virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0; + virtual QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) = 0; static UINT TaskbarCreatedMsgId() { return _taskbarCreatedMsgId; diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index 0c473abf5..80c306a07 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -28,8 +28,7 @@ profileTopBarBackIconPosition: point(12px, 20px); profileTopBarBackFont: font(14px); profileTopBarBackFg: #1485c2; profileTopBarBackPosition: point(32px, 17px); -profileFixedBarButton: RoundButton(topBarButton) { -} +profileFixedBarButton: topBarButton; profileMarginTop: 13px; profilePhotoSize: 112px; @@ -57,18 +56,11 @@ profileMarginBottom: 30px; profileButtonLeft: 27px; profileButtonTop: 88px; profileButtonSkip: 10px; -profilePrimaryButton: RoundButton(defaultActiveButton) { -} -profileSecondaryButton: RoundButton(profilePrimaryButton) { - textFg: semiboldButtonBlueText; - textFgOver: semiboldButtonBlueText; - textBg: #ffffff; - textBgOver: #f2f7fa; -} -profileAddMemberIcon: icon {{ "profile_add_member", windowActiveFill, point(20px, 10px) }}; +profilePrimaryButton: defaultActiveButton; +profileSecondaryButton: defaultLightButton; profileAddMemberButton: RoundButton(profileSecondaryButton) { width: 62px; - icon: profileAddMemberIcon; + icon: icon {{ "profile_add_member", windowActiveFill, point(20px, 10px) }}; } profileDropAreaBg: profileBg; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 7e3d6c00a..8d3f41886 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -22,6 +22,7 @@ using "basic.style"; using "basic_types.style"; using "dialogs/dialogs.style"; using "ui/widgets/widgets.style"; +using "boxes/boxes.style"; settingsMaxWidth: 520px; settingsMaxPadding: 48px; @@ -33,16 +34,15 @@ settingsFixedBarFont: font(14px semibold); settingsFixedBarFg: windowTextFg; settingsFixedBarTextLeft: 20px; settingsFixedBarTextTop: 16px; -settingsFixedBarClose: IconButton(defaultIconButton) { +settingsFixedBarClose: MaskButton(defaultMaskButton) { width: settingsFixedBarHeight; height: settingsFixedBarHeight; - opacity: 0.31; - overOpacity: 0.5; + bg: settingsFixedBarBg; + icon: settingsFixedBarCloseIcon; - icon: boxCancelIcon; - iconPosition: point(20px, 20px); - downIconPosition: point(20px, 20px); + iconBg: settingsCloseFg; + iconBgOver: settingsCloseFgOver; } settingsFixedBarShadowBg1: #00000021; settingsFixedBarShadowBg2: #0000000b; @@ -72,31 +72,16 @@ settingsMarginBottom: 35px; settingsButtonLeft: 27px; settingsButtonTop: 75px; settingsButtonSkip: 10px; -settingsPrimaryButton: RoundButton(defaultActiveButton) { -} -settingsSecondaryButton: RoundButton(settingsPrimaryButton) { - textFg: semiboldButtonBlueText; - textFgOver: semiboldButtonBlueText; - textBg: #ffffff; - textBgOver: #f2f7fa; -} -settingsEditButton: RoundButton { +settingsPrimaryButton: defaultActiveButton; +settingsSecondaryButton: defaultLightButton; +settingsEditButton: IconButton(dialogsNewChatButton) { width: 24px; height: 34px; - icon: settingsEditIcon; - textTop: 0px; - downTextTop: 1px; - - textFg: transparent; - textFgOver: transparent; - secondaryTextFg: transparent; - secondaryTextFgOver: transparent; - textBg: transparent; - textBgOver: transparent; + iconPosition: point(3px, 9px); + downIconPosition: point(3px, 10px); } - settingsBlocksTop: 7px; settingsBlocksBottom: 20px; settingsBlockMarginTop: 14px; diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index 77eed91a6..83a68e2c5 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -65,8 +65,8 @@ void BackgroundRow::paintEvent(QPaintEvent *e) { auto outer = radialRect(); QRect inner(QPoint(outer.x() + (outer.width() - st::radialSize.width()) / 2, outer.y() + (outer.height() - st::radialSize.height()) / 2), st::radialSize); p.setPen(Qt::NoPen); - p.setBrush(st::black); - p.setOpacity(radialOpacity * st::radialBgOpacity); + p.setOpacity(radialOpacity); + p.setBrush(st::radialBg); p.setRenderHint(QPainter::HighQualityAntialiasing); p.drawEllipse(inner); @@ -74,7 +74,7 @@ void BackgroundRow::paintEvent(QPaintEvent *e) { p.setOpacity(1); QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine))); - _radial.draw(p, arc, st::radialLine, st::white); + _radial.draw(p, arc, st::radialLine, st::radialFg); } else { p.drawPixmap(0, 0, _background); } diff --git a/Telegram/SourceFiles/settings/settings_cover.cpp b/Telegram/SourceFiles/settings/settings_cover.cpp index 01ab0d450..6792f8d05 100644 --- a/Telegram/SourceFiles/settings/settings_cover.cpp +++ b/Telegram/SourceFiles/settings/settings_cover.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/flatlabel.h" #include "ui/buttons/round_button.h" +#include "ui/buttons/icon_button.h" #include "observer_peer.h" #include "lang.h" #include "application.h" @@ -32,7 +33,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "boxes/photocropbox.h" #include "boxes/addcontactbox.h" - #include "styles/style_settings.h" #include "styles/style_profile.h" // for divider @@ -42,7 +42,7 @@ CoverWidget::CoverWidget(QWidget *parent, UserData *self) : BlockWidget(parent, , _self(App::self()) , _userpicButton(this, _self) , _name(this, st::settingsNameLabel) -, _editNameInline(this, QString(), st::settingsEditButton) +, _editNameInline(this, st::settingsEditButton) , _setPhoto(this, lang(lng_settings_upload), st::settingsPrimaryButton) , _editName(this, lang(lng_settings_edit), st::settingsSecondaryButton) { setAcceptDrops(true); diff --git a/Telegram/SourceFiles/settings/settings_cover.h b/Telegram/SourceFiles/settings/settings_cover.h index e062c4981..08ca9c0b4 100644 --- a/Telegram/SourceFiles/settings/settings_cover.h +++ b/Telegram/SourceFiles/settings/settings_cover.h @@ -30,6 +30,7 @@ class LinkButton; namespace Ui { class RoundButton; +class IconButton; } // namespace Ui namespace Notify { @@ -93,7 +94,7 @@ private: ChildWidget _dropArea = { nullptr }; ChildWidget _name; - ChildWidget _editNameInline; + ChildWidget _editNameInline; ChildWidget _cancelPhotoUpload = { nullptr }; QPoint _statusPosition; diff --git a/Telegram/SourceFiles/settings/settings_fixed_bar.cpp b/Telegram/SourceFiles/settings/settings_fixed_bar.cpp index 0c4615fa5..82c1cdad8 100644 --- a/Telegram/SourceFiles/settings/settings_fixed_bar.cpp +++ b/Telegram/SourceFiles/settings/settings_fixed_bar.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_fixed_bar.h" #include "styles/style_settings.h" +#include "styles/style_boxes.h" #include "ui/buttons/icon_button.h" #include "mainwindow.h" #include "lang.h" @@ -30,6 +31,7 @@ namespace Settings { FixedBar::FixedBar(QWidget *parent) : TWidget(parent) , _close(this, st::settingsFixedBarClose) { + setAttribute(Qt::WA_OpaquePaintEvent); _close->setClickedCallback([]() { Ui::hideSettingsAndLayer(); }); @@ -46,6 +48,8 @@ void FixedBar::resizeEvent(QResizeEvent *e) { void FixedBar::paintEvent(QPaintEvent *e) { Painter p(this); + p.fillRect(e->rect(), st::settingsFixedBarBg); + p.setFont(st::settingsFixedBarFont); p.setPen(st::windowTextFg); p.drawTextLeft(st::settingsFixedBarTextLeft, st::settingsFixedBarTextTop, width(), lang(lng_menu_settings)); diff --git a/Telegram/SourceFiles/settings/settings_fixed_bar.h b/Telegram/SourceFiles/settings/settings_fixed_bar.h index 7ebc87e3d..720da2b91 100644 --- a/Telegram/SourceFiles/settings/settings_fixed_bar.h +++ b/Telegram/SourceFiles/settings/settings_fixed_bar.h @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once namespace Ui { -class IconButton; +class MaskButton; } // namespace Ui namespace Settings { @@ -37,7 +37,7 @@ protected: int resizeGetHeight(int newWidth) override; private: - ChildWidget _close; + ChildWidget _close; }; diff --git a/Telegram/SourceFiles/settings/settings_widget.cpp b/Telegram/SourceFiles/settings/settings_widget.cpp index 054a8a7ba..12ce92218 100644 --- a/Telegram/SourceFiles/settings/settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_widget.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_inner_widget.h" #include "settings/settings_fixed_bar.h" #include "styles/style_settings.h" +#include "styles/style_boxes.h" #include "ui/widgets/shadow.h" #include "ui/scrollarea.h" #include "mainwindow.h" @@ -197,7 +198,7 @@ void Widget::showDone() { void Widget::paintEvent(QPaintEvent *e) { Painter p(this); - p.fillRect(rect(), st::windowBg); + p.fillRect(e->rect(), st::settingsBg); } void Widget::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index fb97c37c9..25bf5f31e 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stickers/emoji_pan.h" #include "styles/style_stickers.h" +#include "styles/style_intro.h" #include "ui/buttons/icon_button.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" @@ -91,7 +92,7 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) { _shadow.paint(p, r, st::defaultDropdownShadowShift); if (_cache.isNull()) { - p.fillRect(e->rect().intersected(r), st::white); + p.fillRect(e->rect().intersected(r), st::emojiPanBg); int32 x = w + 2 * st::emojiColorsPadding + st::emojiPanSize.width(); if (rtl()) x = width() - x - st::emojiColorsSep; @@ -342,7 +343,7 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { if (r != rect()) { p.setClipRect(r); } - p.fillRect(r, st::white->b); + p.fillRect(r, st::emojiPanBg); 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); @@ -942,7 +943,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { if (r != rect()) { p.setClipRect(r); } - p.fillRect(r, st::white); + p.fillRect(r, st::emojiPanBg); if (showingInlineItems()) { paintInlineItems(p, r); @@ -2750,7 +2751,7 @@ void EmojiPan::paintEvent(QPaintEvent *e) { 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); + p.fillRect(myrtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height()), st::emojiPanBg); if (_stickersShown && s_inner.showSectionIcons()) { p.fillRect(r.left(), _iconsTop, r.width(), st::emojiCategory.height, st::emojiPanCategories); paintStickerSettingsIcon(p); @@ -2804,22 +2805,22 @@ void EmojiPan::paintEvent(QPaintEvent *e) { float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), 0., 1.); if (o_right > 0) { p.setOpacity(o_right); - st::stickerIconRight.fill(p, rtlrect(width() - _iconsLeft - 7 * st::emojiCategory.width, _iconsTop, st::stickerIconRight.width(), st::emojiCategory.height, width())); + st::stickerIconRight.fill(p, rtlrect(_iconsLeft + 7 * st::emojiCategory.width - st::stickerIconRight.width(), _iconsTop, st::stickerIconRight.width(), st::emojiCategory.height, width())); } } } 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::emojiCategory.height, st::white); + p.fillRect(x, _recent->y(), r.left() + r.width() - x, st::emojiCategory.height, st::emojiPanBg); } else { p.fillRect(r.left(), _iconsTop, r.width(), st::emojiCategory.height, st::emojiPanCategories); } } else { - p.fillRect(r, st::white); + p.fillRect(r, st::emojiPanBg); p.drawPixmap(r.left(), r.top(), _cache); } } else { - p.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::emojiCategory.height), st::white->b); - p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::emojiCategory.height), st::emojiPanCategories->b); + p.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::emojiCategory.height), st::emojiPanBg); + p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::emojiCategory.height), st::emojiPanCategories); 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()); diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 4e28e6f4d..950d70dca 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -56,7 +56,7 @@ stickersFeaturedUnreadBg: msgFileInBg; stickersFeaturedUnreadSize: 5px; stickersFeaturedUnreadSkip: 5px; stickersFeaturedUnreadTop: 7px; -stickersFeaturedInstalled: icon {{ "mediaview_save_check", #40ace3 }}; +stickersFeaturedInstalled: icon {{ "mediaview_save_check", windowActiveFill }}; stickersMaxHeight: 440px; stickersPadding: margins(19px, 17px, 19px, 17px); diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index e6da02fbe..1e1ec37c1 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -58,8 +58,8 @@ TitleWidget::Hider::Hider(QWidget *parent) : TWidget(parent) { void TitleWidget::Hider::paintEvent(QPaintEvent *e) { QPainter p(this); - p.setOpacity(_level * st::layerAlpha); - p.fillRect(App::main()->dlgsWidth(), 0, width() - App::main()->dlgsWidth(), height(), st::layerBg->b); + p.setOpacity(_level); + p.fillRect(App::main()->dlgsWidth(), 0, width() - App::main()->dlgsWidth(), height(), st::layerBg); } void TitleWidget::Hider::mousePressEvent(QMouseEvent *e) { @@ -325,8 +325,6 @@ void TitleWidget::updateCounter() { int32 counter = App::histories().unreadBadge(); bool muted = App::histories().unreadOnlyMuted(); - auto &bg = (muted ? st::counterMuteBg : st::counterBg); - if (counter > 0) { int32 size = cRetina() ? -32 : -16; switch (cScale()) { @@ -334,7 +332,9 @@ void TitleWidget::updateCounter() { case dbisOneAndHalf: size = -24; break; case dbisTwo: size = -32; break; } - _counter = App::pixmapFromImageInPlace(App::wnd()->iconWithCounter(size, counter, bg, false)); + auto &bg = (muted ? st::titleCounterBgMute : st::titleCounterBg); + auto &fg = st::titleCounterFg; + _counter = App::pixmapFromImageInPlace(App::wnd()->iconWithCounter(size, counter, bg, fg, false)); _counter.setDevicePixelRatio(cRetinaFactor()); update(QRect(st::titleCounterPosition, _counter.size() / cIntRetinaFactor())); } else { diff --git a/Telegram/SourceFiles/ui/buttons/checkbox.cpp b/Telegram/SourceFiles/ui/buttons/checkbox.cpp index 36aeedbb4..b5188f52d 100644 --- a/Telegram/SourceFiles/ui/buttons/checkbox.cpp +++ b/Telegram/SourceFiles/ui/buttons/checkbox.cpp @@ -182,7 +182,7 @@ void Checkbox::paintEvent(QPaintEvent *e) { QRect r(e->rect()); p.setClipRect(r); - p.fillRect(r, _st.textBg->b); + p.fillRect(r, _st.textBg); if (_checkRect.intersects(r)) { p.setRenderHint(QPainter::HighQualityAntialiasing); @@ -196,12 +196,12 @@ void Checkbox::paintEvent(QPaintEvent *e) { pen.setWidth(_st.thickness); p.setPen(pen); if (checked > 0) { - color.setRedF(color.redF() * checked + st::white->c.redF() * (1. - checked)); - color.setGreenF(color.greenF() * checked + st::white->c.greenF() * (1. - checked)); - color.setBlueF(color.blueF() * checked + st::white->c.blueF() * (1. - checked)); + color.setRedF(color.redF() * checked + _st.checkBg->c.redF() * (1. - checked)); + color.setGreenF(color.greenF() * checked + _st.checkBg->c.greenF() * (1. - checked)); + color.setBlueF(color.blueF() * checked + _st.checkBg->c.blueF() * (1. - checked)); p.setBrush(color); } else { - p.setBrush(st::white); + p.setBrush(_st.checkBg); } p.drawRoundedRect(QRectF(_checkRect).marginsRemoved(QMarginsF(_st.thickness / 2., _st.thickness / 2., _st.thickness / 2., _st.thickness / 2.)), st::buttonRadius - (_st.thickness / 2.), st::buttonRadius - (_st.thickness / 2.)); p.setRenderHint(QPainter::HighQualityAntialiasing, false); @@ -340,7 +340,7 @@ void Radiobutton::paintEvent(QPaintEvent *e) { } pen.setWidth(_st.thickness); p.setPen(pen); - p.setBrush(Qt::NoBrush); + p.setBrush(_st.checkBg); //int32 skip = qCeil(_st.thickness / 2.); //p.drawEllipse(_checkRect.marginsRemoved(QMargins(skip, skip, skip, skip))); p.drawEllipse(QRectF(_checkRect).marginsRemoved(QMarginsF(_st.thickness / 2., _st.thickness / 2., _st.thickness / 2., _st.thickness / 2.))); diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp index f994f7f27..f2801e591 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp @@ -41,7 +41,7 @@ HistoryDownButton::HistoryDownButton(QWidget *parent) : Button(parent) { Painter p(&cache); p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(0, 0, iconWidth, iconHeight, st::transparent); + p.fillRect(0, 0, iconWidth, iconHeight, Qt::transparent); st::historyToDown.paint(p, QPoint(0, 0), st::historyToDown.width()); } _cache = App::pixmapFromImageInPlace(std_::move(cache)); @@ -149,7 +149,7 @@ void EmojiButton::paintEvent(QPaintEvent *e) { uint64 ms = getms(); - p.fillRect(e->rect(), st::white); + p.fillRect(e->rect(), st::historyComposeAreaBg); auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); auto opacity = over * _st.overOpacity + (1. - over) * _st.opacity; diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.cpp b/Telegram/SourceFiles/ui/buttons/icon_button.cpp index 9720e0288..c0269e260 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/icon_button.cpp @@ -57,4 +57,45 @@ void IconButton::onStateChanged(int oldState, ButtonStateChangeSource source) { } } +MaskButton::MaskButton(QWidget *parent, const style::MaskButton &st) : Button(parent) +, _st(st) { + resize(_st.width, _st.height); + setCursor(style::cur_pointer); + setAttribute(Qt::WA_OpaquePaintEvent); +} + +void MaskButton::onStateChanged(int oldState, ButtonStateChangeSource source) { + auto over = (_state & StateOver); + if (over != (oldState & StateOver)) { + auto from = over ? _st.iconBg->c : _st.iconBgOver->c; + auto to = over ? _st.iconBgOver->c : _st.iconBg->c; + _a_iconBg.start([this] { update(); }, from, to, _st.duration); + } +} + +void MaskButton::paintEvent(QPaintEvent *e) { + Painter p(this); + + auto clip = e->rect(); + auto position = (_state & StateDown) ? _st.downIconPosition : _st.iconPosition; + if (position.x() < 0) { + position.setX((width() - _st.icon.width()) / 2); + } + if (position.y() < 0) { + position.setY((height() - _st.icon.height()) / 2); + } + auto icon = myrtlrect(position.x(), position.y(), _st.icon.width(), _st.icon.height()); + if (!icon.contains(clip)) { + p.fillRect(clip, _st.bg); + } + if (icon.intersects(clip)) { + if (_a_iconBg.animating(getms())) { + p.fillRect(icon.intersected(clip), _a_iconBg.current()); + } else { + p.fillRect(icon.intersected(clip), (_state & StateOver) ? _st.iconBgOver : _st.iconBg); + } + _st.icon.paint(p, position, width()); + } +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.h b/Telegram/SourceFiles/ui/buttons/icon_button.h index 104a5825a..ae23fc2e9 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.h +++ b/Telegram/SourceFiles/ui/buttons/icon_button.h @@ -45,4 +45,20 @@ private: }; +class MaskButton : public Button { +public: + MaskButton(QWidget *parent, const style::MaskButton &st); + +protected: + void paintEvent(QPaintEvent *e) override; + + void onStateChanged(int oldState, ButtonStateChangeSource source) override; + +private: + const style::MaskButton &_st; + + ColorAnimation _a_iconBg; + +}; + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/round_button.cpp b/Telegram/SourceFiles/ui/buttons/round_button.cpp index 7dea05a67..fad19eccc 100644 --- a/Telegram/SourceFiles/ui/buttons/round_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/round_button.cpp @@ -27,11 +27,7 @@ RoundButton::RoundButton(QWidget *parent, const QString &text, const style::Roun , _text(text) , _fullText(text) , _textWidth(st.font->width(_text)) -, _st(st) -, a_textBgOverOpacity(0) -, a_textFg(st.textFg->c) -, a_secondaryTextFg(st.secondaryTextFg->c) -, _a_over(animation(this, &RoundButton::step_over)) { +, _st(st) { resizeToText(); setCursor(style::cur_pointer); @@ -104,11 +100,9 @@ void RoundButton::paintEvent(QPaintEvent *e) { } App::roundRect(p, rounded, _st.textBg, ImageRoundRadius::Small); - auto o = a_textBgOverOpacity.current(); - if (o > 0) { - p.setOpacity(o); + auto over = (_state & StateOver); + if (over) { App::roundRect(p, rounded, _st.textBgOver, ImageRoundRadius::Small); - p.setOpacity(1); } p.setFont(_st.font); @@ -119,57 +113,19 @@ void RoundButton::paintEvent(QPaintEvent *e) { int textTopDelta = (_state & StateDown) ? (_st.downTextTop - _st.textTop) : 0; int textTop = _st.padding.top() + _st.textTop + textTopDelta; if (!_text.isEmpty()) { - if (o > 0) { - p.setPen(a_textFg.current()); - } else { - p.setPen(_st.textFg); - } + p.setPen(over ? _st.textFgOver : _st.textFg); p.drawTextLeft(textLeft, textTop, width(), _text); } if (!_secondaryText.isEmpty()) { textLeft += _textWidth + (_textWidth ? _st.secondarySkip : 0); - if (o > 0) { - p.setPen(a_secondaryTextFg.current()); - } else { - p.setPen(_st.secondaryTextFg); - } + p.setPen(over ? _st.secondaryTextFgOver : _st.secondaryTextFg); p.drawTextLeft(textLeft, textTop, width(), _secondaryText); } _st.icon.paint(p, QPoint(_st.padding.left(), _st.padding.right() + textTopDelta), width()); } -void RoundButton::step_over(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_over.stop(); - a_textFg.finish(); - a_secondaryTextFg.finish(); - a_textBgOverOpacity.finish(); - } else { - a_textFg.update(dt, anim::linear); - a_secondaryTextFg.update(dt, anim::linear); - a_textBgOverOpacity.update(dt, anim::linear); - } - if (timer) update(); -} - void RoundButton::onStateChanged(int oldState, ButtonStateChangeSource source) { - auto textBgOverOpacity = (_state & StateOver) ? 1. : 0.; - auto textFg = (_state & StateOver) ? (_st.textFgOver) : _st.textFg; - auto secondaryTextFg = (_state & StateOver) ? (_st.secondaryTextFgOver) : _st.secondaryTextFg; - - a_textBgOverOpacity.start(textBgOverOpacity); - a_textFg.start(textFg->c); - a_secondaryTextFg.start(secondaryTextFg->c); - if (source == ButtonByUser || source == ButtonByPress || true) { - _a_over.stop(); - a_textFg.finish(); - a_secondaryTextFg.finish(); - a_textBgOverOpacity.finish(); - update(); - } else { - _a_over.start(); - } + update(); } } // namespace Ui diff --git a/Telegram/SourceFiles/ui/buttons/round_button.h b/Telegram/SourceFiles/ui/buttons/round_button.h index e39669c42..406ce1346 100644 --- a/Telegram/SourceFiles/ui/buttons/round_button.h +++ b/Telegram/SourceFiles/ui/buttons/round_button.h @@ -47,8 +47,6 @@ protected: void onStateChanged(int oldState, ButtonStateChangeSource source) override; private: - void step_over(float64 ms, bool timer); - void updateText(); void resizeToText(); @@ -62,10 +60,6 @@ private: const style::RoundButton &_st; - anim::fvalue a_textBgOverOpacity; - anim::cvalue a_textFg, a_secondaryTextFg; - Animation _a_over; - TextTransform _transform = TextTransform::NoTransform; }; diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index ff146b3bc..71ecb4bda 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -103,10 +103,10 @@ CountryInput::CountryInput(QWidget *parent, const style::countryInput &st) : QWi QPainter p(&trImage); p.setRenderHint(QPainter::Antialiasing); p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(0, 0, trImage.width(), trImage.height(), st::transparent->b); + p.fillRect(0, 0, trImage.width(), trImage.height(), Qt::transparent); p.setPen(Qt::NoPen); - p.setBrush(_st.bgColor->b); + p.setBrush(_st.bgColor); p.drawPolygon(trPoints, 3); } _arrow = App::pixmapFromImageInPlace(std_::move(trImage)); @@ -314,7 +314,7 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) { int l = countriesNow->size(); if (l) { if (r.intersects(QRect(0, 0, width(), st::countriesSkip))) { - p.fillRect(r.intersected(QRect(0, 0, width(), st::countriesSkip)), st::white->b); + p.fillRect(r.intersected(QRect(0, 0, width(), st::countriesSkip)), st::countryRowBg); } int32 from = floorclamp(r.y() - st::countriesSkip, _rowHeight, 0, l); int32 to = ceilclamp(r.y() + r.height() - st::countriesSkip, _rowHeight, 0, l); @@ -322,7 +322,7 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) { bool sel = (i == _sel); int32 y = st::countriesSkip + i * _rowHeight; - p.fillRect(0, y, width(), _rowHeight, (sel ? st::countryRowBgOver : st::white)->b); + p.fillRect(0, y, width(), _rowHeight, sel ? st::countryRowBgOver : st::countryRowBg); QString code = QString("+") + (*countriesNow)[i]->code; int32 codeWidth = st::countryRowCodeFont->width(code); @@ -336,16 +336,17 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) { } p.setFont(st::countryRowNameFont); - p.setPen(st::black); + p.setPen(st::countryRowNameFg); p.drawTextLeft(st::countryRowPadding.left(), y + st::countryRowPadding.top(), width(), name); + p.setFont(st::countryRowCodeFont); p.setPen(sel ? st::countryRowCodeFgOver : st::countryRowCodeFg); p.drawTextLeft(st::countryRowPadding.left() + nameWidth + st::countryRowPadding.right(), y + st::countryRowPadding.top(), width(), code); } } else { - p.fillRect(r, st::white->b); - p.setFont(st::noContactsFont->f); - p.setPen(st::noContactsColor->p); + p.fillRect(r, st::boxBg); + p.setFont(st::noContactsFont); + p.setPen(st::noContactsColor); p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_country_none), style::al_center); } } diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 434d6f1e0..3f27bc79e 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -167,10 +167,7 @@ BoxButton::BoxButton(QWidget *parent, const QString &text, const style::RoundBut , _text(text.toUpper()) , _fullText(text.toUpper()) , _textWidth(st.font->width(_text)) -, _st(st) -, a_textBgOverOpacity(0) -, a_textFg(st.textFg->c) -, _a_over(animation(this, &BoxButton::step_over)) { +, _st(st) { resizeToText(); connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); @@ -203,48 +200,19 @@ void BoxButton::resizeToText() { void BoxButton::paintEvent(QPaintEvent *e) { Painter p(this); - p.fillRect(rect(), _st.textBg->b); + auto over = (_state & StateOver); + p.fillRect(rect(), _st.textBg); - float64 o = a_textBgOverOpacity.current(); - if (o > 0) { - p.setOpacity(o); + if (over) { App::roundRect(p, rect(), _st.textBgOver, ImageRoundRadius::Small); - p.setOpacity(1); - p.setPen(a_textFg.current()); - } else { - p.setPen(_st.textFg); } + p.setPen(over ? _st.textFgOver : _st.textFg); p.setFont(_st.font); auto textTop = (_state & StateDown) ? _st.downTextTop : _st.textTop; p.drawText((width() - _textWidth) / 2, textTop + _st.font->ascent, _text); } -void BoxButton::step_over(float64 ms, bool timer) { - float64 dt = ms / _st.duration; - if (dt >= 1) { - _a_over.stop(); - a_textFg.finish(); - a_textBgOverOpacity.finish(); - } else { - a_textFg.update(dt, anim::linear); - a_textBgOverOpacity.update(dt, anim::linear); - } - if (timer) update(); -} - void BoxButton::onStateChange(int oldState, ButtonStateChangeSource source) { - float64 textBgOverOpacity = (_state & StateOver) ? 1 : 0; - style::color textFg = (_state & StateOver) ? (_st.textFgOver) : _st.textFg; - - a_textBgOverOpacity.start(textBgOverOpacity); - a_textFg.start(textFg->c); - if (source == ButtonByUser || source == ButtonByPress || true) { - _a_over.stop(); - a_textBgOverOpacity.finish(); - a_textFg.finish(); - update(); - } else { - _a_over.start(); - } + update(); } diff --git a/Telegram/SourceFiles/ui/flatbutton.h b/Telegram/SourceFiles/ui/flatbutton.h index 6b1c2576f..5feea438e 100644 --- a/Telegram/SourceFiles/ui/flatbutton.h +++ b/Telegram/SourceFiles/ui/flatbutton.h @@ -105,8 +105,4 @@ private: const style::RoundButton &_st; - anim::fvalue a_textBgOverOpacity; - anim::cvalue a_textFg; - Animation _a_over; - }; diff --git a/Telegram/SourceFiles/ui/flatinput.cpp b/Telegram/SourceFiles/ui/flatinput.cpp index 2829086ca..13a086750 100644 --- a/Telegram/SourceFiles/ui/flatinput.cpp +++ b/Telegram/SourceFiles/ui/flatinput.cpp @@ -745,9 +745,9 @@ void InputArea::paintEvent(QPaintEvent *e) { Painter p(this); QRect r(rect().intersected(e->rect())); - p.fillRect(r, st::white); + p.fillRect(r, _st.textBg); if (_st.border) { - p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b); + p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg); } if (_st.borderActive && a_borderOpacityActive.current() > 0) { p.setOpacity(a_borderOpacityActive.current()); @@ -2182,7 +2182,7 @@ void MaskedInputField::paintEvent(QPaintEvent *e) { Painter p(this); QRect r(rect().intersected(e->rect())); - p.fillRect(r, st::white->b); + p.fillRect(r, _st.textBg); if (_st.border) { p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b); } diff --git a/Telegram/SourceFiles/ui/flattextarea.cpp b/Telegram/SourceFiles/ui/flattextarea.cpp index 8b78905cd..1e9635733 100644 --- a/Telegram/SourceFiles/ui/flattextarea.cpp +++ b/Telegram/SourceFiles/ui/flattextarea.cpp @@ -927,7 +927,7 @@ void prepareFormattingOptimization(QTextDocument *document) { } } -void removeTags(QTextDocument *document, int from, int end) { +void removeTags(const style::color &textFg, QTextDocument *document, int from, int end) { QTextCursor c(document->docHandle(), 0); c.setPosition(from); c.setPosition(end, QTextCursor::KeepAnchor); @@ -935,12 +935,12 @@ void removeTags(QTextDocument *document, int from, int end) { QTextCharFormat format; format.setAnchor(false); format.setAnchorName(QString()); - format.setForeground(st::black); + format.setForeground(textFg); c.mergeCharFormat(format); } // Returns the position of the first inserted tag or "changedEnd" value if none found. -int processInsertedTags(QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags, FlatTextarea::TagMimeProcessor *processor) { +int processInsertedTags(const style::color &textFg, QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags, FlatTextarea::TagMimeProcessor *processor) { int firstTagStart = changedEnd; int applyNoTagFrom = changedEnd; for_const (auto &tag, tags) { @@ -955,7 +955,7 @@ int processInsertedTags(QTextDocument *document, int changedPosition, int change prepareFormattingOptimization(document); if (applyNoTagFrom < tagFrom) { - removeTags(document, applyNoTagFrom, tagFrom); + removeTags(textFg, document, applyNoTagFrom, tagFrom); } QTextCursor c(document->docHandle(), 0); c.setPosition(tagFrom); @@ -971,7 +971,7 @@ int processInsertedTags(QTextDocument *document, int changedPosition, int change } } if (applyNoTagFrom < changedEnd) { - removeTags(document, applyNoTagFrom, changedEnd); + removeTags(textFg, document, applyNoTagFrom, changedEnd); } return firstTagStart; @@ -1036,7 +1036,7 @@ void FlatTextarea::processFormatting(int insertPosition, int insertEnd) { // Apply inserted tags. auto insertedTagsProcessor = _insertedTagsAreFromMime ? _tagMimeProcessor.get() : nullptr; - int breakTagOnNotLetterTill = processInsertedTags(doc, insertPosition, insertEnd, + int breakTagOnNotLetterTill = processInsertedTags(_st.textColor, doc, insertPosition, insertEnd, _insertedTags, insertedTagsProcessor); using ActionType = FormattingAction::Type; while (true) { @@ -1143,7 +1143,7 @@ void FlatTextarea::processFormatting(int insertPosition, int insertEnd) { QTextCharFormat format; format.setAnchor(false); format.setAnchorName(QString()); - format.setForeground(st::black); + format.setForeground(_st.textColor); c.mergeCharFormat(format); } else if (action.type == ActionType::TildeFont) { QTextCharFormat format; diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index a6bb352bb..868551128 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -36,7 +36,7 @@ WebImages webImages; Image *generateBlankImage() { auto data = QImage(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - data.fill(QColor(255, 255, 255, 0)); + data.fill(Qt::transparent); data.setDevicePixelRatio(cRetinaFactor()); return internal::getImage(App::pixmapFromImageInPlace(std_::move(data)), "GIF"); } @@ -317,7 +317,7 @@ QImage imageBlur(QImage img) { QPainter p(&imgsmall); p.setCompositionMode(QPainter::CompositionMode_Source); p.setRenderHint(QPainter::SmoothPixmapTransform); - p.fillRect(0, 0, w, h, st::transparent->b); + p.fillRect(0, 0, w, h, Qt::transparent); p.drawImage(QRect(radius, radius, w - 2 * radius, h - 2 * radius), img, QRect(0, 0, w, h)); } QImage was = img; @@ -423,8 +423,8 @@ const QPixmap &circleMask(int width, int height) { Painter p(&mask); p.setRenderHint(QPainter::HighQualityAntialiasing); p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(0, 0, width, height, st::transparent); - p.setBrush(st::white); + p.fillRect(0, 0, width, height, Qt::transparent); + p.setBrush(Qt::white); p.setPen(Qt::NoPen); p.drawEllipse(0, 0, width, height); } @@ -534,7 +534,7 @@ QPixmap imagePix(QImage img, int32 w, int32 h, ImagePixOptions options, int32 ou { QPainter p(&result); if (w < outerw || h < outerh) { - p.fillRect(0, 0, result.width(), result.height(), st::black); + p.fillRect(0, 0, result.width(), result.height(), st::imageBg); } p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img); } @@ -576,14 +576,14 @@ QPixmap Image::pixNoCache(int w, int h, ImagePixOptions options, int outerw, int { QPainter p(&result); if (w < outerw) { - p.fillRect(0, 0, (outerw - w) / 2, result.height(), st::black); - p.fillRect(((outerw - w) / 2) + w, 0, result.width() - (((outerw - w) / 2) + w), result.height(), st::black); + p.fillRect(0, 0, (outerw - w) / 2, result.height(), st::imageBg); + p.fillRect(((outerw - w) / 2) + w, 0, result.width() - (((outerw - w) / 2) + w), result.height(), st::imageBg); } if (h < outerh) { - p.fillRect(qMax(0, (outerw - w) / 2), 0, qMin(result.width(), w), (outerh - h) / 2, st::black); - p.fillRect(qMax(0, (outerw - w) / 2), ((outerh - h) / 2) + h, qMin(result.width(), w), result.height() - (((outerh - h) / 2) + h), st::black); + p.fillRect(qMax(0, (outerw - w) / 2), 0, qMin(result.width(), w), (outerh - h) / 2, st::imageBg); + p.fillRect(qMax(0, (outerw - w) / 2), ((outerh - h) / 2) + h, qMin(result.width(), w), result.height() - (((outerh - h) / 2) + h), st::imageBg); } - p.fillRect(qMax(0, (outerw - w) / 2), qMax(0, (outerh - h) / 2), qMin(result.width(), w), qMin(result.height(), h), st::white); + p.fillRect(qMax(0, (outerw - w) / 2), qMax(0, (outerh - h) / 2), qMin(result.width(), w), qMin(result.height(), h), st::imageBgTransparent); } if (options.testFlag(ImagePixCircled)) { diff --git a/Telegram/SourceFiles/ui/scrollarea.cpp b/Telegram/SourceFiles/ui/scrollarea.cpp index 6176f850f..f6c23dc53 100644 --- a/Telegram/SourceFiles/ui/scrollarea.cpp +++ b/Telegram/SourceFiles/ui/scrollarea.cpp @@ -47,8 +47,8 @@ ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st) , _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar()) , _scrollMax(_connected->maximum()) , _hideIn(-1) -, a_bg((_st->hiding ? st::transparent : _st->bgColor)->c) -, a_bar((_st->hiding ? st::transparent : _st->barColor)->c) +, a_bg(_st->hiding ? _st->bgColor->transparent() : _st->bgColor->c) +, a_bar(_st->hiding ? _st->barColor->transparent() : _st->barColor->c) , _a_appearance(animation(this, &ScrollBar::step_appearance)) { recountSize(); diff --git a/Telegram/SourceFiles/ui/style/style_core_color.h b/Telegram/SourceFiles/ui/style/style_core_color.h index 8e3c88284..1b4c1b190 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.h +++ b/Telegram/SourceFiles/ui/style/style_core_color.h @@ -69,13 +69,15 @@ private: class ColorData { public: - QColor c; QPen p; QBrush b; -private: + QColor transparent() const { + return QColor(c.red(), c.green(), c.blue(), 0); + } +private: ColorData(uchar r, uchar g, uchar b, uchar a); void set(const QColor &c); diff --git a/Telegram/SourceFiles/ui/text/text_block.h b/Telegram/SourceFiles/ui/text/text_block.h index babc1f4f3..63bf7de6f 100644 --- a/Telegram/SourceFiles/ui/text/text_block.h +++ b/Telegram/SourceFiles/ui/text/text_block.h @@ -112,7 +112,6 @@ protected: class NewlineBlock : public ITextBlock { public: - Qt::LayoutDirection nextDirection() const { return _nextDir; } @@ -122,8 +121,7 @@ public: } private: - - NewlineBlock(const style::font &font, const QString &str, uint16 from, uint16 length) : ITextBlock(font, str, from, length, 0, st::transparent, 0), _nextDir(Qt::LayoutDirectionAuto) { + NewlineBlock(const style::font &font, const QString &str, uint16 from, uint16 length) : ITextBlock(font, str, from, length, 0, st::windowTextFg, 0), _nextDir(Qt::LayoutDirectionAuto) { _flags |= ((TextBlockTNewline & 0x0F) << 8); } @@ -133,6 +131,7 @@ private: friend class TextParser; friend class TextPainter; + }; class TextWord { diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index d4bc4f68c..a55cd9ea6 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -584,7 +584,7 @@ void MultiSelect::Inner::paintEvent(QPaintEvent *e) { _iconOpacity.step(ms); auto paintRect = e->rect(); - p.fillRect(paintRect, st::windowBg); + p.fillRect(paintRect, _st.bg); auto offset = QPoint(rtl() ? _st.padding.right() : _st.padding.left(), _st.padding.top()); p.translate(offset); diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.h b/Telegram/SourceFiles/ui/widgets/multi_select.h index 9ac568ee6..423ea1797 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.h +++ b/Telegram/SourceFiles/ui/widgets/multi_select.h @@ -26,7 +26,7 @@ class InputField; namespace Ui { -class IconButton; +class MaskButton; class MultiSelect : public TWidget { public: @@ -154,7 +154,7 @@ private: int _fieldTop = 0; int _fieldWidth = 0; ChildWidget _field; - ChildWidget _cancel; + ChildWidget _cancel; int _newHeight = 0; IntAnimation _height; diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index 685a90404..c11e81138 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -112,7 +112,8 @@ void PopupMenu::paintEvent(QPaintEvent *e) { return; } - p.fillRect(clip, st::almostTransparent); + // This is the minimal alpha value that allowed mouse tracking in OS X. + p.fillRect(clip, QColor(255, 255, 255, 13)); p.setCompositionMode(compositionMode); _shadow.paint(p, _inner, _st.shadowShift); diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 563cef7f7..4765bab69 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -41,6 +41,22 @@ IconButton { duration: int; } +MaskButton { + width: pixels; + height: pixels; + + bg: color; + icon: icon; + + iconBg: color; + iconBgOver: color; + + iconPosition: point; + downIconPosition: point; + + duration: int; +} + MediaSlider { width: pixels; activeFg: color; @@ -92,6 +108,7 @@ MultiSelectItem { } MultiSelect { + bg: color; padding: margins; maxHeight: pixels; scroll: flatScroll; @@ -103,7 +120,7 @@ MultiSelect { fieldMinWidth: pixels; fieldIcon: icon; fieldIconSkip: pixels; - fieldCancel: IconButton; + fieldCancel: MaskButton; fieldCancelSkip: pixels; } @@ -173,6 +190,12 @@ defaultIconButton: IconButton { duration: 150; } +defaultMaskButton: MaskButton { + iconPosition: point(-1px, -1px); + downIconPosition: point(-1px, -1px); + duration: 150; +} + widgetSlideDuration: 200; widgetFadeDuration: 200; @@ -191,10 +214,10 @@ defaultMenuArrow: icon {{ "dropdown_submenu_arrow", #373737 }}; defaultMenu: Menu { skip: 5px; - itemBg: white; + itemBg: windowBg; itemBgOver: windowOverBg; - itemFg: black; - itemFgOver: black; + itemFg: windowTextFg; + itemFgOver: windowTextFg; itemFgDisabled: #cccccc; itemFgShortcut: #999999; itemFgShortcutOver: #7c99b2; diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 1be72cfa8..50f584808 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -477,7 +477,7 @@ Background::Background(QWidget *parent) : TWidget(parent) { void Background::paintEvent(QPaintEvent *e) { Painter p(this); - p.fillRect(rect(), st::notifyBg); + p.fillRect(rect(), st::notificationBg); p.fillRect(0, 0, st::notifyBorderWidth, height(), st::notifyBorder); p.fillRect(width() - st::notifyBorderWidth, 0, st::notifyBorderWidth, height(), st::notifyBorder); p.fillRect(st::notifyBorderWidth, height() - st::notifyBorderWidth, width() - 2 * st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder); @@ -530,11 +530,11 @@ void Notification::prepareActionsCache() { auto actionsCacheHeight = height() - actionsTop; auto actionsCacheImg = QImage(actionsCacheWidth * cIntRetinaFactor(), actionsCacheHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); actionsCacheImg.setDevicePixelRatio(cRetinaFactor()); - actionsCacheImg.fill(st::transparent->c); + actionsCacheImg.fill(Qt::transparent); { Painter p(&actionsCacheImg); st::notifyFadeRight.fill(p, rtlrect(0, 0, fadeWidth, actionsCacheHeight, actionsCacheWidth)); - p.fillRect(rtlrect(fadeWidth, 0, actionsCacheWidth - fadeWidth, actionsCacheHeight, actionsCacheWidth), st::notifyBg); + p.fillRect(rtlrect(fadeWidth, 0, actionsCacheWidth - fadeWidth, actionsCacheHeight, actionsCacheWidth), st::notificationBg); p.drawPixmapRight(_replyPadding, _reply->y() - actionsTop, actionsCacheWidth, replyCache); } _buttonsCache = App::pixmapFromImageInPlace(std_::move(actionsCacheImg)); @@ -615,7 +615,7 @@ void Notification::updateNotifyDisplay() { int32 w = width(), h = height(); QImage img(w * cIntRetinaFactor(), h * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); if (cRetina()) img.setDevicePixelRatio(cRetinaFactor()); - img.fill(st::notifyBg->c); + img.fill(st::notificationBg->c); { Painter p(&img); diff --git a/Telegram/SourceFiles/window/slide_animation.cpp b/Telegram/SourceFiles/window/slide_animation.cpp index 01f65a3a7..5bab75b93 100644 --- a/Telegram/SourceFiles/window/slide_animation.cpp +++ b/Telegram/SourceFiles/window/slide_animation.cpp @@ -33,8 +33,8 @@ void SlideAnimation::paintContents(Painter &p, const QRect &update) const { _animation.step(getms()); if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, _cacheUnder.height())); - p.setOpacity(a_progress.current() * st::slideFadeOut); - p.fillRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina, st::black); + p.setOpacity(a_progress.current()); + p.fillRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina, st::slideFadeOutBg); p.setOpacity(1); } p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, _cacheOver.height() / retina), _cacheOver, QRect(0, 0, _cacheOver.width(), _cacheOver.height())); diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 6aa796332..28900ff04 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -140,7 +140,7 @@ bool TopBarWidget::eventFilter(QObject *obj, QEvent *e) { void TopBarWidget::paintEvent(QPaintEvent *e) { Painter p(this); - p.fillRect(QRect(0, 0, width(), st::topBarHeight), st::topBarBG->b); + p.fillRect(QRect(0, 0, width(), st::topBarHeight), st::topBarBg); if (_clearSelection->isHidden()) { p.save(); int decreaseWidth = 0; diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp index 2034628bb..4c6f9e85d 100644 --- a/Telegram/SourceFiles/window/window_theme.cpp +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -174,7 +174,7 @@ void applyBackground(QImage &&background, bool tiled, Instance *out) { } bool loadThemeFromCache(const QByteArray &content, Cached &cache) { - if (cache.paletteChecksum != style::palette::kChecksum) { + if (cache.paletteChecksum != style::palette::Checksum()) { return false; } if (cache.contentChecksum != hashCrc32(content.constData(), content.size())) { @@ -285,7 +285,7 @@ bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr } else { cache.colors = style::main_palette::save(); } - cache.paletteChecksum = style::palette::kChecksum; + cache.paletteChecksum = style::palette::Checksum(); cache.contentChecksum = hashCrc32(content.constData(), content.size()); return true; From c932f3d9df63f8f36ad8315d55234ece77d6ecf7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 1 Nov 2016 15:46:34 +0300 Subject: [PATCH 008/100] Removing almost all animated over states in IconButton. --- Telegram/Resources/basic.style | 7 +- Telegram/Resources/colors.palette | 11 ++- Telegram/Resources/sample.tdesktop-theme | 10 ++- Telegram/SourceFiles/boxes/boxes.style | 8 +- .../SourceFiles/boxes/notifications_box.cpp | 1 - .../SourceFiles/codegen/style/generator.cpp | 10 ++- .../SourceFiles/codegen/style/parsed_file.cpp | 8 +- Telegram/SourceFiles/dialogs/dialogs.style | 6 +- Telegram/SourceFiles/dialogswidget.cpp | 4 +- .../history/field_autocomplete.cpp | 4 +- Telegram/SourceFiles/history/history.style | 81 ++++++++++------- Telegram/SourceFiles/historywidget.cpp | 45 +++++----- Telegram/SourceFiles/historywidget.h | 1 - Telegram/SourceFiles/intro/intro.style | 9 +- Telegram/SourceFiles/mainwidget.cpp | 12 +-- .../media/player/media_player.style | 22 ++--- .../media/view/media_clip_controller.cpp | 4 +- .../view/media_clip_volume_controller.cpp | 12 ++- .../SourceFiles/media/view/mediaview.style | 38 ++++---- Telegram/SourceFiles/settings/settings.style | 2 +- .../SourceFiles/settings/settings_widget.cpp | 2 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 4 +- Telegram/SourceFiles/stickers/stickers.style | 15 ++-- Telegram/SourceFiles/ui/animation.h | 8 +- .../ui/buttons/history_down_button.cpp | 27 +++--- .../ui/buttons/history_down_button.h | 2 - .../SourceFiles/ui/buttons/icon_button.cpp | 44 ++++++++-- Telegram/SourceFiles/ui/buttons/icon_button.h | 3 +- Telegram/SourceFiles/ui/style/style_core.cpp | 60 +++++++++---- Telegram/SourceFiles/ui/style/style_core.h | 8 +- .../SourceFiles/ui/style/style_core_color.cpp | 4 +- .../SourceFiles/ui/style/style_core_color.h | 30 +++++-- .../SourceFiles/ui/style/style_core_icon.cpp | 86 +++++++++++++------ .../SourceFiles/ui/style/style_core_icon.h | 52 ++++++----- .../SourceFiles/ui/widgets/dropdown_menu.cpp | 4 +- .../SourceFiles/ui/widgets/dropdown_menu.h | 2 +- .../SourceFiles/ui/widgets/media_slider.cpp | 29 +++++-- Telegram/SourceFiles/ui/widgets/menu.cpp | 13 ++- Telegram/SourceFiles/ui/widgets/menu.h | 5 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 4 +- Telegram/SourceFiles/ui/widgets/popup_menu.h | 2 +- Telegram/SourceFiles/ui/widgets/widgets.style | 29 ++----- Telegram/SourceFiles/window/top_bar_widget.h | 4 +- Telegram/SourceFiles/window/window.style | 10 ++- 44 files changed, 435 insertions(+), 307 deletions(-) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index d882b86eb..8c925db16 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -594,7 +594,7 @@ msgImgReplyBarColor: #ffffff; msgBotKbDuration: 200; msgBotKbFont: semiboldFont; -msgBotKbOverBg: #fffffffc; +msgBotKbOverBg: #ffffff1a; msgBotKbIconPadding: 2px; msgBotKbUrlIcon: icon {{ "inline_button_url", #ffffff }}; msgBotKbSwitchPmIcon: icon {{ "inline_button_switch", #ffffff }}; @@ -900,7 +900,8 @@ contactsScroll: flatScroll(boxScroll) { deltab: 0px; } -simpleCloseIcon: icon {{ "simple_close", #000000 }}; +simpleCloseIcon: icon {{ "simple_close", #c7c7c7 }}; +simpleCloseIconOver: icon {{ "simple_close", #a3a3a3 }}; boxPhotoPadding: margins(28px, 28px, 28px, 18px); boxPhotoCompressedPadding: margins(0px, 2px, 0px, 22px); @@ -1197,7 +1198,5 @@ settingsFixedBarCloseIcon: icon {{ "box_button_close", settingsFixedBarBg }}; notifyFadeRight: icon {{ "fade_horizontal_right", notificationBg }}; -emojiPanBg: #ffffff; -emojiPanCategories: #f7f7f7; stickerIconLeft: icon {{ "fade_horizontal_left", emojiPanCategories }}; stickerIconRight: icon {{ "fade_horizontal_right", emojiPanCategories }}; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index cf3f2d4af..cd38d6629 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -82,8 +82,7 @@ photoCropFadeBg: #0000007f; photoCropPointFg: #ffffff7f; // settings -settingsBg: windowBg; -settingsFixedBarBg: settingsBg; +settingsFixedBarBg: boxBg; settingsCloseFg: cancelIconFg; settingsCloseFgOver: cancelIconFgOver; @@ -101,7 +100,14 @@ introErrorFg: windowTextFg; // history topBarBg: windowBg; +emojiPanBg: windowBg; +emojiPanCategories: #f7f7f7 | windowBg; +emojiPanHeaderFg: #999999 | windowSubTextFg; +emojiPanHeaderBg: #fffffff2 | emojiPanBg; + historyComposeAreaBg: windowBg; +historyComposeIconFg: #cccccc; +historyComposeIconFgOver: #bebebe; historyPinnedBg: historyComposeAreaBg; historyReplyBg: historyComposeAreaBg; historyReplyCancelIconFg: cancelIconFg; @@ -109,6 +115,7 @@ historyReplyCancelIconFgOver: cancelIconFgOver; historySendBg: historyComposeAreaBg; historySendBgOver: #f5f5f5 | historySendBg; +historyMenuItemBgOver: historySendBgOver; historyTextInFg: windowTextFg; historyTextOutFg: windowTextFg; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index 7c64a1fb7..b38bf9404 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -66,8 +66,7 @@ contactsBgOver: windowOverBg; contactsNameFg: boxTextFg; photoCropFadeBg: #0000007f; photoCropPointFg: #ffffff7f; -settingsBg: windowBg; -settingsFixedBarBg: settingsBg; +settingsFixedBarBg: boxBg; settingsCloseFg: cancelIconFg; settingsCloseFgOver: cancelIconFgOver; notificationsBoxMonitorFg: windowTextFg; @@ -78,13 +77,20 @@ notificationSampleNameFg: #939393; // windowSubTextFg; introHeaderFg: windowTextFg; introErrorFg: windowTextFg; topBarBg: windowBg; +emojiPanBg: windowBg; +emojiPanCategories: #f7f7f7; // windowBg; +emojiPanHeaderFg: #999999; // windowSubTextFg; +emojiPanHeaderBg: #fffffff2; // emojiPanBg; historyComposeAreaBg: windowBg; +historyComposeIconFg: #cccccc; +historyComposeIconFgOver: #bebebe; historyPinnedBg: historyComposeAreaBg; historyReplyBg: historyComposeAreaBg; historyReplyCancelIconFg: cancelIconFg; historyReplyCancelIconFgOver: cancelIconFgOver; historySendBg: historyComposeAreaBg; historySendBgOver: #f5f5f5; // historySendBg; +historyMenuItemBgOver: historySendBgOver; historyTextInFg: windowTextFg; historyTextOutFg: windowTextFg; historyCaptionInFg: historyTextInFg; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 3af7e9d24..8c7be1bc3 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -157,7 +157,7 @@ contactsMultiSelect: MultiSelect { iconBgOver: boxSearchCancelIconFgOver; iconPosition: point(8px, 18px); - downIconPosition: point(8px, 18px); + iconPositionDown: point(8px, 18px); } fieldCancelSkip: 34px; } @@ -230,13 +230,15 @@ sessionInfoFont: msgFont; sessionInfoFg: #888888; sessionTerminateTop: 30px; sessionTerminateSkip: 18px; -sessionTerminate: IconButton(defaultIconButton) { +sessionTerminate: IconButton { width: 16px; height: 16px; icon: simpleCloseIcon; + iconOver: simpleCloseIconOver; + iconPosition: point(3px, 3px); - downIconPosition: point(3px, 4px); + iconPositionDown: point(3px, 4px); } passcodeHeaderFont: font(19px); diff --git a/Telegram/SourceFiles/boxes/notifications_box.cpp b/Telegram/SourceFiles/boxes/notifications_box.cpp index b63e365f3..d6312f588 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.cpp +++ b/Telegram/SourceFiles/boxes/notifications_box.cpp @@ -289,7 +289,6 @@ void NotificationsBox::prepareNotificationSampleLarge() { auto notifyTitle = st::msgNameFont->elided(qsl("Telegram Desktop"), rectForName.width()); p.drawText(rectForName.left(), rectForName.top() + st::msgNameFont->ascent, notifyTitle); - p.setOpacity(st::notifyClose.opacity); st::notifyClose.icon.paint(p, w - st::notifyClosePos.x() - st::notifyClose.width + st::notifyClose.iconPosition.x(), st::notifyClosePos.y() + st::notifyClose.iconPosition.y(), w); } diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index b01feaee1..a475920de 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -934,9 +934,13 @@ QByteArray iconMaskValueSize(int width, int height) { return result; } -QByteArray iconMaskValuePng(const QString &filepath) { +QByteArray iconMaskValuePng(QString filepath) { QByteArray result; + auto inverted = filepath.endsWith("-invert"); + if (inverted) { + filepath.chop(QLatin1String("-invert").size()); + } QImage png100x(filepath + ".png"); QImage png200x(filepath + "@2x.png"); png100x.setDevicePixelRatio(1.); @@ -957,6 +961,10 @@ QByteArray iconMaskValuePng(const QString &filepath) { common::logError(kErrorBadIconSize, filepath + ".png") << "bad icons size, 1x: " << png100x.width() << "x" << png100x.height() << ", 2x: " << png200x.width() << "x" << png200x.height(); return result; } + if (inverted) { + png100x.invertPixels(); + png200x.invertPixels(); + } QImage png125x = png200x.scaled(structure::data::pxAdjust(png100x.width(), 5), structure::data::pxAdjust(png100x.height(), 5), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QImage png150x = png200x.scaled(structure::data::pxAdjust(png100x.width(), 6), structure::data::pxAdjust(png100x.height(), 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index 8a2fe4dea..7277dc2e5 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -822,16 +822,20 @@ QString ParsedFile::readMonoIconFilename() { if (auto filename = readValue()) { if (filename.type().tag == structure::TypeTag::String) { auto filepath = QString::fromStdString(filename.String()); + auto inverted = filepath.endsWith("-invert"); + if (inverted) { + filepath.chop(QLatin1String("-invert").size()); + } for (const auto &path : options_.includePaths) { QFileInfo fileinfo(path + '/' + filepath + ".png"); if (fileinfo.exists()) { - return path + '/' + filepath; + return path + '/' + filepath + (inverted ? "-invert" : ""); } } for (const auto &path : options_.includePaths) { QFileInfo fileinfo(path + "/icons/" + filepath + ".png"); if (fileinfo.exists()) { - return path + "/icons/" + filepath; + return path + "/icons/" + filepath + (inverted ? "-invert" : ""); } } logError(common::kErrorFileNotFound) << "could not open icon file '" << filename.String() << "'"; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index f6a78f96a..c20e90955 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -84,18 +84,18 @@ dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) { } dialogsNewChatIcon: icon {{ "dialogs_new_chat", #b7b7b7 }}; -dialogsNewChatButton: IconButton(defaultIconButton) { +dialogsNewChatButton: IconButton { width: 36px; height: 36px; icon: dialogsNewChatIcon; iconPosition: point(9px, 10px); - downIconPosition: point(9px, 11px); + iconPositionDown: point(9px, 11px); } dialogsAddContact: IconButton(dialogsNewChatButton) { icon: icon {{ "dialogs_add_contact", #a6a6a6 }}; iconPosition: point(8px, 8px); - downIconPosition: point(8px, 9px); + iconPositionDown: point(8px, 9px); } dialogsCancelSearch: IconButton(dialogsAddContact) { icon: icon {{ "dialogs_cancel_search", #a6a6a6 }}; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 5fc8b3b10..b3d59aac2 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -126,8 +126,8 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO p.fillRect(0, 0, w, st::mentionHeight, selected ? st::mentionBgOver : st::dialogsBg); if (!paintingOther) { if (selected) { - int skip = (st::mentionHeight - st::simpleCloseIcon.height()) / 2; - st::simpleCloseIcon.paint(p, QPoint(w - st::simpleCloseIcon.width() - skip, skip), width()); + int skip = (st::mentionHeight - st::simpleCloseIconOver.height()) / 2; + st::simpleCloseIconOver.paint(p, QPoint(w - st::simpleCloseIconOver.width() - skip, skip), width()); } QString first = (_hashtagFilter.size() < 2) ? QString() : ('#' + _hashtagResults.at(from).mid(0, _hashtagFilter.size() - 1)), second = (_hashtagFilter.size() < 2) ? ('#' + _hashtagResults.at(from)) : _hashtagResults.at(from).mid(_hashtagFilter.size() - 1); int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second); diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index ada41bb66..b46b759a8 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -605,9 +605,9 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { bool selected = (i == _sel); if (selected) { p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver); - int skip = (st::mentionHeight - st::simpleCloseIcon.height()) / 2; + int skip = (st::mentionHeight - st::simpleCloseIconOver.height()) / 2; if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) { - st::simpleCloseIcon.paint(p, QPoint(width() - st::simpleCloseIcon.width() - skip, i * st::mentionHeight + skip), width()); + st::simpleCloseIconOver.paint(p, QPoint(width() - st::simpleCloseIconOver.width() - skip, i * st::mentionHeight + skip), width()); } } if (!_mrows->isEmpty()) { diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 2c0d2dea8..a5486dc6d 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -161,52 +161,73 @@ historyComposeField: flatTextarea { historyComposeFieldMaxHeight: 224px; // historyMinHeight: 56px; -historyMediaTypeFile: icon {{ "media_type_file", #b3b3b3, point(2px, 2px) }}; -historyMediaTypePhoto: icon {{ "media_type_photo", #bebebe, point(2px, 2px) }}; -historyMediaTypeVideo: icon {{ "media_type_video", #bebebe, point(2px, 2px) }}; -historyMediaTypeSong: icon {{ "media_type_song", #bebebe, point(0px, 0px) }}; -historyMediaTypeVoice: icon {{ "media_type_voice", #bebebe, point(2px, 2px) }}; -historyMediaTypeLink: icon {{ "media_type_link", #bebebe, point(2px, 2px) }}; +historyMediaTypeFile: icon {{ "media_type_file", historyComposeIconFg, point(2px, 2px) }}; +historyMediaTypeFileOver: icon {{ "media_type_file", historyComposeIconFgOver, point(2px, 2px) }}; +historyMediaTypePhoto: icon {{ "media_type_photo", historyComposeIconFg, point(2px, 2px) }}; +historyMediaTypePhotoOver: icon {{ "media_type_photo", historyComposeIconFgOver, point(2px, 2px) }}; +historyMediaTypeVideo: icon {{ "media_type_video", historyComposeIconFg, point(2px, 2px) }}; +historyMediaTypeVideoOver: icon {{ "media_type_video", historyComposeIconFgOver, point(2px, 2px) }}; +historyMediaTypeSong: icon {{ "media_type_song", historyComposeIconFg, point(0px, 0px) }}; +historyMediaTypeSongOver: icon {{ "media_type_song", historyComposeIconFgOver, point(0px, 0px) }}; +historyMediaTypeVoice: icon {{ "media_type_voice", historyComposeIconFg, point(2px, 2px) }}; +historyMediaTypeVoiceOver: icon {{ "media_type_voice", historyComposeIconFgOver, point(2px, 2px) }}; +historyMediaTypeLink: icon {{ "media_type_link", historyComposeIconFg, point(2px, 2px) }}; +historyMediaTypeLinkOver: icon {{ "media_type_link", historyComposeIconFgOver, point(2px, 2px) }}; -historyAttachDocument: IconButton(defaultIconButton) { +historyAttachDocument: IconButton { width: 46px; height: 46px; icon: historyMediaTypeFile; + iconOver: historyMediaTypeFileOver; + iconPosition: point(9px, 9px); - downIconPosition: point(9px, 10px); + iconPositionDown: point(9px, 10px); } historyAttachPhoto: IconButton(historyAttachDocument) { icon: historyMediaTypePhoto; + iconOver: historyMediaTypePhotoOver; } historyAttachEmoji: IconButton(historyAttachDocument) { width: 33px; - icon: icon {{ "send_control_emoji", #b9b9b9 }}; + icon: icon {{ "send_control_emoji", historyComposeIconFg }}; + iconOver: icon {{ "send_control_emoji", historyComposeIconFgOver }}; + iconPosition: point(12px, 16px); - downIconPosition: point(12px, 16px); + iconPositionDown: point(12px, 16px); } historyEmojiCircle: size(19px, 19px); historyEmojiCirclePeriod: 1500; historyEmojiCircleDuration: 500; historyEmojiCircleTop: 13px; historyEmojiCircleLine: 2px; -historyEmojiCircleFg: #b9b9b9; +historyEmojiCircleFg: historyComposeIconFg; +historyEmojiCircleFgOver: historyComposeIconFgOver; historyEmojiCirclePart: 3.5; historyBotKeyboardShow: IconButton(historyAttachEmoji) { - icon: icon {{ "send_control_bot_keyboard", #b3b3b3 }}; + icon: icon {{ "send_control_bot_keyboard", historyComposeIconFg }}; + iconOver: icon {{ "send_control_bot_keyboard", historyComposeIconFgOver }}; + iconPosition: point(6px, 12px); - downIconPosition: point(6px, 12px); + iconPositionDown: point(6px, 12px); } historyBotKeyboardHide: IconButton(historyAttachEmoji) { - icon: icon {{ "send_control_bot_keyboard_hide", #b3b3b3 }}; + icon: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFg }}; + iconOver: icon {{ "send_control_bot_keyboard_hide", historyComposeIconFgOver }}; + iconPosition: point(5px, 17px); - downIconPosition: point(5px, 17px); + iconPositionDown: point(5px, 17px); } historyBotCommandStart: IconButton(historyBotKeyboardShow) { - icon: icon {{ "send_control_bot_command", #b3b3b3 }}; + icon: icon {{ "send_control_bot_command", historyComposeIconFg }}; + iconOver: icon {{ "send_control_bot_command", historyComposeIconFgOver }}; } -historyRecordVoice: icon {{ "send_control_record", #b9b9b9 }}; -historyRecordVoiceActive: icon {{ "send_control_record", #58b2ed }}; +historyRecordVoiceFg: historyComposeIconFg; +historyRecordVoiceFgOver: historyComposeIconFgOver; +historyRecordVoiceFgActive: #58b2ed; +historyRecordVoice: icon {{ "send_control_record", historyRecordVoiceFg }}; +historyRecordVoiceOver: icon {{ "send_control_record", historyRecordVoiceFgOver }}; +historyRecordVoiceActive: icon {{ "send_control_record", historyRecordVoiceFgActive }}; historyRecordSignalColor: #f17077; historyRecordSignalMin: 5px; historyRecordSignalMax: 12px; @@ -220,18 +241,18 @@ historyAttachDropdownMenu: DropdownMenu(defaultDropdownMenu) { menu: Menu(defaultMenu) { skip: 5px; - itemBgOver: historySendBgOver; + itemBgOver: historyMenuItemBgOver; itemIconPosition: point(12px, 6px); - itemIconOpacity: 0.78; - itemIconOverOpacity: 1.; itemPadding: margins(48px, 11px, 48px, 11px); } } historySilentToggle: IconButton(historyBotKeyboardShow) { - icon: icon {{ "send_control_silent_off", #b3b3b3 }}; + icon: icon {{ "send_control_silent_off", historyComposeIconFg }}; + iconOver: icon {{ "send_control_silent_off", historyComposeIconFgOver }}; } -historySilentToggleOn: icon {{ "send_control_silent_on", #b3b3b3 }}; +historySilentToggleOn: icon {{ "send_control_silent_on", historyComposeIconFg }}; +historySilentToggleOnOver: icon {{ "send_control_silent_on", historyComposeIconFgOver }}; historyReplySkip: 51px; historyReplyColor: #377aae; @@ -256,14 +277,16 @@ historyInlineBotCancel: MaskButton(historyReplyCancel) { height: 46px; } -topBarSearch: IconButton(defaultIconButton) { +topBarSearch: MaskButton { width: 44px; height: topBarHeight; - icon: icon {{ "title_search", #000000 }}; - iconPosition: point(13px, 18px); - downIconPosition: point(13px, 18px); + bg: topBarBg; + icon: icon {{ "title_search-invert", topBarBg }}; - opacity: 0.22; - overOpacity: 0.36; + iconBg: #c7c7c7; + iconBgOver: #a3a3a3; + + iconPosition: point(13px, 18px); + iconPositionDown: point(13px, 18px); } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 6179aceb3..ec0d31e7b 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2929,7 +2929,7 @@ void SilentToggle::mouseMoveEvent(QMouseEvent *e) { void SilentToggle::setChecked(bool checked) { if (_checked != checked) { _checked = checked; - setIcon(_checked ? &st::historySilentToggleOn : nullptr); + setIcon(_checked ? &st::historySilentToggleOn : nullptr, _checked ? &st::historySilentToggleOnOver : nullptr); } } @@ -3134,8 +3134,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(_botKeyboardHide, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(_botCommandStart, SIGNAL(clicked()), this, SLOT(onCmdStart())); - _attachType->addAction(lang(lng_attach_file), this, SLOT(onDocumentSelect()), &st::historyMediaTypeFile); - _attachType->addAction(lang(lng_attach_photo), this, SLOT(onPhotoSelect()), &st::historyMediaTypePhoto); + _attachType->addAction(lang(lng_attach_file), this, SLOT(onDocumentSelect()), &st::historyMediaTypeFile, &st::historyMediaTypeFileOver); + _attachType->addAction(lang(lng_attach_photo), this, SLOT(onPhotoSelect()), &st::historyMediaTypePhoto, &st::historyMediaTypePhotoOver); _attachType->hide(); _emojiPan->hide(); _attachDragDocument->hide(); @@ -3277,7 +3277,7 @@ void HistoryWidget::onTextChange() { updateMouseTracking(); _a_record.stop(); _inRecord = _inField = false; - a_recordOver = a_recordDown = anim::fvalue(0, 0); + a_recordDown = anim::fvalue(0, 0); a_recordCancel = anim::cvalue(st::historyRecordCancel->c, st::historyRecordCancel->c); } } @@ -4625,7 +4625,6 @@ void HistoryWidget::updateControlsVisibility() { } _a_record.stop(); _inRecord = _inField = false; - a_recordOver = anim::fvalue(0, 0); } if (_recording) { _field.hide(); @@ -5555,11 +5554,9 @@ void HistoryWidget::step_record(float64 ms, bool timer) { float64 dt = ms / st::btnSend.duration; if (dt >= 1 || !_send.isHidden() || isBotStart() || isBlocked()) { _a_record.stop(); - a_recordOver.finish(); a_recordDown.finish(); a_recordCancel.finish(); } else { - a_recordOver.update(dt, anim::linear); a_recordDown.update(dt, anim::linear); a_recordCancel.update(dt, anim::linear); } @@ -5674,20 +5671,15 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); bool inReplyEdit = QRect(st::historyReplySkip, _field.y() - st::sendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId()); bool inPinnedMsg = QRect(0, 0, width(), st::historyReplyHeight).contains(pos) && _pinnedBar; - bool startAnim = false; if (inRecord != _inRecord) { _inRecord = inRecord; - a_recordOver.start(_inRecord ? 1 : 0); - a_recordDown.restart(); - a_recordCancel.restart(); - startAnim = true; + update(_send.geometry()); } if (inField != _inField && _recording) { _inField = inField; - a_recordOver.restart(); a_recordDown.start(_inField ? 1 : 0); a_recordCancel.start(_inField ? st::historyRecordCancel->c : st::historyRecordCancelActive->c); - startAnim = true; + _a_record.start(); } if (inReplyEdit != _inReplyEdit) { _inReplyEdit = inReplyEdit; @@ -5697,7 +5689,6 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { _inPinnedMsg = inPinnedMsg; setCursor(inPinnedMsg ? style::cur_pointer : style::cur_default); } - if (startAnim) _a_record.start(); } void HistoryWidget::leaveToChildEvent(QEvent *e, QWidget *child) { // e -- from enterEvent() of child TWidget @@ -5736,7 +5727,6 @@ void HistoryWidget::stopRecording(bool send) { updateField(); a_recordDown.start(0); - a_recordOver.restart(); a_recordCancel = anim::cvalue(st::historyRecordCancel->c, st::historyRecordCancel->c); _a_record.start(); } @@ -7435,7 +7425,6 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { updateField(); a_recordDown.start(1); - a_recordOver.restart(); _a_record.start(); } else if (_inReplyEdit) { Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId()); @@ -8645,15 +8634,21 @@ void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int } void HistoryWidget::drawRecordButton(Painter &p) { - if (a_recordDown.current() < 1) { - p.setOpacity(st::historyAttachEmoji.opacity * (1 - a_recordOver.current()) + st::historyAttachEmoji.overOpacity * a_recordOver.current()); - st::historyRecordVoice.paint(p, _send.x() + (_send.width() - st::historyRecordVoice.width()) / 2, _send.y() + (_send.height() - st::historyRecordVoice.height()) / 2, width()); + auto down = a_recordDown.current(); + auto fastIcon = [down, this] { + if (down == 1.) { + return &st::historyRecordVoiceActive; + } else if (_inRecord) { + return &st::historyRecordVoiceOver; + } + return &st::historyRecordVoice; + }; + fastIcon()->paintInCenter(p, _send.geometry()); + if (down > 0. && down < 1.) { + p.setOpacity(down); + st::historyRecordVoiceActive.paintInCenter(p, _send.geometry()); + p.setOpacity(1.); } - if (a_recordDown.current() > 0) { - p.setOpacity(a_recordDown.current()); - st::historyRecordVoiceActive.paint(p, _send.x() + (_send.width() - st::historyRecordVoiceActive.width()) / 2, _send.y() + (_send.height() - st::historyRecordVoiceActive.height()) / 2, width()); - } - p.setOpacity(1); } void HistoryWidget::drawRecording(Painter &p) { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 4b642c23e..8b508b9c7 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -1118,7 +1118,6 @@ private: bool _inPinnedMsg = false; anim::ivalue a_recordingLevel = { 0, 0 }; int32 _recordingSamples = 0; - anim::fvalue a_recordOver = { 0, 0 }; anim::fvalue a_recordDown = { 0, 0 }; anim::cvalue a_recordCancel; int32 _recordCancelWidth; diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 392159d3b..e2a43097e 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -145,15 +145,20 @@ introErrorLabel: flatLabel(labelDefFlat) { font: introErrorFont; align: align(center); } -introBackButton: IconButton(defaultIconButton) { +introBackButton: IconButton { width: 40px; height: 40px; icon: icon { + { size(40px, 40px), #f2f2f2 }, + { "title_previous", #adadad, point(12px, 12px) }, + }; + iconOver: icon { { size(40px, 40px), #eeeeee }, { "title_previous", #969696, point(12px, 12px) }, }; + iconPosition: point(0px, 0px); - downIconPosition: point(0px, 0px); + iconPositionDown: point(0px, 0px); } introBackPosition: point(32px, 32px); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 317217ee0..ad4fdd15f 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1391,12 +1391,12 @@ void MainWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { for (int32 i = 0; i < OverviewCount; ++i) { if (mask & (1 << i)) { switch (i) { - case OverviewPhotos: _mediaType->addAction(lang(lng_media_type_photos), this, SLOT(onPhotosSelect()), &st::historyMediaTypePhoto); break; - case OverviewVideos: _mediaType->addAction(lang(lng_media_type_videos), this, SLOT(onVideosSelect()), &st::historyMediaTypeVideo); break; - case OverviewMusicFiles: _mediaType->addAction(lang(lng_media_type_songs), this, SLOT(onSongsSelect()), &st::historyMediaTypeSong); break; - case OverviewFiles: _mediaType->addAction(lang(lng_media_type_files), this, SLOT(onDocumentsSelect()), &st::historyMediaTypeFile); break; - case OverviewVoiceFiles: _mediaType->addAction(lang(lng_media_type_audios), this, SLOT(onAudiosSelect()), &st::historyMediaTypeVoice); break; - case OverviewLinks: _mediaType->addAction(lang(lng_media_type_links), this, SLOT(onLinksSelect()), &st::historyMediaTypeLink); break; + case OverviewPhotos: _mediaType->addAction(lang(lng_media_type_photos), this, SLOT(onPhotosSelect()), &st::historyMediaTypePhoto, &st::historyMediaTypePhotoOver); break; + case OverviewVideos: _mediaType->addAction(lang(lng_media_type_videos), this, SLOT(onVideosSelect()), &st::historyMediaTypeVideo, &st::historyMediaTypeVideoOver); break; + case OverviewMusicFiles: _mediaType->addAction(lang(lng_media_type_songs), this, SLOT(onSongsSelect()), &st::historyMediaTypeSong, &st::historyMediaTypeSongOver); break; + case OverviewFiles: _mediaType->addAction(lang(lng_media_type_files), this, SLOT(onDocumentsSelect()), &st::historyMediaTypeFile, &st::historyMediaTypeFileOver); break; + case OverviewVoiceFiles: _mediaType->addAction(lang(lng_media_type_audios), this, SLOT(onAudiosSelect()), &st::historyMediaTypeVoice, &st::historyMediaTypeVoiceOver); break; + case OverviewLinks: _mediaType->addAction(lang(lng_media_type_links), this, SLOT(onLinksSelect()), &st::historyMediaTypeLink, &st::historyMediaTypeLinkOver); break; } } } diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 621173dd1..fd7d12a42 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -72,16 +72,11 @@ mediaPlayerRepeatButton: IconButton { width: 31px; height: 30px; - opacity: 1.; - overOpacity: 1.; - icon: icon { { "player_repeat", mediaPlayerActiveFg, point(9px, 11px) } }; iconPosition: point(0px, 0px); - downIconPosition: point(0px, 0px); - - duration: 0; + iconPositionDown: point(0px, 0px); } mediaPlayerRepeatDisabledIcon: icon { { "player_repeat", #c8c8c8, point(9px, 11px)} @@ -106,14 +101,9 @@ mediaPlayerVolumeToggle: IconButton { width: 31px; height: 30px; - opacity: 1.; - overOpacity: 1.; - icon: mediaPlayerVolumeIcon0; iconPosition: point(8px, 11px); - downIconPosition: point(8px, 11px); - - duration: 0; + iconPositionDown: point(8px, 11px); } mediaPlayerVolumeMargin: 10px; mediaPlayerVolumeSize: size(27px, 100px); @@ -219,10 +209,10 @@ mediaPlayerPanelPlayback: MediaSlider { width: 3px; activeFg: mediaPlayerActiveFg; inactiveFg: mediaPlayerInactiveFg; - disabledActiveFg: mediaPlayerInactiveFg; - disabledInactiveFg: windowBg; - activeOpacity: 1.; - inactiveOpacity: 1.; + activeFgOver: mediaPlayerActiveFg; + inactiveFgOver: mediaPlayerInactiveFg; + activeFgDisabled: mediaPlayerInactiveFg; + inactiveFgDisabled: windowBg; seekSize: size(9px, 9px); duration: 150; } diff --git a/Telegram/SourceFiles/media/view/media_clip_controller.cpp b/Telegram/SourceFiles/media/view/media_clip_controller.cpp index ae693ac6b..9b48227c0 100644 --- a/Telegram/SourceFiles/media/view/media_clip_controller.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_controller.cpp @@ -118,7 +118,7 @@ void Controller::updatePlayPauseResumeState(const AudioPlaybackState &playbackSt _showPause = showPause; connect(_playPauseResume, SIGNAL(clicked()), this, _showPause ? SIGNAL(pausePressed()) : SIGNAL(playPressed())); - _playPauseResume->setIcon(_showPause ? &st::mediaviewPauseIcon : nullptr); + _playPauseResume->setIcon(_showPause ? &st::mediaviewPauseIcon : nullptr, _showPause ? &st::mediaviewPauseIconOver : nullptr); } } @@ -169,7 +169,7 @@ void Controller::refreshTimeTexts() { } void Controller::setInFullScreen(bool inFullScreen) { - _fullScreenToggle->setIcon(inFullScreen ? &st::mediaviewFullScreenOutIcon : nullptr); + _fullScreenToggle->setIcon(inFullScreen ? &st::mediaviewFullScreenOutIcon : nullptr, inFullScreen ? &st::mediaviewFullScreenOutIconOver : nullptr); disconnect(_fullScreenToggle, SIGNAL(clicked()), this, SIGNAL(toFullScreenPressed())); disconnect(_fullScreenToggle, SIGNAL(clicked()), this, SIGNAL(fromFullScreenPressed())); diff --git a/Telegram/SourceFiles/media/view/media_clip_volume_controller.cpp b/Telegram/SourceFiles/media/view/media_clip_volume_controller.cpp index c1b83746f..9e2fa880e 100644 --- a/Telegram/SourceFiles/media/view/media_clip_volume_controller.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_volume_controller.cpp @@ -46,10 +46,16 @@ void VolumeController::paintEvent(QPaintEvent *e) { int32 right = left + st::mediaviewVolumeIcon.width(); if (mid > left) { - auto over = _a_over.current(getms(), _over ? 1. : 0.); - p.setOpacity(over * st::mediaviewActiveOpacity + (1. - over) * st::mediaviewInactiveOpacity); p.setClipRect(rtlrect(left, top, mid - left, st::mediaviewVolumeIcon.height(), width())); - st::mediaviewVolumeOnIcon.paint(p, QPoint(left, top), width()); + auto over = _a_over.current(getms(), _over ? 1. : 0.); + if (over < 1.) { + st::mediaviewVolumeOnIcon.paint(p, QPoint(left, top), width()); + } + if (over > 0.) { + p.setOpacity(over); + st::mediaviewVolumeOnIconOver.paint(p, QPoint(left, top), width()); + p.setOpacity(1.); + } } if (right > mid) { p.setClipRect(rtlrect(mid, top, right - mid, st::mediaviewVolumeIcon.height(), width())); diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index eba4b7774..23d8280a4 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -22,20 +22,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org using "basic.style"; using "ui/widgets/widgets.style"; -mediaviewActiveOpacity: 1.; -mediaviewInactiveOpacity: 0.78; mediaviewOverDuration: 150; - -mediaviewPlaybackActive: #ffffff; -mediaviewPlaybackInactive: #474747; +mediaviewPlaybackActive: #c7c7c7; +mediaviewPlaybackInactive: #252525; +mediaviewPlaybackActiveOver: #ffffff; +mediaviewPlaybackInactiveOver: #474747; mediaviewPlayback: MediaSlider { width: 3px; activeFg: mediaviewPlaybackActive; inactiveFg: mediaviewPlaybackInactive; - disabledActiveFg: mediaviewPlaybackActive; - disabledInactiveFg: mediaviewPlaybackInactive; - activeOpacity: mediaviewActiveOpacity; - inactiveOpacity: mediaviewInactiveOpacity; + activeFgOver: mediaviewPlaybackActiveOver; + inactiveFgOver: mediaviewPlaybackInactiveOver; + activeFgDisabled: mediaviewPlaybackActive; + inactiveFgDisabled: mediaviewPlaybackInactive; seekSize: size(11px, 11px); duration: mediaviewOverDuration; } @@ -50,32 +49,35 @@ mediaviewPlayButton: IconButton { width: 25px; height: 24px; - opacity: mediaviewInactiveOpacity; - overOpacity: mediaviewActiveOpacity; + icon: icon {{ "media_play", #c7c7c7, point(3px, 0px) }}; + iconOver: icon {{ "media_play", #ffffff, point(3px, 0px) }}; - icon: icon {{ "media_play", #ffffff, point(3px, 0px) }}; iconPosition: point(3px, 1px); - downIconPosition: point(3px, 1px); + iconPositionDown: point(3px, 1px); duration: mediaviewOverDuration; } -mediaviewPauseIcon: icon {{ "media_pause", #ffffff, point(1px, 1px) }}; +mediaviewPauseIcon: icon {{ "media_pause", #c7c7c7, point(1px, 1px) }}; +mediaviewPauseIconOver: icon {{ "media_pause", #ffffff, point(1px, 1px) }}; mediaviewPlayPauseLeft: 17px; mediaviewFullScreenLeft: 17px; mediaviewVolumeLeft: 15px; mediaviewFullScreenButton: IconButton(mediaviewPlayButton) { - icon: icon {{ "media_fullscreen_to", #ffffff, point(0px, 0px) }}; + icon: icon {{ "media_fullscreen_to", #c7c7c7, point(0px, 0px) }}; + iconOver: icon {{ "media_fullscreen_to", #ffffff, point(0px, 0px) }}; iconPosition: point(0px, 1px); - downIconPosition: point(0px, 1px); + iconPositionDown: point(0px, 1px); } -mediaviewFullScreenOutIcon: icon {{ "media_fullscreen_from", #ffffff, point(0px, 0px) }}; +mediaviewFullScreenOutIcon: icon {{ "media_fullscreen_from", #c7c7c7, point(0px, 0px) }}; +mediaviewFullScreenOutIconOver: icon {{ "media_fullscreen_from", #ffffff, point(0px, 0px) }}; mediaviewPlaybackTop: 28px; mediaviewVolumeSize: size(44px, 20px); -mediaviewVolumeIcon: icon {{ "media_volume", mediaviewPlaybackInactive, point(0px, 0px) }}; +mediaviewVolumeIcon: icon {{ "media_volume", mediaviewPlaybackInactiveOver, point(0px, 0px) }}; mediaviewVolumeOnIcon: icon {{ "media_volume", mediaviewPlaybackActive, point(0px, 0px) }}; +mediaviewVolumeOnIconOver: icon {{ "media_volume", mediaviewPlaybackActiveOver, point(0px, 0px) }}; mediaviewVolumeIconTop: 8px; mediaviewControllerRadius: 25px; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 8d3f41886..b56e2f95b 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -79,7 +79,7 @@ settingsEditButton: IconButton(dialogsNewChatButton) { height: 34px; iconPosition: point(3px, 9px); - downIconPosition: point(3px, 10px); + iconPositionDown: point(3px, 10px); } settingsBlocksTop: 7px; diff --git a/Telegram/SourceFiles/settings/settings_widget.cpp b/Telegram/SourceFiles/settings/settings_widget.cpp index 12ce92218..9e5da544a 100644 --- a/Telegram/SourceFiles/settings/settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_widget.cpp @@ -198,7 +198,7 @@ void Widget::showDone() { void Widget::paintEvent(QPaintEvent *e) { Painter p(this); - p.fillRect(e->rect(), st::settingsBg); + p.fillRect(e->rect(), st::boxBg); } void Widget::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 25bf5f31e..7ddc28a65 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -2489,10 +2489,10 @@ void EmojiPanel::paintEvent(QPaintEvent *e) { Painter p(this); if (!_deleteVisible) { - p.fillRect(0, 0, width(), st::emojiPanHeader, st::emojiPanHeaderBg->b); + p.fillRect(0, 0, width(), st::emojiPanHeader, st::emojiPanHeaderBg); } p.setFont(st::emojiPanHeaderFont); - p.setPen(st::emojiPanHeaderColor); + p.setPen(st::emojiPanHeaderFg); p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); } diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 950d70dca..397d149d2 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -105,13 +105,8 @@ emojiCategory: IconButton { width: 42px; height: 46px; - opacity: 1.; - overOpacity: 1.; - iconPosition: point(11px, 12px); - downIconPosition: point(11px, 12px); - - duration: 0; + iconPositionDown: point(11px, 12px); } emojiCategoryRecent: IconButton(emojiCategory) { icon: emojiRecent; } emojiCategoryPeople: IconButton(emojiCategory) { icon: emojiPeople; } @@ -131,10 +126,8 @@ emojiPanHover: #f0f4f7; emojiPanHeader: 42px; emojiPanHeaderFont: semiboldFont; -emojiPanHeaderColor: #999999; emojiPanHeaderLeft: 22px; emojiPanHeaderTop: 12px; -emojiPanHeaderBg: #fffffff2; emojiColorsPadding: 5px; emojiColorsSep: 1px; @@ -162,11 +155,13 @@ stickerPreviewDuration: 150; stickerPreviewBg: #FFFFFFB0; stickerPreviewMin: 0.1; -hashtagClose: IconButton(defaultIconButton) { +hashtagClose: IconButton { width: 30px; height: 30px; icon: simpleCloseIcon; + iconOver: simpleCloseIconOver; + iconPosition: point(10px, 10px); - downIconPosition: point(10px, 11px); + iconPositionDown: point(10px, 11px); } diff --git a/Telegram/SourceFiles/ui/animation.h b/Telegram/SourceFiles/ui/animation.h index 8f2b7f427..52f17e015 100644 --- a/Telegram/SourceFiles/ui/animation.h +++ b/Telegram/SourceFiles/ui/animation.h @@ -219,14 +219,14 @@ namespace anim { using ValueType = QColor; cvalue() = default; - cvalue(const QColor &from) + explicit cvalue(QColor from) : _cur(from) , _from_r(from.redF()) , _from_g(from.greenF()) , _from_b(from.blueF()) , _from_a(from.alphaF()) { } - cvalue(const QColor &from, const QColor &to) + cvalue(QColor from, QColor to) : _cur(from) , _from_r(from.redF()) , _from_g(from.greenF()) @@ -237,7 +237,7 @@ namespace anim { , _delta_b(to.blueF() - from.blueF()) , _delta_a(to.alphaF() - from.alphaF()) { } - void start(const QColor &to) { + void start(QColor to) { _from_r = _cur.redF(); _from_g = _cur.greenF(); _from_b = _cur.blueF(); @@ -265,7 +265,7 @@ namespace anim { result.setAlphaF(_from_a); return result; } - const QColor ¤t() const { + QColor current() const { return _cur; } QColor to() const { diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp index f2801e591..bb1fc740b 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.cpp @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { HistoryDownButton::HistoryDownButton(QWidget *parent) : Button(parent) -, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity) +//, a_arrowOpacity(st::historyAttachEmoji.opacity, st::historyAttachEmoji.opacity) , _a_arrowOver(animation(this, &HistoryDownButton::step_arrowOver)) { setCursor(style::cur_pointer); @@ -53,10 +53,8 @@ HistoryDownButton::HistoryDownButton(QWidget *parent) : Button(parent) void HistoryDownButton::paintEvent(QPaintEvent *e) { Painter p(this); - float64 opacity = 1.; if (_a_show.animating(getms())) { - opacity = _a_show.current(); - p.setOpacity(opacity); + p.setOpacity(_a_show.current()); p.drawPixmap(0, st::historyToDownPaddingTop, _cache); } else if (!_shown) { hide(); @@ -64,10 +62,8 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) { } else { st::historyToDown.paint(p, QPoint(0, st::historyToDownPaddingTop), width()); } - p.setOpacity(opacity * a_arrowOpacity.current()); st::historyToDownArrow.paint(p, QPoint(0, st::historyToDownPaddingTop), width()); if (_unreadCount > 0) { - p.setOpacity(opacity); auto unreadString = QString::number(_unreadCount); if (unreadString.size() > 4) { unreadString = qsl("..") + unreadString.mid(unreadString.size() - 4); @@ -83,7 +79,7 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) { } void HistoryDownButton::onStateChanged(int oldState, ButtonStateChangeSource source) { - a_arrowOpacity.start((_state & (StateOver | StateDown)) ? st::historyAttachEmoji.overOpacity : st::historyAttachEmoji.opacity); + //a_arrowOpacity.start((_state & (StateOver | StateDown)) ? st::historyAttachEmoji.overOpacity : st::historyAttachEmoji.opacity); if (source == ButtonByUser || source == ButtonByPress) { _a_arrowOver.stop(); @@ -151,16 +147,15 @@ void EmojiButton::paintEvent(QPaintEvent *e) { p.fillRect(e->rect(), st::historyComposeAreaBg); - auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); - auto opacity = over * _st.overOpacity + (1. - over) * _st.opacity; - auto loading = a_loading.current(ms, _loading ? 1 : 0); - p.setOpacity(opacity * (1 - loading)); + p.setOpacity(1 - loading); - _st.icon.paint(p, (_state & StateDown) ? _st.downIconPosition : _st.iconPosition, width()); + auto over = (_state & StateOver); + auto icon = &(over ? _st.iconOver : _st.icon); + icon->paint(p, (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition, width()); - p.setOpacity(opacity); - p.setPen(QPen(st::historyEmojiCircleFg, st::historyEmojiCircleLine)); + p.setOpacity(1.); + p.setPen(QPen(over ? st::historyEmojiCircleFgOver : st::historyEmojiCircleFg, st::historyEmojiCircleLine)); p.setBrush(Qt::NoBrush); p.setRenderHint(QPainter::HighQualityAntialiasing); @@ -191,9 +186,7 @@ void EmojiButton::setLoading(bool loading) { void EmojiButton::onStateChanged(int oldState, ButtonStateChangeSource source) { auto over = (_state & StateOver); if (over != (oldState & StateOver)) { - auto from = over ? 0. : 1.; - auto to = over ? 1. : 0.; - _a_over.start([this] { update(); }, from, to, _st.duration); + update(); } } diff --git a/Telegram/SourceFiles/ui/buttons/history_down_button.h b/Telegram/SourceFiles/ui/buttons/history_down_button.h index 03e2d7976..70dc27247 100644 --- a/Telegram/SourceFiles/ui/buttons/history_down_button.h +++ b/Telegram/SourceFiles/ui/buttons/history_down_button.h @@ -75,8 +75,6 @@ protected: private: const style::IconButton &_st; - FloatAnimation _a_over; - bool _loading = false; FloatAnimation a_loading; Animation _a_loading; diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.cpp b/Telegram/SourceFiles/ui/buttons/icon_button.cpp index c0269e260..ee61a36c9 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/icon_button.cpp @@ -29,8 +29,9 @@ IconButton::IconButton(QWidget *parent, const style::IconButton &st) : Button(pa setCursor(style::cur_pointer); } -void IconButton::setIcon(const style::icon *icon) { +void IconButton::setIcon(const style::icon *icon, const style::icon *iconOver) { _iconOverride = icon; + _iconOverrideOver = iconOver; update(); } @@ -38,22 +39,47 @@ void IconButton::paintEvent(QPaintEvent *e) { Painter p(this); auto over = _a_over.current(getms(), (_state & StateOver) ? 1. : 0.); - p.setOpacity(over * _st.overOpacity + (1. - over) * _st.opacity); - - auto icon = (_iconOverride ? _iconOverride : &_st.icon); - auto position = (_state & StateDown) ? _st.downIconPosition : _st.iconPosition; + auto overIcon = [this] { + if (_iconOverrideOver) { + return _iconOverrideOver; + } else if (!_st.iconOver.empty()) { + return &_st.iconOver; + } else if (_iconOverride) { + return _iconOverride; + } + return &_st.icon; + }; + auto justIcon = [this] { + if (_iconOverride) { + return _iconOverride; + } + return &_st.icon; + }; + auto icon = (over == 1.) ? overIcon() : justIcon(); + auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition; if (position.x() < 0) { position.setX((width() - icon->width()) / 2); } icon->paint(p, position, width()); + if (over > 0. && over < 1.) { + auto iconOver = overIcon(); + if (iconOver != icon) { + p.setOpacity(over); + iconOver->paint(p, position, width()); + } + } } void IconButton::onStateChanged(int oldState, ButtonStateChangeSource source) { auto over = (_state & StateOver); if (over != (oldState & StateOver)) { - auto from = over ? 0. : 1.; - auto to = over ? 1. : 0.; - _a_over.start([this] { update(); }, from, to, _st.duration); + if (_st.duration) { + auto from = over ? 0. : 1.; + auto to = over ? 1. : 0.; + _a_over.start([this] { update(); }, from, to, _st.duration); + } else { + update(); + } } } @@ -77,7 +103,7 @@ void MaskButton::paintEvent(QPaintEvent *e) { Painter p(this); auto clip = e->rect(); - auto position = (_state & StateDown) ? _st.downIconPosition : _st.iconPosition; + auto position = (_state & StateDown) ? _st.iconPositionDown : _st.iconPosition; if (position.x() < 0) { position.setX((width() - _st.icon.width()) / 2); } diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.h b/Telegram/SourceFiles/ui/buttons/icon_button.h index ae23fc2e9..a782681e1 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.h +++ b/Telegram/SourceFiles/ui/buttons/icon_button.h @@ -30,7 +30,7 @@ public: IconButton(QWidget *parent, const style::IconButton &st); // Pass nullptr to restore the default icon. - void setIcon(const style::icon *icon); + void setIcon(const style::icon *icon, const style::icon *iconOver = nullptr); protected: void paintEvent(QPaintEvent *e) override; @@ -40,6 +40,7 @@ protected: private: const style::IconButton &_st; const style::icon *_iconOverride = nullptr; + const style::icon *_iconOverrideOver = nullptr; FloatAnimation _a_over; diff --git a/Telegram/SourceFiles/ui/style/style_core.cpp b/Telegram/SourceFiles/ui/style/style_core.cpp index 736c1fe67..75535e917 100644 --- a/Telegram/SourceFiles/ui/style/style_core.cpp +++ b/Telegram/SourceFiles/ui/style/style_core.cpp @@ -75,34 +75,58 @@ void stopManager() { internal::destroyIcons(); } -QImage colorizeImage(const QImage &src, const color &c, const QRect &r) { +QImage colorizeImage(const QImage &src, QColor color, const QRect &r) { t_assert(r.x() >= 0 && src.width() >= r.x() + r.width()); t_assert(r.y() >= 0 && src.height() >= r.y() + r.height()); - int a = c->c.alpha() + 1; - int fg_r = c->c.red() * a, fg_g = c->c.green() * a, fg_b = c->c.blue() * a, fg_a = 255 * a; + auto initialAlpha = color.alpha() + 1; + auto red = color.red() * initialAlpha; + auto green = color.green() * initialAlpha; + auto blue = color.blue() * initialAlpha; + auto alpha = 255 * initialAlpha; + auto alpha_red = static_cast(alpha) | (static_cast(red) << 32); + auto green_blue = static_cast(green) | (static_cast(blue) << 32); - QImage result(r.width(), r.height(), QImage::Format_ARGB32_Premultiplied); - auto bits = result.bits(); - auto maskbits = src.constBits(); - int bpp = result.depth(), maskbpp = src.depth(); - int bpl = result.bytesPerLine(), maskbpl = src.bytesPerLine(); - for (int x = 0, xoffset = r.x(); x < r.width(); ++x) { - for (int y = 0, yoffset = r.y(); y < r.height(); ++y) { - int s = y * bpl + ((x * bpp) >> 3); - int o = maskbits[(y + yoffset) * maskbpl + (((x + xoffset) * maskbpp) >> 3)] + 1; - bits[s + 0] = (fg_b * o) >> 16; - bits[s + 1] = (fg_g * o) >> 16; - bits[s + 2] = (fg_r * o) >> 16; - bits[s + 3] = (fg_a * o) >> 16; + auto result = QImage(r.width(), r.height(), QImage::Format_ARGB32_Premultiplied); + auto resultBytesPerPixel = (src.depth() >> 3); + auto resultIntsPerPixel = 1; + auto resultIntsPerLine = (result.bytesPerLine() >> 2); + auto resultIntsAdded = resultIntsPerLine - r.width() * resultIntsPerPixel; + auto resultInts = reinterpret_cast(result.bits()); + t_assert(resultIntsAdded >= 0); + t_assert(result.depth() == ((resultIntsPerPixel * sizeof(uint32)) << 3)); + t_assert(result.bytesPerLine() == (resultIntsPerLine << 2)); + + auto maskBytesPerPixel = (src.depth() >> 3); + auto maskBytesPerLine = src.bytesPerLine(); + auto maskBytesAdded = maskBytesPerLine - r.width() * maskBytesPerPixel; + auto maskBytes = src.constBits() + r.y() * maskBytesPerLine + r.x() * maskBytesPerPixel; + t_assert(maskBytesAdded >= 0); + t_assert(src.depth() == (maskBytesPerPixel << 3)); + for (int y = 0; y != r.height(); ++y) { + for (int x = 0; x != r.width(); ++x) { + auto maskOpacity = static_cast(*maskBytes) + 1; + auto alpha_red_masked = (alpha_red * maskOpacity) >> 16; + auto green_blue_masked = (green_blue * maskOpacity) >> 16; + auto alpha = static_cast(alpha_red_masked & 0xFF); + auto red = static_cast((alpha_red_masked >> 32) & 0xFF); + auto green = static_cast(green_blue_masked & 0xFF); + auto blue = static_cast((green_blue_masked >> 32) & 0xFF); + *resultInts = blue | (green << 8) | (red << 16) | (alpha << 24); + maskBytes += maskBytesPerPixel; + resultInts += resultIntsPerPixel; } + maskBytes += maskBytesAdded; + resultInts += resultIntsAdded; } - return result; + + result.setDevicePixelRatio(src.devicePixelRatio()); + return std_::move(result); } namespace internal { -QImage createCircleMask(int size, const QColor &bg, const QColor &fg) { +QImage createCircleMask(int size, QColor bg, QColor fg) { int realSize = size * cIntRetinaFactor(); #ifndef OS_MAC_OLD auto result = QImage(realSize, realSize, QImage::Format::Format_Grayscale8); diff --git a/Telegram/SourceFiles/ui/style/style_core.h b/Telegram/SourceFiles/ui/style/style_core.h index cf96db24b..5cda06071 100644 --- a/Telegram/SourceFiles/ui/style/style_core.h +++ b/Telegram/SourceFiles/ui/style/style_core.h @@ -63,11 +63,15 @@ bool setPaletteColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a); void startManager(); void stopManager(); -QImage colorizeImage(const QImage &src, const color &c, const QRect &r); +QImage colorizeImage(const QImage &src, QColor c, const QRect &r); + +inline QImage colorizeImage(const QImage &src, const color &c, const QRect &r) { + return colorizeImage(src, c->c, r); +} namespace internal { -QImage createCircleMask(int size, const QColor &bg, const QColor &fg); +QImage createCircleMask(int size, QColor bg, QColor fg); } // namespace internal diff --git a/Telegram/SourceFiles/ui/style/style_core_color.cpp b/Telegram/SourceFiles/ui/style/style_core_color.cpp index ef57a8de5..c70d5e29a 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_color.cpp @@ -44,7 +44,7 @@ void destroyColors() { Color::Color(const Color &c) : ptr(c.owner ? new ColorData(*c.ptr) : c.ptr), owner(c.owner) { } -Color::Color(const QColor &c) : owner(false) { +Color::Color(QColor c) : owner(false) { init(c.red(), c.green(), c.blue(), c.alpha()); } @@ -81,7 +81,7 @@ Color::~Color() { ColorData::ColorData(uchar r, uchar g, uchar b, uchar a) : c(int(r), int(g), int(b), int(a)), p(c), b(c) { } -void ColorData::set(const QColor &color) { +void ColorData::set(QColor color) { c = color; p = QPen(color); b = QBrush(color); diff --git a/Telegram/SourceFiles/ui/style/style_core_color.h b/Telegram/SourceFiles/ui/style/style_core_color.h index 1b4c1b190..175805255 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.h +++ b/Telegram/SourceFiles/ui/style/style_core_color.h @@ -31,12 +31,12 @@ public: Color(Qt::Initialization = Qt::Uninitialized) { } Color(const Color &c); - explicit Color(const QColor &c); + explicit Color(QColor c); Color(uchar r, uchar g, uchar b, uchar a = 255); Color &operator=(const Color &c); ~Color(); - void set(const QColor &newv); + void set(QColor newv); void set(uchar r, uchar g, uchar b, uchar a = 255); operator const QBrush &() const; @@ -79,7 +79,7 @@ public: private: ColorData(uchar r, uchar g, uchar b, uchar a); - void set(const QColor &c); + void set(QColor c); friend class Color; @@ -102,12 +102,26 @@ inline Color::operator const QPen &() const { } // namespace internal +inline QColor interpolate(QColor a, QColor b, float64 opacity_b) { + auto bOpacity = static_cast(opacity_b * 255), aOpacity = (255 - bOpacity); + return { + (a.red() * aOpacity + b.red() * bOpacity + 1) >> 8, + (a.green() * aOpacity + b.green() * bOpacity + 1) >> 8, + (a.blue() * aOpacity + b.blue() * bOpacity + 1) >> 8, + (a.alpha() * aOpacity + b.alpha() * bOpacity + 1) >> 8 + }; +} + +inline QColor interpolate(const style::internal::Color &a, QColor b, float64 opacity_b) { + return interpolate(a->c, b, opacity_b); +} + +inline QColor interpolate(QColor a, const style::internal::Color &b, float64 opacity_b) { + return interpolate(a, b->c, opacity_b); +} + inline QColor interpolate(const style::internal::Color &a, const style::internal::Color &b, float64 opacity_b) { - QColor result; - result.setRedF((a->c.redF() * (1. - opacity_b)) + (b->c.redF() * opacity_b)); - result.setGreenF((a->c.greenF() * (1. - opacity_b)) + (b->c.greenF() * opacity_b)); - result.setBlueF((a->c.blueF() * (1. - opacity_b)) + (b->c.blueF() * opacity_b)); - return result; + return interpolate(a->c, b->c, opacity_b); } } // namespace style diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.cpp b/Telegram/SourceFiles/ui/style/style_core_icon.cpp index 2df173ae9..0fe70595d 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_icon.cpp @@ -25,11 +25,13 @@ namespace style { namespace internal { namespace { -uint32 colorKey(const QColor &c) { +uint32 colorKey(QColor c) { return (((((uint32(c.red()) << 8) | uint32(c.green())) << 8) | uint32(c.blue())) << 8) | uint32(c.alpha()); } +using IconMasks = QMap; using IconPixmaps = QMap, QPixmap>; +NeverFreedPointer iconMasks; NeverFreedPointer iconPixmaps; inline int pxAdjust(int value, int scale) { @@ -39,8 +41,9 @@ inline int pxAdjust(int value, int scale) { return qFloor((value * scale / 4.) + 0.1); } -QPixmap createIconPixmap(const IconMask *mask, const Color &color) { +QImage createIconMask(const IconMask *mask) { auto maskImage = QImage::fromData(mask->data(), mask->size(), "PNG"); + maskImage.setDevicePixelRatio(cRetinaFactor()); t_assert(!maskImage.isNull()); // images are layouted like this: @@ -64,9 +67,11 @@ QPixmap createIconPixmap(const IconMask *mask, const Color &color) { } } } - auto finalImage = colorizeImage(maskImage, color, r); - finalImage.setDevicePixelRatio(cRetinaFactor()); - return App::pixmapFromImageInPlace(std_::move(finalImage)); + return maskImage.copy(r); +} + +QImage createIconImage(const QImage &mask, QColor color) { + return colorizeImage(mask, color, QRect(0, 0, mask.width(), mask.height())); } } // namespace @@ -119,6 +124,29 @@ void MonoIcon::fill(QPainter &p, const QRect &rect) const { } } +void MonoIcon::paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOverride) const { + int w = width(), h = height(); + QPoint fullOffset = pos + offset(); + int partPosX = rtl() ? (outerw - fullOffset.x() - w) : fullOffset.x(); + int partPosY = fullOffset.y(); + + ensureLoaded(); + if (_pixmap.isNull()) { + p.fillRect(partPosX, partPosY, w, h, colorOverride); + } else { + p.drawImage(partPosX, partPosY, createIconImage(_maskImage, colorOverride)); + } +} + +void MonoIcon::fill(QPainter &p, const QRect &rect, QColor colorOverride) const { + ensureLoaded(); + if (_pixmap.isNull()) { + p.fillRect(rect, colorOverride); + } else { + p.drawImage(rect, createIconImage(_maskImage, colorOverride), QRect(0, 0, _pixmap.width(), _pixmap.height())); + } +} + void MonoIcon::ensureLoaded() const { if (_size.isValid()) { return; @@ -155,34 +183,24 @@ void MonoIcon::ensureLoaded() const { t_assert(!"Bad data in generated icon!"); } } else { - if (_owningPixmap) { - _pixmap = createIconPixmap(_mask, _color); - } else { - iconPixmaps.createIfNull(); - auto key = qMakePair(_mask, colorKey(_color->c)); - auto i = iconPixmaps->constFind(key); - if (i == iconPixmaps->cend()) { - i = iconPixmaps->insert(key, createIconPixmap(_mask, _color)); - } - _pixmap = i.value(); + iconMasks.createIfNull(); + auto i = iconMasks->constFind(_mask); + if (i == iconMasks->cend()) { + i = iconMasks->insert(_mask, createIconMask(_mask)); } + _maskImage = i.value(); + + iconPixmaps.createIfNull(); + auto key = qMakePair(_mask, colorKey(_color->c)); + auto j = iconPixmaps->constFind(key); + if (j == iconPixmaps->cend()) { + j = iconPixmaps->insert(key, App::pixmapFromImageInPlace(createIconImage(_maskImage, _color->c))); + } + _pixmap = j.value(); _size = _pixmap.size() / cIntRetinaFactor(); } } -MonoIcon::MonoIcon(const IconMask *mask, const Color &color, QPoint offset, OwningPixmapTag) -: _mask(mask) -, _color(color) -, _offset(offset) -, _owningPixmap(true) { -} - -void Icon::paint(QPainter &p, const QPoint &pos, int outerw) const { - for_const (auto &part, _parts) { - part.paint(p, pos, outerw); - } -} - void Icon::fill(QPainter &p, const QRect &rect) const { if (_parts.isEmpty()) return; @@ -194,6 +212,17 @@ void Icon::fill(QPainter &p, const QRect &rect) const { } } +void Icon::fill(QPainter &p, const QRect &rect, QColor colorOverride) const { + if (_parts.isEmpty()) return; + + auto partSize = _parts.at(0).size(); + for_const (auto &part, _parts) { + t_assert(part.offset() == QPoint(0, 0)); + t_assert(part.size() == partSize); + part.fill(p, rect, colorOverride); + } +} + int Icon::width() const { if (_width < 0) { _width = 0; @@ -216,6 +245,7 @@ int Icon::height() const { void destroyIcons() { iconPixmaps.clear(); + iconMasks.clear(); } } // namespace internal diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.h b/Telegram/SourceFiles/ui/style/style_core_icon.h index c4e6b73a7..5fada3cd5 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.h +++ b/Telegram/SourceFiles/ui/style/style_core_icon.h @@ -55,45 +55,31 @@ public: QSize size() const; QPoint offset() const; + void paint(QPainter &p, const QPoint &pos, int outerw) const; void fill(QPainter &p, const QRect &rect) const; - - MonoIcon clone(const Color &color) const { - return MonoIcon(_mask, color ? color : _color, _offset, OwningPixmapTag()); - } + void paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOverride) const; + void fill(QPainter &p, const QRect &rect, QColor colorOverride) const; ~MonoIcon() { } private: - struct OwningPixmapTag { - }; - MonoIcon(const IconMask *mask, const Color &color, QPoint offset, OwningPixmapTag); void ensureLoaded() const; const IconMask *_mask = nullptr; Color _color; QPoint _offset = { 0, 0 }; + mutable QImage _maskImage; mutable QPixmap _pixmap; // for pixmaps mutable QSize _size; // for rects - bool _owningPixmap = false; }; class Icon { - struct ColoredCopy; - public: Icon(Qt::Initialization) { } - Icon(const ColoredCopy &makeCopy) { - _parts.reserve(makeCopy.copyFrom._parts.size()); - auto colorIt = makeCopy.colors.cbegin(), colorsEnd = makeCopy.colors.cend(); - for_const (auto &part, makeCopy.copyFrom._parts) { - auto newPart = part.clone((colorIt == colorsEnd) ? Color(Qt::Uninitialized) : *(colorIt++)); - _parts.push_back(newPart); - } - } template Icon(const MonoIcons&... icons) { @@ -101,14 +87,15 @@ public: addIcons(icons...); } - std_::unique_ptr clone(const QVector &colors) { - return std_::make_unique(ColoredCopy { *this, colors }); - } - bool empty() const { return _parts.empty(); } - void paint(QPainter &p, const QPoint &pos, int outerw) const; + + void paint(QPainter &p, const QPoint &pos, int outerw) const { + for_const (auto &part, _parts) { + part.paint(p, pos, outerw); + } + } void paint(QPainter &p, int x, int y, int outerw) const { paint(p, QPoint(x, y), outerw); } @@ -116,6 +103,20 @@ public: paint(p, outer.x() + (outer.width() - width()) / 2, outer.y() + (outer.height() - height()) / 2, outer.x() * 2 + outer.width()); } void fill(QPainter &p, const QRect &rect) const; + + void paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOverride) const { + for_const (auto &part, _parts) { + part.paint(p, pos, outerw, colorOverride); + } + } + void paint(QPainter &p, int x, int y, int outerw, QColor colorOverride) const { + paint(p, QPoint(x, y), outerw, colorOverride); + } + void paintInCenter(QPainter &p, const QRect &outer, QColor colorOverride) const { + paint(p, outer.x() + (outer.width() - width()) / 2, outer.y() + (outer.height() - height()) / 2, outer.x() * 2 + outer.width(), colorOverride); + } + void fill(QPainter &p, const QRect &rect, QColor colorOverride) const; + int width() const; int height() const; QSize size() const { @@ -123,11 +124,6 @@ public: } private: - struct ColoredCopy { - const Icon ©From; - const QVector &colors; - }; - template void addIcons() { } diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp index 77ecdbf0a..a13a453d2 100644 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp @@ -64,8 +64,8 @@ void DropdownMenu::init() { hide(); } -QAction *DropdownMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) { - return _menu->addAction(text, receiver, member, icon); +QAction *DropdownMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) { + return _menu->addAction(text, receiver, member, icon, iconOver); } QAction *DropdownMenu::addSeparator() { diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h index e88c33e59..3d0092c14 100644 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h @@ -30,7 +30,7 @@ class DropdownMenu : public InnerDropdown { public: DropdownMenu(QWidget *parent, const style::DropdownMenu &st = st::defaultDropdownMenu); - QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr); + QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); QAction *addSeparator(); void clearActions(); diff --git a/Telegram/SourceFiles/ui/widgets/media_slider.cpp b/Telegram/SourceFiles/ui/widgets/media_slider.cpp index 43d302eac..743390f3c 100644 --- a/Telegram/SourceFiles/ui/widgets/media_slider.cpp +++ b/Telegram/SourceFiles/ui/widgets/media_slider.cpp @@ -43,10 +43,10 @@ void MediaSlider::paintEvent(QPaintEvent *e) { Painter p(this); p.setPen(Qt::NoPen); p.setRenderHint(QPainter::HighQualityAntialiasing); + p.setOpacity(fadeOpacity()); auto horizontal = isHorizontal(); auto ms = getms(); - auto masterOpacity = fadeOpacity(); auto radius = _st.width / 2; auto disabled = isDisabled(); auto over = getCurrentOverFactor(ms); @@ -62,16 +62,21 @@ void MediaSlider::paintEvent(QPaintEvent *e) { auto length = _alwaysDisplayMarker ? (horizontal ? width() : height()) : markerLength; auto mid = qRound(from + value * length); auto end = from + length; - auto &activeFg = disabled ? _st.disabledActiveFg : _st.activeFg; - auto &inactiveFg = disabled ? _st.disabledInactiveFg : _st.inactiveFg; + auto activeFg = disabled ? &_st.activeFgDisabled : (over == 1. ? &_st.activeFgOver : (over == 0. ? &_st.activeFg : nullptr)); + auto inactiveFg = disabled ? &_st.inactiveFgDisabled : (over == 1. ? &_st.inactiveFgOver : (over == 0. ? &_st.inactiveFg : nullptr)); + auto activeFgOver = activeFg ? QColor() : style::interpolate(_st.activeFg, _st.activeFgOver, over); + auto inactiveFgOver = inactiveFg ? QColor() : style::interpolate(_st.inactiveFg, _st.inactiveFgOver, over); if (mid > from) { auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid); auto fromRect = horizontal ? QRect(from, (height() - _st.width) / 2, mid + radius - from, _st.width) : QRect((width() - _st.width) / 2, from, _st.width, mid + radius - from); p.setClipRect(fromClipRect); - p.setOpacity(masterOpacity * (over * _st.activeOpacity + (1. - over) * _st.inactiveOpacity)); - p.setBrush(horizontal ? activeFg : inactiveFg); + if (auto brush = (horizontal ? activeFg : inactiveFg)) { + p.setBrush(*brush); + } else { + p.setBrush(horizontal ? activeFgOver : inactiveFgOver); + } p.drawRoundedRect(fromRect, radius, radius); } if (end > mid) { @@ -80,8 +85,11 @@ void MediaSlider::paintEvent(QPaintEvent *e) { ? QRect(mid - radius, (height() - _st.width) / 2, end - (mid - radius), _st.width) : QRect((width() - _st.width) / 2, mid - radius, _st.width, end - (mid - radius)); p.setClipRect(endClipRect); - p.setOpacity(masterOpacity); - p.setBrush(horizontal ? inactiveFg : activeFg); + if (auto brush = (horizontal ? inactiveFg : activeFg)) { + p.setBrush(*brush); + } else { + p.setBrush(horizontal ? inactiveFgOver : activeFgOver); + } p.drawRoundedRect(endRect, radius, radius); } auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over); @@ -94,8 +102,11 @@ void MediaSlider::paintEvent(QPaintEvent *e) { auto remove = static_cast(((1. - markerSizeRatio) * size) / 2.); if (remove * 2 < size) { p.setClipRect(rect()); - p.setOpacity(masterOpacity * _st.activeOpacity); - p.setBrush(activeFg); + if (activeFg) { + p.setBrush(*activeFg); + } else { + p.setBrush(activeFgOver); + } p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove))); } } diff --git a/Telegram/SourceFiles/ui/widgets/menu.cpp b/Telegram/SourceFiles/ui/widgets/menu.cpp index 46b374dd1..baa2c2da1 100644 --- a/Telegram/SourceFiles/ui/widgets/menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/menu.cpp @@ -49,18 +49,19 @@ void Menu::init() { setAttribute(Qt::WA_OpaquePaintEvent); } -QAction *Menu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) { +QAction *Menu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) { auto action = new QAction(text, this); connect(action, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection); - return addAction(action, icon); + return addAction(action, icon, iconOver); } -QAction *Menu::addAction(QAction *action, const style::icon *icon) { +QAction *Menu::addAction(QAction *action, const style::icon *icon, const style::icon *iconOver) { connect(action, SIGNAL(changed()), this, SLOT(actionChanged())); _actions.push_back(action); ActionData data; data.icon = icon; + data.iconOver = iconOver ? iconOver : icon; data.hasSubmenu = (action->menu() != nullptr); _actionsData.push_back(data); @@ -167,10 +168,8 @@ void Menu::paintEvent(QPaintEvent *e) { } else { auto enabled = action->isEnabled(), selected = (i == _selected && enabled); p.fillRect(0, 0, width(), actionHeight, selected ? _st.itemBgOver : _st.itemBg); - if (data.icon) { - p.setOpacity(selected ? _st.itemIconOverOpacity : _st.itemIconOpacity); - data.icon->paint(p, _st.itemIconPosition, width()); - p.setOpacity(1.); + if (auto icon = (selected ? data.iconOver : data.icon)) { + icon->paint(p, _st.itemIconPosition, width()); } p.setPen(selected ? _st.itemFgOver : (enabled ? _st.itemFg : _st.itemFgDisabled)); p.drawTextLeft(_st.itemPadding.left(), _st.itemPadding.top(), width(), data.text); diff --git a/Telegram/SourceFiles/ui/widgets/menu.h b/Telegram/SourceFiles/ui/widgets/menu.h index 64ecf8b11..51e706001 100644 --- a/Telegram/SourceFiles/ui/widgets/menu.h +++ b/Telegram/SourceFiles/ui/widgets/menu.h @@ -31,7 +31,7 @@ public: Menu(QWidget *parent, const style::Menu &st = st::defaultMenu); Menu(QWidget *parent, QMenu *menu, const style::Menu &st = st::defaultMenu); - QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr); + QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); QAction *addSeparator(); void clearActions(); @@ -92,7 +92,7 @@ private: // Returns the new width. int processAction(QAction *action, int index, int width); - QAction *addAction(QAction *a, const style::icon *icon = nullptr); + QAction *addAction(QAction *a, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); void setSelected(int selected); void clearMouseSelection(); @@ -115,6 +115,7 @@ private: QString text; QString shortcut; const style::icon *icon = nullptr; + const style::icon *iconOver = nullptr; }; using ActionsData = QList; diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index c11e81138..03e267598 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -80,8 +80,8 @@ void PopupMenu::handleMenuResize() { _inner = QRect(_padding.left(), _padding.top(), width() - _padding.left() - _padding.right(), height() - _padding.top() - _padding.bottom()); } -QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon) { - return _menu->addAction(text, receiver, member, icon); +QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) { + return _menu->addAction(text, receiver, member, icon, iconOver); } QAction *PopupMenu::addSeparator() { diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.h b/Telegram/SourceFiles/ui/widgets/popup_menu.h index 7e9047465..9699cd6dc 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.h +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.h @@ -28,7 +28,7 @@ public: PopupMenu(const style::PopupMenu &st = st::defaultPopupMenu); PopupMenu(QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu); - QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr); + QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); QAction *addSeparator(); void clearActions(); diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 4765bab69..b144ffa49 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -31,12 +31,11 @@ IconButton { width: pixels; height: pixels; - opacity: double; - overOpacity: double; - icon: icon; + iconOver: icon; + iconPosition: point; - downIconPosition: point; + iconPositionDown: point; duration: int; } @@ -52,7 +51,7 @@ MaskButton { iconBgOver: color; iconPosition: point; - downIconPosition: point; + iconPositionDown: point; duration: int; } @@ -61,10 +60,10 @@ MediaSlider { width: pixels; activeFg: color; inactiveFg: color; - disabledActiveFg: color; - disabledInactiveFg: color; - activeOpacity: double; - inactiveOpacity: double; + activeFgOver: color; + inactiveFgOver: color; + activeFgDisabled: color; + inactiveFgDisabled: color; seekSize: size; duration: int; } @@ -137,8 +136,6 @@ Menu { itemFgShortcutDisabled: color; itemPadding: margins; itemIconPosition: point; - itemIconOpacity: double; - itemIconOverOpacity: double; itemFont: font; separatorPadding: margins; @@ -184,15 +181,9 @@ defaultLabelSimple: LabelSimple { textFg: windowTextFg; } -defaultIconButton: IconButton { - opacity: 0.78; - overOpacity: 1.; - duration: 150; -} - defaultMaskButton: MaskButton { iconPosition: point(-1px, -1px); - downIconPosition: point(-1px, -1px); + iconPositionDown: point(-1px, -1px); duration: 150; } @@ -223,8 +214,6 @@ defaultMenu: Menu { itemFgShortcutOver: #7c99b2; itemFgShortcutDisabled: #cccccc; itemIconPosition: point(0px, 0px); - itemIconOpacity: 1.; - itemIconOverOpacity: 1.; itemPadding: margins(17px, 8px, 17px, 7px); itemFont: normalFont; diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index 1c610778b..cd4c1711d 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class PeerAvatarButton; class RoundButton; -class IconButton; +class MaskButton; } // namespace Ui namespace Window { @@ -87,7 +87,7 @@ private: ChildWidget _info; ChildWidget _mediaType; - ChildWidget _search; + ChildWidget _search; ChildWidget _membersShowArea = { nullptr }; }; diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 90ca2ce53..bf3b19430 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -36,13 +36,15 @@ notifyPhotoSize: 62px; notifyMacPhotoSize: 64px; notifyPhotoPos: point(9px, 9px); notifyClosePos: point(1px, 2px); -notifyClose: IconButton(defaultIconButton) { +notifyClose: IconButton { width: 30px; height: 30px; icon: simpleCloseIcon; + iconOver: simpleCloseIconOver; + iconPosition: point(10px, 10px); - downIconPosition: point(10px, 11px); + iconPositionDown: point(10px, 11px); } notifyItemTop: 12px; notifyTextLeft: 12px; @@ -68,11 +70,11 @@ notifyReplyArea: InputArea(defaultInputArea) { borderActive: 0px; borderError: 0px; } -notifySendReply: IconButton(defaultIconButton) { +notifySendReply: IconButton { width: 36px; height: 36px; icon: icon {{ "notification_send", windowActiveFill, point(3px, 9px) }}; iconPosition: point(0px, 0px); - downIconPosition: point(0px, 1px); + iconPositionDown: point(0px, 1px); } From af9edc17d2ec685257309d07439fbb1f249dd126 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 1 Nov 2016 23:59:51 +0300 Subject: [PATCH 009/100] Applying color theme without restarting the application. --- Telegram/Resources/basic.style | 1 - Telegram/Resources/colors.palette | 6 + Telegram/Resources/sample.tdesktop-theme | 4 + Telegram/SourceFiles/app.cpp | 39 +----- Telegram/SourceFiles/app.h | 1 - .../SourceFiles/codegen/style/generator.cpp | 113 ++++++++++++----- Telegram/SourceFiles/facades.cpp | 4 - Telegram/SourceFiles/facades.h | 2 - Telegram/SourceFiles/localstorage.cpp | 33 ++--- Telegram/SourceFiles/mainwidget.cpp | 1 - Telegram/SourceFiles/overviewwidget.cpp | 9 +- Telegram/SourceFiles/overviewwidget.h | 2 - .../settings/settings_background_widget.cpp | 2 +- .../SourceFiles/ui/style/style_core_color.cpp | 36 +++--- .../SourceFiles/ui/style/style_core_color.h | 25 ++-- .../SourceFiles/ui/style/style_core_icon.cpp | 59 ++++++--- .../SourceFiles/ui/style/style_core_icon.h | 115 ++++++++++++++---- 17 files changed, 275 insertions(+), 177 deletions(-) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 8c925db16..4da47a1fb 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -277,7 +277,6 @@ boxScroll: flatScroll(solidScroll) { boxScrollSkip: 6px; boxScrollShadowBg: #00000012; -titleBg: #6389a8; titleHeight: 39px; titleFont: font(17px); titlePos: point(44px, 29px); diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index cd38d6629..ddc62dd09 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -29,6 +29,8 @@ windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over windowActiveTextFg: #1485c2; // online blue: fallback for active color windowShadowFg: #000000; // black: fallback for shadow color +titleBg: #6389a8; + imageBg: #000000; imageBgTransparent: #ffffff; @@ -124,6 +126,10 @@ historyCaptionOutFg: historyTextOutFg; historyFileNameInFg: historyTextInFg; historyFileNameOutFg: historyTextOutFg; +historySystemBg: #89a0b47f; +historySystemBgSelected: #bbc8d4a2; +historySystemFg: #ffffff; + // mediaview mediaviewFileBg: windowBg; mediaviewFileNameFg: windowTextFg; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index b38bf9404..bd97b03b8 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -29,6 +29,7 @@ windowOverBg: #edf2f5; windowSubTextFgOver: #7c99b2; windowActiveTextFg: #1485c2; windowShadowFg: #000000; +titleBg: #6389a8; imageBg: #000000; imageBgTransparent: #ffffff; trayCounterBg: #f23c34; @@ -97,6 +98,9 @@ historyCaptionInFg: historyTextInFg; historyCaptionOutFg: historyTextOutFg; historyFileNameInFg: historyTextInFg; historyFileNameOutFg: historyTextOutFg; +historySystemBg: #89a0b47f; +historySystemBgSelected: #bbc8d4a2; +historySystemFg: #ffffff; mediaviewFileBg: windowBg; mediaviewFileNameFg: windowTextFg; mediaviewFileSizeFg: windowSubTextFg; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index fb54feaa0..b4e26531a 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2774,37 +2774,6 @@ namespace { roundRect(p, x, y, w, h, bg, i.value(), 0); } - void initBackground(int32 id, const QImage &p, bool nowrite) { - if (Local::readBackground()) return; - - QImage img(p); - bool remove = (id == Window::Theme::kThemeBackground); - if (p.isNull()) { - if (id == Window::Theme::kDefaultBackground) { - img.load(qsl(":/gui/art/bg.jpg")); - } else { - img.load(qsl(":/gui/art/bg_old.png")); - if (cRetina()) { - img = img.scaledToWidth(img.width() * 2, Qt::SmoothTransformation); - } else if (cScale() != dbisOne) { - img = img.scaledToWidth(convertScale(img.width()), Qt::SmoothTransformation); - } - id = Window::Theme::kOldBackground; - } - remove = true; - } - if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_ARGB32_Premultiplied && img.format() != QImage::Format_RGB32) { - img = img.convertToFormat(QImage::Format_RGB32); - } - img.setDevicePixelRatio(cRetinaFactor()); - - if (!nowrite) { - Local::writeBackground(id, remove ? QImage() : img); - } - - initColorsFromBackground(img); - } - void initColorsFromBackground(const QImage &img) { uint64 components[3] = { 0 }, componentsScroll[3] = { 0 }; auto w = img.width(); @@ -2889,10 +2858,10 @@ namespace { prepareCorners(StickerSelectedCorners, st::dateRadius, _msgServiceSelectBg); uchar rScroll = uchar(componentsScroll[0]), gScroll = uchar(componentsScroll[1]), bScroll = uchar(componentsScroll[2]); - _historyScrollBarColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.barColor->c.alphaF() * 0xFF)); - _historyScrollBgColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.bgColor->c.alphaF() * 0xFF)); - _historyScrollBarOverColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.barOverColor->c.alphaF() * 0xFF)); - _historyScrollBgOverColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.bgOverColor->c.alphaF() * 0xFF)); + _historyScrollBarColor = style::color(rScroll, gScroll, bScroll, st::historyScroll.barColor->c.alpha()); + _historyScrollBgColor = style::color(rScroll, gScroll, bScroll, st::historyScroll.bgColor->c.alpha()); + _historyScrollBarOverColor = style::color(rScroll, gScroll, bScroll, st::historyScroll.barOverColor->c.alpha()); + _historyScrollBgOverColor = style::color(rScroll, gScroll, bScroll, st::historyScroll.bgOverColor->c.alpha()); if (App::main()) { App::main()->updateScrollColors(); diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index e7b9fd858..4dceffcc3 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -294,7 +294,6 @@ namespace App { } void initColorsFromBackground(const QImage &image); - void initBackground(int32 id, const QImage &p = QImage(), bool nowrite = false); const style::color &msgServiceBg(); const style::color &msgServiceSelectBg(); diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index a475920de..9a199fd02 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -435,6 +435,9 @@ bool Generator::writePaletteDefinition() { header_->stream() << "\ class palette {\n\ public:\n\ + palette() = default;\n\ + palette(const palette &other) = delete;\n\ +\n\ QByteArray save() const;\n\ bool load(const QByteArray &cache);\n\ bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\ @@ -451,29 +454,90 @@ public:\n\ return false; } - auto type = typeToString(variable.value.type()); auto index = (indexInPalette++); - header_->stream() << "\tinline const " << type << " &" << name << "() const { return _colors[" << index << "]; };\n"; + header_->stream() << "\tinline const color &" << name << "() const { return _colors[" << index << "]; };\n"; return true; })) return false; auto count = indexInPalette; - auto type = typeToString({ structure::TypeTag::Color }); header_->stream() << "\ +\n\ + palette &operator=(const palette &other) {\n\ + auto wasReady = _ready;\n\ + for (int i = 0; i != " << count << "; ++i) {\n\ + if (other._status[i] == Status::Loaded) {\n\ + if (_status[i] == Status::Initial) {\n\ + new (data(i)) internal::ColorData(*other.data(i));\n\ + } else {\n\ + *data(i) = *other.data(i);\n\ + }\n\ + } else if (_status[i] != Status::Initial) {\n\ + data(i)->~ColorData();\n\ + _status[i] = Status::Initial;\n\ + _ready = false;\n\ + }\n\ + }\n\ + if (wasReady && !_ready) {\n\ + finalize();\n\ + }\n\ + return *this;\n\ + }\n\ \n\ static int32 Checksum();\n\ +\n\ + ~palette() {\n\ + for (int i = 0; i != " << count << "; ++i) {\n\ + if (_status[i] != Status::Initial) {\n\ + data(i)->~ColorData();\n\ + }\n\ + }\n\ + }\n\ \n\ private:\n\ struct TempColorData { uchar r, g, b, a; };\n\ - void compute(int index, int fallbackIndex, TempColorData data);\n\ + void compute(int index, int fallbackIndex, TempColorData value) {\n\ + if (_status[index] == Status::Initial) {\n\ + if (fallbackIndex >= 0 && _status[fallbackIndex] != Status::Initial) {\n\ + _status[index] = Status::Loaded;\n\ + new (data(index)) internal::ColorData(*data(fallbackIndex));\n\ + } else {\n\ + _status[index] = Status::Created;\n\ + new (data(index)) internal::ColorData(value.r, value.g, value.b, value.a);\n\ + }\n\ + }\n\ + }\n\ +\n\ + internal::ColorData *data(int index) {\n\ + return reinterpret_cast(_data) + index;\n\ + }\n\ +\n\ + const internal::ColorData *data(int index) const {\n\ + return reinterpret_cast(_data) + index;\n\ + }\n\ +\n\ + void setData(int index, const internal::ColorData &value) {\n\ + if (_status[index] == Status::Initial) {\n\ + new (data(index)) internal::ColorData(value);\n\ + } else {\n\ + *data(index) = value;\n\ + }\n\ + _status[index] = Status::Loaded;\n\ + }\n\ \n\ enum class Status {\n\ Initial,\n\ + Created,\n\ Loaded,\n\ - Fallback,\n\ };\n\ \n\ - " << type << " _colors[" << count << "] = { Qt::Uninitialized };\n\ + alignas(alignof(internal::ColorData)) char _data[sizeof(internal::ColorData) * " << count << "];\n\ +\n\ + color _colors[" << count << "] = {\n"; + for (int i = 0; i != count; ++i) { + header_->stream() << "\t\tdata(" << i << "),\n"; + } + header_->stream() << "\ + };\n\ Status _status[" << count << "] = { Status::Initial };\n\ bool _ready = false;\n\ \n\ @@ -485,9 +549,9 @@ QByteArray save();\n\ bool load(const QByteArray &cache);\n\ bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\ bool setColor(QLatin1String name, QLatin1String from);\n\ +void apply(const palette &other);\n\ \n\ -} // namespace main_palette\n\ -\n"; +} // namespace main_palette\n"; header_->newline(); @@ -648,17 +712,6 @@ void palette::finalize() {\n\ \n\ int32 palette::Checksum() {\n\ return " << checksum << ";\n\ -}\n\ -\n\ -void palette::compute(int index, int fallbackIndex, TempColorData data) {\n\ - if (_status[index] == Status::Initial) {\n\ - if (fallbackIndex >= 0 && _status[fallbackIndex] != Status::Initial) {\n\ - _status[index] = Status::Fallback;\n\ - _colors[index] = _colors[fallbackIndex];\n\ - } else {\n\ - _colors[index] = { data.r, data.g, data.b, data.a };\n\ - }\n\ - }\n\ }\n"; source_->newline().pushNamespace().newline(); @@ -731,10 +784,10 @@ QByteArray palette::save() const {\n\ \n\ auto result = QByteArray(" << (count * 4) << ", Qt::Uninitialized);\n\ for (auto i = 0, index = 0; i != " << count << "; ++i) {\n\ - result[index++] = static_cast(_colors[i]->c.red());\n\ - result[index++] = static_cast(_colors[i]->c.green());\n\ - result[index++] = static_cast(_colors[i]->c.blue());\n\ - result[index++] = static_cast(_colors[i]->c.alpha());\n\ + result[index++] = static_cast(data(i)->c.red());\n\ + result[index++] = static_cast(data(i)->c.green());\n\ + result[index++] = static_cast(data(i)->c.blue());\n\ + result[index++] = static_cast(data(i)->c.alpha());\n\ }\n\ return result;\n\ }\n\ @@ -744,8 +797,7 @@ bool palette::load(const QByteArray &cache) {\n\ \n\ auto p = reinterpret_cast(cache.constData());\n\ for (auto i = 0; i != " << count << "; ++i) {\n\ - _colors[i] = { p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3] };\n\ - _status[i] = Status::Loaded;\n\ + setData(i, { p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3] });\n\ }\n\ return true;\n\ }\n\ @@ -753,8 +805,7 @@ bool palette::load(const QByteArray &cache) {\n\ bool palette::setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\ auto index = getPaletteIndex(name);\n\ if (index >= 0) {\n\ - _colors[index] = { r, g, b, a };\n\ - _status[index] = Status::Loaded;\n\ + setData(index, { r, g, b, a });\n\ return true;\n\ }\n\ return false;\n\ @@ -764,8 +815,7 @@ bool palette::setColor(QLatin1String name, QLatin1String from) {\n\ auto nameIndex = getPaletteIndex(name);\n\ auto fromIndex = getPaletteIndex(from);\n\ if (nameIndex >= 0 && fromIndex >= 0 && _status[fromIndex] == Status::Loaded) {\n\ - _colors[nameIndex] = _colors[fromIndex];\n\ - _status[nameIndex] = Status::Loaded;\n\ + setData(nameIndex, *data(fromIndex));\n\ return true;\n\ }\n\ return false;\n\ @@ -789,6 +839,11 @@ bool setColor(QLatin1String name, QLatin1String from) {\n\ return _palette.setColor(name, from);\n\ }\n\ \n\ +void apply(const palette &other) {\n\ + _palette = other;\n\ + style::internal::resetIcons();\n\ +}\n\ +\n\ } // namespace main_palette\n\ \n"; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 17493cb6f..8457a0775 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -667,8 +667,6 @@ struct Data { base::Observable ItemRemoved; - bool ApplyingTheme = false; - }; } // namespace internal @@ -784,6 +782,4 @@ DefineRefVar(Global, base::Observable, LocalPasscodeChanged); DefineRefVar(Global, base::Observable, ItemRemoved); -DefineVar(Global, bool, ApplyingTheme); - } // namespace Global diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index d049c6a9f..dc3eaed73 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -356,8 +356,6 @@ DeclareRefVar(base::Observable, LocalPasscodeChanged); DeclareRefVar(base::Observable, ItemRemoved); -DeclareVar(bool, ApplyingTheme); - } // namespace Global namespace Adaptive { diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 690c4ca6e..5e17abe53 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -598,7 +598,7 @@ FileKey _backgroundKey = 0; bool _backgroundWasRead = false; bool _backgroundCanWrite = true; -FileKey _themeKey = 0, _applyingThemeKey = 0; +FileKey _themeKey = 0; bool _readingUserSettings = false; FileKey _userSettingsKey = 0; @@ -1066,12 +1066,11 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) { } break; case dbiTheme: { - quint64 themeKey = 0, applyingThemeKey = 0; - stream >> themeKey >> applyingThemeKey; + quint64 themeKey = 0; + stream >> themeKey; if (!_checkStreamStatus(stream)) return false; _themeKey = themeKey; - _applyingThemeKey = applyingThemeKey; } break; case dbiTryIPv6: { @@ -2232,7 +2231,7 @@ void writeSettings() { auto &proxy = Global::ConnectionProxy(); size += Serialize::stringSize(proxy.host) + sizeof(qint32) + Serialize::stringSize(proxy.user) + Serialize::stringSize(proxy.password); } - if (_themeKey || _applyingThemeKey) { + if (_themeKey) { size += sizeof(quint32) + 2 * sizeof(quint64); } size += sizeof(quint32) + sizeof(qint32) * 7; @@ -2264,8 +2263,8 @@ void writeSettings() { data.stream << proxy.host << qint32(proxy.port) << proxy.user << proxy.password; } data.stream << quint32(dbiTryIPv6) << qint32(Global::TryIPv6()); - if (_themeKey || _applyingThemeKey) { - data.stream << quint32(dbiTheme) << quint64(_themeKey) << quint64(_applyingThemeKey); + if (_themeKey) { + data.stream << quint32(dbiTheme) << quint64(_themeKey); } TWindowPos pos(cWindowPos()); @@ -3639,7 +3638,7 @@ void writeBackground(int32 id, const QImage &img) { } bool readBackground() { - if (_backgroundWasRead || Global::ApplyingTheme()) { + if (_backgroundWasRead) { return false; } _backgroundWasRead = true; @@ -3729,9 +3728,8 @@ bool readThemeUsingKey(FileKey key) { } void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache) { - auto key = _applyingThemeKey ? _applyingThemeKey : _themeKey; - if (!key) { - key = _themeKey = genKey(); + if (!_themeKey) { + _themeKey = genKey(); writeSettings(); } @@ -3744,21 +3742,12 @@ void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const data.stream << pathRelative << pathAbsolute; data.stream << cache.paletteChecksum << cache.contentChecksum << cache.colors << cache.background << backgroundTiled; - FileWriteDescriptor file(key, FileOption::Safe); + FileWriteDescriptor file(_themeKey, FileOption::Safe); file.writeEncrypted(data, _settingsKey); } void readTheme() { - if (_applyingThemeKey) { - if (readThemeUsingKey(_applyingThemeKey)) { - Global::SetApplyingTheme(true); - } else { - clearKey(_applyingThemeKey); - _applyingThemeKey = 0; - writeSettings(); - readTheme(); - } - } else if (_themeKey && !readThemeUsingKey(_themeKey)) { + if (_themeKey && !readThemeUsingKey(_themeKey)) { clearKey(_themeKey); _themeKey = 0; writeSettings(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index ad4fdd15f..b2a776fd1 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1905,7 +1905,6 @@ void MainWidget::backgroundParams(const QRect &forRect, QRect &to, QRect &from) void MainWidget::updateScrollColors() { _history->updateScrollColors(); - if (_overview) _overview->updateScrollColors(); } void MainWidget::setChatBackground(const App::WallPaper &wp) { diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 05c3498f6..063357479 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1874,7 +1874,7 @@ OverviewInner::~OverviewInner() { } OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewType type) : TWidget(parent) -, _scroll(this, st::historyScroll, false) +, _scroll(this, st::setScroll, false) , _inner(this, &_scroll, peer, type) , _a_show(animation(this, &OverviewWidget::step_show)) , _topShadow(this, st::shadowColor) { @@ -1883,8 +1883,6 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp _scroll.move(0, 0); _inner.move(0, 0); - updateScrollColors(); - _scroll.show(); connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); @@ -2195,11 +2193,6 @@ void OverviewWidget::fillSelectedItems(SelectedItemSet &sel, bool forDelete) { _inner.fillSelectedItems(sel, forDelete); } -void OverviewWidget::updateScrollColors() { - if (!App::historyScrollBarColor()) return; - _scroll.updateColors(App::historyScrollBarColor(), App::historyScrollBgColor(), App::historyScrollBarOverColor(), App::historyScrollBgOverColor()); -} - void OverviewWidget::updateAfterDrag() { _inner.dragActionUpdate(QCursor::pos()); } diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 32a7c7e47..9b81e2ee7 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -315,8 +315,6 @@ public: void fillSelectedItems(SelectedItemSet &sel, bool forDelete); - void updateScrollColors(); - void updateAfterDrag(); void grabStart() override { diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index 83a68e2c5..f9f5008c9 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -239,7 +239,7 @@ void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &upd if (!theme.cached.background.isEmpty()) { Local::writeBackground(Window::Theme::kThemeBackground, QImage()); } - App::restart(); + style::main_palette::apply(theme.palette); } return; } diff --git a/Telegram/SourceFiles/ui/style/style_core_color.cpp b/Telegram/SourceFiles/ui/style/style_core_color.cpp index c70d5e29a..aa8b92a0d 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_color.cpp @@ -41,30 +41,25 @@ void destroyColors() { colorsMap.clear(); } -Color::Color(const Color &c) : ptr(c.owner ? new ColorData(*c.ptr) : c.ptr), owner(c.owner) { +Color::Color(const Color &c) : ptr(c.ptr) { } -Color::Color(QColor c) : owner(false) { - init(c.red(), c.green(), c.blue(), c.alpha()); +Color::Color(ColorData *data) : ptr(data) { } -Color::Color(uchar r, uchar g, uchar b, uchar a) : owner(false) { +Color::Color(uchar r, uchar g, uchar b, uchar a) { init(r, g, b, a); } -Color &Color::operator=(const Color &c) { - if (this != &c) { - if (owner) { - delete ptr; - } - ptr = c.owner ? new ColorData(*c.ptr) : c.ptr; - owner = c.owner; - } - return *this; +void Color::set(uchar r, uchar g, uchar b, uchar a) const { + ptr->set(r, g, b, a); } void Color::init(uchar r, uchar g, uchar b, uchar a) { - uint32 key = colorKey(r, g, b, a); + if (ptr) { + return set(r, g, b, a); + } + auto key = colorKey(r, g, b, a); auto i = colorsMap.constFind(key); if (i == colorsMap.cend()) { i = colorsMap.insert(key, new ColorData(r, g, b, a)); @@ -72,19 +67,16 @@ void Color::init(uchar r, uchar g, uchar b, uchar a) { ptr = i.value(); } -Color::~Color() { - if (owner) { - delete ptr; - } +ColorData::ColorData() : p(Qt::NoPen), b(Qt::NoBrush) { } ColorData::ColorData(uchar r, uchar g, uchar b, uchar a) : c(int(r), int(g), int(b), int(a)), p(c), b(c) { } -void ColorData::set(QColor color) { - c = color; - p = QPen(color); - b = QBrush(color); +void ColorData::set(uchar r, uchar g, uchar b, uchar a) { + this->c = QColor(r, g, b, a); + this->p = QPen(c); + this->b = QBrush(c); } } // namespace internal diff --git a/Telegram/SourceFiles/ui/style/style_core_color.h b/Telegram/SourceFiles/ui/style/style_core_color.h index 175805255..a52f62652 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.h +++ b/Telegram/SourceFiles/ui/style/style_core_color.h @@ -21,6 +21,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once namespace style { + +class palette; + namespace internal { void destroyColors(); @@ -31,13 +34,9 @@ public: Color(Qt::Initialization = Qt::Uninitialized) { } Color(const Color &c); - explicit Color(QColor c); - Color(uchar r, uchar g, uchar b, uchar a = 255); - Color &operator=(const Color &c); - ~Color(); + Color(uchar r, uchar g, uchar b, uchar a); - void set(QColor newv); - void set(uchar r, uchar g, uchar b, uchar a = 255); + void set(uchar r, uchar g, uchar b, uchar a) const; operator const QBrush &() const; operator const QPen &() const; @@ -54,16 +53,12 @@ public: } private: - ColorData *ptr = nullptr; - bool owner = false; - + Color(ColorData *data); void init(uchar r, uchar g, uchar b, uchar a); - friend void startManager(); + ColorData *ptr = nullptr; - Color(ColorData *p) : ptr(p) { - } - friend class ColorData; + friend class style::palette; }; @@ -78,10 +73,12 @@ public: } private: + ColorData(); ColorData(uchar r, uchar g, uchar b, uchar a); - void set(QColor c); + void set(uchar r, uchar g, uchar b, uchar a); friend class Color; + friend class style::palette; }; diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.cpp b/Telegram/SourceFiles/ui/style/style_core_icon.cpp index 0fe70595d..21759927b 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_icon.cpp @@ -31,8 +31,10 @@ uint32 colorKey(QColor c) { using IconMasks = QMap; using IconPixmaps = QMap, QPixmap>; +using IconDatas = OrderedSet; NeverFreedPointer iconMasks; NeverFreedPointer iconPixmaps; +NeverFreedPointer iconData; inline int pxAdjust(int value, int scale) { if (value < 0) { @@ -82,6 +84,11 @@ MonoIcon::MonoIcon(const IconMask *mask, const Color &color, QPoint offset) , _offset(offset) { } +void MonoIcon::reset() const { + _pixmap = QPixmap(); + _size = QSize(); +} + int MonoIcon::width() const { ensureLoaded(); return _size.width(); @@ -151,8 +158,13 @@ void MonoIcon::ensureLoaded() const { if (_size.isValid()) { return; } - const uchar *data = _mask->data(); - int size = _mask->size(); + if (!_maskImage.isNull()) { + createCachedPixmap(); + return; + } + + auto data = _mask->data(); + auto size = _mask->size(); auto generateTag = qstr("GENERATE:"); if (size > generateTag.size() && !memcmp(data, generateTag.data(), generateTag.size())) { @@ -190,18 +202,27 @@ void MonoIcon::ensureLoaded() const { } _maskImage = i.value(); - iconPixmaps.createIfNull(); - auto key = qMakePair(_mask, colorKey(_color->c)); - auto j = iconPixmaps->constFind(key); - if (j == iconPixmaps->cend()) { - j = iconPixmaps->insert(key, App::pixmapFromImageInPlace(createIconImage(_maskImage, _color->c))); - } - _pixmap = j.value(); - _size = _pixmap.size() / cIntRetinaFactor(); + createCachedPixmap(); } } -void Icon::fill(QPainter &p, const QRect &rect) const { +void MonoIcon::createCachedPixmap() const { + iconPixmaps.createIfNull(); + auto key = qMakePair(_mask, colorKey(_color->c)); + auto j = iconPixmaps->constFind(key); + if (j == iconPixmaps->cend()) { + j = iconPixmaps->insert(key, App::pixmapFromImageInPlace(createIconImage(_maskImage, _color->c))); + } + _pixmap = j.value(); + _size = _pixmap.size() / cIntRetinaFactor(); +} + +void IconData::created() { + iconData.createIfNull(); + iconData->insert(this); +} + +void IconData::fill(QPainter &p, const QRect &rect) const { if (_parts.isEmpty()) return; auto partSize = _parts.at(0).size(); @@ -212,7 +233,7 @@ void Icon::fill(QPainter &p, const QRect &rect) const { } } -void Icon::fill(QPainter &p, const QRect &rect, QColor colorOverride) const { +void IconData::fill(QPainter &p, const QRect &rect, QColor colorOverride) const { if (_parts.isEmpty()) return; auto partSize = _parts.at(0).size(); @@ -223,7 +244,7 @@ void Icon::fill(QPainter &p, const QRect &rect, QColor colorOverride) const { } } -int Icon::width() const { +int IconData::width() const { if (_width < 0) { _width = 0; for_const (auto &part, _parts) { @@ -233,7 +254,7 @@ int Icon::width() const { return _width; } -int Icon::height() const { +int IconData::height() const { if (_height < 0) { _height = 0; for_const (auto &part, _parts) { @@ -243,7 +264,17 @@ int Icon::height() const { return _height; } +void resetIcons() { + iconPixmaps.clear(); + if (iconData) { + for (auto data : *iconData) { + data->reset(); + } + } +} + void destroyIcons() { + iconData.clear(); iconPixmaps.clear(); iconMasks.clear(); } diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.h b/Telegram/SourceFiles/ui/style/style_core_icon.h index 5fada3cd5..0877f2ba0 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.h +++ b/Telegram/SourceFiles/ui/style/style_core_icon.h @@ -50,6 +50,7 @@ public: MonoIcon() = default; MonoIcon(const IconMask *mask, const Color &color, QPoint offset); + void reset() const; int width() const; int height() const; QSize size() const; @@ -66,6 +67,7 @@ public: private: void ensureLoaded() const; + void createCachedPixmap() const; const IconMask *_mask = nullptr; Color _color; @@ -76,17 +78,20 @@ private: }; -class Icon { +class IconData { public: - Icon(Qt::Initialization) { - } - - template - Icon(const MonoIcons&... icons) { + template + IconData(const MonoIcons &...icons) { + created(); _parts.reserve(sizeof...(MonoIcons)); addIcons(icons...); } + void reset() { + for_const (auto &part, _parts) { + part.reset(); + } + } bool empty() const { return _parts.empty(); } @@ -96,12 +101,6 @@ public: part.paint(p, pos, outerw); } } - void paint(QPainter &p, int x, int y, int outerw) const { - paint(p, QPoint(x, y), outerw); - } - void paintInCenter(QPainter &p, const QRect &outer) const { - paint(p, outer.x() + (outer.width() - width()) / 2, outer.y() + (outer.height() - height()) / 2, outer.x() * 2 + outer.width()); - } void fill(QPainter &p, const QRect &rect) const; void paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOverride) const { @@ -109,24 +108,18 @@ public: part.paint(p, pos, outerw, colorOverride); } } - void paint(QPainter &p, int x, int y, int outerw, QColor colorOverride) const { - paint(p, QPoint(x, y), outerw, colorOverride); - } - void paintInCenter(QPainter &p, const QRect &outer, QColor colorOverride) const { - paint(p, outer.x() + (outer.width() - width()) / 2, outer.y() + (outer.height() - height()) / 2, outer.x() * 2 + outer.width(), colorOverride); - } void fill(QPainter &p, const QRect &rect, QColor colorOverride) const; int width() const; int height() const; - QSize size() const { - return QSize(width(), height()); - } private: + void created(); + template void addIcons() { } + template void addIcons(const MonoIcon &icon, const MonoIcons&... icons) { _parts.push_back(icon); @@ -139,6 +132,86 @@ private: }; +class Icon { +public: + Icon(Qt::Initialization) { + } + + template + Icon(const MonoIcons&... icons) : _data(new IconData(icons...)), _owner(true) { + } + Icon(const Icon &other) : _data(other._data) { + } + Icon(Icon &&other) : _data(base::take(other._data)), _owner(base::take(_owner)) { + } + Icon &operator=(const Icon &other) { + t_assert(!_owner); + _data = other._data; + _owner = false; + return *this; + } + Icon &operator=(Icon &&other) { + t_assert(!_owner); + _data = base::take(other._data); + _owner = base::take(other._owner); + return *this; + } + + bool empty() const { + return _data->empty(); + } + + void paint(QPainter &p, const QPoint &pos, int outerw) const { + return _data->paint(p, pos, outerw); + } + void paint(QPainter &p, int x, int y, int outerw) const { + return _data->paint(p, QPoint(x, y), outerw); + } + void paintInCenter(QPainter &p, const QRect &outer) const { + return _data->paint(p, QPoint(outer.x() + (outer.width() - width()) / 2, outer.y() + (outer.height() - height()) / 2), outer.x() * 2 + outer.width()); + } + void fill(QPainter &p, const QRect &rect) const { + return _data->fill(p, rect); + } + + void paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOverride) const { + return _data->paint(p, pos, outerw, colorOverride); + } + void paint(QPainter &p, int x, int y, int outerw, QColor colorOverride) const { + return _data->paint(p, QPoint(x, y), outerw, colorOverride); + } + void paintInCenter(QPainter &p, const QRect &outer, QColor colorOverride) const { + return _data->paint(p, QPoint(outer.x() + (outer.width() - width()) / 2, outer.y() + (outer.height() - height()) / 2), outer.x() * 2 + outer.width(), colorOverride); + } + void fill(QPainter &p, const QRect &rect, QColor colorOverride) const { + return _data->fill(p, rect, colorOverride); + } + + int width() const { + return _data->width(); + } + int height() const { + return _data->height(); + } + QSize size() const { + return QSize(width(), height()); + } + + ~Icon() { + if (auto data = base::take(_data)) { + if (_owner) { + delete data; + } + } + } + +private: + IconData *_data = nullptr; + bool _owner = false; + +}; + +void resetIcons(); void destroyIcons(); } // namespace internal From 5d10c02b5b62491a152c6358bb90f73bc71f3d09 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 2 Nov 2016 17:44:33 +0300 Subject: [PATCH 010/100] Applying color themes with confirmation / reverting (15 seconds). --- Telegram/Resources/langs/lang.strings | 4 + Telegram/SourceFiles/boxes/boxes.style | 6 + .../SourceFiles/codegen/style/generator.cpp | 6 +- Telegram/SourceFiles/localstorage.cpp | 2 +- Telegram/SourceFiles/mainwindow.cpp | 27 +++- Telegram/SourceFiles/mainwindow.h | 10 ++ .../settings/settings_background_widget.cpp | 13 +- Telegram/SourceFiles/structs.cpp | 27 +++- Telegram/SourceFiles/ui/style/style_core.cpp | 61 ++++---- Telegram/SourceFiles/ui/style/style_core.h | 15 +- .../SourceFiles/ui/style/style_core_color.h | 10 +- .../SourceFiles/ui/style/style_core_icon.cpp | 18 ++- .../SourceFiles/ui/style/style_core_icon.h | 3 +- Telegram/SourceFiles/window/window_theme.cpp | 140 ++++++++++++++++- Telegram/SourceFiles/window/window_theme.h | 27 +++- .../window/window_theme_warning.cpp | 148 ++++++++++++++++++ .../SourceFiles/window/window_theme_warning.h | 71 +++++++++ Telegram/gyp/Telegram.gyp | 2 + 18 files changed, 517 insertions(+), 73 deletions(-) create mode 100644 Telegram/SourceFiles/window/window_theme_warning.cpp create mode 100644 Telegram/SourceFiles/window/window_theme_warning.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1a168fd14..48c1fb41f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -292,6 +292,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_settings_adaptive_wide" = "Adaptive layout for wide screens"; "lng_backgrounds_header" = "Choose your new chat background"; +"lng_theme_sure_keep" = "Keep this color theme?"; +"lng_theme_reverting" = "Reverting to previous color theme in {count:_not_used_|# second|# seconds}."; +"lng_theme_keep_changes" = "Keep changes"; +"lng_theme_revert" = "Revert"; "lng_download_path_dont_ask" = "Don't ask download path for each file"; "lng_download_path_label" = "Download path:"; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 8c7be1bc3..5c6f5e84a 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -283,3 +283,9 @@ newGroupDescription: InputArea(defaultInputArea) { newGroupPublicLinkPadding: margins(0px, 20px, 0px, 5px); newGroupLinkFadeDuration: 5000; + +themeWarningWidth: boxWideWidth; +themeWarningHeight: 150px; +themeWarningShadow: boxShadow; +themeWarningShadowShift: boxShadowShift; +themeWarningTextTop: 60px; diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 9a199fd02..b7f85dbb2 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -828,7 +828,11 @@ QByteArray save() {\n\ }\n\ \n\ bool load(const QByteArray &cache) {\n\ - return _palette.load(cache);\n\ + if (_palette.load(cache)) {\n\ + style::internal::resetIcons();\n\ + return true;\n\ + }\n\ + return false;\n\ }\n\ \n\ bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\ diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 5e17abe53..a4d188441 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -1626,7 +1626,7 @@ void _writeUserSettings() { EncryptedDescriptor data(size); data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter); - data.stream << quint32(dbiTileBackground) << qint32(Window::Theme::Background()->tile() ? 1 : 0); + data.stream << quint32(dbiTileBackground) << qint32(Window::Theme::Background()->tileForSave() ? 1 : 0); data.stream << quint32(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0); data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock()); data.stream << quint32(dbiReplaceEmojis) << qint32(cReplaceEmojis() ? 1 : 0); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 758afa19d..063248c52 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -43,8 +43,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "apiwrap.h" #include "settings/settings_widget.h" -#include "window/notifications_manager.h" #include "platform/platform_notifications_manager.h" +#include "window/notifications_manager.h" +#include "window/window_theme.h" +#include "window/window_theme_warning.h" ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : QWidget(parent) , _shadow(st::boxShadow) @@ -125,6 +127,9 @@ MainWindow::MainWindow() { connect(&_autoLockTimer, SIGNAL(timeout()), this, SLOT(checkAutoLock())); subscribe(Global::RefSelfChanged(), [this]() { updateGlobalMenu(); }); + subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &data) { + themeUpdated(data); + }); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_OpaquePaintEvent); @@ -594,6 +599,20 @@ void MainWindow::hideConnecting() { } } +void MainWindow::themeUpdated(const Window::Theme::BackgroundUpdate &data) { + using Type = Window::Theme::BackgroundUpdate::Type; + if (data.type == Type::TestingTheme) { + if (!_testingThemeWarning) { + _testingThemeWarning.create(this); + _testingThemeWarning->setGeometry(rect()); + _testingThemeWarning->setHiddenCallback([this] { _testingThemeWarning.destroyDelayed(); }); + } + _testingThemeWarning->showAnimated(); + } else if (data.type == Type::RevertingTheme || data.type == Type::ApplyingTheme) { + _testingThemeWarning->hideAnimated(); + } +} + bool MainWindow::doWeReadServerHistory() const { return isActive(false) && main && !Ui::isLayerShown() && main->doWeReadServerHistory(); } @@ -645,7 +664,9 @@ bool MainWindow::contentOverlapped(const QRect &globalRect) { } void MainWindow::setInnerFocus() { - if (layerBg && layerBg->canSetFocus()) { + if (_testingThemeWarning) { + _testingThemeWarning->setFocus(); + } else if (layerBg && layerBg->canSetFocus()) { layerBg->setInnerFocus(); } else if (_passcode) { _passcode->setInnerFocus(); @@ -951,6 +972,7 @@ void MainWindow::fixOrder() { if (layerBg) layerBg->raise(); if (_mediaPreview) _mediaPreview->raise(); if (_connecting) _connecting->raise(); + if (_testingThemeWarning) _testingThemeWarning->raise(); } void MainWindow::showFromTray(QSystemTrayIcon::ActivationReason reason) { @@ -1045,6 +1067,7 @@ void MainWindow::updateControlsGeometry() { if (layerBg) layerBg->resize(width(), height()); if (_mediaPreview) _mediaPreview->setGeometry(0, title->height(), width(), height() - title->height()); if (_connecting) _connecting->setGeometry(0, height() - _connecting->height(), _connecting->width(), _connecting->height()); + if (_testingThemeWarning) _testingThemeWarning->setGeometry(rect()); } MainWindow::TempDirState MainWindow::tempDirState() { diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index aa87b5a08..a51423e7f 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -42,6 +42,13 @@ namespace Settings { class Widget; } // namespace Settings +namespace Window { +namespace Theme { +struct BackgroundUpdate; +class WarningWidget; +} // namespace Theme +} // namespace Window + class ConnectingWidget : public QWidget { Q_OBJECT @@ -228,6 +235,8 @@ private: void showConnecting(const QString &text, const QString &reconnect = QString()); void hideConnecting(); + void themeUpdated(const Window::Theme::BackgroundUpdate &data); + void updateControlsGeometry(); QPixmap grabInner(); @@ -253,6 +262,7 @@ private: bool _isActive = false; ChildWidget _connecting = { nullptr }; + ChildWidget _testingThemeWarning = { nullptr }; Local::ClearManager *_clearManager = nullptr; diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index f9f5008c9..e40b51c17 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -229,18 +229,7 @@ void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &upd auto filePath = update.filePaths.front(); if (filePath.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)) { - QByteArray themeContent; - Window::Theme::Instance theme; - if (Window::Theme::LoadFromFile(filePath, &theme, &themeContent)) { - Local::writeTheme(QDir().relativeFilePath(filePath), QFileInfo(filePath).absoluteFilePath(), themeContent, theme.cached); - if (Window::Theme::Background()->tile() != theme.cached.tiled) { - Window::Theme::Background()->setTile(theme.cached.tiled); - } - if (!theme.cached.background.isEmpty()) { - Local::writeBackground(Window::Theme::kThemeBackground, QImage()); - } - style::main_palette::apply(theme.palette); - } + Window::Theme::Apply(filePath); return; } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index caa1f75b5..b57ae68ed 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -35,6 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "history/history_media_types.h" #include "styles/style_history.h" +#include "window/window_theme.h" namespace { @@ -54,7 +55,7 @@ struct ColorReferenceWrap { }; ImagePtr generateUserpicImage(const style::icon &icon) { - auto data = QImage(icon.width() * cIntRetinaFactor(), icon.height() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + auto data = QImage(icon.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); data.setDevicePixelRatio(cRetinaFactor()); { Painter p(&data); @@ -984,6 +985,15 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, bool playVideo = data->isVideo() && audioPlayer(); bool playAnimation = data->isAnimation(); auto &location = data->location(true); + if (auto applyTheme = data->name.endsWith(qstr(".tdesktop-theme"))) { + if (!location.isEmpty() && location.accessEnable()) { + if (Window::Theme::Apply(location.name())) { + location.accessDisable(); + return; + } + location.accessDisable(); + } + } if (!location.isEmpty() || (!data->data().isEmpty() && (playVoice || playMusic || playVideo || playAnimation))) { if (playVoice) { AudioMsgId playing; @@ -1277,13 +1287,22 @@ void DocumentData::automaticLoadSettingsChanged() { void DocumentData::performActionOnLoad() { if (_actionOnLoad == ActionOnLoadNone) return; - const FileLocation &loc(location(true)); - QString already = loc.name(); - HistoryItem *item = _actionOnLoadMsgId.msg ? App::histItemById(_actionOnLoadMsgId) : nullptr; + auto loc = location(true); + auto already = loc.name(); + auto item = _actionOnLoadMsgId.msg ? App::histItemById(_actionOnLoadMsgId) : nullptr; bool showImage = !isVideo() && (size < MediaViewImageSizeLimit); bool playVoice = voice() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); bool playMusic = song() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); bool playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item && item->getMedia(); + if (auto applyTheme = name.endsWith(qstr(".tdesktop-theme"))) { + if (!loc.isEmpty() && loc.accessEnable()) { + if (Window::Theme::Apply(loc.name())) { + loc.accessDisable(); + return; + } + loc.accessDisable(); + } + } if (playVoice) { if (loaded()) { AudioMsgId playing; diff --git a/Telegram/SourceFiles/ui/style/style_core.cpp b/Telegram/SourceFiles/ui/style/style_core.cpp index 75535e917..d43e1ff0c 100644 --- a/Telegram/SourceFiles/ui/style/style_core.cpp +++ b/Telegram/SourceFiles/ui/style/style_core.cpp @@ -75,43 +75,49 @@ void stopManager() { internal::destroyIcons(); } -QImage colorizeImage(const QImage &src, QColor color, const QRect &r) { - t_assert(r.x() >= 0 && src.width() >= r.x() + r.width()); - t_assert(r.y() >= 0 && src.height() >= r.y() + r.height()); +void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect, QPoint dstPoint) { + if (srcRect.isNull()) { + srcRect = src.rect(); + } else { + t_assert(src.rect().contains(srcRect)); + } + auto width = srcRect.width(); + auto height = srcRect.height(); + t_assert(outResult && outResult->rect().contains(QRect(dstPoint, srcRect.size()))); - auto initialAlpha = color.alpha() + 1; - auto red = color.red() * initialAlpha; - auto green = color.green() * initialAlpha; - auto blue = color.blue() * initialAlpha; - auto alpha = 255 * initialAlpha; - auto alpha_red = static_cast(alpha) | (static_cast(red) << 32); - auto green_blue = static_cast(green) | (static_cast(blue) << 32); + auto initialAlpha = c.alpha() + 1; + auto red = (c.red() * initialAlpha) >> 8; + auto green = (c.green() * initialAlpha) >> 8; + auto blue = (c.blue() * initialAlpha) >> 8; + auto alpha = (255 * initialAlpha) >> 8; + auto pattern = static_cast(alpha) + | (static_cast(red) << 16) + | (static_cast(green) << 32) + | (static_cast(blue) << 48); - auto result = QImage(r.width(), r.height(), QImage::Format_ARGB32_Premultiplied); auto resultBytesPerPixel = (src.depth() >> 3); auto resultIntsPerPixel = 1; - auto resultIntsPerLine = (result.bytesPerLine() >> 2); - auto resultIntsAdded = resultIntsPerLine - r.width() * resultIntsPerPixel; - auto resultInts = reinterpret_cast(result.bits()); + auto resultIntsPerLine = (outResult->bytesPerLine() >> 2); + auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel; + auto resultInts = reinterpret_cast(outResult->bits()) + dstPoint.y() * resultIntsPerLine + dstPoint.x() * resultIntsPerPixel; t_assert(resultIntsAdded >= 0); - t_assert(result.depth() == ((resultIntsPerPixel * sizeof(uint32)) << 3)); - t_assert(result.bytesPerLine() == (resultIntsPerLine << 2)); + t_assert(outResult->depth() == ((resultIntsPerPixel * sizeof(uint32)) << 3)); + t_assert(outResult->bytesPerLine() == (resultIntsPerLine << 2)); auto maskBytesPerPixel = (src.depth() >> 3); auto maskBytesPerLine = src.bytesPerLine(); - auto maskBytesAdded = maskBytesPerLine - r.width() * maskBytesPerPixel; - auto maskBytes = src.constBits() + r.y() * maskBytesPerLine + r.x() * maskBytesPerPixel; + auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel; + auto maskBytes = src.constBits() + srcRect.y() * maskBytesPerLine + srcRect.x() * maskBytesPerPixel; t_assert(maskBytesAdded >= 0); t_assert(src.depth() == (maskBytesPerPixel << 3)); - for (int y = 0; y != r.height(); ++y) { - for (int x = 0; x != r.width(); ++x) { + for (int y = 0; y != height; ++y) { + for (int x = 0; x != width; ++x) { auto maskOpacity = static_cast(*maskBytes) + 1; - auto alpha_red_masked = (alpha_red * maskOpacity) >> 16; - auto green_blue_masked = (green_blue * maskOpacity) >> 16; - auto alpha = static_cast(alpha_red_masked & 0xFF); - auto red = static_cast((alpha_red_masked >> 32) & 0xFF); - auto green = static_cast(green_blue_masked & 0xFF); - auto blue = static_cast((green_blue_masked >> 32) & 0xFF); + auto masked = (pattern * maskOpacity) >> 8; + auto alpha = static_cast(masked & 0xFF); + auto red = static_cast((masked >> 16) & 0xFF); + auto green = static_cast((masked >> 32) & 0xFF); + auto blue = static_cast((masked >> 48) & 0xFF); *resultInts = blue | (green << 8) | (red << 16) | (alpha << 24); maskBytes += maskBytesPerPixel; resultInts += resultIntsPerPixel; @@ -120,8 +126,7 @@ QImage colorizeImage(const QImage &src, QColor color, const QRect &r) { resultInts += resultIntsAdded; } - result.setDevicePixelRatio(src.devicePixelRatio()); - return std_::move(result); + outResult->setDevicePixelRatio(src.devicePixelRatio()); } namespace internal { diff --git a/Telegram/SourceFiles/ui/style/style_core.h b/Telegram/SourceFiles/ui/style/style_core.h index 5cda06071..f6ae0332f 100644 --- a/Telegram/SourceFiles/ui/style/style_core.h +++ b/Telegram/SourceFiles/ui/style/style_core.h @@ -63,10 +63,19 @@ bool setPaletteColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a); void startManager(); void stopManager(); -QImage colorizeImage(const QImage &src, QColor c, const QRect &r); +// *outResult must be r.width() x r.height(), ARGB32_Premultiplied. +// QRect(0, 0, src.width(), src.height()) must contain r. +void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect = QRect(), QPoint dstPoint = QPoint(0, 0)); -inline QImage colorizeImage(const QImage &src, const color &c, const QRect &r) { - return colorizeImage(src, c->c, r); +inline QImage colorizeImage(const QImage &src, QColor c, QRect srcRect = QRect()) { + if (srcRect.isNull()) srcRect = src.rect(); + auto result = QImage(srcRect.size(), QImage::Format_ARGB32_Premultiplied); + colorizeImage(src, c, &result, srcRect); + return std_::move(result); +} + +inline QImage colorizeImage(const QImage &src, const color &c, QRect srcRect = QRect()) { + return colorizeImage(src, c->c, srcRect); } namespace internal { diff --git a/Telegram/SourceFiles/ui/style/style_core_color.h b/Telegram/SourceFiles/ui/style/style_core_color.h index a52f62652..644a31c07 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.h +++ b/Telegram/SourceFiles/ui/style/style_core_color.h @@ -100,12 +100,12 @@ inline Color::operator const QPen &() const { } // namespace internal inline QColor interpolate(QColor a, QColor b, float64 opacity_b) { - auto bOpacity = static_cast(opacity_b * 255), aOpacity = (255 - bOpacity); + auto bOpacity = static_cast(opacity_b * 255) + 1, aOpacity = (256 - bOpacity); return { - (a.red() * aOpacity + b.red() * bOpacity + 1) >> 8, - (a.green() * aOpacity + b.green() * bOpacity + 1) >> 8, - (a.blue() * aOpacity + b.blue() * bOpacity + 1) >> 8, - (a.alpha() * aOpacity + b.alpha() * bOpacity + 1) >> 8 + (a.red() * aOpacity + b.red() * bOpacity) >> 8, + (a.green() * aOpacity + b.green() * bOpacity) >> 8, + (a.blue() * aOpacity + b.blue() * bOpacity) >> 8, + (a.alpha() * aOpacity + b.alpha() * bOpacity) >> 8 }; } diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.cpp b/Telegram/SourceFiles/ui/style/style_core_icon.cpp index 21759927b..ecfabb807 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_icon.cpp @@ -72,10 +72,6 @@ QImage createIconMask(const IconMask *mask) { return maskImage.copy(r); } -QImage createIconImage(const QImage &mask, QColor color) { - return colorizeImage(mask, color, QRect(0, 0, mask.width(), mask.height())); -} - } // namespace MonoIcon::MonoIcon(const IconMask *mask, const Color &color, QPoint offset) @@ -141,7 +137,8 @@ void MonoIcon::paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOve if (_pixmap.isNull()) { p.fillRect(partPosX, partPosY, w, h, colorOverride); } else { - p.drawImage(partPosX, partPosY, createIconImage(_maskImage, colorOverride)); + ensureColorizedImage(colorOverride); + p.drawImage(partPosX, partPosY, _colorizedImage); } } @@ -150,7 +147,8 @@ void MonoIcon::fill(QPainter &p, const QRect &rect, QColor colorOverride) const if (_pixmap.isNull()) { p.fillRect(rect, colorOverride); } else { - p.drawImage(rect, createIconImage(_maskImage, colorOverride), QRect(0, 0, _pixmap.width(), _pixmap.height())); + ensureColorizedImage(colorOverride); + p.drawImage(rect, _colorizedImage, _colorizedImage.rect()); } } @@ -206,12 +204,18 @@ void MonoIcon::ensureLoaded() const { } } +void MonoIcon::ensureColorizedImage(QColor color) const { + if (_colorizedImage.isNull()) _colorizedImage = QImage(_maskImage.size(), QImage::Format_ARGB32_Premultiplied); + colorizeImage(_maskImage, color, &_colorizedImage); +} + void MonoIcon::createCachedPixmap() const { iconPixmaps.createIfNull(); auto key = qMakePair(_mask, colorKey(_color->c)); auto j = iconPixmaps->constFind(key); if (j == iconPixmaps->cend()) { - j = iconPixmaps->insert(key, App::pixmapFromImageInPlace(createIconImage(_maskImage, _color->c))); + auto image = colorizeImage(_maskImage, _color); + j = iconPixmaps->insert(key, App::pixmapFromImageInPlace(std_::move(image))); } _pixmap = j.value(); _size = _pixmap.size() / cIntRetinaFactor(); diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.h b/Telegram/SourceFiles/ui/style/style_core_icon.h index 0877f2ba0..d7f1fc476 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.h +++ b/Telegram/SourceFiles/ui/style/style_core_icon.h @@ -68,11 +68,12 @@ public: private: void ensureLoaded() const; void createCachedPixmap() const; + void ensureColorizedImage(QColor color) const; const IconMask *_mask = nullptr; Color _color; QPoint _offset = { 0, 0 }; - mutable QImage _maskImage; + mutable QImage _maskImage, _colorizedImage; mutable QPixmap _pixmap; // for pixmaps mutable QSize _size; // for rects diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp index 4c6f9e85d..5deaa9e05 100644 --- a/Telegram/SourceFiles/window/window_theme.cpp +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -35,7 +35,15 @@ constexpr int kThemeBackgroundSizeLimit = 4 * 1024 * 1024; constexpr int kThemeSchemeSizeLimit = 1024 * 1024; struct Data { + struct Applying { + QString path; + QByteArray content; + QByteArray paletteForRevert; + Cached cached; + }; + ChatBackground background; + Applying applying; }; NeverFreedPointer instance; @@ -137,11 +145,11 @@ bool loadColorScheme(const QByteArray &content, Instance *out = nullptr) { auto size = value.size(); auto error = false; - if (value[0] == '#' && (size == 7 || size == 8)) { + if (value[0] == '#' && (size == 7 || size == 9)) { auto r = readHexUchar(value[1], value[2], error); auto g = readHexUchar(value[3], value[4], error); auto b = readHexUchar(value[5], value[6], error); - auto a = (size == 8) ? readHexUchar(value[7], value[8], error) : uchar(255); + auto a = (size == 9) ? readHexUchar(value[7], value[8], error) : uchar(255); if (!error) { if (out) { error = !out->palette.setColor(QLatin1String(name), r, g, b, a); @@ -157,7 +165,7 @@ bool loadColorScheme(const QByteArray &content, Instance *out = nullptr) { } } if (error) { - LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme.")); + LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme (while applying '%1: %2')").arg(QLatin1String(name)).arg(QLatin1String(value))); return false; } } @@ -320,6 +328,12 @@ void ChatBackground::setImage(int32 id, QImage &&image) { if (_id == kThemeBackground) { _tile = _themeTile; setPreparedImage(QImage(_themeImage)); + } else if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) { + if (_id == internal::kTestingDefaultBackground || image.isNull()) { + image.load(qsl(":/gui/art/bg.jpg")); + _id = internal::kTestingDefaultBackground; + } + setPreparedImage(std_::move(image)); } else { if (_id == kDefaultBackground) { image.load(qsl(":/gui/art/bg.jpg")); @@ -356,19 +370,98 @@ bool ChatBackground::tile() const { return _tile; } -void ChatBackground::setTile(bool tile) { +bool ChatBackground::tileForSave() const { + if (_id == internal::kTestingThemeBackground || + _id == internal::kTestingDefaultBackground) { + return _tileForRevert; + } + return tile(); +} + +void ChatBackground::ensureStarted() { if (_image.isNull()) { // We should start first, otherwise the default call // to start() will reset this value to _themeTile. start(); } +} + +void ChatBackground::setTile(bool tile) { + ensureStarted(); if (_tile != tile) { _tile = tile; - Local::writeUserSettings(); + if (_id != internal::kTestingThemeBackground && _id != internal::kTestingDefaultBackground) { + Local::writeUserSettings(); + } notify(BackgroundUpdate(BackgroundUpdate::Type::Changed, _tile)); } } +void ChatBackground::reset() { + if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) { + if (_themeImage.isNull()) { + _idForRevert = kDefaultBackground; + _imageForRevert = QImage(); + _tileForRevert = false; + } else { + _idForRevert = kThemeBackground; + _imageForRevert = _themeImage; + _tileForRevert = _themeTile; + } + } else { + setImage(kThemeBackground); + } +} + +void ChatBackground::saveForRevert() { + ensureStarted(); + if (_id != internal::kTestingThemeBackground && _id != internal::kTestingDefaultBackground) { + _idForRevert = _id; + _imageForRevert = std_::move(_image).toImage(); + _tileForRevert = _tile; + } +} + +void ChatBackground::setTestingTheme(Instance &&theme) { + style::main_palette::apply(theme.palette); + if (!theme.background.isNull() || _id == kThemeBackground) { + saveForRevert(); + setImage(internal::kTestingThemeBackground, std_::move(theme.background)); + setTile(theme.tiled); + } + notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, _tile), true); +} + +void ChatBackground::keepApplied() { + if (_id == internal::kTestingThemeBackground) { + _id = kThemeBackground; + _themeImage = _image.toImage(); + _themeTile = _tile; + } else if (_id == internal::kTestingDefaultBackground) { + _id = kDefaultBackground; + _themeImage = QImage(); + _themeTile = false; + writeNewBackgroundSettings(); + } + notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, _tile), true); +} + +void ChatBackground::writeNewBackgroundSettings() { + if (_tile != _tileForRevert) { + Local::writeUserSettings(); + } + Local::writeBackground(_id, QImage()); +} + +void ChatBackground::revert() { + if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) { + setTile(_tileForRevert); + setImage(_idForRevert, std_::move(_imageForRevert)); + } + notify(BackgroundUpdate(BackgroundUpdate::Type::RevertingTheme, _tile), true); +} + + ChatBackground *Background() { instance.createIfNull(); return &instance->background; @@ -396,6 +489,43 @@ void Unload() { instance.clear(); } +bool Apply(const QString &filepath) { + QByteArray content; + Instance theme; + if (!LoadFromFile(filepath, &theme, &content)) { + return false; + } + instance.createIfNull(); + instance->applying.path = filepath; + instance->applying.content = content; + instance->applying.cached = theme.cached; + if (instance->applying.paletteForRevert.isEmpty()) { + instance->applying.paletteForRevert = style::main_palette::save(); + } + Background()->setTestingTheme(std_::move(theme)); + return true; +} + +void KeepApplied() { + auto filepath = instance ? instance->applying.path : QString(); + if (filepath.isEmpty()) { + return; + } + auto pathRelative = QDir().relativeFilePath(filepath); + auto pathAbsolute = QFileInfo(filepath).absoluteFilePath(); + Local::writeTheme(pathRelative, pathAbsolute, instance->applying.content, instance->applying.cached); + instance->applying = Data::Applying(); + Background()->keepApplied(); +} + +void Revert() { + if (!instance->applying.paletteForRevert.isEmpty()) { + style::main_palette::load(instance->applying.paletteForRevert); + } + instance->applying = Data::Applying(); + Background()->revert(); +} + bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) { *outContent = readThemeContent(path); if (outContent->size() < 4) { diff --git a/Telegram/SourceFiles/window/window_theme.h b/Telegram/SourceFiles/window/window_theme.h index caa5925cc..85c9d8b52 100644 --- a/Telegram/SourceFiles/window/window_theme.h +++ b/Telegram/SourceFiles/window/window_theme.h @@ -24,7 +24,9 @@ namespace Window { namespace Theme { namespace internal { -constexpr int32 kUninitializedBackground = -3; +constexpr int32 kUninitializedBackground = -999; +constexpr int32 kTestingThemeBackground = -666; +constexpr int32 kTestingDefaultBackground = -665; } // namespace internal @@ -43,6 +45,10 @@ struct Cached { bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache); void Unload(); +bool Apply(const QString &filepath); +void KeepApplied(); +void Revert(); + struct Instance { style::palette palette; QImage background; @@ -56,6 +62,9 @@ struct BackgroundUpdate { New, Changed, Start, + TestingTheme, + RevertingTheme, + ApplyingTheme, }; BackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) { @@ -73,16 +82,22 @@ public: void start(); void setImage(int32 id, QImage &&image = QImage()); void setTile(bool tile); - void reset() { - setImage(kThemeBackground); - } + void reset(); + + void setTestingTheme(Instance &&theme); + void keepApplied(); + void revert(); int32 id() const; const QPixmap &image() const; bool tile() const; + bool tileForSave() const; private: + void ensureStarted(); + void saveForRevert(); void setPreparedImage(QImage &&image); + void writeNewBackgroundSettings(); int32 _id = internal::kUninitializedBackground; QPixmap _image; @@ -91,6 +106,10 @@ private: QImage _themeImage; bool _themeTile = false; + int32 _idForRevert = internal::kUninitializedBackground; + QImage _imageForRevert; + bool _tileForRevert = false; + }; ChatBackground *Background(); diff --git a/Telegram/SourceFiles/window/window_theme_warning.cpp b/Telegram/SourceFiles/window/window_theme_warning.cpp new file mode 100644 index 000000000..e75a33c05 --- /dev/null +++ b/Telegram/SourceFiles/window/window_theme_warning.cpp @@ -0,0 +1,148 @@ +/* +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 "window/window_theme_warning.h" + +#include "styles/style_boxes.h" +#include "ui/buttons/round_button.h" +#include "window/window_theme.h" +#include "lang.h" + +namespace Window { +namespace Theme { +namespace { + +constexpr int kWaitBeforeRevertMs = 15999; + +} // namespace + +WarningWidget::WarningWidget(QWidget *parent) : TWidget(parent) +, _secondsLeft(kWaitBeforeRevertMs / 1000) +, _shadow(st::themeWarningShadow) +, _keepChanges(this, lang(lng_theme_keep_changes), st::defaultBoxButton) +, _revert(this, lang(lng_theme_revert), st::cancelBoxButton) { + _keepChanges->setClickedCallback([] { Window::Theme::KeepApplied(); }); + _revert->setClickedCallback([] { Window::Theme::Revert(); }); + _timer.setTimeoutHandler([this] { handleTimer(); }); + updateText(); +} + +void WarningWidget::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Escape) { + Window::Theme::Revert(); + } +} + +void WarningWidget::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_cache.isNull()) { + if (!_animation.animating(getms())) { + if (isHidden()) { + return; + } + } + p.setOpacity(_animation.current(_hiding ? 0. : 1.)); + p.drawPixmap(_outer.topLeft(), _cache); + if (!_animation.animating()) { + _cache = QPixmap(); + showChildren(); + _started = getms(true); + _timer.start(100); + } + return; + } + + _shadow.paint(p, _inner, st::themeWarningShadowShift); + p.fillRect(_inner, st::boxBg); + + p.setFont(st::boxTitleFont); + p.setPen(st::boxTitleFg); + p.drawTextLeft(_inner.x() + st::boxTitlePosition.x(), _inner.y() + st::boxTitlePosition.y(), width(), lang(lng_theme_sure_keep)); + + p.setFont(st::boxTextFont); + p.setPen(st::boxTextFg); + p.drawTextLeft(_inner.x() + st::boxTitlePosition.x(), _inner.y() + st::themeWarningTextTop, width(), _text); +} + +void WarningWidget::resizeEvent(QResizeEvent *e) { + _inner = QRect((width() - st::themeWarningWidth) / 2, (height() - st::themeWarningHeight) / 2, st::themeWarningWidth, st::themeWarningHeight); + _outer = _inner.marginsAdded(_shadow.getDimensions(st::themeWarningShadowShift)); + auto left = _inner.x() + _inner.width() - st::boxButtonPadding.right() - _keepChanges->width(); + _keepChanges->moveToLeft(left, _inner.y() + _inner.height() - st::boxButtonPadding.bottom() - _keepChanges->height()); + _revert->moveToLeft(left - st::boxButtonPadding.left() - _revert->width(), _keepChanges->y()); + update(); +} + +void WarningWidget::handleTimer() { + auto msPassed = getms(true) - _started; + setSecondsLeft((kWaitBeforeRevertMs - msPassed) / 1000); +} + +void WarningWidget::setSecondsLeft(int secondsLeft) { + if (secondsLeft <= 0) { + Window::Theme::Revert(); + } else { + if (_secondsLeft != secondsLeft) { + _secondsLeft = secondsLeft; + updateText(); + update(); + } + _timer.start(100); + } +} + +void WarningWidget::updateText() { + _text = lng_theme_reverting(lt_count, _secondsLeft); +} + +void WarningWidget::showAnimated() { + startAnimation(false); + show(); + setFocus(); +} + +void WarningWidget::hideAnimated() { + startAnimation(true); +} + +void WarningWidget::startAnimation(bool hiding) { + _timer.stop(); + _hiding = hiding; + if (_cache.isNull()) { + showChildren(); + myEnsureResized(this); + _cache = myGrab(this, _outer); + } + hideChildren(); + _animation.start([this] { + update(); + if (_hiding) { + hide(); + if (_hiddenCallback) { + _hiddenCallback(); + } + } + }, _hiding ? 1. : 0., _hiding ? 0. : 1., st::layerSlideDuration); +} + +} // namespace Theme +} // namespace Window diff --git a/Telegram/SourceFiles/window/window_theme_warning.h b/Telegram/SourceFiles/window/window_theme_warning.h new file mode 100644 index 000000000..27e4b7ad7 --- /dev/null +++ b/Telegram/SourceFiles/window/window_theme_warning.h @@ -0,0 +1,71 @@ +/* +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/effects/rect_shadow.h" + +class BoxButton; + +namespace Window { +namespace Theme { + +class WarningWidget : public TWidget { +public: + WarningWidget(QWidget *parent); + + void setHiddenCallback(base::lambda_unique callback) { + _hiddenCallback = std_::move(callback); + } + + void showAnimated(); + void hideAnimated(); + +protected: + void keyPressEvent(QKeyEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + void setSecondsLeft(int secondsLeft); + void startAnimation(bool hiding); + void updateText(); + void handleTimer(); + + bool _hiding = false; + FloatAnimation _animation; + QPixmap _cache; + QRect _inner, _outer; + + SingleTimer _timer; + uint64 _started = 0; + int _secondsLeft = 0; + QString _text; + + Ui::RectShadow _shadow; + ChildWidget _keepChanges; + ChildWidget _revert; + + base::lambda_unique _hiddenCallback; + +}; + +} // namespace Theme +} // namespace Window diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 22c622fe5..9c27e5302 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -559,6 +559,8 @@ '<(src_loc)/window/top_bar_widget.h', '<(src_loc)/window/window_theme.cpp', '<(src_loc)/window/window_theme.h', + '<(src_loc)/window/window_theme_warning.cpp', + '<(src_loc)/window/window_theme_warning.h', '<(sp_media_key_tap_loc)/SPMediaKeyTap.m', '<(sp_media_key_tap_loc)/SPMediaKeyTap.h', From 2a3fd0066d345515c76ec9b8e84d80b99db04bfb Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 3 Nov 2016 13:33:57 +0300 Subject: [PATCH 011/100] Closed beta 10019001: style::color copy is denied, only const refs. --- Telegram/Resources/winrc/Telegram.rc | 8 +- Telegram/Resources/winrc/Updater.rc | 8 +- Telegram/SourceFiles/app.cpp | 143 +++--------------- Telegram/SourceFiles/app.h | 9 -- Telegram/SourceFiles/boxes/photosendbox.cpp | 4 +- Telegram/SourceFiles/boxes/sharebox.cpp | 2 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 2 +- .../SourceFiles/codegen/style/generator.cpp | 29 +++- .../SourceFiles/core/vector_of_moveable.h | 2 +- Telegram/SourceFiles/core/version.h | 2 +- .../SourceFiles/dialogs/dialogs_layout.cpp | 17 ++- .../history/history_media_types.cpp | 22 +-- .../SourceFiles/history/history_message.cpp | 16 +- .../history/history_service_layout.cpp | 8 +- Telegram/SourceFiles/historywidget.cpp | 3 +- .../inline_bot_layout_internal.cpp | 9 +- Telegram/SourceFiles/layout.cpp | 44 ++++-- Telegram/SourceFiles/layout.h | 10 +- Telegram/SourceFiles/mediaview.cpp | 4 +- Telegram/SourceFiles/mediaview.h | 2 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 2 +- Telegram/SourceFiles/structs.cpp | 26 ++-- Telegram/SourceFiles/structs.h | 2 +- Telegram/SourceFiles/sysbuttons.cpp | 34 ++--- Telegram/SourceFiles/sysbuttons.h | 2 +- Telegram/SourceFiles/ui/countryinput.h | 2 +- Telegram/SourceFiles/ui/flatbutton.cpp | 20 +-- Telegram/SourceFiles/ui/flatbutton.h | 6 +- Telegram/SourceFiles/ui/flatinput.cpp | 4 - Telegram/SourceFiles/ui/flatinput.h | 6 +- Telegram/SourceFiles/ui/flatlabel.h | 4 +- Telegram/SourceFiles/ui/flattextarea.h | 2 +- Telegram/SourceFiles/ui/scrollarea.cpp | 6 +- Telegram/SourceFiles/ui/scrollarea.h | 4 +- .../SourceFiles/ui/style/style_core_color.cpp | 11 +- .../SourceFiles/ui/style/style_core_color.h | 67 ++++---- .../SourceFiles/ui/style/style_core_icon.cpp | 4 +- .../SourceFiles/ui/style/style_core_icon.h | 21 ++- Telegram/SourceFiles/ui/text/text.cpp | 47 +----- Telegram/SourceFiles/ui/text/text.h | 4 - Telegram/SourceFiles/ui/text/text_block.cpp | 6 +- Telegram/SourceFiles/ui/text/text_block.h | 13 +- Telegram/SourceFiles/window/window_theme.cpp | 86 ++++++++++- Telegram/build/version | 2 +- 44 files changed, 344 insertions(+), 381 deletions(-) diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 348614fec..2251dae34 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,20,0 - PRODUCTVERSION 0,10,20,0 + FILEVERSION 0,10,19,1 + PRODUCTVERSION 0,10,19,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.20.0" + VALUE "FileVersion", "0.10.19.1" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.20.0" + VALUE "ProductVersion", "0.10.19.1" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index c53259b83..22abe67ef 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,20,0 - PRODUCTVERSION 0,10,20,0 + FILEVERSION 0,10,19,1 + PRODUCTVERSION 0,10,19,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.20.0" + VALUE "FileVersion", "0.10.19.1" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.20.0" + VALUE "ProductVersion", "0.10.19.1" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index b4e26531a..a1745c8f9 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -127,13 +127,6 @@ namespace { LastPhotosList lastPhotos; using LastPhotosMap = QHash; LastPhotosMap lastPhotosMap; - - style::color _msgServiceBg; - style::color _msgServiceSelectBg; - style::color _historyScrollBarColor; - style::color _historyScrollBgColor; - style::color _historyScrollBarOverColor; - style::color _historyScrollBgOverColor; } namespace App { @@ -2270,6 +2263,23 @@ namespace { prepareCorners(MessageInSelectedCorners, msgRadius(), st::msgInBgSelected, &st::msgInShadowSelected); prepareCorners(MessageOutCorners, msgRadius(), st::msgOutBg, &st::msgOutShadow); prepareCorners(MessageOutSelectedCorners, msgRadius(), st::msgOutBgSelected, &st::msgOutShadowSelected); + + static auto subscription = Window::Theme::Background()->add_subscription([](const Window::Theme::BackgroundUpdate &update) { + if (update.type != Window::Theme::BackgroundUpdate::Type::New) { + return; + } + for (int i = 0; i < 4; ++i) { + delete ::corners[StickerCorners].p[i]; ::corners[StickerCorners].p[i] = nullptr; + delete ::corners[StickerSelectedCorners].p[i]; ::corners[StickerSelectedCorners].p[i] = nullptr; + } + prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg); + prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceSelectBg); + + if (App::main()) { + App::main()->updateScrollColors(); + } + HistoryLayout::serviceColorsUpdated(); + }); } void clearHistories() { @@ -2774,125 +2784,6 @@ namespace { roundRect(p, x, y, w, h, bg, i.value(), 0); } - void initColorsFromBackground(const QImage &img) { - uint64 components[3] = { 0 }, componentsScroll[3] = { 0 }; - auto w = img.width(); - auto h = img.height(); - auto size = w * h; - if (auto pix = img.constBits()) { - for (int i = 0, l = size * 4; i != l; i += 4) { - components[2] += pix[i + 0]; - components[1] += pix[i + 1]; - components[0] += pix[i + 2]; - } - } - - if (size) { - for (int i = 0; i != 3; ++i) { - components[i] /= size; - } - } - int maxtomin[3] = { 0, 1, 2 }; - if (components[maxtomin[0]] < components[maxtomin[1]]) { - qSwap(maxtomin[0], maxtomin[1]); - } - if (components[maxtomin[1]] < components[maxtomin[2]]) { - qSwap(maxtomin[1], maxtomin[2]); - if (components[maxtomin[0]] < components[maxtomin[1]]) { - qSwap(maxtomin[0], maxtomin[1]); - } - } - - uint64 max = qMax(1ULL, components[maxtomin[0]]), mid = qMax(1ULL, components[maxtomin[1]]), min = qMax(1ULL, components[maxtomin[2]]); - - memcpy(componentsScroll, components, sizeof(components)); - - if (max != min) { - if (min > uint64(qRound(0.77 * max))) { - uint64 newmin = qRound(0.77 * max); // min saturation 23% - uint64 newmid = max - ((max - mid) * (max - newmin)) / (max - min); - components[maxtomin[1]] = newmid; - components[maxtomin[2]] = newmin; - } - uint64 newmin = qRound(0.77 * max); // saturation 23% for scroll - uint64 newmid = max - ((max - mid) * (max - newmin)) / (max - min); - componentsScroll[maxtomin[1]] = newmid; - componentsScroll[maxtomin[2]] = newmin; - } - - float64 luminance = 0.299 * componentsScroll[0] + 0.587 * componentsScroll[1] + 0.114 * componentsScroll[2]; - uint64 maxScroll = max; - if (luminance < 0.5 * 0xFF) { - maxScroll += qRound(0.2 * 0xFF); - } else { - maxScroll -= qRound(0.2 * 0xFF); - } - componentsScroll[maxtomin[2]] = qMin(uint64(float64(componentsScroll[maxtomin[2]]) * maxScroll / float64(componentsScroll[maxtomin[0]])), 0xFFULL); - componentsScroll[maxtomin[1]] = qMin(uint64(float64(componentsScroll[maxtomin[1]]) * maxScroll / float64(componentsScroll[maxtomin[0]])), 0xFFULL); - componentsScroll[maxtomin[0]] = qMin(maxScroll, 0xFFULL); - - if (max > uint64(qRound(0.2 * 0xFF))) { // brightness greater than 20% - max -= qRound(0.2 * 0xFF); - } else { - max = 0; - } - components[maxtomin[2]] = uint64(float64(components[maxtomin[2]]) * max / float64(components[maxtomin[0]])); - components[maxtomin[1]] = uint64(float64(components[maxtomin[1]]) * max / float64(components[maxtomin[0]])); - components[maxtomin[0]] = max; - - uchar r = uchar(components[0]), g = uchar(components[1]), b = uchar(components[2]); - float64 alpha = st::msgServiceBg->c.alphaF(); - _msgServiceBg = style::color(r, g, b, qRound(alpha * 0xFF)); - - float64 alphaSel = st::msgServiceSelectBg->c.alphaF(), addSel = (1. - ((1. - alphaSel) / (1. - alpha))) * 0xFF; - uchar rsel = snap(qRound(((1. - alphaSel) * r + addSel) / alphaSel), 0, 0xFF); - uchar gsel = snap(qRound(((1. - alphaSel) * g + addSel) / alphaSel), 0, 0xFF); - uchar bsel = snap(qRound(((1. - alphaSel) * b + addSel) / alphaSel), 0, 0xFF); - _msgServiceSelectBg = style::color(r, g, b, qRound(alphaSel * 0xFF)); - - for (int i = 0; i < 4; ++i) { - delete ::corners[StickerCorners].p[i]; ::corners[StickerCorners].p[i] = nullptr; - delete ::corners[StickerSelectedCorners].p[i]; ::corners[StickerSelectedCorners].p[i] = nullptr; - } - prepareCorners(StickerCorners, st::dateRadius, _msgServiceBg); - prepareCorners(StickerSelectedCorners, st::dateRadius, _msgServiceSelectBg); - - uchar rScroll = uchar(componentsScroll[0]), gScroll = uchar(componentsScroll[1]), bScroll = uchar(componentsScroll[2]); - _historyScrollBarColor = style::color(rScroll, gScroll, bScroll, st::historyScroll.barColor->c.alpha()); - _historyScrollBgColor = style::color(rScroll, gScroll, bScroll, st::historyScroll.bgColor->c.alpha()); - _historyScrollBarOverColor = style::color(rScroll, gScroll, bScroll, st::historyScroll.barOverColor->c.alpha()); - _historyScrollBgOverColor = style::color(rScroll, gScroll, bScroll, st::historyScroll.bgOverColor->c.alpha()); - - if (App::main()) { - App::main()->updateScrollColors(); - } - HistoryLayout::serviceColorsUpdated(); - } - - const style::color &msgServiceBg() { - return _msgServiceBg; - } - - const style::color &msgServiceSelectBg() { - return _msgServiceSelectBg; - } - - const style::color &historyScrollBarColor() { - return _historyScrollBarColor; - } - - const style::color &historyScrollBgColor() { - return _historyScrollBgColor; - } - - const style::color &historyScrollBarOverColor() { - return _historyScrollBarOverColor; - } - - const style::color &historyScrollBgOverColor() { - return _historyScrollBgOverColor; - } - WallPapers gServerBackgrounds; } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 4dceffcc3..2e14e7d94 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -293,15 +293,6 @@ namespace App { return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius); } - void initColorsFromBackground(const QImage &image); - - const style::color &msgServiceBg(); - const style::color &msgServiceSelectBg(); - const style::color &historyScrollBarColor(); - const style::color &historyScrollBgColor(); - const style::color &historyScrollBarOverColor(); - const style::color &historyScrollBgOverColor(); - struct WallPaper { WallPaper(int32 id, ImagePtr thumb, ImagePtr full) : id(id), thumb(thumb), full(full) { } diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index bec77968c..31395b637 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -284,7 +284,7 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) { p.setPen(st::historyFileNameOutFg); _name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); - style::color status(st::mediaOutFg); + auto &status = st::mediaOutFg; p.setFont(st::normalFont); p.setPen(status); p.drawTextLeft(x + nameleft, y + statustop, width(), _status); @@ -602,7 +602,7 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { p.setPen(st::historyFileNameInFg); _name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); - style::color status(st::mediaInFg); + auto &status = st::mediaInFg; p.setFont(st::normalFont); p.setPen(status); p.drawTextLeft(x + nameleft, y + statustop, width(), _status); diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 448abd9f9..fd5482118 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -484,7 +484,7 @@ ShareBox::Inner::Chat *ShareBox::Inner::getChat(Dialogs::Row *row) { void ShareBox::Inner::setActive(int active) { if (active != _active) { - auto changeNameFg = [this](int index, style::color from, style::color to) { + auto changeNameFg = [this](int index, const style::color &from, const style::color &to) { if (auto chat = getChatAtIndex(index)) { chat->nameFg.start([this, peer = chat->peer] { repaintChat(peer); diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index dc5d29c3c..7bf031fd0 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -646,7 +646,7 @@ void StickersBox::Inner::paintRow(Painter &p, int32 index) { int addy = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersAddSize.height()) / 2; QRect add(myrtlrect(addx, addy, addw, st::stickersAddSize.height())); - auto textBg = (_actionSel == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg; + auto &textBg = (_actionSel == index) ? st::defaultActiveButton.textBgOver : st::defaultActiveButton.textBg; App::roundRect(p, add, textBg, ImageRoundRadius::Small); int iconx = addx + (st::stickersAddSize.width() - st::stickersAddIcon.width()) / 2; int icony = addy + (st::stickersAddSize.height() - st::stickersAddIcon.height()) / 2; diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index b7f85dbb2..22e879252 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -330,7 +330,13 @@ QString Generator::typeToDefaultValue(structure::Type type) const { QString Generator::valueAssignmentCode(structure::Value value) const { auto copy = value.copyOf(); if (!copy.isEmpty()) { - return "st::" + copy.back(); + auto result = "st::" + copy.back(); + + // Copy is disabled for colors. + if (value.type().tag == Tag::Color || value.type().tag == Tag::Struct) { + result += ".clone()"; + } + return result; } switch (value.type().tag) { @@ -590,15 +596,30 @@ bool Generator::writeStructsDefinitions() { } bool result = module_.enumStructs([this](const Struct &value) -> bool { - header_->stream() << "struct " << value.name.back() << " {\n"; - for (const auto &field : value.fields) { + QStringList fields; + for (auto field : value.fields) { + auto clone = field.name.back(); + if (field.type.tag == Tag::Color || field.type.tag == Tag::Struct) { + clone += ".clone()"; + } + fields.push_back(clone); + } + header_->stream() << "\ +struct " << value.name.back() << " {\n\ + " << value.name.back() << " clone() const {\n\ + return { " << fields.join(", ") << " };\n\ + }\n"; + if (!fields.empty()) header_->newline(); + + for (auto &field : value.fields) { auto type = typeToString(field.type); if (type.isEmpty()) { return false; } header_->stream() << "\t" << type << " " << field.name.back() << ";\n"; } - header_->stream() << "};\n\n"; + header_->stream() << "\ +};\n\n"; return true; }); diff --git a/Telegram/SourceFiles/core/vector_of_moveable.h b/Telegram/SourceFiles/core/vector_of_moveable.h index 727bbac2d..c32e01312 100644 --- a/Telegram/SourceFiles/core/vector_of_moveable.h +++ b/Telegram/SourceFiles/core/vector_of_moveable.h @@ -119,7 +119,7 @@ public: return insertAt; } inline void push_back(T &&value) { - insert(end(), std_::forward(value)); + insert(end(), std_::move(value)); } inline void pop_back() { erase(end() - 1); diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index bc397ab9b..595eb1879 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (0ULL) +#define BETA_VERSION_MACRO (10019001ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 312c3d7da..6f3bbf1e4 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -144,7 +144,12 @@ struct UnreadBadgeSizeData { class UnreadBadgeStyleData : public Data::AbstractStructure { public: UnreadBadgeSizeData sizes[UnreadBadgeSizesCount]; - style::color bg[4] = { st::dialogsUnreadBg, st::dialogsUnreadBgActive, st::dialogsUnreadBgMuted, st::dialogsUnreadBgMutedActive }; + const style::color *bg[4] = { + &st::dialogsUnreadBg, + &st::dialogsUnreadBgActive, + &st::dialogsUnreadBgMuted, + &st::dialogsUnreadBgMutedActive + }; }; Data::GlobalStructurePointer unreadBadgeStyle; @@ -154,7 +159,7 @@ void createCircleMask(UnreadBadgeSizeData *data, int size) { data->circle = style::createCircleMask(size); } -QImage colorizeCircleHalf(UnreadBadgeSizeData *data, int size, int half, int xoffset, style::color color) { +QImage colorizeCircleHalf(UnreadBadgeSizeData *data, int size, int half, int xoffset, const style::color &color) { auto result = style::colorizeImage(data->circle, color, QRect(xoffset, 0, half, size)); result.setDevicePixelRatio(cRetinaFactor()); return result; @@ -183,18 +188,18 @@ void paintUnreadBadge(Painter &p, const QRect &rect, const UnreadBadgeStyle &st) t_assert(st.sizeId < UnreadBadgeSizesCount); badgeData = &unreadBadgeStyle->sizes[st.sizeId]; } - auto &bg = unreadBadgeStyle->bg[index]; + auto bg = unreadBadgeStyle->bg[index]; if (badgeData->left[index].isNull()) { int imgsize = size * cIntRetinaFactor(), imgsizehalf = sizehalf * cIntRetinaFactor(); createCircleMask(badgeData, size); - badgeData->left[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(badgeData, imgsize, imgsizehalf, 0, bg)); - badgeData->right[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(badgeData, imgsize, imgsizehalf, imgsize - imgsizehalf, bg)); + badgeData->left[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(badgeData, imgsize, imgsizehalf, 0, *bg)); + badgeData->right[index] = App::pixmapFromImageInPlace(colorizeCircleHalf(badgeData, imgsize, imgsizehalf, imgsize - imgsizehalf, *bg)); } int bar = rect.width() - 2 * sizehalf; p.drawPixmap(rect.x(), rect.y(), badgeData->left[index]); if (bar) { - p.fillRect(rect.x() + sizehalf, rect.y(), bar, rect.height(), bg); + p.fillRect(rect.x() + sizehalf, rect.y(), bar, rect.height(), *bg); } p.drawPixmap(rect.x() + sizehalf + bar, rect.y(), badgeData->right[index]); } diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 80e320824..0f8be9e4e 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -1211,8 +1211,8 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, float64 prg = voice->_playback ? voice->_playback->a_progress.current() : 0; // rescale waveform by going in waveform.size * bar_count 1D grid - style::color active(outbg ? (selected ? st::msgWaveformOutActiveSelected : st::msgWaveformOutActive) : (selected ? st::msgWaveformInActiveSelected : st::msgWaveformInActive)); - style::color inactive(outbg ? (selected ? st::msgWaveformOutInactiveSelected : st::msgWaveformOutInactive) : (selected ? st::msgWaveformInInactiveSelected : st::msgWaveformInInactive)); + auto &active = outbg ? (selected ? st::msgWaveformOutActiveSelected : st::msgWaveformOutActive) : (selected ? st::msgWaveformInActiveSelected : st::msgWaveformInActive); + auto &inactive = outbg ? (selected ? st::msgWaveformOutInactiveSelected : st::msgWaveformOutInactive) : (selected ? st::msgWaveformInInactiveSelected : st::msgWaveformInInactive); int32 wf_size = wf ? wf->size() : WaveformSamplesCount, availw = int32(namewidth + st::msgWaveformSkip), activew = qRound(availw * prg); if (!outbg && !voice->_playback && _parent->isMediaUnread()) { activew = availw; @@ -1261,7 +1261,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, } } - style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); + auto &status = outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg); p.setFont(st::normalFont); p.setPen(status); p.drawTextLeft(nameleft, statustop, _width, _statusText); @@ -2066,7 +2066,7 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, u // Make the bottom of the rect at the same level as the bottom of the info rect. recty -= st::msgDateImgDelta; - App::roundRect(p, rectx, recty, rectw, recth, selected ? App::msgServiceSelectBg() : App::msgServiceBg(), selected ? StickerSelectedCorners : StickerCorners); + App::roundRect(p, rectx, recty, rectw, recth, selected ? st::msgServiceSelectBg : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); rectx += st::msgReplyPadding.left(); rectw -= st::msgReplyPadding.left() + st::msgReplyPadding.right(); if (via) { @@ -2317,7 +2317,7 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, u p.setPen(outbg ? st::historyFileNameOutFg : st::historyFileNameInFg); _name.drawLeftElided(p, nameleft, nametop, namewidth, width); - style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); + auto &status = outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg); p.setFont(st::normalFont); p.setPen(status); p.drawTextLeft(nameleft, statustop, width, _phone); @@ -2649,9 +2649,9 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u bool out = _parent->out(), isPost = _parent->isPost(), outbg = out && !isPost; bool selected = (selection == FullSelection); - style::color barfg = (selected ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor)); - style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); - style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg)); + auto &barfg = selected ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor); + auto &semibold = selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg); + auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins()); auto padding = inBubblePadding(); @@ -3048,9 +3048,9 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint bool out = _parent->out(), isPost = _parent->isPost(), outbg = out && !isPost; bool selected = (selection == FullSelection); - style::color barfg = (selected ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor)); - style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); - style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg)); + auto &barfg = selected ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor); + auto &semibold = selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg); + auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins()); auto padding = inBubblePadding(); diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index dddced027..99e12e1d4 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -243,14 +243,12 @@ void HistoryMessageReply::itemRemoved(HistoryMessage *holder, HistoryItem *remov void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, int y, int w, PaintFlags flags) const { bool selected = (flags & PaintSelected), outbg = holder->hasOutLayout(); - style::color bar; + const style::color *bar = &st::msgImgReplyBarColor; if (flags & PaintInBubble) { - bar = ((flags & PaintSelected) ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor)); - } else { - bar = st::msgImgReplyBarColor; + bar = &((flags & PaintSelected) ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor)); } QRect rbar(rtlrect(x + st::msgReplyBarPos.x(), y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), w + 2 * x)); - p.fillRect(rbar, bar); + p.fillRect(rbar, *bar); if (w > st::msgReplyBarSkip) { if (replyToMsg) { @@ -282,7 +280,7 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in HistoryMessage *replyToAsMsg = replyToMsg->toHistoryMessage(); if (!(flags & PaintInBubble)) { } else if ((replyToAsMsg && replyToAsMsg->emptyText()) || replyToMsg->serviceMsg()) { - style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg)); + auto &date = outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg); p.setPen(date); } else { p.setPen(st::msgColor); @@ -291,7 +289,7 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in } } else { p.setFont(st::msgDateFont); - style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg)); + auto &date = outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg); p.setPen((flags & PaintInBubble) ? date : st::msgDateImgColor); p.drawTextLeft(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2, w + 2 * x, st::msgDateFont->elided(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message), w - st::msgReplyBarSkip)); } @@ -311,7 +309,7 @@ void HistoryMessage::KeyboardStyle::repaint(const HistoryItem *item) const { } void HistoryMessage::KeyboardStyle::paintButtonBg(Painter &p, const QRect &rect, bool down, float64 howMuchOver) const { - App::roundRect(p, rect, App::msgServiceBg(), StickerCorners); + App::roundRect(p, rect, st::msgServiceBg, StickerCorners); if (down) { howMuchOver = 1.; } @@ -1153,7 +1151,7 @@ void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, int32 width App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); } else if (type == InfoDisplayOverBackground) { int32 dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); - App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? App::msgServiceSelectBg() : App::msgServiceBg(), selected ? StickerSelectedCorners : StickerCorners); + App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgServiceSelectBg : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); } dateX += HistoryMessage::timeLeft(); diff --git a/Telegram/SourceFiles/history/history_service_layout.cpp b/Telegram/SourceFiles/history/history_service_layout.cpp index b69742070..121800cab 100644 --- a/Telegram/SourceFiles/history/history_service_layout.cpp +++ b/Telegram/SourceFiles/history/history_service_layout.cpp @@ -102,7 +102,7 @@ QPixmap circleCorner(int corner) { yoffset = size; } auto part = QRect(xoffset, yoffset, size, size); - auto result = style::colorizeImage(serviceMessageStyle->circle[maskType], App::msgServiceBg(), part); + auto result = style::colorizeImage(serviceMessageStyle->circle[maskType], st::msgServiceBg, part); result.setDevicePixelRatio(cRetinaFactor()); serviceMessageStyle->corners[corner] = App::pixmapFromImageInPlace(std_::move(result)); } @@ -127,7 +127,7 @@ int paintBubbleSide(Painter &p, int x, int y, int width, SideStyle style, Corner p.drawPixmap(x + width - rightWidth, y, right); int cornerHeight = left.height() / cIntRetinaFactor(); - p.fillRect(x + leftWidth, y, width - leftWidth - rightWidth, cornerHeight, App::msgServiceBg()); + p.fillRect(x + leftWidth, y, width - leftWidth - rightWidth, cornerHeight, st::msgServiceBg); return cornerHeight; } else if (style == SideStyle::Inverted) { // CornerLeft and CornerRight are inverted for SideStyle::Inverted sprites. @@ -161,7 +161,7 @@ void paintBubblePart(Painter &p, int x, int y, int width, int height, SideStyle height -= skip; } - p.fillRect(x, y, width, height, App::msgServiceBg()); + p.fillRect(x, y, width, height, st::msgServiceBg); } void paintPreparedDate(Painter &p, const QString &dateText, int dateTextWidth, int y, int w) { @@ -337,7 +337,7 @@ QVector ServiceMessagePainter::countLineWidths(const Text &text, const QRec void paintEmpty(Painter &p, int width, int height) { auto position = QPoint((width - st::historyEmptySize) / 2, ((height - st::historyEmptySize) * 4) / 9); p.setPen(Qt::NoPen); - p.setBrush(App::msgServiceBg()); + p.setBrush(st::msgServiceBg); p.setRenderHint(QPainter::HighQualityAntialiasing); p.drawEllipse(rtlrect(position.x(), position.y(), st::historyEmptySize, st::historyEmptySize, width)); p.setRenderHint(QPainter::HighQualityAntialiasing, false); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index ec0d31e7b..a7714d59f 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -7090,8 +7090,7 @@ void HistoryWidget::itemEdited(HistoryItem *item) { } void HistoryWidget::updateScrollColors() { - if (!App::historyScrollBarColor()) return; - _scroll.updateColors(App::historyScrollBarColor(), App::historyScrollBgColor(), App::historyScrollBarOverColor(), App::historyScrollBgOverColor()); + _scroll.updateBars(); } MsgId HistoryWidget::replyToId() const { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 3e852e96c..e86ed36a6 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -1047,9 +1047,14 @@ void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) ImagePtr thumb = getResultThumb(); if (thumb->isNull() && !_thumbLetter.isEmpty()) { int32 index = (_thumbLetter.at(0).unicode() % 4); - style::color colors[] = { st::msgFileRedColor, st::msgFileYellowColor, st::msgFileGreenColor, st::msgFileBlueColor }; + const style::color *colors[] = { + &st::msgFileRedColor, + &st::msgFileYellowColor, + &st::msgFileGreenColor, + &st::msgFileBlueColor + }; - p.fillRect(rthumb, colors[index]); + p.fillRect(rthumb, *colors[index]); if (!_thumbLetter.isEmpty()) { p.setFont(st::linksLetterFont); p.setPen(st::linksLetterFg); diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index bcdcd1e2e..90f3ce4eb 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -210,24 +210,44 @@ int32 documentColorIndex(DocumentData *document, QString &ext) { return colorIndex; } -style::color documentColor(int32 colorIndex) { - static style::color colors[] = { st::msgFileBlueColor, st::msgFileGreenColor, st::msgFileRedColor, st::msgFileYellowColor }; - return colors[colorIndex & 3]; +const style::color &documentColor(int32 colorIndex) { + static const style::color *colors[] = { + &st::msgFileBlueColor, + &st::msgFileGreenColor, + &st::msgFileRedColor, + &st::msgFileYellowColor + }; + return *colors[colorIndex & 3]; } -style::color documentDarkColor(int32 colorIndex) { - static style::color colors[] = { st::msgFileBlueDark, st::msgFileGreenDark, st::msgFileRedDark, st::msgFileYellowDark }; - return colors[colorIndex & 3]; +const style::color &documentDarkColor(int32 colorIndex) { + static const style::color *colors[] = { + &st::msgFileBlueDark, + &st::msgFileGreenDark, + &st::msgFileRedDark, + &st::msgFileYellowDark + }; + return *colors[colorIndex & 3]; } -style::color documentOverColor(int32 colorIndex) { - static style::color colors[] = { st::msgFileBlueOver, st::msgFileGreenOver, st::msgFileRedOver, st::msgFileYellowOver }; - return colors[colorIndex & 3]; +const style::color &documentOverColor(int32 colorIndex) { + static const style::color *colors[] = { + &st::msgFileBlueOver, + &st::msgFileGreenOver, + &st::msgFileRedOver, + &st::msgFileYellowOver + }; + return *colors[colorIndex & 3]; } -style::color documentSelectedColor(int32 colorIndex) { - static style::color colors[] = { st::msgFileBlueSelected, st::msgFileGreenSelected, st::msgFileRedSelected, st::msgFileYellowSelected }; - return colors[colorIndex & 3]; +const style::color &documentSelectedColor(int32 colorIndex) { + static const style::color *colors[] = { + &st::msgFileBlueSelected, + &st::msgFileGreenSelected, + &st::msgFileRedSelected, + &st::msgFileYellowSelected + }; + return *colors[colorIndex & 3]; } RoundCorners documentCorners(int32 colorIndex) { diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index e80783a4b..db82d0759 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -82,11 +82,11 @@ QString formatPlayedText(qint64 played, qint64 duration); QString documentName(DocumentData *document); TextWithEntities documentNameWithEntities(DocumentData *document); int32 documentColorIndex(DocumentData *document, QString &ext); -style::color documentColor(int32 colorIndex); -style::color documentDarkColor(int32 colorIndex); -style::color documentOverColor(int32 colorIndex); -style::color documentSelectedColor(int32 colorIndex); -RoundCorners documentCorners(int32 colorIndex); +const style::color &documentColor(int colorIndex); +const style::color &documentDarkColor(int colorIndex); +const style::color &documentOverColor(int colorIndex); +const style::color &documentSelectedColor(int colorIndex); +RoundCorners documentCorners(int colorIndex); bool documentIsValidMediaFile(const QString &filepath); class PaintContextBase { diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 1ddd03d19..b43afe02f 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -1228,7 +1228,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty if (!fileShown()) { if (!_doc || _doc->thumb->isNull()) { int32 colorIndex = documentColorIndex(_doc, _docExt); - _docIconColor = documentColor(colorIndex); + _docIconColor = &documentColor(colorIndex); const style::icon *(thumbs[]) = { &st::mediaviewFileBlue, &st::mediaviewFileGreen, &st::mediaviewFileRed, &st::mediaviewFileYellow }; _docIcon = thumbs[colorIndex]; @@ -1660,7 +1660,7 @@ void MediaView::paintEvent(QPaintEvent *e) { radialOpacity = _radial.opacity(); } if (!_doc || _doc->thumb->isNull()) { - p.fillRect(_docIconRect, _docIconColor->b); + p.fillRect(_docIconRect, (*_docIconColor)->b); if ((!_doc || _doc->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { _docIcon->paint(p, _docIconRect.x() + (_docIconRect.width() - _docIcon->width()), _docIconRect.y(), width()); p.setPen(st::mediaviewFileExtFg); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 2f4600851..ed7872c63 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -238,7 +238,7 @@ private: void stopGif(); const style::icon *_docIcon = nullptr; - style::color _docIconColor; + const style::color *_docIconColor = nullptr; QString _docName, _docSize, _docExt; int _docNameWidth = 0, _docSizeWidth = 0, _docExtWidth = 0; QRect _docRect, _docIconRect; diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 7ddc28a65..abe0d7143 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -1020,7 +1020,7 @@ void StickerPanInner::paintStickers(Painter &p, const QRect &r) { if (featuredHasAddButton(c)) { auto add = featuredAddRect(c); auto selected = (_selectedFeaturedSetAdd == c); - auto textBg = selected ? st::stickersTrendingAdd.textBgOver : st::stickersTrendingAdd.textBg; + auto &textBg = selected ? st::stickersTrendingAdd.textBgOver : st::stickersTrendingAdd.textBg; auto textTop = (selected && _selectedFeaturedSetAdd == _pressedFeaturedSetAdd) ? st::stickersTrendingAdd.downTextTop : st::stickersTrendingAdd.textTop; App::roundRect(p, myrtlrect(add), textBg, ImageRoundRadius::Small); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index b57ae68ed..8b0162193 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -48,12 +48,6 @@ int peerColorIndex(const PeerId &peer) { return (md5[peerId & 0x0F] & (peerIsUser(peer) ? 0x07 : 0x03)); } -struct ColorReferenceWrap { - ColorReferenceWrap(const style::color &data) : data(data) { - } - const style::color &data; -}; - ImagePtr generateUserpicImage(const style::icon &icon) { auto data = QImage(icon.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); data.setDevicePixelRatio(cRetinaFactor()); @@ -67,17 +61,17 @@ ImagePtr generateUserpicImage(const style::icon &icon) { } // namespace const style::color &peerColor(int index) { - static const ColorReferenceWrap peerColors[kUserColorsCount] = { - st::historyPeer1NameFg, - st::historyPeer2NameFg, - st::historyPeer3NameFg, - st::historyPeer4NameFg, - st::historyPeer5NameFg, - st::historyPeer6NameFg, - st::historyPeer7NameFg, - st::historyPeer8NameFg, + static const style::color *peerColors[kUserColorsCount] = { + &st::historyPeer1NameFg, + &st::historyPeer2NameFg, + &st::historyPeer3NameFg, + &st::historyPeer4NameFg, + &st::historyPeer5NameFg, + &st::historyPeer6NameFg, + &st::historyPeer7NameFg, + &st::historyPeer8NameFg, }; - return peerColors[index].data; + return *peerColors[index]; } ImagePtr userDefPhoto(int index) { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 9085b960f..0af012287 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -304,7 +304,7 @@ public: MTPinputPeer input; int colorIndex; - style::color color; + const style::color &color; void setUserpic(ImagePtr userpic); void paintUserpic(Painter &p, int size, int x, int y) const; diff --git a/Telegram/SourceFiles/sysbuttons.cpp b/Telegram/SourceFiles/sysbuttons.cpp index a61564afe..63a60eca8 100644 --- a/Telegram/SourceFiles/sysbuttons.cpp +++ b/Telegram/SourceFiles/sysbuttons.cpp @@ -27,19 +27,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "autoupdater.h" SysBtn::SysBtn(QWidget *parent, const style::sysButton &st, const QString &text) : Button(parent) -, _st(st) -, a_color(_st.color->c) +, _st(&st) +, a_color(_st->color->c) , _a_color(animation(this, &SysBtn::step_color)) , _text(text) { - int32 w = _st.size.width() + (_text.isEmpty() ? 0 : ((_st.size.width() - _st.icon.width()) / 2 + st::titleTextButton.font->width(_text))); - resize(w, _st.size.height()); + int32 w = _st->size.width() + (_text.isEmpty() ? 0 : ((_st->size.width() - _st->icon.width()) / 2 + st::titleTextButton.font->width(_text))); + resize(w, _st->size.height()); setCursor(style::cur_default); } void SysBtn::setText(const QString &text) { _text = text; - int32 w = _st.size.width() + (_text.isEmpty() ? 0 : ((_st.size.width() - _st.icon.width()) / 2 + st::titleTextButton.font->width(_text))); - resize(w, _st.size.height()); + int32 w = _st->size.width() + (_text.isEmpty() ? 0 : ((_st->size.width() - _st->icon.width()) / 2 + st::titleTextButton.font->width(_text))); + resize(w, _st->size.height()); } void SysBtn::setOverLevel(float64 level) { @@ -48,7 +48,7 @@ void SysBtn::setOverLevel(float64 level) { } void SysBtn::onStateChanged(int oldState, ButtonStateChangeSource source) { - a_color.start((_state & StateOver ? _st.overColor : _st.color)->c); + a_color.start((_state & StateOver ? _st->overColor : _st->color)->c); if (source == ButtonByUser || source == ButtonByPress) { _a_color.stop(); @@ -62,29 +62,29 @@ void SysBtn::onStateChanged(int oldState, ButtonStateChangeSource source) { void SysBtn::paintEvent(QPaintEvent *e) { Painter p(this); - int x = width() - ((_st.size.width() + _st.icon.width()) / 2), y = (height() - _st.icon.height()) / 2; + int x = width() - ((_st->size.width() + _st->icon.width()) / 2), y = (height() - _st->icon.height()) / 2; QColor c = a_color.current(); if (_overLevel > 0) { if (_overLevel >= 1) { - c = _st.overColor->c; + c = _st->overColor->c; } else { - c.setRedF(c.redF() * (1 - _overLevel) + _st.overColor->c.redF() * _overLevel); - c.setGreenF(c.greenF() * (1 - _overLevel) + _st.overColor->c.greenF() * _overLevel); - c.setBlueF(c.blueF() * (1 - _overLevel) + _st.overColor->c.blueF() * _overLevel); + c.setRedF(c.redF() * (1 - _overLevel) + _st->overColor->c.redF() * _overLevel); + c.setGreenF(c.greenF() * (1 - _overLevel) + _st->overColor->c.greenF() * _overLevel); + c.setBlueF(c.blueF() * (1 - _overLevel) + _st->overColor->c.blueF() * _overLevel); } } - p.fillRect(x, y, _st.icon.width(), _st.icon.height(), c); - _st.icon.paint(p, x, y, width()); + p.fillRect(x, y, _st->icon.width(), _st->icon.height(), c); + _st->icon.paint(p, x, y, width()); if (!_text.isEmpty()) { p.setFont(st::titleTextButton.font->f); p.setPen(c); - p.drawText((_st.size.width() - _st.icon.width()) / 2, st::titleTextButton.textTop + st::titleTextButton.font->ascent, _text); + p.drawText((_st->size.width() - _st->icon.width()) / 2, st::titleTextButton.textTop + st::titleTextButton.font->ascent, _text); } } void SysBtn::setSysBtnStyle(const style::sysButton &st) { - _st = st; + _st = &st; update(); } @@ -97,7 +97,7 @@ HitTestType SysBtn::hitTest(const QPoint &p) const { } void SysBtn::step_color(float64 ms, bool timer) { - float64 dt = ms / _st.duration; + float64 dt = ms / _st->duration; if (dt >= 1) { _a_color.stop(); a_color.finish(); diff --git a/Telegram/SourceFiles/sysbuttons.h b/Telegram/SourceFiles/sysbuttons.h index 9d1fced28..06dba6ac9 100644 --- a/Telegram/SourceFiles/sysbuttons.h +++ b/Telegram/SourceFiles/sysbuttons.h @@ -56,7 +56,7 @@ protected: void onStateChanged(int oldState, ButtonStateChangeSource source) override; void paintEvent(QPaintEvent *e) override; - style::sysButton _st; + const style::sysButton *_st; anim::cvalue a_color; Animation _a_color; diff --git a/Telegram/SourceFiles/ui/countryinput.h b/Telegram/SourceFiles/ui/countryinput.h index 2514a9e7f..be20f24ea 100644 --- a/Telegram/SourceFiles/ui/countryinput.h +++ b/Telegram/SourceFiles/ui/countryinput.h @@ -58,7 +58,7 @@ private: QPixmap _arrow; QRect _inner, _arrowRect; - style::countryInput _st; + const style::countryInput &_st; bool _active; QString _text; diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 3f27bc79e..17a9f7466 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -31,12 +31,14 @@ FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatBu , _a_appearance(animation(this, &FlatButton::step_appearance)) , _opacity(1) { if (_st.width < 0) { - _st.width = textWidth() - _st.width; + _width = textWidth() - _st.width; } else if (!_st.width) { - _st.width = textWidth() + _st.height - _st.font->height; + _width = textWidth() + _st.height - _st.font->height; + } else { + _width = _st.width; } connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); - resize(_st.width, _st.height); + resize(_width, _st.height); setCursor(_st.cursor); } @@ -55,13 +57,13 @@ void FlatButton::setText(const QString &text) { } void FlatButton::setWidth(int32 w) { - _st.width = w; - if (_st.width < 0) { - _st.width = textWidth() - _st.width; - } else if (!_st.width) { - _st.width = textWidth() + _st.height - _st.font->height; + _width = w; + if (_width < 0) { + _width = textWidth() - _st.width; + } else if (!_width) { + _width = textWidth() + _st.height - _st.font->height; } - resize(_st.width, height()); + resize(_width, height()); } int32 FlatButton::textWidth() const { diff --git a/Telegram/SourceFiles/ui/flatbutton.h b/Telegram/SourceFiles/ui/flatbutton.h index 5feea438e..83670ec64 100644 --- a/Telegram/SourceFiles/ui/flatbutton.h +++ b/Telegram/SourceFiles/ui/flatbutton.h @@ -47,9 +47,9 @@ public slots: private: QString _text, _textForAutoSize; - int32 _textWidth; + int _width, _textWidth; - style::flatButton _st; + const style::flatButton &_st; anim::cvalue a_bg, a_text; Animation _a_appearance; @@ -79,7 +79,7 @@ public slots: private: QString _text; int _textWidth = 0; - style::linkButton _st; + const style::linkButton &_st; }; diff --git a/Telegram/SourceFiles/ui/flatinput.cpp b/Telegram/SourceFiles/ui/flatinput.cpp index 13a086750..de9a5eae8 100644 --- a/Telegram/SourceFiles/ui/flatinput.cpp +++ b/Telegram/SourceFiles/ui/flatinput.cpp @@ -111,10 +111,6 @@ void FlatInput::customUpDown(bool custom) { _customUpDown = custom; } -void FlatInput::setTextMargins(const QMargins &mrg) { - _st.textMrg = mrg; -} - void FlatInput::onTouchTimer() { _touchRightButton = true; } diff --git a/Telegram/SourceFiles/ui/flatinput.h b/Telegram/SourceFiles/ui/flatinput.h index f730be8f8..5f5dddd30 100644 --- a/Telegram/SourceFiles/ui/flatinput.h +++ b/Telegram/SourceFiles/ui/flatinput.h @@ -49,8 +49,6 @@ public: return _oldtext; } - void setTextMargins(const QMargins &mrg); - public slots: void onTextChange(const QString &text); void onTextEdited(); @@ -104,9 +102,7 @@ private: Animation _a_appearance; int _notingBene; - style::flatInput _st; - - style::font _font; + const style::flatInput &_st; QTimer _touchTimer; bool _touchPress, _touchRightButton, _touchMove; diff --git a/Telegram/SourceFiles/ui/flatlabel.h b/Telegram/SourceFiles/ui/flatlabel.h index 4c16382bb..5d8128c18 100644 --- a/Telegram/SourceFiles/ui/flatlabel.h +++ b/Telegram/SourceFiles/ui/flatlabel.h @@ -109,8 +109,8 @@ private: void showContextMenu(QContextMenuEvent *e, ContextMenuReason reason); Text _text; - style::flatLabel _st; - style::textStyle _tst; + const style::flatLabel &_st; + const style::textStyle &_tst; float64 _opacity = 1.; int _allowedWidth = 0; diff --git a/Telegram/SourceFiles/ui/flattextarea.h b/Telegram/SourceFiles/ui/flattextarea.h index a29687e1e..51929cc73 100644 --- a/Telegram/SourceFiles/ui/flattextarea.h +++ b/Telegram/SourceFiles/ui/flattextarea.h @@ -212,7 +212,7 @@ private: std_::unique_ptr _tagMimeProcessor; - style::flatTextarea _st; + const style::flatTextarea &_st; bool _undoAvailable = false; bool _redoAvailable = false; diff --git a/Telegram/SourceFiles/ui/scrollarea.cpp b/Telegram/SourceFiles/ui/scrollarea.cpp index f6c23dc53..7aac81107 100644 --- a/Telegram/SourceFiles/ui/scrollarea.cpp +++ b/Telegram/SourceFiles/ui/scrollarea.cpp @@ -780,11 +780,7 @@ void ScrollArea::onVerticalScroll() { void ScrollArea::rangeChanged(int oldMax, int newMax, bool vertical) { } -void ScrollArea::updateColors(const style::color &bar, const style::color &bg, const style::color &barOver, const style::color &bgOver) { - _st.barColor = bar; - _st.bgColor = bg; - _st.barOverColor = barOver; - _st.bgOverColor = bgOver; +void ScrollArea::updateBars() { _horizontalBar->update(); _verticalBar->update(); } diff --git a/Telegram/SourceFiles/ui/scrollarea.h b/Telegram/SourceFiles/ui/scrollarea.h index 12c80c027..b09348c96 100644 --- a/Telegram/SourceFiles/ui/scrollarea.h +++ b/Telegram/SourceFiles/ui/scrollarea.h @@ -179,7 +179,7 @@ public: void rangeChanged(int oldMax, int newMax, bool vertical); - void updateColors(const style::color &bar, const style::color &bg, const style::color &barOver, const style::color &bgOver); + void updateBars(); bool focusNextPrevChild(bool next) override; void setMovingByScrollBar(bool movingByScrollBar); @@ -239,7 +239,7 @@ private: bool _ownsWidget = false; // if true, the widget is deleted in destructor. bool _movingByScrollBar = false; - style::flatScroll _st; + const style::flatScroll &_st; ChildWidget _horizontalBar, _verticalBar; ChildWidget _topShadow, _bottomShadow; int _horizontalValue, _verticalValue; diff --git a/Telegram/SourceFiles/ui/style/style_core_color.cpp b/Telegram/SourceFiles/ui/style/style_core_color.cpp index aa8b92a0d..edaa9533b 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_color.cpp @@ -41,9 +41,6 @@ void destroyColors() { colorsMap.clear(); } -Color::Color(const Color &c) : ptr(c.ptr) { -} - Color::Color(ColorData *data) : ptr(data) { } @@ -51,6 +48,14 @@ Color::Color(uchar r, uchar g, uchar b, uchar a) { init(r, g, b, a); } +Color::Color(Color &&other) : ptr(other.ptr) { +} + +Color &Color::operator=(Color &&other) { + ptr = other.ptr; + return *this; +} + void Color::set(uchar r, uchar g, uchar b, uchar a) const { ptr->set(r, g, b, a); } diff --git a/Telegram/SourceFiles/ui/style/style_core_color.h b/Telegram/SourceFiles/ui/style/style_core_color.h index 644a31c07..f02373b9e 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.h +++ b/Telegram/SourceFiles/ui/style/style_core_color.h @@ -28,18 +28,50 @@ namespace internal { void destroyColors(); -class ColorData; +class Color; +class ColorData { +public: + QColor c; + QPen p; + QBrush b; + + QColor transparent() const { + return QColor(c.red(), c.green(), c.blue(), 0); + } + +private: + ColorData(); + ColorData(uchar r, uchar g, uchar b, uchar a); + void set(uchar r, uchar g, uchar b, uchar a); + + friend class Color; + friend class style::palette; + +}; + class Color { public: Color(Qt::Initialization = Qt::Uninitialized) { } - Color(const Color &c); Color(uchar r, uchar g, uchar b, uchar a); + Color(const Color &other) = delete; + Color &operator=(const Color &other) = delete; + Color(Color &&other); + Color &operator=(Color &&other); + + Color clone() const { + return Color(ptr); + } void set(uchar r, uchar g, uchar b, uchar a) const; - operator const QBrush &() const; - operator const QPen &() const; + operator const QBrush &() const { + return ptr->b; + } + + operator const QPen &() const { + return ptr->p; + } ColorData *operator->() const { return ptr; @@ -62,26 +94,6 @@ private: }; -class ColorData { -public: - QColor c; - QPen p; - QBrush b; - - QColor transparent() const { - return QColor(c.red(), c.green(), c.blue(), 0); - } - -private: - ColorData(); - ColorData(uchar r, uchar g, uchar b, uchar a); - void set(uchar r, uchar g, uchar b, uchar a); - - friend class Color; - friend class style::palette; - -}; - inline bool operator==(const Color &a, const Color &b) { return a->c == b->c; } @@ -90,13 +102,6 @@ inline bool operator!=(const Color &a, const Color &b) { return a->c != b->c; } -inline Color::operator const QBrush &() const { - return ptr->b; -} -inline Color::operator const QPen &() const { - return ptr->p; -} - } // namespace internal inline QColor interpolate(QColor a, QColor b, float64 opacity_b) { diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.cpp b/Telegram/SourceFiles/ui/style/style_core_icon.cpp index ecfabb807..0e6d80da1 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_icon.cpp @@ -74,9 +74,9 @@ QImage createIconMask(const IconMask *mask) { } // namespace -MonoIcon::MonoIcon(const IconMask *mask, const Color &color, QPoint offset) +MonoIcon::MonoIcon(const IconMask *mask, Color &&color, QPoint offset) : _mask(mask) -, _color(color) +, _color(std_::move(color)) , _offset(offset) { } diff --git a/Telegram/SourceFiles/ui/style/style_core_icon.h b/Telegram/SourceFiles/ui/style/style_core_icon.h index d7f1fc476..f2799d7ab 100644 --- a/Telegram/SourceFiles/ui/style/style_core_icon.h +++ b/Telegram/SourceFiles/ui/style/style_core_icon.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include "ui/style/style_core_color.h" +#include "core/vector_of_moveable.h" namespace style { namespace internal { @@ -48,7 +49,11 @@ private: class MonoIcon { public: MonoIcon() = default; - MonoIcon(const IconMask *mask, const Color &color, QPoint offset); + MonoIcon(const MonoIcon &other) = delete; + MonoIcon &operator=(const MonoIcon &other) = delete; + MonoIcon(MonoIcon &&other) = default; + MonoIcon &operator=(MonoIcon &&other) = default; + MonoIcon(const IconMask *mask, Color &&color, QPoint offset); void reset() const; int width() const; @@ -82,10 +87,10 @@ private: class IconData { public: template - IconData(const MonoIcons &...icons) { + IconData(MonoIcons &&...icons) { created(); _parts.reserve(sizeof...(MonoIcons)); - addIcons(icons...); + addIcons(std_::forward(icons)...); } void reset() { @@ -122,12 +127,12 @@ private: } template - void addIcons(const MonoIcon &icon, const MonoIcons&... icons) { - _parts.push_back(icon); - addIcons(icons...); + void addIcons(MonoIcon &&icon, MonoIcons&&... icons) { + _parts.push_back(std_::move(icon)); + addIcons(std_::forward(icons)...); } - QVector _parts; + std_::vector_of_moveable _parts; mutable int _width = -1; mutable int _height = -1; @@ -139,7 +144,7 @@ public: } template - Icon(const MonoIcons&... icons) : _data(new IconData(icons...)), _owner(true) { + Icon(MonoIcons&&... icons) : _data(new IconData(std_::forward(icons)...)), _owner(true) { } Icon(const Icon &other) : _data(other._data) { } diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index 1ca8fd9bb..8248f5d52 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -91,18 +91,6 @@ QString textcmdLink(const QString &url, const QString &text) { return result.append(textcmdStartLink(url)).append(text).append(textcmdStopLink()); } -QString textcmdStartColor(const style::color &color) { - QString result; - result.reserve(7); - return result.append(TextCommand).append(QChar(TextCommandColor)).append(QChar(color->c.red())).append(QChar(color->c.green())).append(QChar(color->c.blue())).append(QChar(color->c.alpha())).append(TextCommand); -} - -QString textcmdStopColor() { - QString result; - result.reserve(3); - return result.append(TextCommand).append(QChar(TextCommandNoColor)).append(TextCommand); -} - QString textcmdStartSemibold() { QString result; result.reserve(3); @@ -132,7 +120,6 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink) case TextCommandNoItalic: case TextCommandUnderline: case TextCommandNoUnderline: - case TextCommandNoColor: break; case TextCommandLinkIndex: @@ -146,15 +133,6 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink) result += len + 1; } break; - case TextCommandColor: { - const QChar *e = result + 4; - if (e >= end) return from; - - for (; result < e; ++result) { - if (result->unicode() >= 256) return from; - } - } break; - case TextCommandSkipBlock: result += 2; break; @@ -216,13 +194,13 @@ public: } lastSkipped = false; if (emoji) { - _t->_blocks.push_back(new EmojiBlock(_t->_font, _t->_text, blockStart, len, flags, color, lnkIndex, emoji)); + _t->_blocks.push_back(new EmojiBlock(_t->_font, _t->_text, blockStart, len, flags, lnkIndex, emoji)); emoji = 0; lastSkipped = true; } else if (newline) { _t->_blocks.push_back(new NewlineBlock(_t->_font, _t->_text, blockStart, len)); } else { - _t->_blocks.push_back(new TextBlock(_t->_font, _t->_text, _t->_minResizeWidth, blockStart, len, flags, color, lnkIndex)); + _t->_blocks.push_back(new TextBlock(_t->_font, _t->_text, _t->_minResizeWidth, blockStart, len, flags, lnkIndex)); } blockStart += len; blockCreated(); @@ -451,24 +429,9 @@ public: lnkIndex = 0x8000 + links.size(); } break; - case TextCommandColor: { - style::color c(ptr->unicode(), (ptr + 1)->unicode(), (ptr + 2)->unicode(), (ptr + 3)->unicode()); - if (color != c) { - createBlock(); - color = c; - } - } break; - case TextCommandSkipBlock: createSkipBlock(ptr->unicode(), (ptr + 1)->unicode()); break; - - case TextCommandNoColor: - if (color) { - createBlock(); - color = style::color(); - } - break; } ptr = afterCmd; @@ -747,7 +710,6 @@ private: int32 diacs; // diac chars skipped without good char QFixed sumWidth, stopAfterWidth; // summary width of all added words bool sumFinished, newlineAwaited; - style::color color; // current color, could be invalid // current char data QChar ch; // current char (low surrogate, if current char is surrogate pair) @@ -1177,9 +1139,6 @@ public: } const QPen &blockPen(ITextBlock *block) { - if (block->color()) { - return block->color()->p; - } if (block->lnkIndex()) { if (ClickHandler::showAsPressed(_t->_links.at(block->lnkIndex() - 1))) { return _textStyle->linkFgDown->p; @@ -1581,7 +1540,7 @@ public: _elideSavedIndex = blockIndex; _elideSavedBlock = _t->_blocks[blockIndex]; - const_cast(_t)->_blocks[blockIndex] = new TextBlock(_t->_font, _t->_text, QFIXED_MAX, elideStart, 0, _elideSavedBlock->flags(), _elideSavedBlock->color(), _elideSavedBlock->lnkIndex()); + const_cast(_t)->_blocks[blockIndex] = new TextBlock(_t->_font, _t->_text, QFIXED_MAX, elideStart, 0, _elideSavedBlock->flags(), _elideSavedBlock->lnkIndex()); _blocksSize = blockIndex + 1; _endBlock = (blockIndex + 1 < _t->_blocks.size() ? _t->_blocks[blockIndex + 1] : 0); } diff --git a/Telegram/SourceFiles/ui/text/text.h b/Telegram/SourceFiles/ui/text/text.h index 57783e788..70d31dbf9 100644 --- a/Telegram/SourceFiles/ui/text/text.h +++ b/Telegram/SourceFiles/ui/text/text.h @@ -38,8 +38,6 @@ enum TextCommands { TextCommandNoSemibold = 0x08, TextCommandLinkIndex = 0x09, // 0 - NoLink TextCommandLinkText = 0x0A, - TextCommandColor = 0x0B, - TextCommandNoColor = 0x0C, TextCommandSkipBlock = 0x0D, TextCommandLangTag = 0x20, @@ -279,8 +277,6 @@ QString textcmdStartLink(const QString &url); QString textcmdStopLink(); QString textcmdLink(ushort lnkIndex, const QString &text); QString textcmdLink(const QString &url, const QString &text); -QString textcmdStartColor(const style::color &color); -QString textcmdStopColor(); QString textcmdStartSemibold(); QString textcmdStopSemibold(); const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true); diff --git a/Telegram/SourceFiles/ui/text/text_block.cpp b/Telegram/SourceFiles/ui/text/text_block.cpp index d8e7696ce..96a3a66b0 100644 --- a/Telegram/SourceFiles/ui/text/text_block.cpp +++ b/Telegram/SourceFiles/ui/text/text_block.cpp @@ -300,7 +300,7 @@ QFixed ITextBlock::f_rbearing() const { return (type() == TextBlockTText) ? static_cast(this)->real_f_rbearing() : 0; } -TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex) : ITextBlock(font, str, from, length, flags, color, lnkIndex) { +TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, uint16 lnkIndex) : ITextBlock(font, str, from, length, flags, lnkIndex) { _flags |= ((TextBlockTText & 0x0F) << 8); if (length) { style::font blockFont = font; @@ -343,12 +343,12 @@ TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResi } } -EmojiBlock::EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex, const EmojiData *emoji) : ITextBlock(font, str, from, length, flags, color, lnkIndex), emoji(emoji) { +EmojiBlock::EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex, const EmojiData *emoji) : ITextBlock(font, str, from, length, flags, lnkIndex), emoji(emoji) { _flags |= ((TextBlockTEmoji & 0x0F) << 8); _width = int(st::emojiSize + 2 * st::emojiPadding); } -SkipBlock::SkipBlock(const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex) : ITextBlock(font, str, from, 1, 0, style::color(), lnkIndex), _height(h) { +SkipBlock::SkipBlock(const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex) : ITextBlock(font, str, from, 1, 0, lnkIndex), _height(h) { _flags |= ((TextBlockTSkip & 0x0F) << 8); _width = w; } diff --git a/Telegram/SourceFiles/ui/text/text_block.h b/Telegram/SourceFiles/ui/text/text_block.h index 63bf7de6f..094202836 100644 --- a/Telegram/SourceFiles/ui/text/text_block.h +++ b/Telegram/SourceFiles/ui/text/text_block.h @@ -41,8 +41,7 @@ enum TextBlockFlags { class ITextBlock { public: - - ITextBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex) : _from(from), _flags((flags & 0xFF) | ((lnkIndex & 0xFFFF) << 12))/*, _color(color)*/, _lpadding(0) { + ITextBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex) : _from(from), _flags((flags & 0xFF) | ((lnkIndex & 0xFFFF) << 12)), _lpadding(0) { if (length) { if (str.at(_from + length - 1).unicode() == QChar::Space) { _rpadding = font->spacew; @@ -91,10 +90,6 @@ public: int32 flags() const { return (_flags & 0xFF); } - const style::color &color() const { - static style::color tmp; - return tmp;//_color; - } virtual ITextBlock *clone() const = 0; virtual ~ITextBlock() { @@ -121,7 +116,7 @@ public: } private: - NewlineBlock(const style::font &font, const QString &str, uint16 from, uint16 length) : ITextBlock(font, str, from, length, 0, st::windowTextFg, 0), _nextDir(Qt::LayoutDirectionAuto) { + NewlineBlock(const style::font &font, const QString &str, uint16 from, uint16 length) : ITextBlock(font, str, from, length, 0, 0), _nextDir(Qt::LayoutDirectionAuto) { _flags |= ((TextBlockTNewline & 0x0F) << 8); } @@ -175,7 +170,7 @@ public: private: - TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex); + TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, uint16 lnkIndex); friend class ITextBlock; QFixed real_f_rbearing() const { @@ -201,7 +196,7 @@ public: private: - EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, const style::color &color, uint16 lnkIndex, const EmojiData *emoji); + EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex, const EmojiData *emoji); const EmojiData *emoji; diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp index 5deaa9e05..01e74ab87 100644 --- a/Telegram/SourceFiles/window/window_theme.cpp +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -307,6 +307,90 @@ QImage prepareBackgroundImage(QImage &&image) { return std_::move(image); } +void initColorsFromBackground(const QImage &img) { + uint64 components[3] = { 0 }, componentsScroll[3] = { 0 }; + auto w = img.width(); + auto h = img.height(); + auto size = w * h; + if (auto pix = img.constBits()) { + for (int i = 0, l = size * 4; i != l; i += 4) { + components[2] += pix[i + 0]; + components[1] += pix[i + 1]; + components[0] += pix[i + 2]; + } + } + + if (size) { + for (int i = 0; i != 3; ++i) { + components[i] /= size; + } + } + int maxtomin[3] = { 0, 1, 2 }; + if (components[maxtomin[0]] < components[maxtomin[1]]) { + qSwap(maxtomin[0], maxtomin[1]); + } + if (components[maxtomin[1]] < components[maxtomin[2]]) { + qSwap(maxtomin[1], maxtomin[2]); + if (components[maxtomin[0]] < components[maxtomin[1]]) { + qSwap(maxtomin[0], maxtomin[1]); + } + } + + uint64 max = qMax(1ULL, components[maxtomin[0]]), mid = qMax(1ULL, components[maxtomin[1]]), min = qMax(1ULL, components[maxtomin[2]]); + + memcpy(componentsScroll, components, sizeof(components)); + + if (max != min) { + if (min > uint64(qRound(0.77 * max))) { + uint64 newmin = qRound(0.77 * max); // min saturation 23% + uint64 newmid = max - ((max - mid) * (max - newmin)) / (max - min); + components[maxtomin[1]] = newmid; + components[maxtomin[2]] = newmin; + } + uint64 newmin = qRound(0.77 * max); // saturation 23% for scroll + uint64 newmid = max - ((max - mid) * (max - newmin)) / (max - min); + componentsScroll[maxtomin[1]] = newmid; + componentsScroll[maxtomin[2]] = newmin; + } + + float64 luminance = 0.299 * componentsScroll[0] + 0.587 * componentsScroll[1] + 0.114 * componentsScroll[2]; + uint64 maxScroll = max; + if (luminance < 0.5 * 0xFF) { + maxScroll += qRound(0.2 * 0xFF); + } else { + maxScroll -= qRound(0.2 * 0xFF); + } + componentsScroll[maxtomin[2]] = qMin(uint64(float64(componentsScroll[maxtomin[2]]) * maxScroll / float64(componentsScroll[maxtomin[0]])), 0xFFULL); + componentsScroll[maxtomin[1]] = qMin(uint64(float64(componentsScroll[maxtomin[1]]) * maxScroll / float64(componentsScroll[maxtomin[0]])), 0xFFULL); + componentsScroll[maxtomin[0]] = qMin(maxScroll, 0xFFULL); + + if (max > uint64(qRound(0.2 * 0xFF))) { // brightness greater than 20% + max -= qRound(0.2 * 0xFF); + } else { + max = 0; + } + components[maxtomin[2]] = uint64(float64(components[maxtomin[2]]) * max / float64(components[maxtomin[0]])); + components[maxtomin[1]] = uint64(float64(components[maxtomin[1]]) * max / float64(components[maxtomin[0]])); + components[maxtomin[0]] = max; + + uchar r = uchar(components[0]); + uchar g = uchar(components[1]); + uchar b = uchar(components[2]); + st::msgServiceBg.set(r, g, b, st::msgServiceBg->c.alpha()); + + float64 alphaSel = st::msgServiceSelectBg->c.alphaF(), addSel = (1. - ((1. - alphaSel) / (1. - st::msgServiceBg->c.alphaF()))) * 0xFF; + uchar rsel = snap(qRound(((1. - alphaSel) * r + addSel) / alphaSel), 0, 0xFF); + uchar gsel = snap(qRound(((1. - alphaSel) * g + addSel) / alphaSel), 0, 0xFF); + uchar bsel = snap(qRound(((1. - alphaSel) * b + addSel) / alphaSel), 0, 0xFF); + st::msgServiceSelectBg.set(r, g, b, qRound(alphaSel * 0xFF)); + + uchar rScroll = uchar(componentsScroll[0]), gScroll = uchar(componentsScroll[1]), bScroll = uchar(componentsScroll[2]); + st::historyScroll.barColor.set(rScroll, gScroll, bScroll, st::historyScroll.barColor->c.alpha()); + st::historyScroll.bgColor.set(rScroll, gScroll, bScroll, st::historyScroll.bgColor->c.alpha()); + st::historyScroll.barOverColor.set(rScroll, gScroll, bScroll, st::historyScroll.barOverColor->c.alpha()); + st::historyScroll.bgOverColor.set(rScroll, gScroll, bScroll, st::historyScroll.bgOverColor->c.alpha()); +} + } // namespace void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) { @@ -354,7 +438,7 @@ void ChatBackground::setImage(int32 id, QImage &&image) { } void ChatBackground::setPreparedImage(QImage &&image) { - App::initColorsFromBackground(image); + initColorsFromBackground(image); _image = App::pixmapFromImageInPlace(std_::move(image)); } diff --git a/Telegram/build/version b/Telegram/build/version index b115c2711..121e18f19 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 0 +BetaVersion 10019001 From 1466338e7e027107fb870f58a1a127dc97ac0d11 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 3 Nov 2016 13:58:10 +0300 Subject: [PATCH 012/100] Closed beta 10019001: Build fixed for Xcode. --- Telegram/SourceFiles/boxes/addcontactbox.cpp | 26 ++++++++----------- Telegram/SourceFiles/boxes/addcontactbox.h | 10 +++---- Telegram/SourceFiles/core/zlib_help.h | 6 ++--- .../profile/profile_inner_widget.h | 2 +- .../settings/settings_inner_widget.h | 2 +- Telegram/SourceFiles/ui/style/style_core.cpp | 2 +- Telegram/gyp/Telegram.gyp | 2 ++ Telegram/gyp/refresh.sh | 2 +- 8 files changed, 25 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index d529be97c..9cf16de1e 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -36,12 +36,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_dialogs.h" AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : AbstractBox(st::boxWidth) -, _save(this, lang(lng_add_contact), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton) , _first(this, st::defaultInputField, lang(lng_signup_firstname), fname) , _last(this, st::defaultInputField, lang(lng_signup_lastname), lname) , _phone(this, st::defaultInputField, lang(lng_contact_phone), phone) +, _save(this, lang(lng_add_contact), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton) , _invertOrder(langFirstNameGoesSecond()) { if (!phone.isEmpty()) { _phone->setDisabled(true); @@ -52,12 +52,12 @@ AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : Abst AddContactBox::AddContactBox(UserData *user) : AbstractBox(st::boxWidth) , _user(user) -, _save(this, lang(lng_settings_save), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) -, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton) , _first(this, st::defaultInputField, lang(lng_signup_firstname), user->firstName) , _last(this, st::defaultInputField, lang(lng_signup_lastname), user->lastName) , _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone()) +, _save(this, lang(lng_settings_save), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) +, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton) , _invertOrder(langFirstNameGoesSecond()) { _phone->setDisabled(true); initBox(); @@ -966,12 +966,11 @@ bool SetupChannelBox::onFirstCheckFail(const RPCError &error) { EditNameTitleBox::EditNameTitleBox(PeerData *peer) : _peer(peer), -_save(this, lang(lng_settings_save), st::defaultBoxButton), -_cancel(this, lang(lng_cancel), st::cancelBoxButton), _first(this, st::defaultInputField, lang(peer->isUser() ? lng_signup_firstname : lng_dlg_new_group_name), peer->isUser() ? peer->asUser()->firstName : peer->name), _last(this, st::defaultInputField, lang(lng_signup_lastname), peer->isUser() ? peer->asUser()->lastName : QString()), -_invertOrder(!peer->isChat() && langFirstNameGoesSecond()), -_requestId(0) { +_save(this, lang(lng_settings_save), st::defaultBoxButton), +_cancel(this, lang(lng_cancel), st::cancelBoxButton), +_invertOrder(!peer->isChat() && langFirstNameGoesSecond()) { if (_invertOrder) { setTabOrder(_last, _first); } @@ -1141,15 +1140,12 @@ void EditNameTitleBox::onSaveChatDone(const MTPUpdates &updates) { EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox() , _channel(channel) -, _save(this, lang(lng_settings_save), st::defaultBoxButton) -, _cancel(this, lang(lng_cancel), st::cancelBoxButton) , _title(this, st::defaultInputField, lang(lng_dlg_new_channel_name), _channel->name) , _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about()) , _sign(this, lang(lng_edit_sign_messages), channel->addsSignature(), st::defaultBoxCheckbox) , _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::defaultBoxLinkButton) -, _saveTitleRequestId(0) -, _saveDescriptionRequestId(0) -, _saveSignRequestId(0) { +, _save(this, lang(lng_settings_save), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(peerUpdated(PeerData*))); setMouseTracking(true); diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index 8ae48c95f..d38ca6095 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -268,9 +268,9 @@ private: ChildWidget _save; ChildWidget _cancel; - bool _invertOrder; + bool _invertOrder = false; - mtpRequestId _requestId; + mtpRequestId _requestId = 0; QString _sentName; }; @@ -318,9 +318,9 @@ private: ChildWidget _save; ChildWidget _cancel; - mtpRequestId _saveTitleRequestId; - mtpRequestId _saveDescriptionRequestId; - mtpRequestId _saveSignRequestId; + mtpRequestId _saveTitleRequestId = 0; + mtpRequestId _saveDescriptionRequestId = 0; + mtpRequestId _saveSignRequestId = 0; QString _sentTitle, _sentDescription; diff --git a/Telegram/SourceFiles/core/zlib_help.h b/Telegram/SourceFiles/core/zlib_help.h index 9e0604515..1198e7539 100644 --- a/Telegram/SourceFiles/core/zlib_help.h +++ b/Telegram/SourceFiles/core/zlib_help.h @@ -243,7 +243,7 @@ public: } auto size = fileInfo.uncompressed_size; - if (size > fileSizeLimit) { + if (size > static_cast(fileSizeLimit)) { if (_error == UNZ_OK) _error = -1; LOG(("Error: current file is too large (should be less than %1, got %2) in a zip file.").arg(fileSizeLimit).arg(size)); return QByteArray(); @@ -257,7 +257,7 @@ public: result.resize(size); auto couldRead = readCurrentFile(result.data(), size); - if (couldRead != size) { + if (couldRead != static_cast(size)) { LOG(("Error: could not read current file in a zip file, got %1.").arg(couldRead)); return QByteArray(); } @@ -273,7 +273,7 @@ public: QByteArray readFileContent(const char *szFileName, int iCaseSensitivity, int fileSizeLimit) { if (locateFile(szFileName, iCaseSensitivity) != UNZ_OK) { LOG(("Error: could not locate '%1' in a zip file.").arg(szFileName)); - return false; + return QByteArray(); } return readCurrentFileContent(fileSizeLimit); } diff --git a/Telegram/SourceFiles/profile/profile_inner_widget.h b/Telegram/SourceFiles/profile/profile_inner_widget.h index 64cc9b4a7..8a6f33180 100644 --- a/Telegram/SourceFiles/profile/profile_inner_widget.h +++ b/Telegram/SourceFiles/profile/profile_inner_widget.h @@ -41,7 +41,7 @@ public: } // Updates the area that is visible inside the scroll container. - void setVisibleTopBottom(int visibleTop, int visibleBottom); + void setVisibleTopBottom(int visibleTop, int visibleBottom) override; // Profile fixed top bar should use this flag to decide // if it shows "Share contact" button or not. diff --git a/Telegram/SourceFiles/settings/settings_inner_widget.h b/Telegram/SourceFiles/settings/settings_inner_widget.h index a14cccf39..0a5fcf557 100644 --- a/Telegram/SourceFiles/settings/settings_inner_widget.h +++ b/Telegram/SourceFiles/settings/settings_inner_widget.h @@ -38,7 +38,7 @@ public: } // Updates the area that is visible inside the scroll container. - void setVisibleTopBottom(int visibleTop, int visibleBottom); + void setVisibleTopBottom(int visibleTop, int visibleBottom) override; void showFinished(); diff --git a/Telegram/SourceFiles/ui/style/style_core.cpp b/Telegram/SourceFiles/ui/style/style_core.cpp index d43e1ff0c..2e45ed8bd 100644 --- a/Telegram/SourceFiles/ui/style/style_core.cpp +++ b/Telegram/SourceFiles/ui/style/style_core.cpp @@ -101,7 +101,7 @@ void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel; auto resultInts = reinterpret_cast(outResult->bits()) + dstPoint.y() * resultIntsPerLine + dstPoint.x() * resultIntsPerPixel; t_assert(resultIntsAdded >= 0); - t_assert(outResult->depth() == ((resultIntsPerPixel * sizeof(uint32)) << 3)); + t_assert(outResult->depth() == static_cast((resultIntsPerPixel * sizeof(uint32)) << 3)); t_assert(outResult->bytesPerLine() == (resultIntsPerLine << 2)); auto maskBytesPerPixel = (src.depth() >> 3); diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 9c27e5302..c3448f815 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -619,6 +619,8 @@ '<(minizip_loc)/ioapi.h', '<(minizip_loc)/zip.c', '<(minizip_loc)/zip.h', + '<(minizip_loc)/unzip.c', + '<(minizip_loc)/unzip.h', ], 'sources!': [ '<(src_loc)/pspecific_win.cpp', diff --git a/Telegram/gyp/refresh.sh b/Telegram/gyp/refresh.sh index c076c08f0..4bc35fb48 100755 --- a/Telegram/gyp/refresh.sh +++ b/Telegram/gyp/refresh.sh @@ -20,7 +20,7 @@ else #gyp --depth=. --generator-output=../.. -Goutput_dir=out Telegram.gyp --format=xcode-ninja #gyp --depth=. --generator-output=../.. -Goutput_dir=out Telegram.gyp --format=xcode # use patched gyp with Xcode project generator - ../../../Libraries/gyp/gyp --depth=. --generator-output=../.. -Goutput_dir=out Telegram.gyp -Gxcode_upgrade_check_project_version=800 --format=xcode + ../../../Libraries/gyp/gyp --depth=. --generator-output=../.. -Goutput_dir=out Telegram.gyp -Gxcode_upgrade_check_project_version=810 --format=xcode fi cd ../.. From 1ecd6866c75b23583fe983ca6ec9345f46d5c7b9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 3 Nov 2016 14:42:56 +0300 Subject: [PATCH 013/100] Closed beta 10019001: improved style apply for FlatButton. --- Telegram/SourceFiles/ui/flatbutton.cpp | 22 +++++++++++++++++----- Telegram/SourceFiles/ui/flatbutton.h | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 17a9f7466..0b83cb649 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -28,8 +28,7 @@ FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatBu , _st(st) , a_bg(st.bgColor->c) , a_text(st.color->c) -, _a_appearance(animation(this, &FlatButton::step_appearance)) -, _opacity(1) { +, _a_appearance(animation(this, &FlatButton::step_appearance)) { if (_st.width < 0) { _width = textWidth() - _st.width; } else if (!_st.width) { @@ -104,20 +103,33 @@ void FlatButton::paintEvent(QPaintEvent *e) { QRect r(0, height() - _st.height, width(), _st.height); + auto animating = _a_appearance.animating(); + auto &bg = (_state & StateOver) ? ((_state & StateDown) ? _st.downBgColor : _st.overBgColor) : _st.bgColor; + auto &fg = (_state & StateOver) ? ((_state & StateDown) ? _st.downColor : _st.overColor) : _st.color; p.setOpacity(_opacity); if (_st.radius > 0) { p.setRenderHint(QPainter::HighQualityAntialiasing); p.setPen(Qt::NoPen); - p.setBrush(QBrush(a_bg.current())); + if (animating) { + p.setBrush(a_bg.current()); + } else { + p.setBrush(bg); + } p.drawRoundedRect(r, _st.radius, _st.radius); p.setRenderHint(QPainter::HighQualityAntialiasing, false); - } else { + } else if (animating) { p.fillRect(r, a_bg.current()); + } else { + p.fillRect(r, bg); } p.setFont((_state & StateOver) ? _st.overFont : _st.font); p.setRenderHint(QPainter::TextAntialiasing); - p.setPen(a_text.current()); + if (animating) { + p.setPen(a_text.current()); + } else { + p.setPen(fg); + } int32 top = (_state & StateOver) ? ((_state & StateDown) ? _st.downTextTop : _st.overTextTop) : _st.textTop; r.setTop(top); diff --git a/Telegram/SourceFiles/ui/flatbutton.h b/Telegram/SourceFiles/ui/flatbutton.h index 83670ec64..a4bcc3148 100644 --- a/Telegram/SourceFiles/ui/flatbutton.h +++ b/Telegram/SourceFiles/ui/flatbutton.h @@ -54,7 +54,7 @@ private: anim::cvalue a_bg, a_text; Animation _a_appearance; - float64 _opacity; + float64 _opacity = 1.; }; From 0cbb0014db6318e86bf2f5186c4aa033f8099ffe Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 4 Nov 2016 11:23:50 +0300 Subject: [PATCH 014/100] Removed blue window title, new small title used only in Windows. --- Telegram/Resources/basic.style | 101 +---- Telegram/Resources/basic_types.style | 8 - Telegram/Resources/colors.palette | 16 +- Telegram/Resources/icons/box_button_close.png | Bin 151 -> 148 bytes .../Resources/icons/box_button_close@2x.png | Bin 283 -> 269 bytes .../Resources/icons/title_button_close.png | Bin 169 -> 164 bytes .../Resources/icons/title_button_close@2x.png | Bin 264 -> 224 bytes .../Resources/icons/title_button_maximize.png | Bin 124 -> 119 bytes .../icons/title_button_maximize@2x.png | Bin 165 -> 144 bytes .../Resources/icons/title_button_minimize.png | Bin 109 -> 100 bytes .../icons/title_button_minimize@2x.png | Bin 147 -> 122 bytes .../Resources/icons/title_button_restore.png | Bin 144 -> 136 bytes .../icons/title_button_restore@2x.png | Bin 185 -> 160 bytes .../Resources/icons/title_button_update.png | Bin 142 -> 0 bytes .../icons/title_button_update@2x.png | Bin 218 -> 0 bytes Telegram/Resources/icons/title_icon.png | Bin 265 -> 0 bytes Telegram/Resources/icons/title_icon@2x.png | Bin 401 -> 0 bytes Telegram/Resources/icons/title_icon_bg.png | Bin 363 -> 0 bytes Telegram/Resources/icons/title_icon_bg@2x.png | Bin 701 -> 0 bytes Telegram/Resources/sample.tdesktop-theme | 11 +- Telegram/SourceFiles/app.cpp | 1 - Telegram/SourceFiles/boxes/boxes.style | 4 +- Telegram/SourceFiles/boxes/passcodebox.cpp | 1 - Telegram/SourceFiles/core/lambda_wrap.h | 21 + Telegram/SourceFiles/core/parse_helper.h | 4 +- Telegram/SourceFiles/history/history.style | 2 + Telegram/SourceFiles/historywidget.cpp | 13 +- Telegram/SourceFiles/intro/introwidget.cpp | 12 +- Telegram/SourceFiles/intro/introwidget.h | 1 - Telegram/SourceFiles/layerwidget.cpp | 3 +- Telegram/SourceFiles/layerwidget.h | 2 + Telegram/SourceFiles/mainwidget.cpp | 53 +-- Telegram/SourceFiles/mainwidget.h | 3 +- Telegram/SourceFiles/mainwindow.cpp | 385 +++++++--------- Telegram/SourceFiles/mainwindow.h | 51 +-- .../media/player/media_player.style | 14 - .../player/media_player_title_button.cpp | 122 ----- .../media/player/media_player_widget.cpp | 2 +- Telegram/SourceFiles/overviewwidget.cpp | 5 +- Telegram/SourceFiles/passcodewidget.cpp | 7 - Telegram/SourceFiles/passcodewidget.h | 14 +- .../platform/linux/main_window_linux.cpp | 15 +- .../platform/linux/main_window_linux.h | 5 +- .../linux/notifications_manager_linux.h | 13 - .../platform/mac/main_window_mac.h | 5 +- .../platform/mac/main_window_mac.mm | 35 +- .../platform/mac/notifications_manager_mac.h | 14 - .../platform/platform_file_dialog.h | 28 +- .../platform/platform_notifications_manager.h | 18 + .../platform/platform_window_title.h | 35 ++ .../platform/win/main_window_win.cpp | 26 +- .../platform/win/main_window_win.h | 2 - .../platform/win/notifications_manager_win.h | 9 - .../platform/win/window_title_win.cpp | 106 +++++ .../platform/win/window_title_win.h | 63 +++ .../platform/win/windows_event_filter.cpp | 62 +-- .../SourceFiles/profile/profile_cover.cpp | 3 +- .../profile/profile_inner_widget.cpp | 3 +- Telegram/SourceFiles/pspecific.h | 4 - Telegram/SourceFiles/pspecific_mac_p.mm | 2 +- .../settings/settings_scale_widget.cpp | 1 - .../SourceFiles/settings/settings_widget.cpp | 15 +- .../SourceFiles/settings/settings_widget.h | 2 +- Telegram/SourceFiles/structs.cpp | 12 +- Telegram/SourceFiles/sysbuttons.cpp | 147 ------ Telegram/SourceFiles/sysbuttons.h | 102 ----- Telegram/SourceFiles/title.cpp | 427 ------------------ Telegram/SourceFiles/title.h | 98 ---- Telegram/SourceFiles/window/window.style | 62 ++- Telegram/SourceFiles/window/window_theme.cpp | 77 ++-- .../window_title.h} | 51 +-- Telegram/gyp/Telegram.gyp | 11 +- 72 files changed, 726 insertions(+), 1583 deletions(-) delete mode 100644 Telegram/Resources/icons/title_button_update.png delete mode 100644 Telegram/Resources/icons/title_button_update@2x.png delete mode 100644 Telegram/Resources/icons/title_icon.png delete mode 100644 Telegram/Resources/icons/title_icon@2x.png delete mode 100644 Telegram/Resources/icons/title_icon_bg.png delete mode 100644 Telegram/Resources/icons/title_icon_bg@2x.png delete mode 100644 Telegram/SourceFiles/media/player/media_player_title_button.cpp create mode 100644 Telegram/SourceFiles/platform/platform_window_title.h create mode 100644 Telegram/SourceFiles/platform/win/window_title_win.cpp create mode 100644 Telegram/SourceFiles/platform/win/window_title_win.h delete mode 100644 Telegram/SourceFiles/sysbuttons.cpp delete mode 100644 Telegram/SourceFiles/sysbuttons.h delete mode 100644 Telegram/SourceFiles/title.cpp delete mode 100644 Telegram/SourceFiles/title.h rename Telegram/SourceFiles/{media/player/media_player_title_button.h => window/window_title.h} (61%) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 4da47a1fb..5b3a6aa48 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -34,17 +34,6 @@ emojiPadding: 0px; lineWidth: 1px; -wndMinWidth: 380px; - -adaptiveNormalWidth: 640px; -adaptiveWideWidth: 1366px; - -wndMinHeight: 480px; -wndDefWidth: 800px; -wndDefHeight: 600px; -wndShadow: icon {{ "window_shadow", windowShadowFg }}; -wndShadowShift: 1px; - labelDefFlat: flatLabel { font: font(fsize); width: 0px; @@ -277,20 +266,11 @@ boxScroll: flatScroll(solidScroll) { boxScrollSkip: 6px; boxScrollShadowBg: #00000012; -titleHeight: 39px; -titleFont: font(17px); -titlePos: point(44px, 29px); -titleMenuOffset: 36px; - -titleRed: #ee4928; -titleGray: #777777; -titleGreen: #41a903; - -titleStatusFg: #999999; -titleStatusActiveFg: #0080c0; -titleTypingFg: #0080c0; - statusFont: font(fsize); +statusFg: #999999; +statusFgActive: #0080c0; +statusFgTyping: statusFgActive; + versionColor: #777777; shadowColor: #00000018; @@ -302,63 +282,11 @@ slideFadeOutBg: #0000003c; slideShadow: icon {{ "slide_shadow", #000000 }}; slideFunction: transition(easeOutCirc); -titleButtonFg: #c4d8e9; -titleButtonActiveFg: #ffffff; -titleButtonDuration: 150; -sysBtnDelta: 6px; -sysUpd: sysButton { - size: size(31px, 39px); - icon: icon {{ "title_button_update", titleBg }}; - color: titleButtonFg; - overColor: titleButtonActiveFg; - duration: titleButtonDuration; -} -updateBlinkDuration: 500; -sysMin: sysButton(sysUpd) { - icon: icon {{ "title_button_minimize", titleBg }}; -} -sysMax: sysButton(sysUpd) { - icon: icon {{ "title_button_maximize", titleBg }}; -} -sysRes: sysButton(sysUpd) { - icon: icon {{ "title_button_restore", titleBg }}; -} -sysCls: sysButton(sysUpd) { - icon: icon {{ "title_button_close", titleBg }}; -} -sysLock: sysButton(sysUpd) { - icon: icon {{ "title_button_lock", titleBg }}; -} -sysUnlock: sysButton(sysUpd) { - icon: icon {{ "title_button_unlock", titleBg }}; -} - btnYesColor: #0080c0; btnYesHover: #0073ad; btnNoColor: #8b8b8b; btnNoHover: #777777; -titleTextButton: flatButton { - color: #d4e3ef; - overColor: #ffffff; - downColor: #ffffff; - bgColor: transparent; - overBgColor: transparent; - downBgColor: transparent; - - width: -14px; - height: 39px; - - textTop: 10px; - overTextTop: 10px; - downTextTop: 11px; - - font: font(fsize); - overFont: font(fsize); - duration: 150; - cursor: cursor(default); -} - linkCropLimit: 360px; linkFont: normalFont; linkOverFont: font(fsize underline); @@ -919,8 +847,8 @@ confirmCompressedSkip: 10px; profileMaxWidth: 410px; profilePadding: margins(28px, 30px, 28px, 0px); -profileOnlineFg: titleStatusActiveFg; -profileOfflineFg: titleStatusFg; +profileOnlineFg: statusFgActive; +profileOfflineFg: statusFg; membersPadding: margins(0px, 10px, 0px, 10px); @@ -1064,17 +992,6 @@ medviewSaveMsgHiding: 2500; medviewSaveMsgFg: #ffffff; medviewSaveMsg: #000000b2; -// Mac specific - -macAccessoryWidth: 450.; -macAccessoryHeight: 90.; -macEnableFilterAdd: 2; -macEnableFilterTop: 5; -macSelectorTop: 6; -macAlwaysThisAppTop: 4; -macAppHintTop: 8; -macCautionIconSize: 16; - radialSize: size(50px, 50px); radialLine: 3px; radialDuration: 350; @@ -1191,9 +1108,9 @@ infoButton: PeerAvatarButton { profileTopBarBackIconFg: #0290d7; profileTopBarBackIcon: icon {{ "title_previous", profileTopBarBackIconFg }}; -historyReplyCancelIcon: icon {{ "box_button_close", historyReplyBg }}; -boxSearchCancelIcon: icon {{ "box_button_close", boxSearchBg }}; -settingsFixedBarCloseIcon: icon {{ "box_button_close", settingsFixedBarBg }}; +historyReplyCancelIcon: icon {{ "box_button_close-invert", historyReplyBg }}; +boxSearchCancelIcon: icon {{ "box_button_close-invert", boxSearchBg }}; +settingsFixedBarCloseIcon: icon {{ "box_button_close-invert", settingsFixedBarBg }}; notifyFadeRight: icon {{ "fade_horizontal_right", notificationBg }}; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index c0d17c4c4..2ca21038a 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -38,14 +38,6 @@ linkButton { overFont: font; } -sysButton { - size: size; - icon: icon; - color: color; - overColor: color; - duration: int; -} - flatButton { color: color; overColor: color; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index ddc62dd09..c449b8ed8 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -29,11 +29,18 @@ windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over windowActiveTextFg: #1485c2; // online blue: fallback for active color windowShadowFg: #000000; // black: fallback for shadow color -titleBg: #6389a8; - imageBg: #000000; imageBgTransparent: #ffffff; +// custom title bar for Windows +titleBg: #f3f3f3; +titleShadow: #00000003; +titleButtonFg: #ababab; +titleButtonBgOver: #e5e5e5; +titleButtonFgOver: #9a9a9a; +titleButtonCloseBgOver: #e81123; +titleButtonCloseFgOver: #ffffff; + // tray icon trayCounterBg: #f23c34; trayCounterBgMute: #888888; @@ -56,11 +63,6 @@ lightButtonBgOver: #f2f7fa | lightButtonBg; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; -// window title -titleCounterBg: trayCounterBg; -titleCounterBgMute: trayCounterBgMute; -titleCounterFg: trayCounterFg; - // layers layerBg: #0000007f; diff --git a/Telegram/Resources/icons/box_button_close.png b/Telegram/Resources/icons/box_button_close.png index 57abcfb8230614a33ff15da39cb1c991dad2ebf7..3b0402893ab0c64450b8d081ba33aa55f8cc5e01 100644 GIT binary patch delta 119 zcmV--0EqvW0h9rdByd|vL_t(2k)2V&4FE9+B3;A%pYGuzum< Zg$E&69LDP^wuk@#002ovPDHLkV1nvzF}MH# delta 122 zcmV-=0EPdQ0ha-gBy(O#L_t(2k)4sj4Ztu61Fej4lQ0MSf0@I5^pjLgD(WZlfdOTv z0e=Z_W?FzH9iiEyXW?YFeFHn_t){8jV%SYxfEn6F9O~+Kq}I}{vvwahB0_+wYP)Au c2~b4n0wttN3IP`wjQ{`u07*qoM6N<$f~ib0-T(jq diff --git a/Telegram/Resources/icons/box_button_close@2x.png b/Telegram/Resources/icons/box_button_close@2x.png index bf2a19e72c50b6a1294447c938a480d0b3e21a18..fea726bdbee71641f170e4b68cfa3ae5365fba56 100644 GIT binary patch delta 241 zcmVNh2crJb1pLXePPSBh`ZMXX{!}+22D%i zM$P{v3IP!jswx4{%qSw4u9Ol1P*vMKRb?rKBw1^{grt;MYb8mRQW^q)>oJS0b^zJRr^1SM(M^RR%s6- zxKjQDlb8sAyQ8X@8Bed?JF1Gi10e2BtrZcWl=2Aay;Dj-M5wjm?ue=)BFB9fd5`b6 zsv>4~dXPm%@x37cPmom{cma2jtM~_yMI!MN5JjT#A#jCc;&@YFg=EKR3e1q~Oj`mg zBsz_MLGDjr3K8%{JH zK~7s{Hoihs^>fp|8y_LZrVp=UOKa^b#}+fAwRRj^egW=g9&exM6_o%0002ovPDHLk FV1oJ|cMt#o diff --git a/Telegram/Resources/icons/title_button_close.png b/Telegram/Resources/icons/title_button_close.png index 0686fc88a97cb28d60edc3bafd7acf156ec6026f..22cfe520c4a6e3b0da93e628ac6a63dbdfbd3ff8 100644 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^{2(?58;~rMGjRk`*`6+rAsQ36PCCeYK!JzZ_o&Ft z`u;Nqnc@!D25x0hoV99NMMBZ0NdXKTp0be!2j1~by6iNm;QNEs@{=a1OuF^aFvjU2 zQ_lwuodcG8CjM|P+Vy<<<#pfZZoS69koN7y(wU7VRCwgTe~DWM4fa(F?q literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S1|*9D%+3I*0#6sm5R2ZoQv$gT7;reh?OR&j z+5Y11$$z&)qbD3p_-XP)QOj)76s6eE1^oxl_}sDO*|~eh&76XO_j8q;7#6!uV%hLT zJwl|z!TUzA0dwmH!3m8)H}c%ymxY;a-jtixxY#=QYKa-cidDgTC;sQQy2ATvU1#O` S3Ql97-3*?telF{r5}E*q7ePt@ diff --git a/Telegram/Resources/icons/title_button_close@2x.png b/Telegram/Resources/icons/title_button_close@2x.png index f598165f0c13974284966f4e82287f8e98d1d98e..e43b4d8a7f57ad85cab66305bb7f748d60fcbf96 100644 GIT binary patch literal 224 zcmV<603ZK}P)J-q9X2TZf}TBYdSdg=YtDpgEik-bCl%E8qq? z4@dY7bPc}3H&B+HTryhKYn5zu$;(ypIh7nBB}Zo2B~f-Qc05^iSgM+#Ji7kreCI#c aPr@fwnMp8>+vjos0000a9?KzKt*W(q_zaoMtBY?+Df~&S~4ch~Yr)`ak=_Uw(RAUKz8) zCLwm4%5>j5Pj5Uf3n;OU;-8Z;ef5DRE1s9bbW7WAJqK Kb6Mw<&;$T)TyC5I diff --git a/Telegram/Resources/icons/title_button_maximize.png b/Telegram/Resources/icons/title_button_maximize.png index a3522751f8107677d1aa615d2bf4223db402f1c6..2759b3f41fb4546fd9657d29a75ff0376a61f91a 100644 GIT binary patch literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XA8<1SE`<)7qvh#Ft4AGdFoRX06w QK$93eUHx3vIVCg!0O)}s5dZ)H literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S1|*9D%+3HQXHOT$5R2aAKmY&#uV>R@RLhby z*}7njLI#baSWcWelF{r5}E)c#U-Es diff --git a/Telegram/Resources/icons/title_button_maximize@2x.png b/Telegram/Resources/icons/title_button_maximize@2x.png index 037bb98de5b2d88646c3b0fe4e251da6211930ec..00daaae190fcb9c1850a3310d52ce1d8b17e11ca 100644 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ey!3HF+&5pANQsJI1jv*eMZ>Je@9WdZw_Fwq_ z|0e5HPSXmGj1?xgocSUx!~!I?&go}oc<@pxw0yDR%guS^3Jkeyex=55OwLZY+sMG8 rz`!Vxf9Ur9$J2nkj+mqM>=le|%G@S}fK!Jzh;<6wA z_s{W*<#Nmm2;Q-MmyBab&zUo;7yd4BIBIYT2(v=C1vjcLuyVWYcgA4Vo#geQ;V*Oe zx6hrCI0Xq_`C_~6_1phn_jk_y$C71jcvu4nSFm4K;0-YCJ^ux0FN3G6pUXO@geCwr C%tJf? diff --git a/Telegram/Resources/icons/title_button_minimize.png b/Telegram/Resources/icons/title_button_minimize.png index 229cb7dc5eb9938a22d0ba6c9f59bbf28226ce98..85934ddde82b3cd1145635cb7d27990c40a2d6ca 100644 GIT binary patch literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XA8<1SE`<)7q()M(54AGdFoRX06R@RLhby z$-1#3adBJVRRggUK4oR)lWYppg|`$-E?s)@K`+B9DK-X=N-pJnAG3Oa`WZZ3{an^L HB{Ts5^V=Xs diff --git a/Telegram/Resources/icons/title_button_minimize@2x.png b/Telegram/Resources/icons/title_button_minimize@2x.png index ddfc4713a245d00daaca421ff95f2911329c8330..d70a497345422f53019806ba8e63117d01e31278 100644 GIT binary patch literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ey!3HF+&5pANQjVT3jv*eMZ?A3SWia4iK3G2G zZ@3zFi=L3{JRz?6D`XZ(%rTYJb7){-WMbhEVA!zR`ReUOQ=aUS|KHG%p#4v_fWb0@ V)mTvD=VG8?44$rjF6*2UngCEZBTxVU literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^SqC8z3Ln2z=-q^^?puoWzxWDlK z)Iz4N>?x1pyo(h>maNL+-BQn9!m}7V^fUMPq_UuzrRVO9#E|Ob{qr4?&C?`Ofv}X3 Yxrxhj^YdLiKwB6*UHx3vIVCg!07t?u&Hw-a diff --git a/Telegram/Resources/icons/title_button_restore.png b/Telegram/Resources/icons/title_button_restore.png index 4ad86be0e4ef5d5a8c682ba1f8d5a737a74833e3..c9f276b51732c3288cbfd0f6fcb37689c993d295 100644 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XA8<1SE`<)7q^7nLc4AGdlb+R{?gCY;J`lG+= zW5N$Pzh-}QdQ)qr(~5=z0cYC;)0e3*bQRaWTyWXA_uZ?kLhd?w#oe2V)2)9jZ=HLr kL-@h6-t})d4*h9mOA8iM_L-zo4m6s<)78&qol`;+0BFE5bpQYW literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S1|*9D%+3I*a8DP<5R2aAKmY&#uV>R@RLhcd zDLowV!PCgdNXUf2a>|S6MkTW^GVVJ4Qk-$gGAWV#Nm379G;9-e@M(DbiSgi#BWwkc s78RR?bCnxrJaL?oI7KYC+58NHkeHI-x9WdZ=Dt+@W z{MqIyVf(F!t!yGE!*kUIn2XrsKm>;9&`;gwlW3Q+Npm;amC7jp^PvS%t^16s-8>FVdQ I&MBb@0A_(UdjJ3c literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^S8a!PbLn2z=&N#?*z<}fEvlsvW zZ?b+Hp^~)n(CNJbO{J;N-u*LqvR8M5bCO7BVA!_>GpBOAndWpmNoets^f&6iA9kc| z$u;Z@ykuPVE6`~DjhAQTE^eD~MC0MwHMicBq&*i~-83yf+6Sar#f|fO#8vHPpsKg` h({j5#dz1`MvAfNY3@vRw#SL@|gQu&X%Q~loCIIJjPqY94 diff --git a/Telegram/Resources/icons/title_button_update.png b/Telegram/Resources/icons/title_button_update.png deleted file mode 100644 index b7dc595fb273ab1ca1777c6741b09ea520f4c5bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S1|*9D%+3I*P)`@f5R2aAKmY&#uV>R@RLhby zF=#V1G736wFvIbPM8|c55@rs`j>`ryQXeHdt{OP$9Z)^rf}D9MA#=Pgg&ebxsLQ0MvjhRR910 diff --git a/Telegram/Resources/icons/title_button_update@2x.png b/Telegram/Resources/icons/title_button_update@2x.png deleted file mode 100644 index bf26691ea3c4e977e90a2619ee0e90a70211e3bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^S7J9lkhD5Z!y=KVAY{=nqv2Y7} z({K5_ZN7r)JPJpnK4x>ko&@ z?qGIj+&%Aqc$vAu*2a}!gPDV^#l-IEm4$Bah&{gV#(^tr%ghY6N>__sI-~eiSrX`2 N22WQ%mvv4FO#r4ZPW1o) diff --git a/Telegram/Resources/icons/title_icon.png b/Telegram/Resources/icons/title_icon.png deleted file mode 100644 index 12772e6a3ae3111478ba0af28580d8f14f4f43b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmV+k0rvihP)ST6a>&qB=Vf-mYNZuUUF7@Ndl&64$i^3W3$yv&XnB_`%Z7S}%Vk+c-}kYqDx&g<*Y+1u zRTYCEz@}-a%U{DV%rJC39`Sy^FDma6MbYpP$g;c&&}FyVeLCyMj@C$B*JB^miOF$4g3y2z8j+VA&0NRp)WAppQI44+6@ vmNaYWAfD%+5W(G<4*>wOEQ`9XQ?2<0Hp`?04t7E<00000NkvXXu0mjfYPPbN diff --git a/Telegram/Resources/icons/title_icon_bg.png b/Telegram/Resources/icons/title_icon_bg.png deleted file mode 100644 index ebdf862916dd6905c371cfd96bed344a17dd5675..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 363 zcmV-x0hIoUP)L!ReYlGEt~ z*L7c`VHo0mzb^vE+-^7czCVwKVfYa?P2cxeuh-8gW9;J&e@AN#$8jF$`F#EpIOcdf zexpJNBj6a%^I*ksOeVA2?a10}Hb&9yc1x?i?_t}vF|=tKS^ diff --git a/Telegram/Resources/icons/title_icon_bg@2x.png b/Telegram/Resources/icons/title_icon_bg@2x.png deleted file mode 100644 index 33924433d23e67484eb4a4b2dbdf83e4b870bd22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 701 zcmV;u0z&=q zzOOC&!7R(7uIm^CK`QQ6K@gy>>&UX~^PvpGV5if;Fbs18FAKvEJDmiK}jGpV9(#sG>sVYn5Jnz zLj^&=BuPj+F-Z~>1mSxqP1EQ&4r${l$8jzn4!>S>yIs<@t9rfOIkZ?TKJ>JCRY4G7 zu~wHg!<5lKU`M3Dw$?LYL{{X5Y8eoxwv+Tm~jYOz?5 zcBD3+&w+AXm$V}_*L8s!jYgy$sf|V>D3r@(+-x?aEk9PNRDkk*AKv+@+O`e8?*jk; zHX04me>-p1Xf)2(OylvGwCSqhaCp7A69j>@>#B<-^Y68@Wm%+cr!335J%C_Y7Cp}+ zZA8!WzMG$(k4{8U#3+i0A)_clQ53Tc$y8M(GTKR!1XWeNZ|tZj3dV8#*N|}>qoOGJ z2Dv=X<8rxtIdZvNBG2>rC(iIS?lDdCWk}OBPh*>hpnu+ElgZ?1z{zBC>!x`OdfX&Q z!hXM>>z5&lA{-0`C`r;?=L7&O9JTkxFbv4D43Z=P$8k`tR)OQVvtwM>h4p$3uIs{d jItAOdVY}VtIURfgG(+updateConnectingStatus(); - w->getTitle()->updateControlsVisibility(); } return true; } diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 5c6f5e84a..1852efe0c 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -37,7 +37,7 @@ boxBlueClose: MaskButton(defaultMaskButton) { iconBg: #c8e1f0; iconBgOver: #ffffff; - icon: icon {{ "box_button_close", boxBlueTitleBg }}; + icon: icon {{ "box_button_close-invert", boxBlueTitleBg }}; } confirmInviteTitle: flatLabel(labelDefFlat) { @@ -198,7 +198,7 @@ notificationsBoxMonitorTop: 63px; notificationsBoxMonitor: icon {{ "monitor", notificationsBoxMonitorFg }}; notificationsBoxScreenTop: 10px; notificationsBoxScreenSize: size(280px, 160px); -notificationsBoxScreenBg: titleBg; +notificationsBoxScreenBg: #6389a8; notificationsBoxCountLabelTop: 80px; notificationsBoxCountTop: 30px; diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp index 8f8decb21..6340da7fc 100644 --- a/Telegram/SourceFiles/boxes/passcodebox.cpp +++ b/Telegram/SourceFiles/boxes/passcodebox.cpp @@ -405,7 +405,6 @@ void PasscodeBox::onSave(bool force) { cSetPasscodeBadTries(0); Local::setPasscode(pwd.toUtf8()); App::wnd()->checkAutoLock(); - App::wnd()->getTitle()->updateControlsVisibility(); onClose(); } } diff --git a/Telegram/SourceFiles/core/lambda_wrap.h b/Telegram/SourceFiles/core/lambda_wrap.h index 3e211781d..cd1a15559 100644 --- a/Telegram/SourceFiles/core/lambda_wrap.h +++ b/Telegram/SourceFiles/core/lambda_wrap.h @@ -394,4 +394,25 @@ public: }; +class lambda_slot_wrap : public QObject { + Q_OBJECT + +public: + lambda_slot_wrap(QObject *parent, lambda_unique lambda) : QObject(parent), _lambda(std_::move(lambda)) { + } + +public slots: + void action() { + _lambda(); + } + +private: + lambda_unique _lambda; + +}; + +inline lambda_slot_wrap *lambda_slot(QObject *parent, lambda_unique lambda) { + return new lambda_slot_wrap(parent, std_::move(lambda)); +} + } // namespace base diff --git a/Telegram/SourceFiles/core/parse_helper.h b/Telegram/SourceFiles/core/parse_helper.h index 6284baaf6..9c7006d04 100644 --- a/Telegram/SourceFiles/core/parse_helper.h +++ b/Telegram/SourceFiles/core/parse_helper.h @@ -38,7 +38,7 @@ inline bool skipWhitespaces(const char *&from, const char *end) { return (from != end); } -inline QByteArray readName(const char *&from, const char *end) { +inline QLatin1String readName(const char *&from, const char *end) { t_assert(from <= end); auto start = from; while (from != end && ( @@ -48,7 +48,7 @@ inline QByteArray readName(const char *&from, const char *end) { (*from == '_'))) { ++from; } - return QByteArray::fromRawData(start, from - start); + return QLatin1String(start, from - start); } } // namespace parse diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index a5486dc6d..6988e7ff9 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -36,6 +36,8 @@ historyToDownPaddingTop: 10px; historyToDownBadgeFont: semiboldFont; historyToDownBadgeSize: 22px; +historyToDownShownAfter: 480px; + historyEmptyDog: icon {{ "history_empty_dog", #ffffff }}; historyEmptySize: 128px; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index a7714d59f..b28f1914f 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2701,7 +2701,6 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString void HistoryHider::init() { connect(&_send, SIGNAL(clicked()), this, SLOT(forward())); connect(&_cancel, SIGNAL(clicked()), this, SLOT(startHide())); - connect(App::wnd()->getTitle(), SIGNAL(hiderClicked()), this, SLOT(startHide())); _chooseWidth = st::forwardFont->width(lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose)); @@ -2720,7 +2719,6 @@ void HistoryHider::step_appearance(float64 ms, bool timer) { } else { a_opacity.update(dt, anim::linear); } - App::wnd()->getTitle()->setHideLevel(a_opacity.current()); if (timer) update(); } @@ -2746,7 +2744,7 @@ void HistoryHider::paintEvent(QPaintEvent *e) { _toText.drawElided(p, _box.left() + st::boxPadding.left(), _box.top() + st::boxPadding.top(), _toTextWidth + 2); } else { int32 w = st::forwardMargins.left() + _chooseWidth + st::forwardMargins.right(), h = st::forwardMargins.top() + st::forwardFont->height + st::forwardMargins.bottom(); - App::roundRect(p, (width() - w) / 2, (height() - st::titleHeight - h) / 2, w, h, st::forwardBg, ForwardCorners); + App::roundRect(p, (width() - w) / 2, (height() - h) / 2, w, h, st::forwardBg, ForwardCorners); p.setPen(st::forwardFg); p.drawText(_box, lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose), QTextOption(style::al_center)); @@ -2835,7 +2833,7 @@ void HistoryHider::resizeEvent(QResizeEvent *e) { _send.hide(); _cancel.hide(); } - _box = QRect((width() - w) / 2, (height() - st::titleHeight - h) / 2, w, h); + _box = QRect((width() - w) / 2, (height() - h) / 2, w, h); _send.moveToRight(width() - (_box.x() + _box.width()) + st::boxButtonPadding.right(), _box.y() + h - st::boxButtonPadding.bottom() - _send.height()); _cancel.moveToRight(width() - (_box.x() + _box.width()) + st::boxButtonPadding.right() + _send.width() + st::boxButtonPadding.left(), _send.y()); } @@ -2909,7 +2907,6 @@ bool HistoryHider::wasOffered() const { HistoryHider::~HistoryHider() { if (_sendPath) cSetSendPaths(QStringList()); - if (App::wnd()) App::wnd()->getTitle()->setHideLevel(0); parent()->noHider(this); } @@ -6269,10 +6266,10 @@ void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { QRect rectForName(st::topBarArrowPadding.right() + increaseLeft, st::topBarArrowPadding.top(), width() - decreaseWidth - st::topBarArrowPadding.left() - st::topBarArrowPadding.right(), st::msgNameFont->height); p.setFont(st::dialogsTextFont); if (_history->typing.isEmpty() && _history->sendActions.isEmpty()) { - p.setPen(_titlePeerTextOnline ? st::titleStatusActiveFg : st::titleStatusFg); + p.setPen(_titlePeerTextOnline ? st::statusFgActive : st::statusFg); p.drawText(rectForName.x(), st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height + st::dialogsTextFont->ascent, _titlePeerText); } else { - p.setPen(st::titleTypingFg); + p.setPen(st::statusFgTyping); _history->typingText.drawElided(p, rectForName.x(), st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height, rectForName.width()); } @@ -7394,7 +7391,7 @@ void HistoryWidget::updateToEndVisibility() { if (!_history->loadedAtBottom() || _replyReturn) { return true; } - if (_scroll.scrollTop() + st::wndMinHeight < _scroll.scrollTopMax()) { + if (_scroll.scrollTop() + st::historyToDownShownAfter < _scroll.scrollTopMax()) { return true; } if (haveUnreadBelowBottom(_history) || haveUnreadBelowBottom(_migrated)) { diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 630fe9cb1..5a225e54a 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -40,8 +40,6 @@ IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent) , _a_stage(animation(this, &IntroWidget::step_stage)) , _a_show(animation(this, &IntroWidget::step_show)) , _back(this, new Ui::IconButton(this, st::introBackButton), base::lambda_unique(), st::introSlideDuration) { - setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); - _back->entity()->setClickedCallback([this] { onBack(); }); _back->hideFast(); @@ -52,8 +50,6 @@ IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent) _stepHistory.push_back(new IntroStart(this)); _back->raise(); - connect(parent, SIGNAL(resized(const QSize&)), this, SLOT(onParentResize(const QSize&))); - show(); setFocus(); @@ -76,10 +72,6 @@ void IntroWidget::onChangeLang() { App::restart(); } -void IntroWidget::onParentResize(const QSize &newSize) { - resize(newSize); -} - void IntroWidget::onStepSubmit() { step()->onSubmit(); } @@ -339,8 +331,8 @@ bool IntroWidget::codeByTelegram() const { } void IntroWidget::resizeEvent(QResizeEvent *e) { - QRect r(innerRect()); - for (IntroStep *step : _stepHistory) { + auto r = innerRect(); + for (auto step : _stepHistory) { step->setGeometry(r); } } diff --git a/Telegram/SourceFiles/intro/introwidget.h b/Telegram/SourceFiles/intro/introwidget.h index e14d255ce..9837539f4 100644 --- a/Telegram/SourceFiles/intro/introwidget.h +++ b/Telegram/SourceFiles/intro/introwidget.h @@ -92,7 +92,6 @@ public: public slots: void onStepSubmit(); void onBack(); - void onParentResize(const QSize &newSize); void onChangeLang(); signals: diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index f22513bcb..f625b1f49 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -294,8 +294,7 @@ void LayerStackWidget::showLayer(LayerWidget *l) { void LayerStackWidget::showSpecialLayer(LayerWidget *l) { clearLayers(); if (_specialLayer) { - _specialLayer->hide(); - _specialLayer->deleteLater(); + _specialLayer.destroyDelayed(); } _specialLayer = l; activateLayer(l); diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index 8e0585547..b44b4bc32 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -26,6 +26,8 @@ class LayerWidget : public TWidget { Q_OBJECT public: + using TWidget::TWidget; + virtual void parentResized() = 0; virtual void showDone() { } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index b2a776fd1..b9749d72f 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -67,7 +67,7 @@ StackItemSection::StackItemSection(std_::unique_ptr &&me StackItemSection::~StackItemSection() { } -MainWidget::MainWidget(MainWindow *window) : TWidget(window) +MainWidget::MainWidget(QWidget *parent) : TWidget(parent) , _a_show(animation(this, &MainWidget::step_show)) , _dialogsWidth(st::dialogsWidthMin) , _sideShadow(this, st::shadowColor) @@ -78,13 +78,10 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window) , _playerPanel(this, Media::Player::Panel::Layout::Full) , _mediaType(this, st::historyAttachDropdownMenu) , _api(new ApiWrap(this)) { - setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); - MTP::setGlobalDoneHandler(rpcDone(&MainWidget::updateReceived)); _ptsWaiter.setRequesting(true); updateScrollColors(); - connect(App::wnd(), SIGNAL(resized(const QSize&)), this, SLOT(onParentResize(const QSize&))); connect(_dialogs, SIGNAL(cancelled()), this, SLOT(dialogsCancelled())); connect(_history, SIGNAL(cancelled()), _dialogs, SLOT(activate())); connect(this, SIGNAL(peerPhotoChanged(PeerData*)), this, SIGNAL(dialogsUpdated())); @@ -156,7 +153,6 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window) } else { _history->show(); } - App::wnd()->getTitle()->updateControlsVisibility(); _topBar->hide(); orderWidgets(); @@ -614,7 +610,6 @@ void MainWidget::noHider(HistoryHider *destroyed) { _history->showAnimated(Window::SlideDirection::FromRight, animationParams); } } - App::wnd()->getTitle()->updateControlsVisibility(); } else { if (_forwardConfirm) { _forwardConfirm->deleteLater(); @@ -651,7 +646,6 @@ void MainWidget::hiderLayer(HistoryHider *h) { resizeEvent(0); _dialogs->showAnimated(Window::SlideDirection::FromLeft, animationParams); } - App::wnd()->getTitle()->updateControlsVisibility(); } else { _hider->show(); resizeEvent(0); @@ -1588,6 +1582,9 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) { if (!_playerUsingPanel && !_player && Media::Player::exists()) { createPlayer(); } + } else if (_player && _player->isHidden() && !_playerUsingPanel) { + _player.destroyDelayed(); + _playerVolume.destroyDelayed(); } } @@ -1632,8 +1629,12 @@ void MainWidget::switchToFixedPlayer() { } void MainWidget::closeBothPlayers() { - _playerUsingPanel = false; - _player.destroyDelayed(); + if (_playerUsingPanel) { + _playerUsingPanel = false; + _player.destroyDelayed(); + } else { + _player->slideUp(); + } _playerVolume.destroyDelayed(); if (Media::Player::exists()) { @@ -1651,7 +1652,7 @@ void MainWidget::closeBothPlayers() { void MainWidget::createPlayer() { _player.create(this, [this] { playerHeightUpdated(); }); - _player->entity()->setCloseCallback([this] { switchToPanelPlayer(); }); + _player->entity()->setCloseCallback([this] { closeBothPlayers(); }); _playerVolume.create(this); _player->entity()->volumeWidgetCreated(_playerVolume); orderWidgets(); @@ -1660,9 +1661,11 @@ void MainWidget::createPlayer() { _player->hide(); } else { _player->hideFast(); - _player->slideDown(); - _playerHeight = _contentScrollAddToY = _player->contentHeight(); - updateControlsGeometry(); + if (_player) { + _player->slideDown(); + _playerHeight = _contentScrollAddToY = _player->contentHeight(); + updateControlsGeometry(); + } } Shortcuts::enableMediaShortcuts(); @@ -1675,9 +1678,13 @@ void MainWidget::playerHeightUpdated() { _playerHeight = playerHeight; updateControlsGeometry(); } - if (_playerUsingPanel && !_playerHeight && _player->isHidden()) { - _playerVolume.destroyDelayed(); - _player.destroyDelayed(); + if (!_playerHeight && _player->isHidden()) { + AudioMsgId playing; + auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); + if (playing && (playbackState.state & AudioPlayerStoppedMask)) { + _playerVolume.destroyDelayed(); + _player.destroyDelayed(); + } } } @@ -1768,10 +1775,6 @@ void MainWidget::mediaMarkRead(const HistoryItemsMap &items) { } } -void MainWidget::onParentResize(const QSize &newSize) { - resize(newSize); -} - void MainWidget::updateOnlineDisplay() { if (this != App::main()) return; _history->updateOnlineDisplay(); @@ -2216,7 +2219,6 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show _dialogs->update(); } topBar()->showAll(); - App::wnd()->getTitle()->updateControlsVisibility(); } PeerData *MainWidget::ui_getPeerForMouseAction() { @@ -2337,8 +2339,6 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool if (Adaptive::OneColumn()) _dialogs->hide(); orderWidgets(); - - App::wnd()->getTitle()->updateControlsVisibility(); } void MainWidget::showWideSection(const Window::SectionMemento &memento) { @@ -2465,8 +2465,6 @@ void MainWidget::showWideSectionAnimated(const Window::SectionMemento *memento, if (Adaptive::OneColumn()) _dialogs->hide(); orderWidgets(); - - App::wnd()->getTitle()->updateControlsVisibility(); } bool MainWidget::stackIsEmpty() const { @@ -3412,7 +3410,6 @@ void MainWidget::getDifference() { _getDifferenceTimeByPts = 0; - LOG(("Getting difference! no updates timer: %1, remains: %2").arg(noUpdatesTimer.isActive() ? 1 : 0).arg(noUpdatesTimer.remainingTime())); if (requestingDifference()) return; _bySeqUpdates.clear(); @@ -3421,7 +3418,6 @@ void MainWidget::getDifference() { noUpdatesTimer.stop(); _getDifferenceTimeAfterFail = 0; - LOG(("Getting difference for %1, %2").arg(_ptsWaiter.current()).arg(updDate)); _ptsWaiter.setRequesting(true); MTP::send(MTPupdates_GetDifference(MTP_int(_ptsWaiter.current()), MTP_int(updDate), MTP_int(updQts)), rpcDone(&MainWidget::gotDifference), rpcFail(&MainWidget::failDifference)); } @@ -3433,14 +3429,12 @@ void MainWidget::getChannelDifference(ChannelData *channel, GetChannelDifference _channelGetDifferenceTimeByPts.remove(channel); } - LOG(("Getting channel difference!")); if (!channel->ptsInited() || channel->ptsRequesting()) return; if (from != GetChannelDifferenceFromFail) { _channelGetDifferenceTimeAfterFail.remove(channel); } - LOG(("Getting channel difference for %1").arg(channel->pts())); channel->ptsSetRequesting(true); auto filter = MTP_channelMessagesFilterEmpty(); @@ -3456,7 +3450,6 @@ void MainWidget::start(const MTPUser &user) { if (MTP::authedId() != uid) { MTP::setAuthedId(uid); Local::writeMtpData(); - App::wnd()->getTitle()->updateControlsVisibility(); } Local::readSavedPeers(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 60f7b6f6c..32cf8519b 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -139,7 +139,7 @@ class MainWidget : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: - MainWidget(MainWindow *window); + MainWidget(QWidget *parent); bool needBackButton(); @@ -426,7 +426,6 @@ public slots: void dialogsCancelled(); - void onParentResize(const QSize &newSize); void getDifference(); void onGetDifferenceTimeByPts(); void onGetDifferenceTimeAfterFail(); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 063248c52..83a2309b6 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -23,13 +23,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" +#include "styles/style_window.h" #include "ui/widgets/popup_menu.h" #include "core/zlib_help.h" #include "lang.h" #include "shortcuts.h" #include "application.h" #include "pspecific.h" -#include "title.h" #include "passcodewidget.h" #include "intro/introwidget.h" #include "mainwidget.h" @@ -44,11 +44,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "apiwrap.h" #include "settings/settings_widget.h" #include "platform/platform_notifications_manager.h" +#include "platform/platform_window_title.h" #include "window/notifications_manager.h" #include "window/window_theme.h" #include "window/window_theme_warning.h" -ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : QWidget(parent) +ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : TWidget(parent) , _shadow(st::boxShadow) , _reconnect(this, QString()) { set(text, reconnect); @@ -85,7 +86,7 @@ void ConnectingWidget::onReconnect() { MTP::restart(); } -MainWindow::MainWindow() { +MainWindow::MainWindow() : Platform::MainWindow(), _body(this) { icon16 = icon256.scaledToWidth(16, Qt::SmoothTransformation); icon32 = icon256.scaledToWidth(32, Qt::SmoothTransformation); icon64 = icon256.scaledToWidth(64, Qt::SmoothTransformation); @@ -107,12 +108,10 @@ MainWindow::MainWindow() { if (objectName().isEmpty()) { setObjectName(qsl("MainWindow")); } - resize(st::wndDefWidth, st::wndDefHeight); + resize(st::windowDefWidth, st::windowDefHeight); setLocale(QLocale(QLocale::English, QLocale::UnitedStates)); - centralwidget = new QWidget(this); - centralwidget->setObjectName(qsl("centralwidget")); - setCentralWidget(centralwidget); + setCentralWidget(_body); QMetaObject::connectSlotsByName(this); @@ -170,6 +169,8 @@ void MainWindow::init() { psInitFrameless(); setWindowIcon(wndIcon); + _title = Platform::CreateTitleWidget(this); + Application::instance()->installEventFilter(this); connect(windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onStateChanged(Qt::WindowState))); connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWindowActiveChanged()), Qt::QueuedConnection); @@ -178,9 +179,9 @@ void MainWindow::init() { p.setColor(QPalette::Window, st::windowBg->c); setPalette(p); - title = new TitleWidget(this); - - psInitSize(); + setMinimumWidth(st::windowMinWidth); + setMinimumHeight(st::windowMinHeight); + psInitSize(); } void MainWindow::onWindowActiveChanged() { @@ -224,38 +225,28 @@ QWidget *MainWindow::filedialogParent() { void MainWindow::clearWidgets() { Ui::hideLayer(true); - if (_passcode) { - _passcode->hide(); - _passcode->deleteLater(); - _passcode = 0; - } - if (main) { - delete main; - main = nullptr; - } - if (intro) { - intro->stop_show(); - intro->hide(); - intro->deleteLater(); - intro->rpcClear(); - intro = 0; + _passcode.destroyDelayed(); + _main.destroy(); + if (_intro) { + _intro->stop_show(); + _intro->rpcClear(); + _intro.destroyDelayed(); } if (_mediaView) { hideMediaview(); _mediaView->rpcClear(); } - title->updateControlsVisibility(); updateGlobalMenu(); } QPixmap MainWindow::grabInner() { QPixmap result; - if (settings) { - result = myGrab(settings); - } else if (intro) { - result = myGrab(intro); - } else if (main) { - result = myGrab(main); + if (_settings) { + result = myGrab(_settings); + } else if (_intro) { + result = myGrab(_intro); + } else if (_main) { + result = myGrab(_main); } else if (_passcode) { result = myGrab(_passcode); } @@ -268,20 +259,17 @@ void MainWindow::clearPasscode() { QPixmap bg = grabInner(); _passcode->stop_show(); - _passcode->hide(); - _passcode->deleteLater(); - _passcode = 0; - if (intro) { - intro->animShow(bg, true); + _passcode.destroyDelayed(); + if (_intro) { + _intro->animShow(bg, true); } else { - main->animShow(bg, true); + _main->animShow(bg, true); } notifyUpdateAll(); - title->updateControlsVisibility(); updateGlobalMenu(); - if (auto main = App::main()) { - main->checkStartUrl(); + if (_main) { + _main->checkStartUrl(); } } @@ -290,16 +278,16 @@ void MainWindow::setupPasscode(bool anim) { if (_passcode) { _passcode->stop_show(); - _passcode->hide(); - _passcode->deleteLater(); + _passcode.destroyDelayed(); } - _passcode = new PasscodeWidget(this); - _passcode->move(0, st::titleHeight); - if (main) main->hide(); - if (settings) { - settings->deleteLater(); + _passcode.create(_body); + updateControlsGeometry(); + + if (_main) _main->hide(); + if (_settings) { + _settings.destroyDelayed(); } - if (intro) intro->hide(); + if (_intro) _intro->hide(); if (anim) { _passcode->animShow(bg); } else { @@ -307,7 +295,6 @@ void MainWindow::setupPasscode(bool anim) { } _shouldLockAt = 0; notifyUpdateAll(); - title->updateControlsVisibility(); updateGlobalMenu(); } @@ -335,7 +322,7 @@ void MainWindow::checkAutoLock() { void MainWindow::setupIntro(bool anim) { cSetContactsReceived(false); cSetDialogsReceived(false); - if (intro && !intro->isHidden() && !main) return; + if (_intro && !_intro->isHidden() && !_main) return; if (_mediaView) { _mediaView->clearData(); @@ -345,10 +332,11 @@ void MainWindow::setupIntro(bool anim) { QPixmap bg = anim ? grabInner() : QPixmap(); clearWidgets(); - intro = new IntroWidget(this); - intro->move(0, st::titleHeight); + _intro.create(_body); + updateControlsGeometry(); + if (anim) { - intro->animShow(bg); + _intro->animShow(bg); } fixOrder(); @@ -363,13 +351,13 @@ void MainWindow::setupIntro(bool anim) { } void MainWindow::serviceNotification(const QString &msg, const MTPMessageMedia &media, bool force) { - History *h = (main && App::userLoaded(ServiceUserId)) ? App::history(ServiceUserId) : 0; + History *h = (_main && App::userLoaded(ServiceUserId)) ? App::history(ServiceUserId) : 0; if (!h || (!force && h->isEmpty())) { _delayedServiceMsgs.push_back(DelayedServiceMsg(msg, media)); return sendServiceHistoryRequest(); } - main->serviceNotification(msg, media); + _main->serviceNotification(msg, media); } void MainWindow::showDelayedServiceMsgs() { @@ -381,32 +369,32 @@ void MainWindow::showDelayedServiceMsgs() { } void MainWindow::sendServiceHistoryRequest() { - if (!main || !main->started() || _delayedServiceMsgs.isEmpty() || _serviceHistoryRequest) return; + if (!_main || !_main->started() || _delayedServiceMsgs.isEmpty() || _serviceHistoryRequest) return; UserData *user = App::userLoaded(ServiceUserId); if (!user) { MTPDuser::Flags userFlags = MTPDuser::Flag::f_first_name | MTPDuser::Flag::f_phone | MTPDuser::Flag::f_status | MTPDuser::Flag::f_verified; user = App::feedUsers(MTP_vector(1, MTP_user(MTP_flags(userFlags), MTP_int(ServiceUserId), MTPlong(), MTP_string("Telegram"), MTPstring(), MTPstring(), MTP_string("42777"), MTP_userProfilePhotoEmpty(), MTP_userStatusRecently(), MTPint(), MTPstring(), MTPstring()))); } - _serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(1), MTP_int(0), MTP_int(0)), main->rpcDone(&MainWidget::serviceHistoryDone), main->rpcFail(&MainWidget::serviceHistoryFail)); + _serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(1), MTP_int(0), MTP_int(0)), _main->rpcDone(&MainWidget::serviceHistoryDone), _main->rpcFail(&MainWidget::serviceHistoryFail)); } void MainWindow::setupMain(bool anim, const MTPUser *self) { QPixmap bg = anim ? grabInner() : QPixmap(); clearWidgets(); - main = new MainWidget(this); - main->move(0, st::titleHeight); + _main.create(_body); + updateControlsGeometry(); + if (anim) { - main->animShow(bg); + _main->animShow(bg); } else { - main->activate(); + _main->activate(); } if (self) { - main->start(*self); + _main->start(*self); } else { - MTP::send(MTPusers_GetUsers(MTP_vector(1, MTP_inputUserSelf())), main->rpcDone(&MainWidget::startFull)); + MTP::send(MTPusers_GetUsers(MTP_vector(1, MTP_inputUserSelf())), _main->rpcDone(&MainWidget::startFull)); } - title->updateControlsVisibility(); fixOrder(); @@ -416,7 +404,6 @@ void MainWindow::setupMain(bool anim, const MTPUser *self) { void MainWindow::updateUnreadCounter() { if (!Global::started() || App::quitting()) return; - title->updateCounter(); psUpdateCounter(); } @@ -425,22 +412,22 @@ void MainWindow::showSettings() { if (isHidden()) showFromTray(); - if (settings) { + if (_settings) { Ui::hideSettingsAndLayer(); return; } - if (!layerBg) { - layerBg = new LayerStackWidget(this); + if (!_layerBg) { + _layerBg.create(_body); } - settings = new Settings::Widget(); - connect(settings, SIGNAL(destroyed(QObject*)), this, SLOT(onSettingsDestroyed(QObject*))); - layerBg->showSpecialLayer(settings); + _settings.create(this); + connect(_settings, SIGNAL(destroyed(QObject*)), this, SLOT(onSettingsDestroyed(QObject*))); + _layerBg->showSpecialLayer(_settings); } void MainWindow::ui_hideSettingsAndLayer(ShowLayerOptions options) { - if (layerBg) { - layerBg->onClose(); + if (_layerBg) { + _layerBg->onClose(); } } @@ -454,7 +441,7 @@ void MainWindow::mtpStateChanged(int32 dc, int32 state) { void MainWindow::updateConnectingStatus() { int32 state = MTP::dcstate(); if (state == MTP::ConnectingState || state == MTP::DisconnectedState || (state < 0 && state > -600)) { - if (main || getms() > 5000 || _connecting) { + if (_main || getms() > 5000 || _connecting) { showConnecting(lang(lng_connecting)); } } else if (state < 0) { @@ -466,11 +453,11 @@ void MainWindow::updateConnectingStatus() { } IntroWidget *MainWindow::introWidget() { - return intro; + return _intro; } MainWidget *MainWindow::mainWidget() { - return main; + return _main; } PasscodeWidget *MainWindow::passcodeWidget() { @@ -504,31 +491,29 @@ void MainWindow::showDocument(DocumentData *doc, HistoryItem *item) { void MainWindow::ui_showLayer(LayerWidget *box, ShowLayerOptions options) { if (box) { - if (!layerBg) { - layerBg = new LayerStackWidget(this); + if (!_layerBg) { + _layerBg.create(_body); } if (options.testFlag(KeepOtherLayers)) { if (options.testFlag(ShowAfterOtherLayers)) { - layerBg->prependLayer(box); + _layerBg->prependLayer(box); } else { - layerBg->appendLayer(box); + _layerBg->appendLayer(box); } } else { - layerBg->showLayer(box); + _layerBg->showLayer(box); } if (options.testFlag(ForceFastShowLayer)) { - layerBg->showFast(); + _layerBg->showFast(); } } else { - if (layerBg) { - if (settings) { - layerBg->onCloseLayers(); + if (_layerBg) { + if (_settings) { + _layerBg->onCloseLayers(); } else { - layerBg->onClose(); + _layerBg->onClose(); if (options.testFlag(ForceFastShowLayer)) { - layerBg->hide(); - layerBg->deleteLater(); - layerBg = nullptr; + _layerBg.destroyDelayed(); } } } @@ -537,7 +522,7 @@ void MainWindow::ui_showLayer(LayerWidget *box, ShowLayerOptions options) { } bool MainWindow::ui_isLayerShown() { - return !!layerBg; + return _layerBg != nullptr; } bool MainWindow::ui_isMediaViewShown() { @@ -547,7 +532,7 @@ bool MainWindow::ui_isMediaViewShown() { void MainWindow::ui_showMediaPreview(DocumentData *document) { if (!document || ((!document->isAnimation() || !document->loaded()) && !document->sticker())) return; if (!_mediaPreview) { - _mediaPreview = std_::make_unique(this); + _mediaPreview.create(_body); updateControlsGeometry(); } if (_mediaPreview->isHidden()) { @@ -559,7 +544,7 @@ void MainWindow::ui_showMediaPreview(DocumentData *document) { void MainWindow::ui_showMediaPreview(PhotoData *photo) { if (!photo) return; if (!_mediaPreview) { - _mediaPreview = std_::make_unique(this); + _mediaPreview.create(_body); updateControlsGeometry(); } if (_mediaPreview->isHidden()) { @@ -576,8 +561,8 @@ void MainWindow::ui_hideMediaPreview() { PeerData *MainWindow::ui_getPeerForMouseAction() { if (_mediaView && !_mediaView->isHidden()) { return _mediaView->ui_getPeerForMouseAction(); - } else if (main) { - return main->ui_getPeerForMouseAction(); + } else if (_main) { + return _main->ui_getPeerForMouseAction(); } return nullptr; } @@ -586,8 +571,7 @@ void MainWindow::showConnecting(const QString &text, const QString &reconnect) { if (_connecting) { _connecting->set(text, reconnect); } else { - _connecting.create(this, text, reconnect); - _connecting->show(); + _connecting.create(_body, text, reconnect); updateControlsGeometry(); fixOrder(); } @@ -602,33 +586,31 @@ void MainWindow::hideConnecting() { void MainWindow::themeUpdated(const Window::Theme::BackgroundUpdate &data) { using Type = Window::Theme::BackgroundUpdate::Type; if (data.type == Type::TestingTheme) { + if (_title) _title->update(); if (!_testingThemeWarning) { - _testingThemeWarning.create(this); + _testingThemeWarning.create(_body); _testingThemeWarning->setGeometry(rect()); _testingThemeWarning->setHiddenCallback([this] { _testingThemeWarning.destroyDelayed(); }); } _testingThemeWarning->showAnimated(); } else if (data.type == Type::RevertingTheme || data.type == Type::ApplyingTheme) { + if (_title) _title->update(); _testingThemeWarning->hideAnimated(); } } bool MainWindow::doWeReadServerHistory() const { - return isActive(false) && main && !Ui::isLayerShown() && main->doWeReadServerHistory(); + return isActive(false) && _main && !Ui::isLayerShown() && _main->doWeReadServerHistory(); } void MainWindow::checkHistoryActivation() { - if (main && MTP::authedId() && doWeReadServerHistory()) { - main->markActiveHistoryAsRead(); + if (_main && MTP::authedId() && doWeReadServerHistory()) { + _main->markActiveHistoryAsRead(); } } void MainWindow::layerHidden() { - if (layerBg) { - layerBg->hide(); - layerBg->deleteLater(); - } - layerBg = nullptr; + _layerBg.destroyDelayed(); hideMediaview(); setInnerFocus(); } @@ -658,85 +640,61 @@ void MainWindow::hideMediaview() { } bool MainWindow::contentOverlapped(const QRect &globalRect) { - if (main && main->contentOverlapped(globalRect)) return true; - if (layerBg && layerBg->contentOverlapped(globalRect)) return true; + if (_main && _main->contentOverlapped(globalRect)) return true; + if (_layerBg && _layerBg->contentOverlapped(globalRect)) return true; return false; } void MainWindow::setInnerFocus() { if (_testingThemeWarning) { _testingThemeWarning->setFocus(); - } else if (layerBg && layerBg->canSetFocus()) { - layerBg->setInnerFocus(); + } else if (_layerBg && _layerBg->canSetFocus()) { + _layerBg->setInnerFocus(); } else if (_passcode) { _passcode->setInnerFocus(); - } else if (settings) { - settings->setInnerFocus(); - } else if (main) { - main->setInnerFocus(); + } else if (_settings) { + _settings->setInnerFocus(); + } else if (_main) { + _main->setInnerFocus(); } } -QRect MainWindow::clientRect() const { - return QRect(0, st::titleHeight, width(), height() - st::titleHeight); -} - -QRect MainWindow::photoRect() const { - if (settings) { - return settings->geometry(); - } else if (main) { - QRect r(main->historyRect()); - r.moveLeft(r.left() + main->x()); - r.moveTop(r.top() + main->y()); - return r; - } - return QRect(0, 0, 0, 0); -} - -void MainWindow::wStartDrag(QMouseEvent *e) { - dragStart = e->globalPos() - frameGeometry().topLeft(); - dragging = true; -} - -void MainWindow::paintEvent(QPaintEvent *e) { -} - -HitTestType MainWindow::hitTest(const QPoint &p) const { +Window::HitTestResult MainWindow::hitTest(const QPoint &p) const { int x(p.x()), y(p.y()), w(width()), h(height()); const int32 raw = psResizeRowWidth(); if (!windowState().testFlag(Qt::WindowMaximized)) { if (y < raw) { if (x < raw) { - return HitTestType::TopLeft; + return Window::HitTestResult::TopLeft; } else if (x > w - raw - 1) { - return HitTestType::TopRight; + return Window::HitTestResult::TopRight; } - return HitTestType::Top; + return Window::HitTestResult::Top; } else if (y > h - raw - 1) { if (x < raw) { - return HitTestType::BottomLeft; + return Window::HitTestResult::BottomLeft; } else if (x > w - raw - 1) { - return HitTestType::BottomRight; + return Window::HitTestResult::BottomRight; } - return HitTestType::Bottom; + return Window::HitTestResult::Bottom; } else if (x < raw) { - return HitTestType::Left; + return Window::HitTestResult::Left; } else if (x > w - raw - 1) { - return HitTestType::Right; + return Window::HitTestResult::Right; } } - auto titleTest = title->hitTest(p - title->geometry().topLeft()); - if (titleTest != HitTestType::None) { + auto titleTest = _title ? _title->hitTest(p - _title->geometry().topLeft()) : Window::HitTestResult::None; + if (titleTest != Window::HitTestResult::None) { return titleTest; } else if (x >= 0 && y >= 0 && x < w && y < h) { - return HitTestType::Client; + return Window::HitTestResult::Client; } - return HitTestType::None; + return Window::HitTestResult::None; } QRect MainWindow::iconRect() const { - return title->iconRect(); + return _title ? _title->iconRect() : QRect(); } bool MainWindow::eventFilter(QObject *obj, QEvent *e) { @@ -749,9 +707,9 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { break; case QEvent::MouseMove: - if (main && main->isIdle()) { + if (_main && _main->isIdle()) { psUserActionDone(); - main->checkIdleFinish(); + _main->checkIdleFinish(); } break; @@ -781,8 +739,8 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { QString url = static_cast(e)->url().toEncoded().trimmed(); if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { cSetStartUrl(url.mid(0, 8192)); - if (auto main = App::main()) { - main->checkStartUrl(); + if (_main) { + _main->checkStartUrl(); } } activate(); @@ -807,26 +765,6 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { return Platform::MainWindow::eventFilter(obj, e); } -void MainWindow::mouseMoveEvent(QMouseEvent *e) { - if (e->buttons() & Qt::LeftButton) { - if (dragging) { - if (windowState().testFlag(Qt::WindowMaximized)) { - setWindowState(windowState() & ~Qt::WindowMaximized); - - dragStart = e->globalPos() - frameGeometry().topLeft(); - } else { - move(e->globalPos() - dragStart); - } - } - } else if (dragging) { - dragging = false; - } -} - -void MainWindow::mouseReleaseEvent(QMouseEvent *e) { - dragging = false; -} - bool MainWindow::minimizeToTray() { if (App::quitting() || !psHasTrayIcon()) return false; @@ -883,25 +821,25 @@ void MainWindow::updateTrayMenu(bool force) { void MainWindow::onShowAddContact() { if (isHidden()) showFromTray(); - if (main) main->showAddContact(); + if (_main) _main->showAddContact(); } void MainWindow::onShowNewGroup() { if (isHidden()) showFromTray(); - if (main) Ui::showLayer(new GroupInfoBox(CreatingGroupGroup, false), KeepOtherLayers); + if (_main) Ui::showLayer(new GroupInfoBox(CreatingGroupGroup, false), KeepOtherLayers); } void MainWindow::onShowNewChannel() { if (isHidden()) showFromTray(); - if (main) Ui::showLayer(new GroupInfoBox(CreatingGroupChannel, false), KeepOtherLayers); + if (_main) Ui::showLayer(new GroupInfoBox(CreatingGroupChannel, false), KeepOtherLayers); } void MainWindow::onLogout() { if (isHidden()) showFromTray(); - ConfirmBox *box = new ConfirmBox(lang(lng_sure_logout), lang(lng_settings_logout), st::attentionBoxButton); + auto box = new ConfirmBox(lang(lng_sure_logout), lang(lng_settings_logout), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onLogoutSure())); Ui::showLayer(box); } @@ -930,46 +868,46 @@ void MainWindow::activate() { activateWindow(); updateIsActive(Global::OnlineFocusTimeout()); if (wasHidden) { - if (main) { - main->windowShown(); + if (_main) { + _main->windowShown(); } } } void MainWindow::noIntro(IntroWidget *was) { - if (was == intro) { - intro = nullptr; + if (was == _intro) { + _intro = nullptr; } } void MainWindow::onSettingsDestroyed(QObject *was) { - if (was == settings) { - settings = nullptr; + if (was == _settings) { + _settings = nullptr; } checkHistoryActivation(); } void MainWindow::noMain(MainWidget *was) { - if (was == main) { - main = 0; + if (was == _main) { + _main = nullptr; } } void MainWindow::noLayerStack(LayerStackWidget *was) { - if (was == layerBg) { - layerBg = nullptr; + if (was == _layerBg) { + _layerBg = nullptr; } } void MainWindow::layerFinishedHide(LayerStackWidget *was) { - if (was == layerBg) { + if (was == _layerBg) { QTimer::singleShot(0, this, SLOT(layerHidden())); } } void MainWindow::fixOrder() { - title->raise(); - if (layerBg) layerBg->raise(); + if (_title) _title->raise(); + if (_layerBg) _layerBg->raise(); if (_mediaPreview) _mediaPreview->raise(); if (_connecting) _connecting->raise(); if (_testingThemeWarning) _testingThemeWarning->raise(); @@ -1041,12 +979,8 @@ void MainWindow::closeEvent(QCloseEvent *e) { } } -TitleWidget *MainWindow::getTitle() { - return title; -} - void MainWindow::resizeEvent(QResizeEvent *e) { - if (!title) return; + if (!_title) return; Adaptive::Layout layout = Adaptive::OneColumnLayout; if (width() > st::adaptiveWideWidth) { @@ -1058,16 +992,25 @@ void MainWindow::resizeEvent(QResizeEvent *e) { Global::SetAdaptiveLayout(layout); Adaptive::Changed().notify(true); } + + auto bodyTop = 0; + if (_title) { + _title->setGeometry(0, bodyTop, width(), st::titleHeight); + bodyTop += _title->height(); + } + _body->setGeometry(0, bodyTop, width(), height() - bodyTop); updateControlsGeometry(); - emit resized(QSize(width(), height() - st::titleHeight)); } void MainWindow::updateControlsGeometry() { - title->setGeometry(0, 0, width(), st::titleHeight); - if (layerBg) layerBg->resize(width(), height()); - if (_mediaPreview) _mediaPreview->setGeometry(0, title->height(), width(), height() - title->height()); - if (_connecting) _connecting->setGeometry(0, height() - _connecting->height(), _connecting->width(), _connecting->height()); - if (_testingThemeWarning) _testingThemeWarning->setGeometry(rect()); + auto body = _body->rect(); + if (_passcode) _passcode->setGeometry(body); + if (_main) _main->setGeometry(body); + if (_intro) _intro->setGeometry(body); + if (_layerBg) _layerBg->setGeometry(body); + if (_mediaPreview) _mediaPreview->setGeometry(body); + if (_connecting) _connecting->moveToLeft(0, body.height() - _connecting->height()); + if (_testingThemeWarning) _testingThemeWarning->setGeometry(body); } MainWindow::TempDirState MainWindow::tempDirState() { @@ -1156,8 +1099,8 @@ void MainWindow::notifySchedule(History *history, HistoryItem *item) { int delay = item->Has() ? 500 : 100, t = unixtime(); uint64 ms = getms(true); - bool isOnline = main->lastWasOnline(), otherNotOld = ((cOtherOnline() * uint64(1000)) + Global::OnlineCloudTimeout() > t * uint64(1000)); - bool otherLaterThanMe = (cOtherOnline() * uint64(1000) + (ms - main->lastSetOnline()) > t * uint64(1000)); + bool isOnline = _main->lastWasOnline(), otherNotOld = ((cOtherOnline() * uint64(1000)) + Global::OnlineCloudTimeout() > t * uint64(1000)); + bool otherLaterThanMe = (cOtherOnline() * uint64(1000) + (ms - _main->lastSetOnline()) > t * uint64(1000)); if (!isOnline && otherNotOld && otherLaterThanMe) { delay = Global::NotifyCloudDelay(); } else if (cOtherOnline() >= t) { @@ -1528,13 +1471,13 @@ void MainWindow::sendPaths() { if (App::passcoded()) return; hideMediaview(); Ui::hideSettingsAndLayer(); - if (main) { - main->activate(); + if (_main) { + _main->activate(); } } void MainWindow::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { - if (main) main->mediaOverviewUpdated(peer, type); + if (_main) _main->mediaOverviewUpdated(peer, type); if (_mediaView && !_mediaView->isHidden()) { _mediaView->mediaOverviewUpdated(peer, type); } @@ -1552,7 +1495,7 @@ void MainWindow::documentUpdated(DocumentData *doc) { } void MainWindow::changingMsgId(HistoryItem *row, MsgId newId) { - if (main) main->changingMsgId(row, newId); + if (_main) _main->changingMsgId(row, newId); if (!_mediaView || _mediaView->isHidden()) return; _mediaView->changingMsgId(row, newId); } @@ -1565,7 +1508,7 @@ bool MainWindow::isActive(bool cached) const { void MainWindow::updateIsActive(int timeout) { if (timeout) return _isActiveTimer.start(timeout); _isActive = isActive(false); - if (main) main->updateOnline(); + if (_main) _main->updateOnline(); } MainWindow::~MainWindow() { @@ -1578,9 +1521,9 @@ MainWindow::~MainWindow() { delete _mediaView; delete trayIcon; delete trayIconMenu; - delete intro; - delete main; - delete settings; + delete _intro; + delete _main; + delete _settings; } PreLaunchWindow *PreLaunchWindowInstance = 0; @@ -2000,11 +1943,11 @@ void LastCrashedWindow::addReportFieldPart(const QLatin1String &name, const QLat void LastCrashedWindow::onSendReport() { if (_checkReply) { _checkReply->deleteLater(); - _checkReply = 0; + _checkReply = nullptr; } if (_sendReply) { _sendReply->deleteLater(); - _sendReply = 0; + _sendReply = nullptr; } App::setProxySettings(_sendManager); @@ -2034,7 +1977,7 @@ void LastCrashedWindow::onCheckingFinished() { QByteArray result = _checkReply->readAll().trimmed(); _checkReply->deleteLater(); - _checkReply = 0; + _checkReply = nullptr; LOG(("Crash report check for sending done, result: %1").arg(QString::fromUtf8(result))); @@ -2057,7 +2000,7 @@ void LastCrashedWindow::onCheckingFinished() { return; } - QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + auto multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); addReportFieldPart(qstr("platform"), qstr("Platform:"), multipart); addReportFieldPart(qstr("version"), qstr("Version:"), multipart); @@ -2454,11 +2397,11 @@ void LastCrashedWindow::onSendingError(QNetworkReply::NetworkError e) { _sendingState = SendingFail; if (_checkReply) { _checkReply->deleteLater(); - _checkReply = 0; + _checkReply = nullptr; } if (_sendReply) { _sendReply->deleteLater(); - _sendReply = 0; + _sendReply = nullptr; } updateControls(); } @@ -2469,7 +2412,7 @@ void LastCrashedWindow::onSendingFinished() { LOG(("Crash report sending done, result: %1").arg(QString::fromUtf8(result))); _sendReply->deleteLater(); - _sendReply = 0; + _sendReply = nullptr; _pleaseSendReport.setText(qsl("Thank you for your report!")); _sendingState = SendingDone; updateControls(); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index a51423e7f..7578e6a7d 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -20,14 +20,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "title.h" #include "pspecific.h" #include "ui/effects/rect_shadow.h" #include "platform/platform_main_window.h" +#include "window/window_title.h" #include "core/single_timer.h" class MediaView; -class TitleWidget; class PasscodeWidget; class IntroWidget; class MainWidget; @@ -49,13 +48,15 @@ class WarningWidget; } // namespace Theme } // namespace Window -class ConnectingWidget : public QWidget { +class ConnectingWidget : public TWidget { Q_OBJECT public: ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect); void set(const QString &text, const QString &reconnect); - void paintEvent(QPaintEvent *e); + +protected: + void paintEvent(QPaintEvent *e) override; public slots: void onReconnect(); @@ -82,20 +83,9 @@ public: QWidget *filedialogParent(); - bool eventFilter(QObject *obj, QEvent *evt); - void inactivePress(bool inactive); bool inactivePress() const; - void wStartDrag(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - void closeEvent(QCloseEvent *e); - - void paintEvent(QPaintEvent *e); - - void resizeEvent(QResizeEvent *e); - void setupPasscode(bool anim); void clearPasscode(); void checkAutoLockIn(int msec); @@ -107,14 +97,9 @@ public: void mtpStateChanged(int32 dc, int32 state); - TitleWidget *getTitle(); - - HitTestType hitTest(const QPoint &p) const; + Window::HitTestResult hitTest(const QPoint &p) const; QRect iconRect() const; - QRect clientRect() const; - QRect photoRect() const; - IntroWidget *introWidget(); MainWidget *mainWidget(); PasscodeWidget *passcodeWidget(); @@ -184,6 +169,11 @@ public: void ui_hideMediaPreview(); PeerData *ui_getPeerForMouseAction(); +protected: + bool eventFilter(QObject *o, QEvent *e) override; + void closeEvent(QCloseEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + public slots: void updateIsActive(int timeout = 0); @@ -220,7 +210,6 @@ public slots: void app_activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button); signals: - void resized(const QSize &size); void tempDirCleared(int task); void tempDirClearFailed(int task); void newAuthorization(); @@ -250,13 +239,14 @@ private: QVector _delayedServiceMsgs; mtpRequestId _serviceHistoryRequest = 0; - TitleWidget *title = nullptr; - PasscodeWidget *_passcode = nullptr; - IntroWidget *intro = nullptr; - MainWidget *main = nullptr; - ChildWidget settings = { nullptr }; - ChildWidget layerBg = { nullptr }; - std_::unique_ptr _mediaPreview; + Window::TitleWidget *_title = nullptr; + ChildWidget _body; + ChildWidget _passcode = { nullptr }; + ChildWidget _intro = { nullptr }; + ChildWidget _main = { nullptr }; + ChildWidget _settings = { nullptr }; + ChildWidget _layerBg = { nullptr }; + ChildWidget _mediaPreview = { nullptr }; QTimer _isActiveTimer; bool _isActive = false; @@ -268,9 +258,6 @@ private: void clearWidgets(); - bool dragging = false; - QPoint dragStart; - bool _inactivePress = false; QTimer _inactiveTimer; diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index fd7d12a42..85e9d2aa6 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -144,22 +144,8 @@ mediaPlayerPlayback: FilledSlider { duration: 150; } -mediaPlayerTitleButtonSize: size(titleHeight, titleHeight); -mediaPlayerTitleButtonInner: size(25px, 25px); -mediaPlayerTitleButtonInnerBg: #49708f; mediaPlayerButtonTransformDuration: 200; -mediaPlayerTitleButton: MediaPlayerButton { - playPosition: point(10px, 7px); - playOuter: size(29px, 25px); - pausePosition: point(8px, 8px); - pauseOuter: size(25px, 25px); - pauseStroke: 3px; - cancelPosition: point(8px, 8px); - cancelOuter: size(25px, 25px); - cancelStroke: 2px; -} - mediaPlayerPanelButton: MediaPlayerButton { playPosition: point(3px, 0px); playOuter: size(22px, 18px); diff --git a/Telegram/SourceFiles/media/player/media_player_title_button.cpp b/Telegram/SourceFiles/media/player/media_player_title_button.cpp deleted file mode 100644 index 2903a10af..000000000 --- a/Telegram/SourceFiles/media/player/media_player_title_button.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* -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 "media/player/media_player_title_button.h" - -#include "media/player/media_player_button.h" -#include "media/media_audio.h" -#include "media/player/media_player_instance.h" -#include "shortcuts.h" - -namespace Media { -namespace Player { - -using State = PlayButtonLayout::State; - -TitleButton::TitleButton(QWidget *parent) : Button(parent) -, _layout(std_::make_unique(st::mediaPlayerTitleButton, [this] { update(); })) { - setAttribute(Qt::WA_OpaquePaintEvent); - resize(st::mediaPlayerTitleButtonSize); - - setClickedCallback([this]() { - if (exists()) { - instance()->playPauseCancelClicked(); - } - }); - - if (exists()) { - subscribe(instance()->updatedNotifier(), [this](const UpdatedEvent &e) { - updatePauseState(); - }); - updatePauseState(); - _layout->finishTransform(); - } -} - -void TitleButton::updatePauseState() { - AudioMsgId playing; - auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); - auto stopped = ((playbackState.state & AudioPlayerStoppedMask) || playbackState.state == AudioPlayerFinishing); - auto showPause = !stopped && (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting); - if (exists() && instance()->isSeeking()) { - showPause = true; - } - auto state = [audio = playing.audio(), showPause] { - if (audio && audio->loading()) { - return State::Cancel; - } else if (showPause) { - return State::Pause; - } - return State::Play; - }; - _layout->setState(state()); -} - -void TitleButton::paintEvent(QPaintEvent *e) { - Painter p(this); - p.fillRect(rect(), st::titleBg); - - p.setBrush(st::mediaPlayerTitleButtonInnerBg); - p.setPen(Qt::NoPen); - - p.setRenderHint(QPainter::HighQualityAntialiasing, true); - p.drawEllipse((width() - st::mediaPlayerTitleButtonInner.width()) / 2, (height() - st::mediaPlayerTitleButtonInner.height()) / 2, st::mediaPlayerTitleButtonInner.width(), st::mediaPlayerTitleButtonInner.height()); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); - - paintIcon(p); -} - -void TitleButton::onStateChanged(int oldState, ButtonStateChangeSource source) { - if ((oldState & StateOver) != (_state & StateOver)) { - auto over = (_state & StateOver); - _iconFg.start([this] { update(); }, over ? st::titleButtonFg->c : st::titleButtonActiveFg->c, over ? st::titleButtonActiveFg->c : st::titleButtonFg->c, st::titleButtonDuration); - } -} - -void TitleButton::paintIcon(Painter &p) { - auto over = (_state & StateOver); - auto icon = _iconFg.current(getms(), over ? st::titleButtonActiveFg->c : st::titleButtonFg->c); - - auto left = (width() - st::mediaPlayerTitleButtonInner.width()) / 2; - auto top = (height() - st::mediaPlayerTitleButtonInner.height()) / 2; - p.translate(left, top); - - _layout->paint(p, icon); -} - -void TitleButton::enterEvent(QEvent *e) { - if (exists()) { - instance()->titleButtonOver().notify(true, true); - } - return Button::enterEvent(e); -} - -void TitleButton::leaveEvent(QEvent *e) { - if (exists()) { - instance()->titleButtonOver().notify(false, true); - } - return Button::leaveEvent(e); -} - -TitleButton::~TitleButton() = default; - -} // namespace Player -} // namespace Media diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index e9fded51f..5597a4509 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -82,7 +82,7 @@ Widget::Widget(QWidget *parent) : TWidget(parent) , _playback(new Ui::FilledSlider(this, st::mediaPlayerPlayback)) { setAttribute(Qt::WA_OpaquePaintEvent); setMouseTracking(true); - resize(st::wndMinWidth, st::mediaPlayerHeight + st::lineWidth); + resize(width(), st::mediaPlayerHeight + st::lineWidth); _nameLabel->setAttribute(Qt::WA_TransparentForMouseEvents); _timeLabel->setAttribute(Qt::WA_TransparentForMouseEvents); diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 063357479..3a952395f 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_overview.h" #include "styles/style_dialogs.h" +#include "styles/style_window.h" #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" #include "boxes/photocropbox.h" @@ -56,13 +57,13 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD , _search(this, st::dlgFilter, lang(lng_dlg_filter)) , _cancelSearch(this, st::dialogsCancelSearch) , _itemsToBeLoaded(LinksOverviewPerPage * 2) -, _width(st::wndMinWidth) { +, _width(st::windowMinWidth) { subscribe(FileDownload::ImageLoaded(), [this] { update(); }); subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { itemRemoved(item); }); - resize(_width, st::wndMinHeight); + resize(_width, st::windowMinHeight); App::contextItem(0); diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 275857ad6..a31ed5301 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -33,9 +33,6 @@ PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) , _passcode(this, st::passcodeInput) , _submit(this, lang(lng_passcode_submit), st::passcodeSubmit) , _logout(this, lang(lng_passcode_logout)) { - setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); - connect(App::wnd(), SIGNAL(resized(const QSize&)), this, SLOT(onParentResize(const QSize&))); - _passcode.setEchoMode(QLineEdit::Password); connect(&_submit, SIGNAL(clicked()), this, SLOT(onSubmit())); @@ -48,10 +45,6 @@ PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) _passcode.setFocus(); } -void PasscodeWidget::onParentResize(const QSize &newSize) { - resize(newSize); -} - void PasscodeWidget::onSubmit() { if (_passcode.text().isEmpty()) { _passcode.notaBene(); diff --git a/Telegram/SourceFiles/passcodewidget.h b/Telegram/SourceFiles/passcodewidget.h index effd6c68f..3f7e6d36a 100644 --- a/Telegram/SourceFiles/passcodewidget.h +++ b/Telegram/SourceFiles/passcodewidget.h @@ -24,13 +24,8 @@ class PasscodeWidget : public TWidget { Q_OBJECT public: - PasscodeWidget(QWidget *parent); - void paintEvent(QPaintEvent *e); - void resizeEvent(QResizeEvent *e); - void mousePressEvent(QMouseEvent *e); - void keyPressEvent(QKeyEvent *e); void setInnerFocus(); void animShow(const QPixmap &bgAnimCache, bool back = false); @@ -39,15 +34,18 @@ public: ~PasscodeWidget(); -public slots: +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void keyPressEvent(QKeyEvent *e) override; - void onParentResize(const QSize &newSize); +public slots: void onError(); void onChanged(); void onSubmit(); private: - void showAll(); void hideAll(); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 5d3916610..167d9202f 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "platform/linux/main_window_linux.h" +#include "styles/style_window.h" #include "platform/linux/linux_libs.h" #include "platform/platform_notifications_manager.h" #include "mainwindow.h" @@ -205,9 +206,6 @@ void MainWindow::psStatusIconCheck() { void MainWindow::psShowTrayMenu() { } -void MainWindow::psRefreshTaskbarIcon() { -} - void MainWindow::psTrayMenuUpdated() { if (noQtTrayIcon && (useAppIndicator || useStatusIcon)) { const QList &actions = trayIconMenu->actions(); @@ -434,13 +432,10 @@ void MainWindow::LibsLoaded() { } void MainWindow::psInitSize() { - setMinimumWidth(st::wndMinWidth); - setMinimumHeight(st::wndMinHeight); - TWindowPos pos(cWindowPos()); QRect avail(QDesktopWidget().availableGeometry()); bool maximized = false; - QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight); + QRect geom(avail.x() + (avail.width() - st::windowDefWidth) / 2, avail.y() + (avail.height() - st::windowDefHeight) / 2, st::windowDefWidth, st::windowDefHeight); if (pos.w && pos.h) { QList screens = Application::screens(); for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { @@ -448,7 +443,7 @@ void MainWindow::psInitSize() { if (pos.moncrc == hashCrc32(name.constData(), name.size())) { QRect screen((*i)->geometry()); int32 w = screen.width(), h = screen.height(); - if (w >= st::wndMinWidth && h >= st::wndMinHeight) { + if (w >= st::windowMinWidth && h >= st::windowMinHeight) { if (pos.w > w) pos.w = w; if (pos.h > h) pos.h = h; pos.x += screen.x(); @@ -649,10 +644,6 @@ void MainWindow::psFirstShow() { posInited = true; } -bool MainWindow::psHandleTitle() { - return false; -} - void MainWindow::psInitSysMenu() { } diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 57f788115..63551b249 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -43,14 +43,13 @@ public: void psUpdateMargins(); void psUpdatedPosition(); - bool psHandleTitle(); - void psFlash(); void psNotifySettingGot(); void psUpdateWorkmode(); - void psRefreshTaskbarIcon(); + void psRefreshTaskbarIcon() { + } bool psPosInited() const { return posInited; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index c1f28d40d..c8e0583e2 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -27,19 +27,6 @@ namespace Notifications { inline void defaultNotificationShown(QWidget *widget) { } -inline bool skipAudio() { - return false; -} -inline bool skipToast() { - return false; -} - -class Manager; - -void start(); -Manager *manager(); -bool supported(); -void finish(); class Manager : public Window::Notifications::NativeManager { public: diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index fdbb7bd01..e82f2b27d 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -51,13 +51,12 @@ public: void psUpdateMargins(); void psUpdatedPosition(); - bool psHandleTitle(); - void psFlash(); void psUpdateWorkmode(); - void psRefreshTaskbarIcon(); + void psRefreshTaskbarIcon() { + } bool psPosInited() const { return posInited; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 64229f4bb..0de7a6a3f 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -18,6 +18,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "platform/mac/main_window_mac.h" +#include "styles/style_window.h" #include "mainwindow.h" #include "mainwidget.h" #include "application.h" @@ -25,6 +26,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "window/notifications_manager_default.h" #include "platform/mac/notifications_manager_mac.h" +#include "boxes/contactsbox.h" +#include "boxes/aboutbox.h" #include "lang.h" @@ -90,9 +93,6 @@ QImage MainWindow::psTrayIcon(bool selected) const { void MainWindow::psShowTrayMenu() { } -void MainWindow::psRefreshTaskbarIcon() { -} - void MainWindow::psTrayMenuUpdated() { } @@ -191,13 +191,10 @@ void MainWindow::psUpdateCounter() { } void MainWindow::psInitSize() { - setMinimumWidth(st::wndMinWidth); - setMinimumHeight(st::wndMinHeight); - TWindowPos pos(cWindowPos()); QRect avail(QDesktopWidget().availableGeometry()); bool maximized = false; - QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight); + QRect geom(avail.x() + (avail.width() - st::windowDefWidth) / 2, avail.y() + (avail.height() - st::windowDefHeight) / 2, st::windowDefWidth, st::windowDefHeight); if (pos.w && pos.h) { QList screens = Application::screens(); for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { @@ -205,7 +202,7 @@ void MainWindow::psInitSize() { if (pos.moncrc == hashCrc32(name.constData(), name.size())) { QRect screen((*i)->geometry()); int32 w = screen.width(), h = screen.height(); - if (w >= st::wndMinWidth && h >= st::wndMinHeight) { + if (w >= st::windowMinWidth && h >= st::windowMinHeight) { if (pos.w > w) pos.w = w; if (pos.h > h) pos.h = h; pos.x += screen.x(); @@ -302,8 +299,14 @@ void MainWindow::psFirstShow() { posInited = true; // init global menu - QMenu *main = psMainMenu.addMenu(qsl("Telegram")); - main->addAction(lng_mac_menu_about_telegram(lt_telegram, qsl("Telegram")), App::wnd()->getTitle(), SLOT(onAbout()))->setMenuRole(QAction::AboutQtRole); + auto main = psMainMenu.addMenu(qsl("Telegram")); + auto about = main->addAction(lng_mac_menu_about_telegram(lt_telegram, qsl("Telegram"))); + connect(about, SIGNAL(triggered()), base::lambda_slot(about, [] { + if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray(); + Ui::showLayer(new AboutBox()); + }), SLOT(action())); + about->setMenuRole(QAction::AboutQtRole); + main->addSeparator(); QAction *prefs = main->addAction(lang(lng_mac_menu_preferences), App::wnd(), SLOT(showSettings()), QKeySequence(Qt::ControlModifier | Qt::Key_Comma)); prefs->setMenuRole(QAction::PreferencesRole); @@ -323,7 +326,13 @@ void MainWindow::psFirstShow() { psSelectAll = edit->addAction(lang(lng_mac_menu_select_all), this, SLOT(psMacSelectAll()), QKeySequence::SelectAll); QMenu *window = psMainMenu.addMenu(lang(lng_mac_menu_window)); - psContacts = window->addAction(lang(lng_mac_menu_contacts), App::wnd()->getTitle(), SLOT(onContacts())); + psContacts = window->addAction(lang(lng_mac_menu_contacts)); + connect(psContacts, SIGNAL(triggered()), base::lambda_slot(psContacts, [] { + if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray(); + + if (!App::self()) return; + Ui::showLayer(new ContactsBox()); + }, SLOT(action())); psAddContact = window->addAction(lang(lng_mac_menu_add_contact), App::wnd(), SLOT(onShowAddContact())); window->addSeparator(); psNewGroup = window->addAction(lang(lng_mac_menu_new_group), App::wnd(), SLOT(onShowNewGroup())); @@ -379,10 +388,6 @@ void MainWindow::psMacSelectAll() { _sendKeySequence(Qt::Key_A, Qt::ControlModifier); } -bool MainWindow::psHandleTitle() { - return false; -} - void MainWindow::psInitSysMenu() { } diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h index a7fd87922..631021944 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h @@ -25,20 +25,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Platform { namespace Notifications { -void defaultNotificationShown(QWidget *widget); -inline bool skipAudio() { - return false; -} -inline bool skipToast() { - return false; -} - -class Manager; - -void start(); -Manager *manager(); -void finish(); - class Manager : public Window::Notifications::NativeManager { public: Manager(); diff --git a/Telegram/SourceFiles/platform/platform_file_dialog.h b/Telegram/SourceFiles/platform/platform_file_dialog.h index 29bd064b8..1a1a6c554 100644 --- a/Telegram/SourceFiles/platform/platform_file_dialog.h +++ b/Telegram/SourceFiles/platform/platform_file_dialog.h @@ -22,20 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" -#ifdef Q_OS_MAC -namespace Platform { -namespace FileDialog { -inline bool Supported() { - return false; -} -inline bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile) { - return false; -} -} // namespace FileDialog -} // namespace Platform -#elif defined Q_OS_LINUX // Q_OS_MAC +#if defined Q_OS_LINUX #include "platform/linux/file_dialog_linux.h" -#elif defined Q_OS_WINRT // Q_OS_MAC || Q_OS_LINUX +#else // Q_OS_LINUX namespace Platform { namespace FileDialog { inline bool Supported() { @@ -46,15 +35,4 @@ inline bool Get(QStringList &files, QByteArray &remoteContent, const QString &ca } } // namespace FileDialog } // namespace Platform -#elif defined Q_OS_WIN // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT -namespace Platform { -namespace FileDialog { -inline bool Supported() { - return false; -} -inline bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile) { - return false; -} -} // namespace FileDialog -} // namespace Platform -#endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT || Q_OS_WIN +#endif // else for Q_OS_LINUX diff --git a/Telegram/SourceFiles/platform/platform_notifications_manager.h b/Telegram/SourceFiles/platform/platform_notifications_manager.h index 81c10ae29..ec7682ef2 100644 --- a/Telegram/SourceFiles/platform/platform_notifications_manager.h +++ b/Telegram/SourceFiles/platform/platform_notifications_manager.h @@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +// Platform module must define a Platform::Notifications::Manager class. +// It should be Window::Notifications::Manager or its derivative. #ifdef Q_OS_MAC #include "platform/mac/notifications_manager_mac.h" #elif defined Q_OS_LINUX // Q_OS_MAC @@ -29,3 +31,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #elif defined Q_OS_WIN // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT #include "platform/win/notifications_manager_win.h" #endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT || Q_OS_WIN + +// Platform-independent API. +namespace Platform { +namespace Notifications { + +void defaultNotificationShown(QWidget *widget); +bool skipAudio(); +bool skipToast(); + +void start(); +Manager *manager(); +bool supported(); +void finish(); + +} // namespace Notifications +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/platform_window_title.h b/Telegram/SourceFiles/platform/platform_window_title.h new file mode 100644 index 000000000..b124c469f --- /dev/null +++ b/Telegram/SourceFiles/platform/platform_window_title.h @@ -0,0 +1,35 @@ +/* +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 "window/window_title.h" + +#if defined Q_OS_WIN +#include "platform/win/window_title_win.h" +#else // Q_OS_WIN +namespace Platform { + +inline Window::TitleWidget *CreateTitleWidget() { + return nullptr; +} + +} // namespace Platform +#endif // else for Q_OS_WIN diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index bf500168a..b44401412 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "platform/win/main_window_win.h" +#include "styles/style_window.h" #include "platform/platform_notifications_manager.h" #include "platform/win/windows_dlls.h" #include "window/notifications_manager.h" @@ -140,13 +141,13 @@ public: } bool init(QColor c) { - _fullsize = st::wndShadow.width(); - _shift = st::wndShadowShift; + _fullsize = st::windowShadow.width(); + _shift = st::windowShadowShift; QImage cornersImage(_fullsize, _fullsize, QImage::Format_ARGB32_Premultiplied); { Painter p(&cornersImage); p.setCompositionMode(QPainter::CompositionMode_Source); - st::wndShadow.paint(p, 0, 0, _fullsize); + st::windowShadow.paint(p, 0, 0, _fullsize); } if (rtl()) cornersImage = cornersImage.mirrored(true, false); @@ -191,9 +192,9 @@ public: QRect avail(Sandbox::availableGeometry()); max_w = avail.width(); - if (max_w < st::wndMinWidth) max_w = st::wndMinWidth; + accumulate_max(max_w, st::windowMinWidth); max_h = avail.height(); - if (max_h < st::wndMinHeight) max_h = st::wndMinHeight; + accumulate_max(max_h, st::windowMinHeight); HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); HWND hwnd = App::wnd() ? App::wnd()->psHwnd() : 0; @@ -645,7 +646,7 @@ void MainWindow::psShowTrayMenu() { void MainWindow::psRefreshTaskbarIcon() { QWidget *w = new QWidget(this); - w->setWindowFlags(::operator|(Qt::Tool, Qt::FramelessWindowHint)); + w->setWindowFlags(static_cast(Qt::Tool) | Qt::FramelessWindowHint); w->setGeometry(x() + 1, y() + 1, 1, 1); QPalette p(w->palette()); p.setColor(QPalette::Background, st::titleBg->c); @@ -776,20 +777,17 @@ BOOL CALLBACK _monitorEnumProc( } // namespace void MainWindow::psInitSize() { - setMinimumWidth(st::wndMinWidth); - setMinimumHeight(st::wndMinHeight); - TWindowPos pos(cWindowPos()); QRect avail(Sandbox::availableGeometry()); bool maximized = false; - QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight); + QRect geom(avail.x() + (avail.width() - st::windowDefWidth) / 2, avail.y() + (avail.height() - st::windowDefHeight) / 2, st::windowDefWidth, st::windowDefHeight); if (pos.w && pos.h) { if (pos.y < 0) pos.y = 0; enumMonitor = 0; EnumDisplayMonitors(0, 0, &_monitorEnumProc, pos.moncrc); if (enumMonitor) { int32 w = enumMonitorWork.right - enumMonitorWork.left, h = enumMonitorWork.bottom - enumMonitorWork.top; - if (w >= st::wndMinWidth && h >= st::wndMinHeight) { + if (w >= st::windowMinWidth && h >= st::windowMinHeight) { if (pos.w > w) pos.w = w; if (pos.h > h) pos.h = h; pos.x += enumMonitorWork.left; @@ -853,7 +851,7 @@ void MainWindow::psSavePosition(Qt::WindowState state) { curPos.moncrc = hashCrc32(info.szDevice, sizeof(info.szDevice)); } - if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { + if (curPos.w >= st::windowMinWidth && curPos.h >= st::windowMinHeight) { if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { cSetWindowPos(curPos); Local::writeSettings(); @@ -902,10 +900,6 @@ void MainWindow::psFirstShow() { } } -bool MainWindow::psHandleTitle() { - return true; -} - void MainWindow::psInitSysMenu() { Qt::WindowStates states = windowState(); ps_menu = GetSystemMenu(ps_hWnd, FALSE); diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 73658bc50..0aad21bc1 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -50,8 +50,6 @@ public: void psUpdateMargins(); void psUpdatedPosition(); - bool psHandleTitle(); - void psFlash(); void psNotifySettingGot(); diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.h b/Telegram/SourceFiles/platform/win/notifications_manager_win.h index 403e4e00a..ce5316409 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.h +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.h @@ -27,15 +27,6 @@ namespace Notifications { inline void defaultNotificationShown(QWidget *widget) { } -bool skipAudio(); -bool skipToast(); - -class Manager; - -void start(); -Manager *manager(); -bool supported(); -void finish(); class Manager : public Window::Notifications::NativeManager { public: diff --git a/Telegram/SourceFiles/platform/win/window_title_win.cpp b/Telegram/SourceFiles/platform/win/window_title_win.cpp new file mode 100644 index 000000000..61c348fd5 --- /dev/null +++ b/Telegram/SourceFiles/platform/win/window_title_win.cpp @@ -0,0 +1,106 @@ +/* +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 "platform/win/window_title_win.h" + +#include "ui/buttons/icon_button.h" +#include "styles/style_window.h" + +namespace Platform { + +TitleWidget::TitleWidget(QWidget *parent) : Window::TitleWidget(parent) +, _minimize(this, st::titleButtonMinimize) +, _maximizeRestore(this, st::titleButtonMaximize) +, _close(this, st::titleButtonClose) +, _maximized(parent->window()->windowState() & Qt::WindowMaximized) { + _minimize->setClickedCallback([this]() { + window()->setWindowState(Qt::WindowMinimized); + _minimize->clearState(); + }); + _maximizeRestore->setClickedCallback([this]() { + window()->setWindowState(_maximized ? Qt::WindowNoState : Qt::WindowMaximized); + _maximizeRestore->clearState(); + }); + _close->setClickedCallback([this]() { + window()->close(); + _close->clearState(); + }); + + setAttribute(Qt::WA_OpaquePaintEvent); + updateMaximizeRestoreButton(); + + onWindowStateChanged(); + + connect(parent->window()->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onWindowStateChanged(Qt::WindowState))); +} + +void TitleWidget::paintEvent(QPaintEvent *e) { + Painter(this).fillRect(rect(), st::titleBg); +} + +void TitleWidget::updateControlsPosition() { + auto right = 0; + _close->moveToRight(right, 0); right += _close->width(); + _maximizeRestore->moveToRight(right, 0); right += _maximizeRestore->width(); + _minimize->moveToRight(right, 0); +} + +void TitleWidget::resizeEvent(QResizeEvent *e) { + updateControlsPosition(); +} + +void TitleWidget::updateControlsVisibility() { + updateControlsPosition(); + update(); +} + +void TitleWidget::onWindowStateChanged(Qt::WindowState state) { + if (state == Qt::WindowMinimized) return; + + auto maximized = (state == Qt::WindowMaximized); + if (_maximized != maximized) { + _maximized = maximized; + updateMaximizeRestoreButton(); + } +} + +void TitleWidget::updateMaximizeRestoreButton() { + if (_maximized) { + _maximizeRestore->setIcon(&st::titleButtonRestoreIcon, &st::titleButtonRestoreIconOver); + } else { + _maximizeRestore->setIcon(nullptr, nullptr); + } +} + +Window::HitTestResult TitleWidget::hitTest(const QPoint &p) const { + if (false + || (_minimize->geometry().contains(p)) + || (_maximizeRestore->geometry().contains(p)) + || (_close->geometry().contains(p)) + ) { + return Window::HitTestResult::SysButton; + } else if (rect().contains(p)) { + return Window::HitTestResult::Caption; + } + return Window::HitTestResult::None; +} + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/window_title_win.h b/Telegram/SourceFiles/platform/win/window_title_win.h new file mode 100644 index 000000000..1fd97d0bf --- /dev/null +++ b/Telegram/SourceFiles/platform/win/window_title_win.h @@ -0,0 +1,63 @@ +/* +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 "window/window_title.h" + +namespace Ui { +class IconButton; +} // namespace Ui + +namespace Platform { + +class TitleWidget : public Window::TitleWidget, private base::Subscriber { + Q_OBJECT + +public: + TitleWidget(QWidget *parent); + + Window::HitTestResult hitTest(const QPoint &p) const override; + +public slots: + void onWindowStateChanged(Qt::WindowState state = Qt::WindowNoState); + void updateControlsVisibility(); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + void updateMaximizeRestoreButton(); + void updateControlsPosition(); + + ChildWidget _minimize; + ChildWidget _maximizeRestore; + ChildWidget _close; + + bool _maximized = false; + +}; + +inline Window::TitleWidget *CreateTitleWidget(QWidget *parent) { + return new TitleWidget(parent); +} + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp index a9a96d9f0..b3202da59 100644 --- a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp +++ b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp @@ -183,20 +183,20 @@ bool EventFilter::mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa GetWindowRect(hWnd, &r); auto res = App::wnd()->hitTest(QPoint(p.x - r.left + App::wnd()->deltaLeft(), p.y - r.top + App::wnd()->deltaTop())); switch (res) { - case HitTestType::Client: - case HitTestType::SysButton: *result = HTCLIENT; break; - case HitTestType::Icon: *result = HTCAPTION; break; - case HitTestType::Caption: *result = HTCAPTION; break; - case HitTestType::Top: *result = HTTOP; break; - case HitTestType::TopRight: *result = HTTOPRIGHT; break; - case HitTestType::Right: *result = HTRIGHT; break; - case HitTestType::BottomRight: *result = HTBOTTOMRIGHT; break; - case HitTestType::Bottom: *result = HTBOTTOM; break; - case HitTestType::BottomLeft: *result = HTBOTTOMLEFT; break; - case HitTestType::Left: *result = HTLEFT; break; - case HitTestType::TopLeft: *result = HTTOPLEFT; break; - case HitTestType::None: - default: *result = HTTRANSPARENT; break; + case Window::HitTestResult::Client: + case Window::HitTestResult::SysButton: *result = HTCLIENT; break; + case Window::HitTestResult::Icon: *result = HTCAPTION; break; + case Window::HitTestResult::Caption: *result = HTCAPTION; break; + case Window::HitTestResult::Top: *result = HTTOP; break; + case Window::HitTestResult::TopRight: *result = HTTOPRIGHT; break; + case Window::HitTestResult::Right: *result = HTRIGHT; break; + case Window::HitTestResult::BottomRight: *result = HTBOTTOMRIGHT; break; + case Window::HitTestResult::Bottom: *result = HTBOTTOM; break; + case Window::HitTestResult::BottomLeft: *result = HTBOTTOMLEFT; break; + case Window::HitTestResult::Left: *result = HTLEFT; break; + case Window::HitTestResult::TopLeft: *result = HTTOPLEFT; break; + case Window::HitTestResult::None: + default: *result = HTTRANSPARENT; break; }; } return true; @@ -210,23 +210,23 @@ bool EventFilter::mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa GetWindowRect(hWnd, &r); auto res = App::wnd()->hitTest(QPoint(p.x - r.left + App::wnd()->deltaLeft(), p.y - r.top + App::wnd()->deltaTop())); switch (res) { - case HitTestType::Icon: - if (menuHidden && getms() < menuHidden + 10) { - menuHidden = 0; - if (getms() < menuShown + GetDoubleClickTime()) { - App::wnd()->close(); + case Window::HitTestResult::Icon: + if (menuHidden && getms() < menuHidden + 10) { + menuHidden = 0; + if (getms() < menuShown + GetDoubleClickTime()) { + App::wnd()->close(); + } + } else { + QRect icon = App::wnd()->iconRect(); + p.x = r.left - App::wnd()->deltaLeft() + icon.left(); + p.y = r.top - App::wnd()->deltaTop() + icon.top() + icon.height(); + App::wnd()->psUpdateSysMenu(App::wnd()->windowHandle()->windowState()); + menuShown = getms(); + menuHidden = 0; + TrackPopupMenu(App::wnd()->psMenu(), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, p.x, p.y, 0, hWnd, 0); + menuHidden = getms(); } - } else { - QRect icon = App::wnd()->iconRect(); - p.x = r.left - App::wnd()->deltaLeft() + icon.left(); - p.y = r.top - App::wnd()->deltaTop() + icon.top() + icon.height(); - App::wnd()->psUpdateSysMenu(App::wnd()->windowHandle()->windowState()); - menuShown = getms(); - menuHidden = 0; - TrackPopupMenu(App::wnd()->psMenu(), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, p.x, p.y, 0, hWnd, 0); - menuHidden = getms(); - } - return true; + return true; }; } return false; @@ -236,7 +236,7 @@ bool EventFilter::mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa GetWindowRect(hWnd, &r); auto res = App::wnd()->hitTest(QPoint(p.x - r.left + App::wnd()->deltaLeft(), p.y - r.top + App::wnd()->deltaTop())); switch (res) { - case HitTestType::Icon: App::wnd()->close(); return true; + case Window::HitTestResult::Icon: App::wnd()->close(); return true; }; } return false; diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index 96dd3da7a..2608aa84b 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_cover.h" #include "styles/style_profile.h" +#include "styles/style_window.h" #include "profile/profile_cover_drop_area.h" #include "profile/profile_userpic_button.h" #include "ui/buttons/round_button.h" @@ -112,7 +113,7 @@ void CoverWidget::onCancelPhotoUpload() { int CoverWidget::countPhotoLeft(int newWidth) const { int result = st::profilePhotoLeftMin; - result += (newWidth - st::wndMinWidth) / 2; + result += (newWidth - st::windowMinWidth) / 2; return qMin(result, st::profilePhotoLeftMax); } diff --git a/Telegram/SourceFiles/profile/profile_inner_widget.cpp b/Telegram/SourceFiles/profile/profile_inner_widget.cpp index de59ee911..87635380e 100644 --- a/Telegram/SourceFiles/profile/profile_inner_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_inner_widget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_inner_widget.h" #include "styles/style_profile.h" +#include "styles/style_window.h" #include "profile/profile_cover.h" #include "profile/profile_info_widget.h" #include "profile/profile_settings_widget.h" @@ -132,7 +133,7 @@ int InnerWidget::countBlocksHeight(BlockSide countSide) const { int InnerWidget::countBlocksLeft(int newWidth) const { int result = st::profileBlockLeftMin; - result += (newWidth - st::wndMinWidth) / 2; + result += (newWidth - st::windowMinWidth) / 2; return qMin(result, st::profileBlockLeftMax); } diff --git a/Telegram/SourceFiles/pspecific.h b/Telegram/SourceFiles/pspecific.h index fb3dd1697..961515c98 100644 --- a/Telegram/SourceFiles/pspecific.h +++ b/Telegram/SourceFiles/pspecific.h @@ -20,10 +20,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include -#include -#include "sysbuttons.h" - #ifdef Q_OS_MAC #include "pspecific_mac.h" #elif defined Q_OS_LINUX // Q_OS_MAC diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index fb69469b1..a99d5585a 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "media/player/media_player_instance.h" #include "platform/mac/mac_utilities.h" - +#include "styles/style_window.h" #include "lang.h" #include diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp index c85be6440..710bd4a39 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp @@ -85,7 +85,6 @@ void ScaleWidget::setScale(DBIScale newScale) { cSetConfigScale(newScale); Local::writeSettings(); - App::wnd()->getTitle()->updateControlsVisibility(); if (newScale == dbisAuto && !_auto->checked()) { _auto->setChecked(true); } else if (newScale != dbisAuto && _auto->checked()) { diff --git a/Telegram/SourceFiles/settings/settings_widget.cpp b/Telegram/SourceFiles/settings/settings_widget.cpp index 9e5da544a..0bad833c0 100644 --- a/Telegram/SourceFiles/settings/settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_widget.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "settings/settings_inner_widget.h" #include "settings/settings_fixed_bar.h" #include "styles/style_settings.h" +#include "styles/style_window.h" #include "styles/style_boxes.h" #include "ui/widgets/shadow.h" #include "ui/scrollarea.h" @@ -123,7 +124,7 @@ void codesFeedString(const QString &text) { } // namespace -Widget::Widget() : LayerWidget() +Widget::Widget(QWidget *parent) : LayerWidget(parent) , _scroll(this, st::setScroll) , _inner(this) , _fixedBar(this) @@ -147,18 +148,18 @@ void Widget::parentResized() { if (windowWidth <= st::settingsMaxWidth) { newWidth = windowWidth; newContentLeft = st::settingsMinPadding; - if (windowWidth > st::wndMinWidth) { - // Width changes from st::wndMinWidth to st::settingsMaxWidth. + if (windowWidth > st::windowMinWidth) { + // Width changes from st::windowMinWidth to st::settingsMaxWidth. // Padding changes from st::settingsMinPadding to st::settingsMaxPadding. - newContentLeft += ((newWidth - st::wndMinWidth) * (st::settingsMaxPadding - st::settingsMinPadding)) / (st::settingsMaxWidth - st::wndMinWidth); + newContentLeft += ((newWidth - st::windowMinWidth) * (st::settingsMaxPadding - st::settingsMinPadding)) / (st::settingsMaxWidth - st::windowMinWidth); } } else if (windowWidth < st::settingsMaxWidth + 2 * st::settingsMargin) { newWidth = windowWidth - 2 * st::settingsMargin; newContentLeft = st::settingsMinPadding; - if (windowWidth > st::wndMinWidth) { - // Width changes from st::wndMinWidth to st::settingsMaxWidth. + if (windowWidth > st::windowMinWidth) { + // Width changes from st::windowMinWidth to st::settingsMaxWidth. // Padding changes from st::settingsMinPadding to st::settingsMaxPadding. - newContentLeft += ((newWidth - st::wndMinWidth) * (st::settingsMaxPadding - st::settingsMinPadding)) / (st::settingsMaxWidth - st::wndMinWidth); + newContentLeft += ((newWidth - st::windowMinWidth) * (st::settingsMaxPadding - st::settingsMinPadding)) / (st::settingsMaxWidth - st::windowMinWidth); } } diff --git a/Telegram/SourceFiles/settings/settings_widget.h b/Telegram/SourceFiles/settings/settings_widget.h index 43207c997..06dde46d8 100644 --- a/Telegram/SourceFiles/settings/settings_widget.h +++ b/Telegram/SourceFiles/settings/settings_widget.h @@ -35,7 +35,7 @@ class Widget : public LayerWidget { Q_OBJECT public: - Widget(); + Widget(QWidget *parent); void parentResized() override; void showDone() override; diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 8b0162193..1e40b0fb0 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -981,11 +981,11 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, auto &location = data->location(true); if (auto applyTheme = data->name.endsWith(qstr(".tdesktop-theme"))) { if (!location.isEmpty() && location.accessEnable()) { - if (Window::Theme::Apply(location.name())) { - location.accessDisable(); - return; + if (!Window::Theme::Apply(location.name())) { + // show error? } location.accessDisable(); + return; } } if (!location.isEmpty() || (!data->data().isEmpty() && (playVoice || playMusic || playVideo || playAnimation))) { @@ -1290,11 +1290,11 @@ void DocumentData::performActionOnLoad() { bool playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item && item->getMedia(); if (auto applyTheme = name.endsWith(qstr(".tdesktop-theme"))) { if (!loc.isEmpty() && loc.accessEnable()) { - if (Window::Theme::Apply(loc.name())) { - loc.accessDisable(); - return; + if (!Window::Theme::Apply(loc.name())) { + // show error? } loc.accessDisable(); + return; } } if (playVoice) { diff --git a/Telegram/SourceFiles/sysbuttons.cpp b/Telegram/SourceFiles/sysbuttons.cpp deleted file mode 100644 index 63a60eca8..000000000 --- a/Telegram/SourceFiles/sysbuttons.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* -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 "sysbuttons.h" - -#include "lang.h" -#include "shortcuts.h" -#include "application.h" -#include "autoupdater.h" - -SysBtn::SysBtn(QWidget *parent, const style::sysButton &st, const QString &text) : Button(parent) -, _st(&st) -, a_color(_st->color->c) -, _a_color(animation(this, &SysBtn::step_color)) -, _text(text) { - int32 w = _st->size.width() + (_text.isEmpty() ? 0 : ((_st->size.width() - _st->icon.width()) / 2 + st::titleTextButton.font->width(_text))); - resize(w, _st->size.height()); - setCursor(style::cur_default); -} - -void SysBtn::setText(const QString &text) { - _text = text; - int32 w = _st->size.width() + (_text.isEmpty() ? 0 : ((_st->size.width() - _st->icon.width()) / 2 + st::titleTextButton.font->width(_text))); - resize(w, _st->size.height()); -} - -void SysBtn::setOverLevel(float64 level) { - _overLevel = level; - update(); -} - -void SysBtn::onStateChanged(int oldState, ButtonStateChangeSource source) { - a_color.start((_state & StateOver ? _st->overColor : _st->color)->c); - - if (source == ButtonByUser || source == ButtonByPress) { - _a_color.stop(); - a_color.finish(); - update(); - } else { - _a_color.start(); - } -} - -void SysBtn::paintEvent(QPaintEvent *e) { - Painter p(this); - - int x = width() - ((_st->size.width() + _st->icon.width()) / 2), y = (height() - _st->icon.height()) / 2; - QColor c = a_color.current(); - if (_overLevel > 0) { - if (_overLevel >= 1) { - c = _st->overColor->c; - } else { - c.setRedF(c.redF() * (1 - _overLevel) + _st->overColor->c.redF() * _overLevel); - c.setGreenF(c.greenF() * (1 - _overLevel) + _st->overColor->c.greenF() * _overLevel); - c.setBlueF(c.blueF() * (1 - _overLevel) + _st->overColor->c.blueF() * _overLevel); - } - } - p.fillRect(x, y, _st->icon.width(), _st->icon.height(), c); - _st->icon.paint(p, x, y, width()); - - if (!_text.isEmpty()) { - p.setFont(st::titleTextButton.font->f); - p.setPen(c); - p.drawText((_st->size.width() - _st->icon.width()) / 2, st::titleTextButton.textTop + st::titleTextButton.font->ascent, _text); - } -} - -void SysBtn::setSysBtnStyle(const style::sysButton &st) { - _st = &st; - update(); -} - -HitTestType SysBtn::hitTest(const QPoint &p) const { - int x(p.x()), y(p.y()), w(width()), h(height()); - if (x >= 0 && y >= 0 && x < w && y < h && isVisible()) { - return HitTestType::SysButton; - } - return HitTestType::None; -} - -void SysBtn::step_color(float64 ms, bool timer) { - float64 dt = ms / _st->duration; - if (dt >= 1) { - _a_color.stop(); - a_color.finish(); - } else { - a_color.update(dt, anim::linear); - } - if (timer) update(); -} - -MinimizeBtn::MinimizeBtn(QWidget *parent) : SysBtn(parent, st::sysMin) { - setClickedCallback([this]() { - window()->setWindowState(Qt::WindowMinimized); - }); -} - -MaximizeBtn::MaximizeBtn(QWidget *parent) : SysBtn(parent, st::sysMax) { - setClickedCallback([this]() { - window()->setWindowState(Qt::WindowMaximized); - }); -} - -RestoreBtn::RestoreBtn(QWidget *parent) : SysBtn(parent, st::sysRes) { - setClickedCallback([this]() { - window()->setWindowState(Qt::WindowNoState); - }); -} - -CloseBtn::CloseBtn(QWidget *parent) : SysBtn(parent, st::sysCls) { - setClickedCallback([this]() { - window()->close(); - }); -} - -UpdateBtn::UpdateBtn(QWidget *parent) : SysBtn(parent, st::sysUpd, lang(lng_menu_update)) { - setClickedCallback([]() { -#ifndef TDESKTOP_DISABLE_AUTOUPDATE - checkReadyUpdate(); -#endif // !TDESKTOP_DISABLE_AUTOUPDATE - App::restart(); - }); -} - -LockBtn::LockBtn(QWidget *parent) : SysBtn(parent, st::sysLock) { - setClickedCallback([] { - Shortcuts::launch(qsl("lock_telegram")); - }); -} diff --git a/Telegram/SourceFiles/sysbuttons.h b/Telegram/SourceFiles/sysbuttons.h deleted file mode 100644 index 06dba6ac9..000000000 --- a/Telegram/SourceFiles/sysbuttons.h +++ /dev/null @@ -1,102 +0,0 @@ -/* -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/animation.h" -#include "ui/button.h" - -enum class HitTestType { - None = 0, - Client, - SysButton, - Icon, - Caption, - Top, - TopRight, - Right, - BottomRight, - Bottom, - BottomLeft, - Left, - TopLeft, -}; - -class SysBtn : public Button { -public: - SysBtn(QWidget *parent, const style::sysButton &st, const QString &text = QString()); - - void setText(const QString &text); - void setSysBtnStyle(const style::sysButton &st); - - HitTestType hitTest(const QPoint &p) const; - - void setOverLevel(float64 level); - - void step_color(float64 ms, bool timer); - -protected: - void onStateChanged(int oldState, ButtonStateChangeSource source) override; - void paintEvent(QPaintEvent *e) override; - - const style::sysButton *_st; - anim::cvalue a_color; - Animation _a_color; - - float64 _overLevel = 0.; - QString _text; - -}; - -class MinimizeBtn : public SysBtn { -public: - MinimizeBtn(QWidget *parent); - -}; - -class MaximizeBtn : public SysBtn { -public: - MaximizeBtn(QWidget *parent); - -}; - -class RestoreBtn : public SysBtn { -public: - RestoreBtn(QWidget *parent); - -}; - -class CloseBtn : public SysBtn { -public: - CloseBtn(QWidget *parent); - -}; - -class UpdateBtn : public SysBtn { -public: - UpdateBtn(QWidget *parent); - -}; - -class LockBtn : public SysBtn { -public: - LockBtn(QWidget *parent); - -}; diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp deleted file mode 100644 index 1e1ec37c1..000000000 --- a/Telegram/SourceFiles/title.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/* -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 "title.h" - -#include "lang.h" -#include "mainwidget.h" -#include "mainwindow.h" -#include "application.h" -#include "boxes/contactsbox.h" -#include "boxes/aboutbox.h" -#include "media/media_audio.h" -#include "media/player/media_player_title_button.h" -#include "media/player/media_player_panel.h" -#include "media/player/media_player_instance.h" -#include "styles/style_window.h" - -class TitleWidget::Hider : public TWidget { -public: - Hider(QWidget *parent); - - using ClickedCallback = base::lambda_unique; - void setClickedCallback(ClickedCallback &&callback) { - _callback = std_::move(callback); - } - void setLevel(float64 level); - -protected: - void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - -private: - ClickedCallback _callback; - float64 _level = 0; - -}; - -TitleWidget::Hider::Hider(QWidget *parent) : TWidget(parent) { -} - -void TitleWidget::Hider::paintEvent(QPaintEvent *e) { - QPainter p(this); - p.setOpacity(_level); - p.fillRect(App::main()->dlgsWidth(), 0, width() - App::main()->dlgsWidth(), height(), st::layerBg); -} - -void TitleWidget::Hider::mousePressEvent(QMouseEvent *e) { - if (e->button() == Qt::LeftButton && _callback) { - _callback(); - } -} - -void TitleWidget::Hider::setLevel(float64 level) { - _level = level; - update(); -} - -TitleWidget::TitleWidget(QWidget *parent) : TWidget(parent) -, _cancel(this, lang(lng_cancel), st::titleTextButton) -, _settings(this, lang(lng_menu_settings), st::titleTextButton) -, _contacts(this, lang(lng_menu_contacts), st::titleTextButton) -, _about(this, lang(lng_menu_about), st::titleTextButton) -, _lock(this) -, _update(this) -, _minimize(this) -, _maximize(this) -, _restore(this) -, _close(this) -, _a_update(animation(this, &TitleWidget::step_update)) -, lastMaximized(!(parent->windowState() & Qt::WindowMaximized)) { - setGeometry(0, 0, parent->width(), st::titleHeight); - setAttribute(Qt::WA_OpaquePaintEvent); - - onWindowStateChanged(); - updateControlsVisibility(); - - connect(&_cancel, SIGNAL(clicked()), this, SIGNAL(hiderClicked())); - connect(&_settings, SIGNAL(clicked()), parent, SLOT(showSettings())); - connect(&_contacts, SIGNAL(clicked()), this, SLOT(onContacts())); - connect(&_about, SIGNAL(clicked()), this, SLOT(onAbout())); - connect(parent->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onWindowStateChanged(Qt::WindowState))); - -#ifndef TDESKTOP_DISABLE_AUTOUPDATE - Sandbox::connect(SIGNAL(updateReady()), this, SLOT(updateControlsVisibility())); -#endif // !TDESKTOP_DISABLE_AUTOUPDATE - - subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); }); - if (Media::Player::exists()) { - subscribe(Media::Player::instance()->usePanelPlayer(), [this](bool usePanel) { - updatePlayerButton(usePanel); - }); - } - - if (cPlatform() != dbipWindows) { - _minimize.hide(); - _maximize.hide(); - _restore.hide(); - _close.hide(); - } -} - -void TitleWidget::paintEvent(QPaintEvent *e) { - Painter p(this); - p.fillRect(rect(), st::titleBg); - if (!_cancel.isHidden()) { - p.setPen(st::titleTextButton.color); - p.setFont(st::titleTextButton.font); - bool inlineSwitchChoose = (App::main() && App::main()->selectingPeerForInlineSwitch()); - auto chooseText = lang(inlineSwitchChoose ? lng_inline_switch_choose : lng_forward_choose); - p.drawText(st::titleMenuOffset - st::titleTextButton.width / 2, st::titleTextButton.textTop + st::titleTextButton.font->ascent, chooseText); - } - st::titleIcon.paint(p, st::titleIconPosition, width()); - if (Adaptive::OneColumn() && !_counter.isNull() && App::main()) { - p.drawPixmap(st::titleCounterPosition, _counter); - } -} - -void TitleWidget::step_update(float64 ms, bool timer) { - float64 phase = sin(M_PI_2 * (ms / st::updateBlinkDuration)); - if (phase < 0) phase = -phase; - _update.setOverLevel(phase); -} - -void TitleWidget::setHideLevel(float64 level) { - if (level != hideLevel) { - hideLevel = level; - if (hideLevel) { - if (!_hider) { - _hider.create(this); - _hider->setGeometry(rect()); - _hider->setClickedCallback([this]() { emit hiderClicked(); }); - _hider->setVisible(!Adaptive::OneColumn()); - } - _hider->setLevel(hideLevel); - } else { - if (_hider) { - _hider.destroyDelayed(); - } - } - } -} - -void TitleWidget::onContacts() { - if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray(); - - if (!App::self()) return; - Ui::showLayer(new ContactsBox()); -} - -void TitleWidget::onAbout() { - if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray(); - Ui::showLayer(new AboutBox()); -} - -void TitleWidget::updatePlayerButton(bool usePanel) { - if (usePanel && !_player) { - _player.create(this); - } else if (!usePanel && _player) { - _player.destroyDelayed(); - } - updateControlsVisibility(); -} - -void TitleWidget::updateControlsPosition() { - QPoint p(width() - ((cPlatform() == dbipWindows && lastMaximized) ? 0 : st::sysBtnDelta), 0); - - if (!_update.isHidden()) { - p.setX(p.x() - _update.width()); - _update.move(p); - if (!_lock.isHidden()) { - p.setX(p.x() - _lock.width()); - _lock.move(p); - p.setX(p.x() + _lock.width()); - } - p.setX(p.x() + _update.width()); - } - _cancel.move(p.x() - _cancel.width(), 0); - - if (cPlatform() == dbipWindows) { - p.setX(p.x() - _close.width()); - _close.move(p); - - p.setX(p.x() - _maximize.width()); - _restore.move(p); _maximize.move(p); - - p.setX(p.x() - _minimize.width()); - _minimize.move(p); - } - if (_update.isHidden() && !_lock.isHidden()) { - p.setX(p.x() - _lock.width()); - _lock.move(p); - } - if (_player) { - p.setX(p.x() - _player->width()); - _player->move(p); - } - - _settings.move(st::titleMenuOffset, 0); - if (_contacts.isHidden()) { - _about.move(_settings.x() + _settings.width(), 0); - } else { - _contacts.move(_settings.x() + _settings.width(), 0); - _about.move(_contacts.x() + _contacts.width(), 0); - } - - if (_hider) { - _hider->resize(size()); - } -} - -void TitleWidget::resizeEvent(QResizeEvent *e) { - updateControlsPosition(); -} - -void TitleWidget::updateControlsVisibility() { - auto passcoded = App::passcoded(); - auto authed = (App::main() != nullptr); - auto selecting = authed && App::main()->selectingPeer(); - auto oneColumnSelecting = (Adaptive::OneColumn() && selecting && !passcoded); - - _cancel.setVisible(oneColumnSelecting); - - updateRestartButtonVisibility(); - updateMenuButtonsVisibility(); - updateSystemButtonsVisibility(); - - updateControlsPosition(); - update(); -} - -void TitleWidget::updateRestartButtonVisibility() { -#ifndef TDESKTOP_DISABLE_AUTOUPDATE - bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady); -#else // !TDESKTOP_DISABLE_AUTOUPDATE - bool updateReady = false; -#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE - auto scaleRestarting = cEvalScale(cConfigScale()) != cEvalScale(cRealScale()); - - auto updateVisible = _cancel.isHidden() && (updateReady || scaleRestarting); - if (updateVisible) { - _update.setText(lang(updateReady ? lng_menu_update : lng_menu_restart)); - _update.show(); - _a_update.start(); - } else { - _update.hide(); - _a_update.stop(); - } -} - -void TitleWidget::updateMenuButtonsVisibility() { - if (_cancel.isHidden()) { - if (App::passcoded()) { - _settings.hide(); - _contacts.hide(); - _about.hide(); - _lock.setSysBtnStyle(st::sysUnlock); - } else { - _lock.setSysBtnStyle(st::sysLock); - _settings.show(); - _contacts.setVisible(App::main() != nullptr); - _about.show(); - } - } else { - _settings.hide(); - _contacts.hide(); - _about.hide(); - } -} - -void TitleWidget::updateSystemButtonsVisibility() { - if (_cancel.isHidden()) { - _lock.setVisible(Global::LocalPasscode()); - if (_player) { - _player->show(); - } - } else { - _lock.hide(); - if (_player) { - _player->hide(); - } - } - if (_update.isHidden() && _cancel.isHidden() && cPlatform() == dbipWindows) { - _minimize.show(); - maximizedChanged(lastMaximized, true); - _close.show(); - } else { - _minimize.hide(); - _restore.hide(); - _maximize.hide(); - _close.hide(); - } -} - -void TitleWidget::updateAdaptiveLayout() { - updateControlsVisibility(); - if (Adaptive::OneColumn()) { - updateCounter(); - } - if (_hider) { - _hider->setVisible(!Adaptive::OneColumn()); - } -} - -void TitleWidget::updateCounter() { - if (!Adaptive::OneColumn() || !MTP::authedId()) return; - - int32 counter = App::histories().unreadBadge(); - bool muted = App::histories().unreadOnlyMuted(); - - if (counter > 0) { - int32 size = cRetina() ? -32 : -16; - switch (cScale()) { - case dbisOneAndQuarter: size = -20; break; - case dbisOneAndHalf: size = -24; break; - case dbisTwo: size = -32; break; - } - auto &bg = (muted ? st::titleCounterBgMute : st::titleCounterBg); - auto &fg = st::titleCounterFg; - _counter = App::pixmapFromImageInPlace(App::wnd()->iconWithCounter(size, counter, bg, fg, false)); - _counter.setDevicePixelRatio(cRetinaFactor()); - update(QRect(st::titleCounterPosition, _counter.size() / cIntRetinaFactor())); - } else { - if (!_counter.isNull()) { - update(QRect(st::titleCounterPosition, _counter.size() / cIntRetinaFactor())); - _counter = QPixmap(); - } - } -} - -void TitleWidget::mousePressEvent(QMouseEvent *e) { - if (auto wnd = App::wnd()) { - if (wnd->psHandleTitle()) return; - if (e->buttons() & Qt::LeftButton) { - wnd->wStartDrag(e); - e->accept(); - } - } -} - -void TitleWidget::mouseDoubleClickEvent(QMouseEvent *e) { - if (auto wnd = App::wnd()) { - if (wnd->psHandleTitle()) return; - Qt::WindowStates s(wnd->windowState()); - if (s.testFlag(Qt::WindowMaximized)) { - wnd->setWindowState(s & ~Qt::WindowMaximized); - } else { - wnd->setWindowState(s | Qt::WindowMaximized); - } - } -} - -void TitleWidget::onWindowStateChanged(Qt::WindowState state) { - if (state == Qt::WindowMinimized) return; - maximizedChanged(state == Qt::WindowMaximized); -} - -void TitleWidget::maximizedChanged(bool maximized, bool force) { - if (lastMaximized == maximized && !force) return; - - lastMaximized = maximized; - - if (cPlatform() != dbipWindows || !_update.isHidden()) return; - if (maximized) { - _maximize.clearState(); - } else { - _restore.clearState(); - } - - _maximize.setVisible(!maximized); - _restore.setVisible(maximized); - - updateControlsPosition(); -} - -HitTestType TitleWidget::hitTest(const QPoint &p) { - if (App::wnd() && Ui::isLayerShown()) return HitTestType::None; - - int x(p.x()), y(p.y()), w(width()), h(height()); - if (!Adaptive::OneColumn() && _hider && x >= App::main()->dlgsWidth()) return HitTestType::None; - - if (x >= st::titleIconPosition.x() && y >= st::titleIconPosition.y() && x < st::titleIconPosition.x() + st::titleIcon.width() && y < st::titleIconPosition.y() + st::titleIcon.height()) { - return HitTestType::Icon; - } else if (false - || (_player && _player->geometry().contains(p)) - || (_lock.hitTest(p - _lock.geometry().topLeft()) == HitTestType::SysButton && _lock.isVisible()) - || (_update.hitTest(p - _update.geometry().topLeft()) == HitTestType::SysButton && _update.isVisible()) - || (_minimize.hitTest(p - _minimize.geometry().topLeft()) == HitTestType::SysButton) - || (_maximize.hitTest(p - _maximize.geometry().topLeft()) == HitTestType::SysButton) - || (_restore.hitTest(p - _restore.geometry().topLeft()) == HitTestType::SysButton) - || (_close.hitTest(p - _close.geometry().topLeft()) == HitTestType::SysButton) - ) { - return HitTestType::SysButton; - } else if (x >= 0 && x < w && y >= 0 && y < h) { - if (false - || (!_cancel.isHidden() && _cancel.geometry().contains(x, y)) - || (!_settings.isHidden() && _settings.geometry().contains(x, y)) - || (!_contacts.isHidden() && _contacts.geometry().contains(x, y)) - || (!_about.isHidden() && _about.geometry().contains(x, y)) - ) { - return HitTestType::Client; - } - return HitTestType::Caption; - } - return HitTestType::None; -} - -QRect TitleWidget::iconRect() const { - return myrtlrect(QRect(st::titleIconPosition, st::titleIcon.size())); -} diff --git a/Telegram/SourceFiles/title.h b/Telegram/SourceFiles/title.h deleted file mode 100644 index ea278669e..000000000 --- a/Telegram/SourceFiles/title.h +++ /dev/null @@ -1,98 +0,0 @@ -/* -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 -#include "sysbuttons.h" - -class MainWindow; -namespace Media { -namespace Player { -class TitleButton; -} // namespace Player -} // namespace Media -class AudioMsgId; - -class TitleWidget : public TWidget, private base::Subscriber { - Q_OBJECT - -public: - TitleWidget(QWidget *parent); - - void updateCounter(); - - void maximizedChanged(bool maximized, bool force = false); - - HitTestType hitTest(const QPoint &p); - QRect iconRect() const; - - void setHideLevel(float64 level); - - void step_update(float64 ms, bool timer); - -public slots: - void onWindowStateChanged(Qt::WindowState state = Qt::WindowNoState); - void updateControlsVisibility(); - void onContacts(); - void onAbout(); - -signals: - void hiderClicked(); - -protected: - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseDoubleClickEvent(QMouseEvent *e) override; - -private: - void updatePlayerButton(bool usePanel); - void updateAdaptiveLayout(); - void updateRestartButtonVisibility(); - void updateMenuButtonsVisibility(); - void updateSystemButtonsVisibility(); - void updateControlsPosition(); - - style::color statusColor; - - class Hider; - float64 hideLevel = 0; - ChildWidget _hider = { nullptr }; - - float64 _lastUpdateMs; - - FlatButton _cancel, _settings, _contacts, _about; - - ChildWidget _player = { nullptr }; - LockBtn _lock; - UpdateBtn _update; - MinimizeBtn _minimize; - MaximizeBtn _maximize; - RestoreBtn _restore; - CloseBtn _close; - - Animation _a_update; - - bool lastMaximized; - - QPixmap _counter; - -}; diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index bf3b19430..262520044 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -22,12 +22,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org using "basic.style"; using "ui/widgets/widgets.style"; -titleIconPosition: point(9px, 9px); -titleIcon: icon { - { "title_icon_bg", #49708f }, - { "title_icon", #ffffff, point(4px, 4px) } -}; -titleCounterPosition: point(17px, 17px); +windowMinWidth: 380px; +windowMinHeight: 480px; +windowDefWidth: 800px; +windowDefHeight: 600px; +windowShadow: icon {{ "window_shadow", windowShadowFg }}; +windowShadowShift: 1px; + +adaptiveNormalWidth: 640px; +adaptiveWideWidth: 1366px; notifyBorder: #f1f1f1; notifyBorderWidth: 1px; @@ -78,3 +81,50 @@ notifySendReply: IconButton { iconPosition: point(0px, 0px); iconPositionDown: point(0px, 1px); } + +titleHeight: 21px; +titleButtonMinimize: IconButton { + width: 24px; + height: 21px; + + icon: icon {{ "title_button_minimize", titleButtonFg, point(4px, 4px) }}; + iconOver: icon { + { size(24px, 21px), titleButtonBgOver }, + { "title_button_minimize", titleButtonFgOver, point(4px, 4px) }, + }; + + iconPosition: point(0px, 0px); + iconPositionDown: point(0px, 0px); +} +titleButtonMaximize: IconButton(titleButtonMinimize) { + icon: icon {{ "title_button_maximize", titleButtonFg, point(4px, 4px) }}; + iconOver: icon { + { size(24px, 21px), titleButtonBgOver }, + { "title_button_maximize", titleButtonFgOver, point(4px, 4px) }, + }; +} +titleButtonRestoreIcon: icon {{ "title_button_restore", titleButtonFg, point(4px, 4px) }}; +titleButtonRestoreIconOver: icon { + { size(24px, 21px), titleButtonBgOver }, + { "title_button_restore", titleButtonFgOver, point(4px, 4px) }, +}; +titleButtonClose: IconButton(titleButtonMinimize) { + width: 25px; + + icon: icon {{ "title_button_close", titleButtonFg, point(5px, 4px) }}; + iconOver: icon { + { size(25px, 21px), titleButtonCloseBgOver }, + { "title_button_close", titleButtonCloseFgOver, point(5px, 4px) }, + }; +} + +// Mac specific + +macAccessoryWidth: 450.; +macAccessoryHeight: 90.; +macEnableFilterAdd: 2; +macEnableFilterTop: 5; +macSelectorTop: 6; +macAlwaysThisAppTop: 4; +macAppHintTop: 8; +macCautionIconSize: 16; diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp index 01e74ab87..67ced2a08 100644 --- a/Telegram/SourceFiles/window/window_theme.cpp +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -82,14 +82,14 @@ inline uchar readHexUchar(char char1, char char2, bool &error) { return ((readHexUchar(char1, error) & 0x0F) << 4) | (readHexUchar(char2, error) & 0x0F); } -bool readNameAndValue(const char *&from, const char *end, QByteArray *outName, QByteArray *outValue) { +bool readNameAndValue(const char *&from, const char *end, QLatin1String *outName, QLatin1String *outValue) { using base::parse::skipWhitespaces; using base::parse::readName; if (!skipWhitespaces(from, end)) return true; *outName = readName(from, end); - if (outName->isEmpty()) { + if (outName->size() == 0) { LOG(("Error: Could not read name in the color scheme.")); return false; } @@ -108,11 +108,11 @@ bool readNameAndValue(const char *&from, const char *end, QByteArray *outName, Q auto valueStart = from; if (*from == '#') ++from; - if (readName(from, end).isEmpty()) { + if (readName(from, end).size() == 0) { LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme.")); return false; } - *outValue = QByteArray::fromRawData(valueStart, from - valueStart); + *outValue = QLatin1String(valueStart, from - valueStart); if (!skipWhitespaces(from, end)) { LOG(("Error: Unexpected end of the color scheme.")); @@ -126,47 +126,66 @@ bool readNameAndValue(const char *&from, const char *end, QByteArray *outName, Q return true; } +enum class SetResult { + Ok, + Bad, + NotFound, +}; +SetResult setColorSchemeValue(QLatin1String name, QLatin1String value, Instance *out) { + auto found = false; + auto size = value.size(); + auto data = value.data(); + if (data[0] == '#' && (size == 7 || size == 9)) { + auto error = false; + auto r = readHexUchar(data[1], data[2], error); + auto g = readHexUchar(data[3], data[4], error); + auto b = readHexUchar(data[5], data[6], error); + auto a = (size == 9) ? readHexUchar(data[7], data[8], error) : uchar(255); + if (error) { + LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme (while applying '%1: %2')").arg(QLatin1String(name)).arg(QLatin1String(value))); + return SetResult::Bad; + } else if (out) { + found = out->palette.setColor(name, r, g, b, a); + } else { + found = style::main_palette::setColor(name, r, g, b, a); + } + } else { + if (out) { + found = out->palette.setColor(name, value); + } else { + found = style::main_palette::setColor(name, value); + } + } + return found ? SetResult::Ok : SetResult::NotFound; +} + bool loadColorScheme(const QByteArray &content, Instance *out = nullptr) { if (content.size() > kThemeSchemeSizeLimit) { LOG(("Error: color scheme file too large (should be less than 1 MB, got %2)").arg(content.size())); return false; } + QMap unsupported; auto data = base::parse::stripComments(content); auto from = data.constData(), end = from + data.size(); while (from != end) { - QByteArray name, value; + QLatin1String name, value; if (!readNameAndValue(from, end, &name, &value)) { return false; } - if (name.isEmpty()) { // End of content reached. + if (name.size() == 0) { // End of content reached. return true; } - auto size = value.size(); - auto error = false; - if (value[0] == '#' && (size == 7 || size == 9)) { - auto r = readHexUchar(value[1], value[2], error); - auto g = readHexUchar(value[3], value[4], error); - auto b = readHexUchar(value[5], value[6], error); - auto a = (size == 9) ? readHexUchar(value[7], value[8], error) : uchar(255); - if (!error) { - if (out) { - error = !out->palette.setColor(QLatin1String(name), r, g, b, a); - } else { - error = !style::main_palette::setColor(QLatin1String(name), r, g, b, a); - } - } - } else { - if (out) { - error = !out->palette.setColor(QLatin1String(name), QLatin1String(value)); - } else { - error = !style::main_palette::setColor(QLatin1String(name), QLatin1String(value)); - } - } - if (error) { - LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme (while applying '%1: %2')").arg(QLatin1String(name)).arg(QLatin1String(value))); + // Find the named value in the already read unsupported list. + value = unsupported.value(value, value); + + auto result = setColorSchemeValue(name, value, out); + if (result == SetResult::Bad) { return false; + } else if (result == SetResult::NotFound) { + LOG(("Warning: unexpected name or value in the color scheme (while applying '%1: %2')").arg(name).arg(value)); + unsupported.insert(name, value); } } return true; diff --git a/Telegram/SourceFiles/media/player/media_player_title_button.h b/Telegram/SourceFiles/window/window_title.h similarity index 61% rename from Telegram/SourceFiles/media/player/media_player_title_button.h rename to Telegram/SourceFiles/window/window_title.h index b9b0f4e47..41916387a 100644 --- a/Telegram/SourceFiles/media/player/media_player_title_button.h +++ b/Telegram/SourceFiles/window/window_title.h @@ -20,35 +20,34 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include "ui/button.h" +namespace Window { -namespace Media { -namespace Player { +enum class HitTestResult { + None = 0, + Client, + SysButton, + Icon, + Caption, + Top, + TopRight, + Right, + BottomRight, + Bottom, + BottomLeft, + Left, + TopLeft, +}; -class PlayButtonLayout; - -class TitleButton : public Button, private base::Subscriber { +class TitleWidget : public TWidget { public: - TitleButton(QWidget *parent); - - void updatePauseState(); - - ~TitleButton(); - -protected: - void paintEvent(QPaintEvent *e) override; - void enterEvent(QEvent *e) override; - void leaveEvent(QEvent *e) override; - - void onStateChanged(int oldState, ButtonStateChangeSource source) override; - -private: - void paintIcon(Painter &p); - - std_::unique_ptr _layout; - ColorAnimation _iconFg; + using TWidget::TWidget; + virtual HitTestResult hitTest(const QPoint &p) const { + return HitTestResult::None; + } + virtual QRect iconRect() const { + return QRect(); + } }; -} // namespace Clip -} // namespace Media +} // namespace Window diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index c3448f815..6faf9b7d5 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -151,10 +151,6 @@ '<(src_loc)/shortcuts.h', '<(src_loc)/structs.cpp', '<(src_loc)/structs.h', - '<(src_loc)/sysbuttons.cpp', - '<(src_loc)/sysbuttons.h', - '<(src_loc)/title.cpp', - '<(src_loc)/title.h', '<(src_loc)/mainwindow.cpp', '<(src_loc)/mainwindow.h', '<(src_loc)/boxes/aboutbox.cpp', @@ -288,8 +284,6 @@ '<(src_loc)/media/player/media_player_list.h', '<(src_loc)/media/player/media_player_panel.cpp', '<(src_loc)/media/player/media_player_panel.h', - '<(src_loc)/media/player/media_player_title_button.cpp', - '<(src_loc)/media/player/media_player_title_button.h', '<(src_loc)/media/player/media_player_volume_controller.cpp', '<(src_loc)/media/player/media_player_volume_controller.h', '<(src_loc)/media/player/media_player_widget.cpp', @@ -348,6 +342,7 @@ '<(src_loc)/mtproto/session.h', '<(src_loc)/overview/overview_layout.cpp', '<(src_loc)/overview/overview_layout.h', + '<(src_loc)/pspecific.h', '<(src_loc)/pspecific_win.cpp', '<(src_loc)/pspecific_win.h', '<(src_loc)/pspecific_mac.cpp', @@ -378,6 +373,8 @@ '<(src_loc)/platform/win/main_window_win.h', '<(src_loc)/platform/win/notifications_manager_win.cpp', '<(src_loc)/platform/win/notifications_manager_win.h', + '<(src_loc)/platform/win/window_title_win.cpp', + '<(src_loc)/platform/win/window_title_win.h', '<(src_loc)/platform/win/windows_app_user_model_id.cpp', '<(src_loc)/platform/win/windows_app_user_model_id.h', '<(src_loc)/platform/win/windows_dlls.cpp', @@ -387,6 +384,7 @@ '<(src_loc)/platform/platform_file_dialog.h', '<(src_loc)/platform/platform_main_window.h', '<(src_loc)/platform/platform_notifications_manager.h', + '<(src_loc)/platform/platform_window_title.h', '<(src_loc)/profile/profile_actions_widget.cpp', '<(src_loc)/profile/profile_actions_widget.h', '<(src_loc)/profile/profile_block_widget.cpp', @@ -561,6 +559,7 @@ '<(src_loc)/window/window_theme.h', '<(src_loc)/window/window_theme_warning.cpp', '<(src_loc)/window/window_theme_warning.h', + '<(src_loc)/window/window_title.h', '<(sp_media_key_tap_loc)/SPMediaKeyTap.m', '<(sp_media_key_tap_loc)/SPMediaKeyTap.h', From 3a599e0752d485884497bca5a64c7fd08bd044f8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 4 Nov 2016 14:14:47 +0300 Subject: [PATCH 015/100] New main menu in DialogsWidget. Also "flip_horizontal" and "flip_vertical" modifiers support added. Also moving parts of MainWindow to Window::MainWindow. --- Telegram/Resources/basic.style | 29 +--- Telegram/Resources/colors.palette | 37 +++-- .../Resources/icons/dialogs_add_contact.png | Bin 124 -> 0 bytes .../icons/dialogs_add_contact@2x.png | Bin 163 -> 0 bytes Telegram/Resources/icons/dialogs_menu.png | Bin 0 -> 110 bytes Telegram/Resources/icons/dialogs_menu@2x.png | Bin 0 -> 148 bytes ...moji_switch_right.png => emoji_switch.png} | Bin ...witch_right@2x.png => emoji_switch@2x.png} | Bin .../Resources/icons/emoji_switch_left.png | Bin 145 -> 0 bytes .../Resources/icons/emoji_switch_left@2x.png | Bin 236 -> 0 bytes ...rizontal_right.png => fade_horizontal.png} | Bin ...al_right@2x.png => fade_horizontal@2x.png} | Bin .../Resources/icons/fade_horizontal_left.png | Bin 139 -> 0 bytes .../icons/fade_horizontal_left@2x.png | Bin 192 -> 0 bytes .../Resources/icons/mediaview_previous.png | Bin 304 -> 0 bytes .../Resources/icons/mediaview_previous@2x.png | Bin 496 -> 0 bytes Telegram/Resources/icons/menu_contacts.png | Bin 0 -> 355 bytes Telegram/Resources/icons/menu_contacts@2x.png | Bin 0 -> 661 bytes Telegram/Resources/icons/menu_help.png | Bin 0 -> 393 bytes Telegram/Resources/icons/menu_help@2x.png | Bin 0 -> 805 bytes Telegram/Resources/icons/menu_new_channel.png | Bin 0 -> 210 bytes .../Resources/icons/menu_new_channel@2x.png | Bin 0 -> 392 bytes Telegram/Resources/icons/menu_new_group.png | Bin 0 -> 336 bytes .../Resources/icons/menu_new_group@2x.png | Bin 0 -> 667 bytes Telegram/Resources/icons/menu_settings.png | Bin 0 -> 559 bytes Telegram/Resources/icons/menu_settings@2x.png | Bin 0 -> 1136 bytes .../Resources/icons/player_panel_previous.png | Bin 182 -> 0 bytes .../icons/player_panel_previous@2x.png | Bin 292 -> 0 bytes Telegram/Resources/icons/player_previous.png | Bin 201 -> 0 bytes .../Resources/icons/player_previous@2x.png | Bin 325 -> 0 bytes ...gs_new_chat.png => settings_edit_name.png} | Bin ..._chat@2x.png => settings_edit_name@2x.png} | Bin .../{title_previous.png => title_back.png} | Bin ...itle_previous@2x.png => title_back@2x.png} | Bin .../Resources/icons/title_button_lock.png | Bin 316 -> 0 bytes .../Resources/icons/title_button_lock@2x.png | Bin 573 -> 0 bytes .../Resources/icons/title_button_unlock.png | Bin 314 -> 0 bytes .../icons/title_button_unlock@2x.png | Bin 557 -> 0 bytes Telegram/Resources/icons/title_next.png | Bin 154 -> 0 bytes Telegram/Resources/icons/title_next@2x.png | Bin 261 -> 0 bytes Telegram/Resources/langs/lang.strings | 2 - Telegram/Resources/sample.tdesktop-theme | 27 ++-- Telegram/SourceFiles/boxes/addcontactbox.cpp | 58 ------- Telegram/SourceFiles/boxes/addcontactbox.h | 28 ---- Telegram/SourceFiles/boxes/boxes.style | 3 +- .../SourceFiles/codegen/style/generator.cpp | 22 +-- .../SourceFiles/codegen/style/parsed_file.cpp | 47 ++++-- .../SourceFiles/codegen/style/parsed_file.h | 6 +- Telegram/SourceFiles/core/stl_subset.h | 2 + Telegram/SourceFiles/core/utils.h | 23 ++- Telegram/SourceFiles/dialogs/dialogs.style | 48 ++++-- Telegram/SourceFiles/dialogswidget.cpp | 151 +++++++++--------- Telegram/SourceFiles/dialogswidget.h | 40 ++--- Telegram/SourceFiles/intro/intro.style | 4 +- Telegram/SourceFiles/mainwidget.cpp | 9 +- Telegram/SourceFiles/mainwidget.h | 2 - Telegram/SourceFiles/mainwindow.cpp | 111 +++---------- Telegram/SourceFiles/mainwindow.h | 10 +- .../media/player/media_player.style | 8 +- .../SourceFiles/media/view/mediaview.style | 2 +- Telegram/SourceFiles/overviewwidget.cpp | 2 +- .../platform/linux/main_window_linux.cpp | 85 +--------- .../platform/linux/main_window_linux.h | 14 -- .../platform/mac/main_window_mac.h | 16 +- .../platform/mac/main_window_mac.mm | 100 ++---------- .../platform/win/main_window_win.cpp | 114 ++----------- .../platform/win/main_window_win.h | 16 +- .../platform/win/window_title_win.cpp | 5 +- .../platform/win/window_title_win.h | 2 + .../platform/win/windows_event_filter.cpp | 41 +---- Telegram/SourceFiles/pspecific_win.cpp | 2 +- Telegram/SourceFiles/settings/settings.style | 3 +- Telegram/SourceFiles/stickers/stickers.style | 4 +- Telegram/SourceFiles/ui/twidget.h | 51 +++--- .../SourceFiles/ui/widgets/dropdown_menu.cpp | 4 + .../SourceFiles/ui/widgets/dropdown_menu.h | 1 + Telegram/SourceFiles/ui/widgets/menu.cpp | 10 +- Telegram/SourceFiles/ui/widgets/menu.h | 1 + .../SourceFiles/ui/widgets/popup_menu.cpp | 4 + Telegram/SourceFiles/ui/widgets/popup_menu.h | 1 + Telegram/SourceFiles/ui/widgets/widgets.style | 3 + Telegram/SourceFiles/window/main_window.cpp | 140 +++++++++++++++- Telegram/SourceFiles/window/main_window.h | 45 +++++- .../window/notifications_manager_default.cpp | 1 + Telegram/SourceFiles/window/window.style | 4 +- Telegram/SourceFiles/window/window_title.h | 1 - 86 files changed, 565 insertions(+), 774 deletions(-) delete mode 100644 Telegram/Resources/icons/dialogs_add_contact.png delete mode 100644 Telegram/Resources/icons/dialogs_add_contact@2x.png create mode 100644 Telegram/Resources/icons/dialogs_menu.png create mode 100644 Telegram/Resources/icons/dialogs_menu@2x.png rename Telegram/Resources/icons/{emoji_switch_right.png => emoji_switch.png} (100%) rename Telegram/Resources/icons/{emoji_switch_right@2x.png => emoji_switch@2x.png} (100%) delete mode 100644 Telegram/Resources/icons/emoji_switch_left.png delete mode 100644 Telegram/Resources/icons/emoji_switch_left@2x.png rename Telegram/Resources/icons/{fade_horizontal_right.png => fade_horizontal.png} (100%) rename Telegram/Resources/icons/{fade_horizontal_right@2x.png => fade_horizontal@2x.png} (100%) delete mode 100644 Telegram/Resources/icons/fade_horizontal_left.png delete mode 100644 Telegram/Resources/icons/fade_horizontal_left@2x.png delete mode 100644 Telegram/Resources/icons/mediaview_previous.png delete mode 100644 Telegram/Resources/icons/mediaview_previous@2x.png create mode 100644 Telegram/Resources/icons/menu_contacts.png create mode 100644 Telegram/Resources/icons/menu_contacts@2x.png create mode 100644 Telegram/Resources/icons/menu_help.png create mode 100644 Telegram/Resources/icons/menu_help@2x.png create mode 100644 Telegram/Resources/icons/menu_new_channel.png create mode 100644 Telegram/Resources/icons/menu_new_channel@2x.png create mode 100644 Telegram/Resources/icons/menu_new_group.png create mode 100644 Telegram/Resources/icons/menu_new_group@2x.png create mode 100644 Telegram/Resources/icons/menu_settings.png create mode 100644 Telegram/Resources/icons/menu_settings@2x.png delete mode 100644 Telegram/Resources/icons/player_panel_previous.png delete mode 100644 Telegram/Resources/icons/player_panel_previous@2x.png delete mode 100644 Telegram/Resources/icons/player_previous.png delete mode 100644 Telegram/Resources/icons/player_previous@2x.png rename Telegram/Resources/icons/{dialogs_new_chat.png => settings_edit_name.png} (100%) rename Telegram/Resources/icons/{dialogs_new_chat@2x.png => settings_edit_name@2x.png} (100%) rename Telegram/Resources/icons/{title_previous.png => title_back.png} (100%) rename Telegram/Resources/icons/{title_previous@2x.png => title_back@2x.png} (100%) delete mode 100644 Telegram/Resources/icons/title_button_lock.png delete mode 100644 Telegram/Resources/icons/title_button_lock@2x.png delete mode 100644 Telegram/Resources/icons/title_button_unlock.png delete mode 100644 Telegram/Resources/icons/title_button_unlock@2x.png delete mode 100644 Telegram/Resources/icons/title_next.png delete mode 100644 Telegram/Resources/icons/title_next@2x.png diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 5b3a6aa48..36f48aa70 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -402,25 +402,12 @@ noContactsHeight: 100px; noContactsFont: font(fsize); noContactsColor: #777777; -fieldSearchIcon: icon {{ "box_search", #aaaaaa, point(10px, 9px) }}; -dlgFilter: flatInput(inpDefGray) { - font: font(fsize); - bgColor: #f2f2f2; - phColor: #949494; - phFocusColor: #a4a4a4; - icon: fieldSearchIcon; - - width: 240px; - height: 34px; - textMrg: margins(34px, 2px, 34px, 4px); -} - topBarHeight: 54px; topBarDuration: 200; -topBarForward: icon {{ "title_next", #a3a3a3 }}; -topBarBackward: icon {{ "title_previous", #a3a3a3 }}; +topBarForward: icon {{ "title_back-flip_horizontal", #a3a3a3 }}; +topBarBackward: icon {{ "title_back", #a3a3a3 }}; topBarForwardAlpha: 0.6; -topBarBack: icon {{ "title_previous", #259fd8 }}; +topBarBack: icon {{ "title_back", #259fd8 }}; topBarBackAlpha: 0.8; topBarBackColor: #005faf; topBarBackFont: font(16px); @@ -1104,15 +1091,15 @@ infoButton: PeerAvatarButton { photoSize: 42px; } -// forward declaration for single "title_previous" usage. +// forward declaration for single "title_back" usage. profileTopBarBackIconFg: #0290d7; -profileTopBarBackIcon: icon {{ "title_previous", profileTopBarBackIconFg }}; +profileTopBarBackIcon: icon {{ "title_back", profileTopBarBackIconFg }}; historyReplyCancelIcon: icon {{ "box_button_close-invert", historyReplyBg }}; boxSearchCancelIcon: icon {{ "box_button_close-invert", boxSearchBg }}; settingsFixedBarCloseIcon: icon {{ "box_button_close-invert", settingsFixedBarBg }}; -notifyFadeRight: icon {{ "fade_horizontal_right", notificationBg }}; +notifyFadeRight: icon {{ "fade_horizontal", notificationBg }}; -stickerIconLeft: icon {{ "fade_horizontal_left", emojiPanCategories }}; -stickerIconRight: icon {{ "fade_horizontal_right", emojiPanCategories }}; +stickerIconLeft: icon {{ "fade_horizontal-flip_horizontal", emojiPanCategories }}; +stickerIconRight: icon {{ "fade_horizontal", emojiPanCategories }}; diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index c449b8ed8..da55dde33 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -24,7 +24,7 @@ windowBg: #ffffff; // white: fallback for background windowTextFg: #000000; // black: fallback for text color windowSubTextFg: #8a8a8a; // gray: fallback for subtext color windowActiveFill: #40ace3; // bright blue: fallback for blue filled active areas -windowOverBg: #edf2f5; // light blue: fallback for over background +windowOverBg: #f3f3f3; // light blue: fallback for over background windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over color windowActiveTextFg: #1485c2; // online blue: fallback for active color windowShadowFg: #000000; // black: fallback for shadow color @@ -32,22 +32,7 @@ windowShadowFg: #000000; // black: fallback for shadow color imageBg: #000000; imageBgTransparent: #ffffff; -// custom title bar for Windows -titleBg: #f3f3f3; -titleShadow: #00000003; -titleButtonFg: #ababab; -titleButtonBgOver: #e5e5e5; -titleButtonFgOver: #9a9a9a; -titleButtonCloseBgOver: #e81123; -titleButtonCloseFgOver: #ffffff; - -// tray icon -trayCounterBg: #f23c34; -trayCounterBgMute: #888888; -trayCounterFg: #ffffff; -trayCounterBgMacInvert: #ffffff; -trayCounterFgMacInvert: #ffffff01; - +// widgets cancelIconFg: #a2a2a2; cancelIconFgOver: #808080; @@ -63,6 +48,24 @@ lightButtonBgOver: #f2f7fa | lightButtonBg; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; +menuIconFg: #808080 | windowSubTextFg; + +// custom title bar for Windows +titleBg: windowOverBg; +titleShadow: #00000003; +titleButtonFg: #ababab; +titleButtonBgOver: #e5e5e5; +titleButtonFgOver: #9a9a9a; +titleButtonCloseBgOver: #e81123; +titleButtonCloseFgOver: #ffffff; + +// tray icon +trayCounterBg: #f23c34; +trayCounterBgMute: #888888; +trayCounterFg: #ffffff; +trayCounterBgMacInvert: #ffffff; +trayCounterFgMacInvert: #ffffff01; + // layers layerBg: #0000007f; diff --git a/Telegram/Resources/icons/dialogs_add_contact.png b/Telegram/Resources/icons/dialogs_add_contact.png deleted file mode 100644 index 0569c8732303693ade3a5a4c946cf16abde251bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|XHOT$5R2Zo6Ak$o6nL2U|Nl>) z6XV@}%%Z}0=Om>AJR5=zn3e4lb&cKkVv$FK_2+3qnL7eSEpk<-F8V(2uj@>U`sut6 X77K{~dMWuGXdHv5tDnm{r-UW|cb6&o diff --git a/Telegram/Resources/icons/dialogs_add_contact@2x.png b/Telegram/Resources/icons/dialogs_add_contact@2x.png deleted file mode 100644 index 6fca9095dd81bd93cd18068d16a86c467c18e9e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBvOHZJLn2z=UUTGXFyLW0=aP;7yEdz}J|9G`+Lg+ZCHT9FHg@2^e-rxELL? zHTi$*w)3&67xJ9hCjRE#`}F9J*>LGwth)cd#b<9{$jxlV>zvM7`wVC+gQu&X%Q~lo FCIEcWKMMc= diff --git a/Telegram/Resources/icons/dialogs_menu.png b/Telegram/Resources/icons/dialogs_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3833ce66df193d19a01ef3667d3b1cfa951b41 GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;=si%u$h(+()iwAib3^)!uu$TUy zdRVA3fRTAlfX)Gy^^mdK II;Vst0OZ3V1poj5 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs_menu@2x.png b/Telegram/Resources/icons/dialogs_menu@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a5beed83c6b38c4cc230047e65aca2bb49a8f790 GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dE(Vi}jArY-_uPX8#Fc4sN-2LkR z{u;-ljNG$4tThTs74jyXJUDMZa~aPP5WM%^u=rZ}4YQN1;wBTa?j#*^IOT9kbs^7^ g7$G17h3SyRXzR=CZ>hq09B2)Lr>mdKI;Vst0Gx9(Pyhe` literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/emoji_switch_right.png b/Telegram/Resources/icons/emoji_switch.png similarity index 100% rename from Telegram/Resources/icons/emoji_switch_right.png rename to Telegram/Resources/icons/emoji_switch.png diff --git a/Telegram/Resources/icons/emoji_switch_right@2x.png b/Telegram/Resources/icons/emoji_switch@2x.png similarity index 100% rename from Telegram/Resources/icons/emoji_switch_right@2x.png rename to Telegram/Resources/icons/emoji_switch@2x.png diff --git a/Telegram/Resources/icons/emoji_switch_left.png b/Telegram/Resources/icons/emoji_switch_left.png deleted file mode 100644 index c4b3f9089f5c9fa2f05da7cd2de6db5b57ad0d2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^96-#&!3HGb=lz)rq#`_B9780g)=oIcd%%E$+4riV z;3GxMg*w90JL;}WW$ vUP-OFr8G_V??g_k>vJyrU;OU!?08$x71Q`0%t-MA+QH!I>gTe~DWM4f0`@g# diff --git a/Telegram/Resources/icons/emoji_switch_left@2x.png b/Telegram/Resources/icons/emoji_switch_left@2x.png deleted file mode 100644 index 63c5dc067c68f9112a8123b3b7e641d0c3980f73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 236 zcmVi%1m62~rq)GL zb3zDtUJvI?(_|AI!7)a4&XolP_`kpap9_rev7iFp3o7AXK?A%A8sU%N0;~_*kn2Y% zN&3FG2^uA~BBhkhB=#zwNa`+AN=b)dunCNkwg)9ko&Z=X$~?~y5sc%w&bqF{vMgJX mYIj0X0qCpr_wSJ0eZK*G=#7uARLa}{0000ADD;F+irOl!V!sJ{e1!xH3(H+t o&b-MpX7=nc+;FXFcfDQQ=Js6S4_7PFfu=Kfy85}Sb4q9e0Mq_4%K!iX diff --git a/Telegram/Resources/icons/fade_horizontal_left@2x.png b/Telegram/Resources/icons/fade_horizontal_left@2x.png deleted file mode 100644 index d4e9f55a11f2b970b3267ce076226729d3ba69cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0YJ>e!3HGHhernjsdi5n#}JK)Qv;*9SPVJ3_x^9K zO|utEUA8$^!qMbdm`96BVaMDopQ57UDuJQfXU**2m+j(n_RPs+@9b`znK0A9*o8%j z=^dZ(Y+GaLxX||f``*r6rDU96z4!8+uh;rkbG=rPW9T?x|EqKkkL6uuPC?1!UG>s` r-(L-3h{&#WjqsPOJ6pDH)dxoQ%y&FydB<-7oy6ej>gTe~DWM4fMWINA diff --git a/Telegram/Resources/icons/mediaview_previous.png b/Telegram/Resources/icons/mediaview_previous.png deleted file mode 100644 index 8fb04e0349dca88df314ee64c64fb7ec7d388ac3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304 zcmV-00nh%4P)MI>4_e$egc24T=Q94@2dj<0000 C8i!K= diff --git a/Telegram/Resources/icons/mediaview_previous@2x.png b/Telegram/Resources/icons/mediaview_previous@2x.png deleted file mode 100644 index d746a1056961eaef94fc8761152c55dccbb2e519..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 496 zcmV~E zcivsxWH-*h2f>j44`%<_St2651Em!9`#pT$|9h{vc(>a{9LE5F)9Hj-t@b{HX0wUg z?S|(%pU-GC8pREK-$xWhNUQ7hidL)TZs2(y!Z1t~=QTR z!D_WKiYG||rIgz@2!g;Ue!t%_7!2H2(R4b6*7~Km)*9pS*ewU6(Fl*n!}#_=5Treu zEv}S8k|aj)#bWX5d9!$@)A>&6t6HzuD3wa~25Bk%TVWW&^E}f_+NYE&ilVgUmTizH zrI*VE^?E(e0{NfP&1Pd1e@dynfn`eV4DzIOI2_uD{}@ar6PJ|cGboVKZ1SXZxm>!5 z6M?dpV@66d`OsREXo>$tB>j>3Vm6z}EmKA`XpCs9Ao^-{yWP%cQmEMx3AbxTbi-jp zw@jwj>&b2UjOf_Hhz?%Nd_I>O9U0N#mk}M`iLBFZZ$Lz-R4Q^uC`R-U$cP?enSQ@7 mcMxZkQaBtAs8*}Le*Xjs4fbL;-Dcqc0000U5FF6epdjE2(C{E?1a#yJ!BT$1bNB`ZDkM`(szf9l#Ds9tjkJw5-qFsiWdi_l zAj~71|Ghw-=a5o@^u zpS1)44U3dg6hhGRJo>)R>-9>dl$J$@uj!BWm?ZkX2LRZ%t(^g|V$m=Rtg4D}9MiI_ z2hXxB#&OK5syGb8XKPy5HG?3St>t&Mtm zXkFLsZYZUkojv@@vTWC%&@>H`Bw5bj2>ky~{{{>xOL9HY{>uOW002ovPDHLkV1hdw BqBj5l literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/menu_contacts@2x.png b/Telegram/Resources/icons/menu_contacts@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5b879588a4053fe275ef3a47c56cf8c87f9b23f9 GIT binary patch literal 661 zcmV;G0&4wxdu< z-1SSo#+l4~zaewZBo+VwA;&7F9pG7g0Mgb6AZ^_Rpp=4P7~nV#AmUr?c^;Ui36#=M z2)t!VDcZJ;Y0I{4q?F#w_{X;g48uq}`(K%+$pt6?r;$011B8&!&-V^_p2y9?1whPJ zgDw;bH-G=xRVa=aV=$dgq0{MrAP67`0;sAAx~{|Ja!GnS0N`AXINI;`*l0Ak7fw+W zJRXm+*5<957=Z8lcf$v6Hk-&8%O1dFG6@YISl9LJ0kqrghXGVoO$;EhGAalHFvcF5 zO9%nSaU!k%DWhRh6y@Q$vMeXHZE^s;UhgGyA5kKgm@$TuB!yn!R;!iGGKz?JI2`Um zv|6p~HnKpBF`Uh2sH!RwLXZ%G-EJ3kU5|D0VE~`kXxhp>z^_*+!C-*9-R`L>6br!jef;|R!eX%)Irw*%N+leR$LRb1djMS5 zMNQM54*uPmrlIF~uLl?ohlz&|JQ|H&4%Kj> v>$*6f&%?s=`#@gwbE{BZ^Z`g)xd-?S7GH46*~<(i00000NkvXXu0mjf8zl(YCmYdQBsYkZiWjgKKZ_ZiNtAAJ;(Vxujr><#T~!_c zVnwMn&7AG=5i_rc_F~0ZYt(L#%)(iWZyq3W8u7ptW|^y#Lpxs;cPw{uq!Xi8H%70FsR3 z_~%XgR@!d2?sg;b#j)$%ivTDh&dlbjEX&~rUSB6;3^tpMH8aoihMU-Aj2Q;-|5ioY zwjVQXnq~~R-EJQP-0$}>;Cj7&3~;$zUcQGxtLxg@9hfl(A~HQ_02qehpIdad+u4_0 znx>1*Pp8wob-e^~JRaw*>pp@YU{zI4V-cAuiyHudzV9=RV`f>#vMkwk9lNgMk?i>bvYVJ1FJ&>xbuXT0>gLRS zo0)UW@bvuZEF8@F|K^-A=NcRUAbc>q+W~0#FW@!;i-Yhy59M+h^7%aEayf`bqYwxL zz~}P;0KnyP0Yy<@yWPTSwSw7f2IKJjVTrz`GMNnZO^T&}rfHZ=CJhA) zH*@uR9Rz{NHi-#duNRul=8x~+8}YoYz0E6U01;7^W#l;S`3#iHWe5ZU)MpJ5?;0(^ zU=T{B646fxmLAs z1_l=QM6tYM;mmRWlbXy#N4UI2^`auZNnZQ3uyF4MkCW_EX7LKyRs33ft{A z%Cc-ZxGc-qYPEj5!t?fo^?D7{=@cfD36nyf jiGZ8@dqw@H@$mv*j-tJrO3JK{00000NkvXXu0mjf#AtQ_ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/menu_new_channel.png b/Telegram/Resources/icons/menu_new_channel.png new file mode 100644 index 0000000000000000000000000000000000000000..03a1b1f405e9e4e078e1f041b3060f730ca4db2c GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjGd*1#Lp(a)PCm`cWXR)ke9A<# z7{|Vs##@)R%Cs||;yRJOmr2>ddXoRVkJdND7+MzV%{%OWAf0g)*Cd7CvmdTrx@yn$ z;QxCMPj8qavOvFZEgF^E|sE+_{Tm<3@rVhQ+GFH zF&!v_qd6=F#~fVbH;Wf#N+fTYI@kD{xBS%aOdG=QRUQbeT+Wz&>OR|(O20Y9LQZq4 zPRvy*KQGJFwP-=1%vRBQnFE&BGV7My-zoe{bKXg$MqV=UJ@?)zh@sBx(z@3f zvElZN?^mwsnwA<^d*PXhZ?YUi!>e6pOcvb%YzzyozfNT-U|Yy4aNO`~Z9Ypu%LOI| zhs;@*40zlasyT8Q7^bAo{L&T5dO{$IvEfhJCB_{V-}&?J`uH<1`QKT4^8a18T2F=> gehfGIzBlAw-Tyqw^hXFUFoYRAUHx3vIVCg!07VX*3;+NC literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/menu_new_group.png b/Telegram/Resources/icons/menu_new_group.png new file mode 100644 index 0000000000000000000000000000000000000000..4d9b1380f557e05d4b1ea09c174b3dc7cf03bdad GIT binary patch literal 336 zcmV-W0k8gvP)Q52`#^M277vkNc` z!^*9-r+n+f5;4Y3>q>NebPf~Lb-i1ws_HV|UC1}*oRm^zj3HwTNhv9cqRV`1hOhc6 zk|aT%=g?ZC?|V4s;G9F(bOQ3_~!+uq?}a4uC!xoUL91`R+Vp i?EWtLvN`xAIMNeMXpY5XCo6;i00006R1# literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/menu_new_group@2x.png b/Telegram/Resources/icons/menu_new_group@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3512799e63e968a475ba2cace8a978a391d77dac GIT binary patch literal 667 zcmV;M0%ZM(P)m1rpUGr!wOYOG zJ2bkEgph~Do6Kf23H_xry~Q^SgUNSl)PJF1Fi6|=N1+a|TCJ!Dwd>33J3$?w-ELD4YS)+5 ze+1RL77B%Mxm-Sdf2Y$a@;v|7{RfSH(GW!u7mJ1CD&d7B2M`2->Z@nva5z9T8ok|zLLs^Z4^{c@PVbo#t?$N z!DLS5!I&M*J={~=1=DQStTnS|vIzkIgkJ>no8q<&8jS`Zq9JOv+P^ahhr_ncer(|N zdTpIq0UyCI3~skuTr3ut%jGZvN5M}}6a}45r`e$0Zof@DolYnS0(Lqb6h-kZ zx7BKy4S1eMRaL18yphy@7Va+3*Q5Gb{r$wbeSLj$ zm6flFdwP0^b9;Mx!1l zo124dHcKr0D_&k+V0LyEOePa7EiFMJk@yr(CX>K0OydTYmzV1nFPF;=3zxzb2m~54 zz;PTD3I*9jHi|-_0363jH*j!p&}!`;*x%omW?*!56spx~t95^%TCIZJZYMK9Q52k= zowZtX4Ngx_$qe{>z8@7zrBdMc`@v$d0K+iAFboV04M89ffOI(*tU?n&|lY4tBd8-Q3)uU@(ZRRx47eR7j;#A)C#HR#sNf_4PFx z9v*Ipo2F@`R;#~_)wm*k_y0?roScyNz{tqRue#7wDiyHVY|=g@_V)H#t@{JJySviA zg;K;>$+=eYlFwf z$4@(QdwUD>^YhTz*$FFld!@Da80(iY%Fc=K5u&@B}c>Gg5kw^ed)416pK6qwk zW+0Qv0MGN_bUJI|9UUF8zP>K$AJXgVE3B@rf?lt$;hUVCgu}x_5Cj3-Za2wfCma8` z6^TTM4e&fKOHCx!15$B+f8Uf`9AqduIr-yhwoU`~Em%-!T zO@?h#gznG$;IG)?pxK(N7IPtLa`Ce7by^JD_3l+iW!o2?o~!>U;NSjp$^6wa7p{u) h|7qRGn^yZ$`@`*1!mV9LTYwH>@O1TaS?83{1OO6wMLYli diff --git a/Telegram/Resources/icons/player_panel_previous@2x.png b/Telegram/Resources/icons/player_panel_previous@2x.png deleted file mode 100644 index bb31bf5311b7caca243afeb0bc82acc5c794923d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 292 zcmV+<0o(qGP)# zF$#n*5JiW%hIj!_6hu6hjg^H*vXFpu{C>3E<_s ztm_){JbyKif++asMi0Y)<2W8o?t&^JG~3PP_nF1gJQBkDVF-0000o^u>`@M#1>5KN!SJiW5Fs!_%Ax{cGt%b6Md7+gNO*TGRC0Rda^qR zArNE47y|%K_Cu{Ta?beA!3c~oIOouN$1^)4r4&+1FZgC>6hc_yDWsIbbzRGboe>d% za}MwQbgrGfwp~hjHtg(vXDQ|K&J&w|n6(zIwXJ;syZOK6wlL)y00000NkvXXu0mjf DgE&s1 diff --git a/Telegram/Resources/icons/player_previous@2x.png b/Telegram/Resources/icons/player_previous@2x.png deleted file mode 100644 index 3ddcc5ab541eeb95d22ea4cddaaf6f43b5040baa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325 zcmV-L0lNN)P)WCRG&0s*meDxeKIASR~`LK;9cKnV!Z0HOtC{Y};@?!egNmDVHu%#R1i z9uE-_xQXK!UDqLsB4?Jkpb!G{JOcoNAaG_`;<~P(>NpOT_{>Tv?Zj!CqVM}Zpp?=| ze8*aSaZwal*YyJ0h;^({+%yezWfDKJT5*42YO-*7O_Eub$CAMudg1~iGbJ&kK z3`4YSt6lgcc43K#kmot3X<~tI*qyDaiVdmyh7HJL`T5o4b92$ZBeC!MX94#(*|HZg zy_>)|j*ky`Y9J<7D^~e=%RI-+C86TwT0H0HgTOgL2rSD2z-{zVEL%)ONRq^P*i-Ba X*RW#K<$)8vKoxWww{qT?PjBLnXhc z2lc7$8%g3xdV3b=Pq4bK5k(O!%R&@IXxsL)$zP}_3Id>%l1Y+KN=X1L%aSB{8Mdw~ zP19r;hFq3~BstGBgCL-klIyx&hjm>?0BoA(!LO={0N8aMFK4Ed0swfP_u%`!4*)oh z1NZL+hhf0J?+`*@-*+gbFijHxU>rv{j)ORk^}(;}q-jb3rb@?P)B1yxm{ z-EPBVG6C0h|4+a-Xti3QrfCQO4@b3H#r1j}%84CpKA)o~ijh#CLy{z1EEewp+wb=% z%W?>ySS(_{-^X6BhlN5RG?&e0(KOBY!8)BzXyb0Ti;MunA`0AMFc|k18G6;eY=@kS4GMP*$H#V^QqKS+RjO)7bf$=;a#2m+gBuSCp zbUF=${6*n;9{5Q2op!q&9FNC8M@$H$QYioc@O?jsJK(gY zuQUs@9(EJ|hzfRpDPN^$)=U~jRwZDpO2Al^fU)ZLg57Snmv8#anpCh#rScM(dnt-S z^-%jU!!Tel7=UG2Up_rT2$V`CFbsq0<&d@SeUpCg<^L`vV66H8E_wXG?s=z-00000 LNkvXXu0mjfOr`>p diff --git a/Telegram/Resources/icons/title_button_unlock.png b/Telegram/Resources/icons/title_button_unlock.png deleted file mode 100644 index 37031e3d753a67343e46e90c55299f299ccee5c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 314 zcmV-A0mc4_P)tNu2Qc7KS=DO}nGSBm_JF_h7 zu#atQ+upI?djWq-b~Z!X%=5fsyE)6U3|-e<%F;C5u^agx|FfUw3kSeJ`|K~q*Z=?k M07*qoM6N<$f;UNsVgLXD diff --git a/Telegram/Resources/icons/title_button_unlock@2x.png b/Telegram/Resources/icons/title_button_unlock@2x.png deleted file mode 100644 index 3e99f232b04efcc1dfcaffa2b4750d7a69c2b81e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 zcmV+|0@D47P)NklOBNOz}al}HDIP` zVm6!g02B%Z>~uQV?RK$PEPBQyNy5Y75I>l%>z>5AuH*T9cE>N53%1*BkEhjY#S3;k z9z91`tJQqH^?KcNq^4=c2R4~ZJUg*ouYJ9n%?1e}u6H~hGl4NZ7pK$7ZRc`1s8*}K z-b$qc`F!5(Tb9L=itQb{-|ucalgR+j^S)l5=RuOBPv=Gs#wrX^xG;gGQmOwF34wGv z?dugq5jc)>`+^`af&B|&+VA&ZSr%-!Tlbr7+c2F@J+L43cs$^CySe?{ZU>9S0#sE6 zP1EQ}X}4oI90q#jgN7go*zfnLw~`7b%W`B;?;*=__~+t!z0$XxQI6wKJybA4qQAI7 z*8H1=Vw_6ASe1aWDgk5F?*)54pOGgGvL+R*R4PRR^HH)aQ$5sr%rFe-^?G1gR_NU$ vgn+85U>F9~%VC)MH%9&5pZ~j*fU)WgSMu+UI}^W=00000NkvXXu0mjf`nm;> diff --git a/Telegram/Resources/icons/title_next.png b/Telegram/Resources/icons/title_next.png deleted file mode 100644 index 62fd6f27605b6d7ca7877d1b6bd744cc5607beed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`iJmTwAr`$`C!gj#V8G+-+xCIs zR=t$&lDN;Aw>Y-5#j8~OkQQ8MXB@1;>acnH$v_27Preiph8+_^O%%VkG2WbB&QU3M ziSbEosPU5TQ8fbpw+2WhW%e$0)|y}RbLO4$eGD^0Qezu;#peMnWAJqKb6Mw<&;$Vc CCN&}e diff --git a/Telegram/Resources/icons/title_next@2x.png b/Telegram/Resources/icons/title_next@2x.png deleted file mode 100644 index 04dd1b4c6e16596db2944a67389acdd20234c85e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 261 zcmV+g0s8)lP)h|niHA?{6ZK-^aw zBi7c=5V5>@tPtw}Bg7uy3c<`8#6DmXVFMV5*4h`M_x^Lv^fG!P{(lKVH(}^!7t-TQRsCX+K_~{WT8!Icw3%x_AEXJc>kKI_CV;*=_|sxumFIZiuQj>#T8i500000 LNkvXXu0mjf>z-%A diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 48c1fb41f..c7b5a66fc 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -519,9 +519,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_create_group_next" = "Next"; "lng_create_group_create" = "Create"; "lng_create_group_title" = "New Group"; -"lng_create_group_about" = "Groups are ideal for limited communities,\nthey can have up to {count:_not_used|# member|# members}"; "lng_create_channel_title" = "New Channel"; -"lng_create_channel_about" = "Channels are a tool for broadcasting your messages to unlimited audiences"; "lng_create_public_channel_title" = "Public Channel"; "lng_create_public_channel_about" = "Anyone can find the channel in search and join"; "lng_create_private_channel_title" = "Private Channel"; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index 93f199062..5c61386ef 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -25,24 +25,12 @@ windowBg: #ffffff; windowTextFg: #000000; windowSubTextFg: #8a8a8a; windowActiveFill: #40ace3; -windowOverBg: #edf2f5; +windowOverBg: #f3f3f3; windowSubTextFgOver: #7c99b2; windowActiveTextFg: #1485c2; windowShadowFg: #000000; imageBg: #000000; imageBgTransparent: #ffffff; -titleBg: #f3f3f3; -titleShadow: #00000003; -titleButtonFg: #ababab; -titleButtonBgOver: #e5e5e5; -titleButtonFgOver: #9a9a9a; -titleButtonCloseBgOver: #e81123; -titleButtonCloseFgOver: #ffffff; -trayCounterBg: #f23c34; -trayCounterBgMute: #888888; -trayCounterFg: #ffffff; -trayCounterBgMacInvert: #ffffff; -trayCounterFgMacInvert: #ffffff01; cancelIconFg: #a2a2a2; cancelIconFgOver: #808080; activeButtonBg: windowActiveFill; @@ -55,6 +43,19 @@ lightButtonBg: windowBg; lightButtonBgOver: #f2f7fa; // lightButtonBg; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; +menuIconFg: #808080; // windowSubTextFg; +titleBg: windowOverBg; +titleShadow: #00000003; +titleButtonFg: #ababab; +titleButtonBgOver: #e5e5e5; +titleButtonFgOver: #9a9a9a; +titleButtonCloseBgOver: #e81123; +titleButtonCloseFgOver: #ffffff; +trayCounterBg: #f23c34; +trayCounterBgMute: #888888; +trayCounterFg: #ffffff; +trayCounterBgMacInvert: #ffffff; +trayCounterFgMacInvert: #ffffff01; layerBg: #0000007f; boxBg: windowBg; boxTextFg: windowTextFg; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 9cf16de1e..17bf2fea9 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -260,64 +260,6 @@ void AddContactBox::onRetry() { update(); } -NewGroupBox::NewGroupBox() : AbstractBox(), -_group(this, qsl("group_type"), 0, lang(lng_create_group_title), true), -_channel(this, qsl("group_type"), 1, lang(lng_create_channel_title)), -_aboutGroupWidth(width() - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultRadiobutton.textPosition.x()), -_aboutGroup(st::normalFont, lng_create_group_about(lt_count, Global::MegagroupSizeMax()), _defaultOptions, _aboutGroupWidth), -_aboutChannel(st::normalFont, lang(lng_create_channel_about), _defaultOptions, _aboutGroupWidth), -_next(this, lang(lng_create_group_next), st::defaultBoxButton), -_cancel(this, lang(lng_cancel), st::cancelBoxButton) { - _aboutGroupHeight = _aboutGroup.countHeight(_aboutGroupWidth); - setMaxHeight(st::boxPadding.top() + st::newGroupPadding.top() + _group->height() + _aboutGroupHeight + st::newGroupSkip + _channel->height() + _aboutChannel.countHeight(_aboutGroupWidth) + st::newGroupPadding.bottom() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _next->height() + st::boxButtonPadding.bottom()); - - connect(_next, SIGNAL(clicked()), this, SLOT(onNext())); - connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose())); - - prepare(); -} - -void NewGroupBox::showAll() { - _group->show(); - _channel->show(); - _cancel->show(); - _next->show(); -} - -void NewGroupBox::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - onNext(); - } else { - AbstractBox::keyPressEvent(e); - } -} - -void NewGroupBox::paintEvent(QPaintEvent *e) { - Painter p(this); - if (paint(p)) return; - - p.setPen(st::newGroupAboutFg->p); - - QRect aboutGroup(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _group->y() + _group->height() + st::lineWidth, _aboutGroupWidth, _aboutGroupHeight); - _aboutGroup.drawLeft(p, aboutGroup.x(), aboutGroup.y(), aboutGroup.width(), width()); - - QRect aboutChannel(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadiobutton.textPosition.x(), _channel->y() + _channel->height() + st::lineWidth, _aboutGroupWidth, _aboutGroupHeight); - _aboutChannel.drawLeft(p, aboutChannel.x(), aboutChannel.y(), aboutChannel.width(), width()); -} - -void NewGroupBox::resizeEvent(QResizeEvent *e) { - _group->moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), st::boxPadding.top() + st::newGroupPadding.top()); - _channel->moveToLeft(st::boxPadding.left() + st::newGroupPadding.left(), _group->y() + _group->height() + _aboutGroupHeight + st::newGroupSkip); - - _next->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next->height()); - _cancel->moveToRight(st::boxButtonPadding.right() + _next->width() + st::boxButtonPadding.left(), _next->y()); - AbstractBox::resizeEvent(e); -} - -void NewGroupBox::onNext() { - Ui::showLayer(new GroupInfoBox(_group->checked() ? CreatingGroupGroup : CreatingGroupChannel, true), KeepOtherLayers); -} - GroupInfoBox::GroupInfoBox(CreatingGroupType creating, bool fromTypeChoose) : AbstractBox(), _creating(creating), a_photoOver(0, 0), diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index d38ca6095..805426311 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -78,34 +78,6 @@ private: QString _sentName; }; -class NewGroupBox : public AbstractBox { - Q_OBJECT - -public: - NewGroupBox(); - -public slots: - void onNext(); - -protected: - void keyPressEvent(QKeyEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - - void showAll() override; - -private: - ChildWidget _group; - ChildWidget _channel; - - int32 _aboutGroupWidth, _aboutGroupHeight; - Text _aboutGroup, _aboutChannel; - - ChildWidget _next; - ChildWidget _cancel; - -}; - class GroupInfoBox : public AbstractBox, public RPCSender { Q_OBJECT diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 1852efe0c..ac9b071f3 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -19,6 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ using "basic.style"; + using "ui/widgets/widgets.style"; using "intro/intro.style"; @@ -143,7 +144,7 @@ contactsMultiSelect: MultiSelect { font: normalFont; } fieldMinWidth: 42px; - fieldIcon: fieldSearchIcon; + fieldIcon: boxFieldSearchIcon; fieldIconSkip: 36px; fieldCancel: MaskButton(defaultMaskButton) { diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 22e879252..0b1db3d34 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -38,8 +38,8 @@ namespace codegen { namespace style { namespace { -constexpr int kErrorBadIconSize = 861; -constexpr int kErrorBadIconFormat = 862; +constexpr int kErrorBadIconSize = 861; +constexpr int kErrorBadIconFormat = 862; // crc32 hash, taken somewhere from the internet @@ -1017,10 +1017,10 @@ QByteArray iconMaskValueSize(int width, int height) { QByteArray iconMaskValuePng(QString filepath) { QByteArray result; - auto inverted = filepath.endsWith("-invert"); - if (inverted) { - filepath.chop(QLatin1String("-invert").size()); - } + auto pathAndModifiers = filepath.split('-'); + filepath = pathAndModifiers[0]; + auto modifiers = pathAndModifiers.mid(1); + QImage png100x(filepath + ".png"); QImage png200x(filepath + "@2x.png"); png100x.setDevicePixelRatio(1.); @@ -1041,9 +1041,13 @@ QByteArray iconMaskValuePng(QString filepath) { common::logError(kErrorBadIconSize, filepath + ".png") << "bad icons size, 1x: " << png100x.width() << "x" << png100x.height() << ", 2x: " << png200x.width() << "x" << png200x.height(); return result; } - if (inverted) { - png100x.invertPixels(); - png200x.invertPixels(); + for (auto modifierName : modifiers) { + if (auto modifier = GetModifier(modifierName)) { + modifier(png100x, png200x); + } else { + common::logError(common::kErrorInternal, filepath) << "modifier should be valid here, name: " << modifierName.toStdString(); + return result; + } } QImage png125x = png200x.scaled(structure::data::pxAdjust(png100x.width(), 5), structure::data::pxAdjust(png100x.height(), 5), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QImage png150x = png200x.scaled(structure::data::pxAdjust(png100x.width(), 6), structure::data::pxAdjust(png100x.height(), 6), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index 7277dc2e5..a0e77fb5e 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -44,6 +44,7 @@ constexpr int kErrorIdentifierNotFound = 804; constexpr int kErrorAlreadyDefined = 805; constexpr int kErrorBadString = 806; constexpr int kErrorIconDuplicate = 807; +constexpr int kErrorBadIconModifier = 808; QString findInputFile(const Options &options) { for (const auto &dir : options.includePaths) { @@ -151,6 +152,25 @@ bool validateAlignString(const QString &value) { } // namespace +Modifier GetModifier(const QString &name) { + static QMap modifiers; + if (modifiers.empty()) { + modifiers.insert("invert", [](QImage &png100x, QImage &png200x) { + png100x.invertPixels(); + png200x.invertPixels(); + }); + modifiers.insert("flip_horizontal", [](QImage &png100x, QImage &png200x) { + png100x = png100x.mirrored(true, false); + png200x = png200x.mirrored(true, false); + }); + modifiers.insert("flip_vertical", [](QImage &png100x, QImage &png200x) { + png100x = png100x.mirrored(false, true); + png200x = png200x.mirrored(false, true); + }); + } + return modifiers.value(name); +} + ParsedFile::ParsedFile(const Options &options) : filePath_(findInputFile(options)) , file_(filePath_) @@ -821,21 +841,26 @@ structure::data::monoicon ParsedFile::readMonoIconFields() { QString ParsedFile::readMonoIconFilename() { if (auto filename = readValue()) { if (filename.type().tag == structure::TypeTag::String) { - auto filepath = QString::fromStdString(filename.String()); - auto inverted = filepath.endsWith("-invert"); - if (inverted) { - filepath.chop(QLatin1String("-invert").size()); - } - for (const auto &path : options_.includePaths) { - QFileInfo fileinfo(path + '/' + filepath + ".png"); - if (fileinfo.exists()) { - return path + '/' + filepath + (inverted ? "-invert" : ""); + auto fullpath = QString::fromStdString(filename.String()); + auto pathAndModifiers = fullpath.split('-'); + auto filepath = pathAndModifiers[0]; + auto modifiers = pathAndModifiers.mid(1); + for (auto modifierName : modifiers) { + if (!GetModifier(modifierName)) { + logError(kErrorBadIconModifier) << "unknown modifier: " << modifierName.toStdString(); + return QString(); } } - for (const auto &path : options_.includePaths) { + for (auto &path : options_.includePaths) { + QFileInfo fileinfo(path + '/' + filepath + ".png"); + if (fileinfo.exists()) { + return path + '/' + fullpath; + } + } + for (auto &path : options_.includePaths) { QFileInfo fileinfo(path + "/icons/" + filepath + ".png"); if (fileinfo.exists()) { - return path + "/icons/" + filepath + (inverted ? "-invert" : ""); + return path + "/icons/" + fullpath; } } logError(common::kErrorFileNotFound) << "could not open icon file '" << filename.String() << "'"; diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.h b/Telegram/SourceFiles/codegen/style/parsed_file.h index 520e0aad2..1c2271fe4 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.h +++ b/Telegram/SourceFiles/codegen/style/parsed_file.h @@ -22,6 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include +#include +#include #include "codegen/common/basic_tokenized_file.h" #include "codegen/style/options.h" #include "codegen/style/module.h" @@ -29,6 +31,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace codegen { namespace style { +using Modifier = std::function; +Modifier GetModifier(const QString &name); + // Parses an input file to the internal struct. class ParsedFile { public: @@ -44,7 +49,6 @@ public: } private: - bool failed() const { return failed_ || file_.failed(); } diff --git a/Telegram/SourceFiles/core/stl_subset.h b/Telegram/SourceFiles/core/stl_subset.h index 26596b22b..41d4f0007 100644 --- a/Telegram/SourceFiles/core/stl_subset.h +++ b/Telegram/SourceFiles/core/stl_subset.h @@ -59,6 +59,8 @@ template struct remove_reference { using type = T; }; +template +using remove_reference_t = typename remove_reference::type; template struct is_lvalue_reference : false_type { diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 60a3527b4..7f6ea2715 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace base { template -inline constexpr size_t array_size(T(&)[N]) { +inline constexpr size_t array_size(const T(&)[N]) { return N; } @@ -35,6 +35,27 @@ inline T take(T &source, T &&new_value = T()) { return std_::move(new_value); } +namespace internal { + +template +inline constexpr D up_cast_helper(std_::true_type, T object) { + return object; +} + +template +inline constexpr D up_cast_helper(std_::false_type, T object) { + return nullptr; +} + +} // namespace internal + +template +inline constexpr D up_cast(T object) { + using DV = std_::decay_simple_t; + using TV = std_::decay_simple_t; + return internal::up_cast_helper(std_::integral_constant::value || std_::is_same::value>(), object); +} + } // namespace base template diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index c20e90955..cf7470bef 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -48,7 +48,7 @@ dialogsNameFg: #000000; dialogsNameTop: 2px; dialogsRowHeight: 62px; -dialogsFilterPadding: 10px; +dialogsFilterPadding: point(11px, 11px); dialogsPhotoSize: 46px; dialogsPhotoPadding: 12px; dialogsPadding: point(10px, 8px); @@ -83,24 +83,44 @@ dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) { linkFgDown: #c6e1f7; } -dialogsNewChatIcon: icon {{ "dialogs_new_chat", #b7b7b7 }}; -dialogsNewChatButton: IconButton { - width: 36px; - height: 36px; +dialogsMenuToggle: IconButton { + width: 32px; + height: 32px; - icon: dialogsNewChatIcon; - iconPosition: point(9px, 10px); - iconPositionDown: point(9px, 11px); + icon: icon {{ "dialogs_menu", #999999 }}; + iconPosition: point(6px, 6px); + iconPositionDown: point(6px, 6px); } -dialogsAddContact: IconButton(dialogsNewChatButton) { - icon: icon {{ "dialogs_add_contact", #a6a6a6 }}; - iconPosition: point(8px, 8px); - iconPositionDown: point(8px, 9px); +dialogsFilter: flatInput(inpDefGray) { + font: font(fsize); + bgColor: #f2f2f2; + phColor: #949494; + phFocusColor: #a4a4a4; + icon: fieldSearchIcon; + + width: 240px; + height: 32px; + textMrg: margins(32px, 3px, 32px, 3px); } -dialogsCancelSearch: IconButton(dialogsAddContact) { - icon: icon {{ "dialogs_cancel_search", #a6a6a6 }}; +dialogsCancelSearch: IconButton(dialogsMenuToggle) { + icon: icon {{ "dialogs_cancel_search", #a6a6a6, point(0px, 1px) }}; } +dialogsMenu: DropdownMenu(defaultDropdownMenu) { + menu: Menu(defaultMenu) { + skip: 8px; + + itemIconPosition: point(15px, 8px); + itemPadding: margins(56px, 10px, 56px, 12px); + } +} +dialogsMenuPosition: point(-3px, -2px); +dialogsMenuNewGroup: icon {{ "menu_new_group", menuIconFg }}; +dialogsMenuNewChannel: icon {{ "menu_new_channel", menuIconFg }}; +dialogsMenuContacts: icon {{ "menu_contacts", menuIconFg }}; +dialogsMenuSettings: icon {{ "menu_settings", menuIconFg }}; +dialogsMenuHelp: icon {{ "menu_help", menuIconFg }}; + dialogsChatTypeSkip: 22px; dialogsChatIcon: icon {{ "dialogs_chat", #373737, point(1px, 4px) }}; dialogsChatActiveIcon: icon {{ "dialogs_chat", #ffffff, point(1px, 4px) }}; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index b3d59aac2..c08c99a85 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -36,10 +36,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/addcontactbox.h" #include "boxes/contactsbox.h" #include "boxes/confirmbox.h" +#include "boxes/aboutbox.h" #include "localstorage.h" #include "apiwrap.h" +#include "ui/widgets/dropdown_menu.h" -DialogsInner::DialogsInner(QWidget *parent, MainWidget *main) : SplittedWidget(parent) +DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(parent) , dialogs(std_::make_unique(Dialogs::SortMode::Date)) , contactsNoDialogs(std_::make_unique(Dialogs::SortMode::Name)) , contacts(std_::make_unique(Dialogs::SortMode::Name)) @@ -1770,28 +1772,13 @@ MsgId DialogsInner::lastSearchMigratedId() const { return _lastSearchMigratedId; } -DialogsWidget::DialogsWidget(MainWidget *parent) : TWidget(parent) -, _dragInScroll(false) -, _dragForward(false) -, _dialogsFull(false) -, _dialogsOffsetDate(0) -, _dialogsOffsetId(0) -, _dialogsOffsetPeer(0) -, _dialogsRequest(0) -, _contactsRequest(0) -, _filter(this, st::dlgFilter, lang(lng_dlg_filter)) -, _newGroup(this, st::dialogsNewChatButton) -, _addContact(this, st::dialogsAddContact) +DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) +, _mainMenuToggle(this, st::dialogsMenuToggle) +, _filter(this, st::dialogsFilter, lang(lng_dlg_filter)) , _cancelSearch(this, st::dialogsCancelSearch) , _scroll(this, st::dialogsScroll) , _inner(&_scroll, parent) -, _a_show(animation(this, &DialogsWidget::step_show)) -, _searchInPeer(0) -, _searchInMigrated(0) -, _searchFull(false) -, _searchFullMigrated(false) -, _peopleFull(false) -{ +, _a_show(animation(this, &DialogsWidget::step_show)) { _scroll.setWidget(&_inner); _scroll.setFocusPolicy(Qt::NoFocus); connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int))); @@ -1803,14 +1790,13 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : TWidget(parent) connect(&_inner, SIGNAL(cancelSearchInPeer()), this, SLOT(onCancelSearchInPeer())); connect(&_scroll, SIGNAL(geometryChanged()), &_inner, SLOT(onParentGeometryChanged())); connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onListScroll())); - connect(&_filter, SIGNAL(cancelled()), this, SLOT(onCancel())); - connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); - connect(&_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int))); - connect(parent, SIGNAL(dialogsUpdated()), this, SLOT(onListScroll())); - connect(_addContact, SIGNAL(clicked()), this, SLOT(onAddContact())); - connect(_newGroup, SIGNAL(clicked()), this, SLOT(onNewGroup())); + connect(_filter, SIGNAL(cancelled()), this, SLOT(onCancel())); + connect(_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); + connect(_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int))); connect(_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); + _mainMenuToggle->setClickedCallback([this] { showMainMenu(); }); + _chooseByDragTimer.setSingleShot(true); connect(&_chooseByDragTimer, SIGNAL(timeout()), this, SLOT(onChooseByDrag())); @@ -1819,21 +1805,13 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : TWidget(parent) _searchTimer.setSingleShot(true); connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchMessages())); - _scroll.show(); - _filter.show(); - _filter.move(st::dialogsPadding.x(), st::dialogsFilterPadding); - _filter.setFocusPolicy(Qt::StrongFocus); - _filter.customUpDown(true); - _addContact->hide(); - _newGroup->show(); + _filter->setFocusPolicy(Qt::StrongFocus); + _filter->customUpDown(true); _cancelSearch->hide(); - _newGroup->move(width() - _newGroup->width() - st::dialogsPadding.x(), 0); - _addContact->move(width() - _addContact->width() - st::dialogsPadding.x(), 0); - _cancelSearch->move(width() - _cancelSearch->width() - st::dialogsPadding.x(), 0); } void DialogsWidget::activate() { - _filter.setFocus(); + _filter->setFocus(); _inner.activate(); } @@ -1858,7 +1836,7 @@ void DialogsWidget::dlgUpdated(History *row, MsgId msgId) { } void DialogsWidget::dialogsToUp() { - if (_filter.getLastText().trimmed().isEmpty()) { + if (_filter->getLastText().trimmed().isEmpty()) { _scroll.scrollToY(0); } } @@ -1873,9 +1851,8 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window: _a_show.stop(); _scroll.hide(); - _filter.hide(); + _filter->hide(); _cancelSearch->hide(); - _newGroup->hide(); int delta = st::slideShift; if (direction == Window::SlideDirection::FromLeft) { @@ -1903,7 +1880,7 @@ void DialogsWidget::step_show(float64 ms, bool timer) { _cacheUnder = _cacheOver = QPixmap(); _scroll.show(); - _filter.show(); + _filter->show(); _a_show.stop(); onFilterUpdate(); @@ -1930,8 +1907,8 @@ void DialogsWidget::updateNotifySettings(PeerData *peer) { void DialogsWidget::notify_userIsContactChanged(UserData *user, bool fromThisApp) { if (fromThisApp) { - _filter.setText(QString()); - _filter.updatePlaceholder(); + _filter->setText(QString()); + _filter->updatePlaceholder(); onFilterUpdate(); } _inner.notify_userIsContactChanged(user, fromThisApp); @@ -2031,7 +2008,7 @@ bool DialogsWidget::dialogsFailed(const RPCError &error, mtpRequestId req) { } bool DialogsWidget::onSearchMessages(bool searchCache) { - QString q = _filter.getLastText().trimmed(); + QString q = _filter->getLastText().trimmed(); if (q.isEmpty()) { if (_searchRequest) { MTP::cancel(_searchRequest); @@ -2098,16 +2075,39 @@ void DialogsWidget::onChooseByDrag() { _inner.choosePeer(); } +void DialogsWidget::showMainMenu() { + if (!_mainMenu) { + _mainMenu.create(this, st::dialogsMenu); + _mainMenu->addAction(lang(lng_create_group_title), [] { + App::wnd()->onShowNewGroup(); + }, &st::dialogsMenuNewGroup); + _mainMenu->addAction(lang(lng_create_channel_title), [] { + App::wnd()->onShowNewChannel(); + }, &st::dialogsMenuNewChannel); + _mainMenu->addAction(lang(lng_menu_contacts), [] { + Ui::showLayer(new ContactsBox()); + }, &st::dialogsMenuContacts); + _mainMenu->addAction(lang(lng_menu_settings), [] { + App::wnd()->showSettings(); + }, &st::dialogsMenuSettings); + _mainMenu->addAction(lang(lng_settings_faq), [] { + QDesktopServices::openUrl(telegramFaqLink()); + }, &st::dialogsMenuHelp); + } + updateMainMenuGeometry(); + _mainMenu->showAnimated(); +} + void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) { - if ((_filter.getLastText() != query) || (inPeer && inPeer != _searchInPeer && inPeer->migrateTo() != _searchInPeer)) { + if ((_filter->getLastText() != query) || (inPeer && inPeer != _searchInPeer && inPeer->migrateTo() != _searchInPeer)) { if (inPeer) { onCancelSearch(); _searchInPeer = inPeer->migrateTo() ? inPeer->migrateTo() : inPeer; _searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0; _inner.searchInPeer(_searchInPeer); } - _filter.setText(query); - _filter.updatePlaceholder(); + _filter->setText(query); + _filter->updatePlaceholder(); onFilterUpdate(true); _searchTimer.stop(); onSearchMessages(); @@ -2371,17 +2371,15 @@ void DialogsWidget::onListScroll() { void DialogsWidget::onFilterUpdate(bool force) { if (_a_show.animating() && !force) return; - QString filterText = _filter.getLastText(); + QString filterText = _filter->getLastText(); _inner.onFilterUpdate(filterText, force); if (filterText.isEmpty()) { _searchCache.clear(); _searchQueries.clear(); _searchQuery = QString(); _cancelSearch->hide(); - _newGroup->show(); } else if (_cancelSearch->isHidden()) { _cancelSearch->show(); - _newGroup->hide(); } if (filterText.size() < MinUsernameLength) { _peopleCache.clear(); @@ -2399,8 +2397,8 @@ void DialogsWidget::searchInPeer(PeerData *peer) { } void DialogsWidget::onFilterCursorMoved(int from, int to) { - if (to < 0) to = _filter.cursorPosition(); - QString t = _filter.getLastText(); + if (to < 0) to = _filter->cursorPosition(); + QString t = _filter->getLastText(); QStringRef r; for (int start = to; start > 0;) { --start; @@ -2415,8 +2413,8 @@ void DialogsWidget::onFilterCursorMoved(int from, int to) { } void DialogsWidget::onCompleteHashtag(QString tag) { - QString t = _filter.getLastText(), r; - int cur = _filter.cursorPosition(); + QString t = _filter->getLastText(), r; + int cur = _filter->cursorPosition(); for (int start = cur; start > 0;) { --start; if (t.size() <= start) break; @@ -2427,8 +2425,8 @@ void DialogsWidget::onCompleteHashtag(QString tag) { } if (cur - start - 1 == tag.size() && cur < t.size() && t.at(cur) == ' ') ++cur; r = t.mid(0, start + 1) + tag + ' ' + t.mid(cur); - _filter.setText(r); - _filter.setCursorPosition(start + 1 + tag.size() + 1); + _filter->setText(r); + _filter->setCursorPosition(start + 1 + tag.size() + 1); onFilterUpdate(true); return; } @@ -2436,22 +2434,23 @@ void DialogsWidget::onCompleteHashtag(QString tag) { } if (!t.at(start).isLetterOrNumber() && t.at(start) != '_') break; } - _filter.setText(t.mid(0, cur) + '#' + tag + ' ' + t.mid(cur)); - _filter.setCursorPosition(cur + 1 + tag.size() + 1); + _filter->setText(t.mid(0, cur) + '#' + tag + ' ' + t.mid(cur)); + _filter->setCursorPosition(cur + 1 + tag.size() + 1); onFilterUpdate(true); } void DialogsWidget::resizeEvent(QResizeEvent *e) { int32 w = width(); - _filter.setGeometry(st::dialogsPadding.x(), st::dialogsFilterPadding, w - 2 * st::dialogsPadding.x(), _filter.height()); - _newGroup->move(w - _newGroup->width() - st::dialogsPadding.x(), _filter.y()); - _addContact->move(w - _addContact->width() - st::dialogsPadding.x(), _filter.y()); - _cancelSearch->move(w - _cancelSearch->width() - st::dialogsPadding.x(), _filter.y()); - _scroll.move(0, _filter.height() + 2 * st::dialogsFilterPadding); + _filter->setGeometryToLeft(st::dialogsFilterPadding.x() * 2 + _mainMenuToggle->width(), st::dialogsFilterPadding.y(), w - 3 * st::dialogsFilterPadding.x() - _mainMenuToggle->width(), _filter->height()); + _mainMenuToggle->moveToLeft(st::dialogsFilterPadding.x(), _filter->y()); + _cancelSearch->moveToRight(st::dialogsFilterPadding.x(), _filter->y()); + _scroll.move(0, _filter->height() + 2 * st::dialogsFilterPadding.y()); + + updateMainMenuGeometry(); int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0; int32 newScrollY = _scroll.scrollTop() + addToY; - _scroll.resize(w, height() - _filter.y() - _filter.height() - st::dialogsFilterPadding - st::dialogsPadding.y()); + _scroll.resize(w, height() - _filter->y() - _filter->height() - st::dialogsFilterPadding.y() - st::dialogsPadding.y()); if (addToY) { _scroll.scrollToY(newScrollY); } else { @@ -2459,6 +2458,12 @@ void DialogsWidget::resizeEvent(QResizeEvent *e) { } } +void DialogsWidget::updateMainMenuGeometry() { + if (!_mainMenu) return; + + _mainMenu->moveToLeft(st::dialogsMenuPosition.x(), st::dialogsMenuPosition.y()); +} + void DialogsWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { e->ignore(); @@ -2548,16 +2553,8 @@ Dialogs::IndexedList *DialogsWidget::dialogsList() { return _inner.dialogsList(); } -void DialogsWidget::onAddContact() { - Ui::showLayer(new AddContactBox(), KeepOtherLayers); -} - -void DialogsWidget::onNewGroup() { - Ui::showLayer(new NewGroupBox()); -} - bool DialogsWidget::onCancelSearch() { - bool clearing = !_filter.getLastText().isEmpty(); + bool clearing = !_filter->getLastText().isEmpty(); if (_searchRequest) { MTP::cancel(_searchRequest); _searchRequest = 0; @@ -2571,8 +2568,8 @@ bool DialogsWidget::onCancelSearch() { clearing = true; } _inner.clearFilter(); - _filter.clear(); - _filter.updatePlaceholder(); + _filter->clear(); + _filter->updatePlaceholder(); onFilterUpdate(); return clearing; } @@ -2590,8 +2587,8 @@ void DialogsWidget::onCancelSearchInPeer() { _inner.searchInPeer(0); } _inner.clearFilter(); - _filter.clear(); - _filter.updatePlaceholder(); + _filter->clear(); + _filter->updatePlaceholder(); onFilterUpdate(); if (!Adaptive::OneColumn() && !App::main()->selectingPeer()) { emit cancelled(); diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index e3aec8945..68a5a2b30 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -31,10 +31,9 @@ class IndexedList; namespace Ui { class IconButton; class PopupMenu; +class DropdownMenu; } // namespace Ui -class MainWidget; - enum DialogsSearchRequestType { DialogsSearchFromStart, DialogsSearchFromOffset, @@ -48,7 +47,7 @@ class DialogsInner : public SplittedWidget, public RPCSender, private base::Subs Q_OBJECT public: - DialogsInner(QWidget *parent, MainWidget *main); + DialogsInner(QWidget *parent, QWidget *main); void dialogsReceived(const QVector &dialogs); void addSavedPeersAfter(const QDateTime &date); @@ -238,7 +237,7 @@ class DialogsWidget : public TWidget, public RPCSender { Q_OBJECT public: - DialogsWidget(MainWidget *parent); + DialogsWidget(QWidget *parent); void dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpRequestId req); void contactsReceived(const MTPcontacts_Contacts &contacts); @@ -295,17 +294,13 @@ public: void notify_historyMuteUpdated(History *history); signals: - void cancelled(); public slots: - void onCancel(); void onListScroll(); void activate(); void onFilterUpdate(bool force = false); - void onAddContact(); - void onNewGroup(); bool onCancelSearch(); void onCancelSearchInPeer(); @@ -319,8 +314,11 @@ public slots: void onChooseByDrag(); private: + void showMainMenu(); + void updateMainMenuGeometry(); - bool _dragInScroll, _dragForward; + bool _dragInScroll = false; + bool _dragForward = false; QTimer _chooseByDragTimer; void unreadCountsReceived(const QVector &dialogs); @@ -329,15 +327,16 @@ private: bool searchFailed(DialogsSearchRequestType type, const RPCError &error, mtpRequestId req); bool peopleFailed(const RPCError &error, mtpRequestId req); - bool _dialogsFull; - int32 _dialogsOffsetDate; - MsgId _dialogsOffsetId; - PeerData *_dialogsOffsetPeer; - mtpRequestId _dialogsRequest, _contactsRequest; + bool _dialogsFull = false; + int32 _dialogsOffsetDate = 0; + MsgId _dialogsOffsetId = 0; + PeerData *_dialogsOffsetPeer = nullptr; + mtpRequestId _dialogsRequest = 0; + mtpRequestId _contactsRequest = 0; - FlatInput _filter; - ChildWidget _newGroup; - ChildWidget _addContact; + ChildWidget _mainMenuToggle; + ChildWidget _mainMenu = { nullptr }; + ChildWidget _filter; ChildWidget _cancelSearch; ScrollArea _scroll; DialogsInner _inner; @@ -347,11 +346,14 @@ private: anim::ivalue a_coordUnder, a_coordOver; anim::fvalue a_progress; - PeerData *_searchInPeer, *_searchInMigrated; + PeerData *_searchInPeer = nullptr; + PeerData *_searchInMigrated = nullptr; QTimer _searchTimer; QString _searchQuery, _peopleQuery; - bool _searchFull, _searchFullMigrated, _peopleFull; + bool _searchFull = false; + bool _searchFullMigrated = false; + bool _peopleFull = false; mtpRequestId _searchRequest, _peopleRequest; typedef QMap SearchCache; diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index e2a43097e..a751a9088 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -151,11 +151,11 @@ introBackButton: IconButton { icon: icon { { size(40px, 40px), #f2f2f2 }, - { "title_previous", #adadad, point(12px, 12px) }, + { "title_back", #adadad, point(12px, 12px) }, }; iconOver: icon { { size(40px, 40px), #eeeeee }, - { "title_previous", #969696, point(12px, 12px) }, + { "title_back", #969696, point(12px, 12px) }, }; iconPosition: point(0px, 0px); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index b9749d72f..db1c94f6f 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -83,6 +83,7 @@ MainWidget::MainWidget(QWidget *parent) : TWidget(parent) updateScrollColors(); connect(_dialogs, SIGNAL(cancelled()), this, SLOT(dialogsCancelled())); + connect(this, SIGNAL(dialogsUpdated()), this, SLOT(onListScroll())); connect(_history, SIGNAL(cancelled()), _dialogs, SLOT(activate())); connect(this, SIGNAL(peerPhotoChanged(PeerData*)), this, SIGNAL(dialogsUpdated())); connect(&noUpdatesTimer, SIGNAL(timeout()), this, SLOT(mtpPing())); @@ -1448,14 +1449,6 @@ void MainWidget::checkLastUpdate(bool afterSleep) { } } -void MainWidget::showAddContact() { - _dialogs->onAddContact(); -} - -void MainWidget::showNewGroup() { - _dialogs->onNewGroup(); -} - void MainWidget::overviewLoaded(History *history, const MTPmessages_Messages &result, mtpRequestId req) { OverviewsPreload::iterator it; MediaOverviewType type = OverviewCount; diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 32cf8519b..8840579ef 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -312,8 +312,6 @@ public: void loadMediaBack(PeerData *peer, MediaOverviewType type, bool many = false); void checkLastUpdate(bool afterSleep); - void showAddContact(); - void showNewGroup(); void serviceNotification(const QString &msg, const MTPMessageMedia &media); void serviceHistoryDone(const MTPmessages_Messages &msgs); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 83a2309b6..c91a2b906 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -44,7 +44,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "apiwrap.h" #include "settings/settings_widget.h" #include "platform/platform_notifications_manager.h" -#include "platform/platform_window_title.h" #include "window/notifications_manager.h" #include "window/window_theme.h" #include "window/window_theme_warning.h" @@ -86,7 +85,7 @@ void ConnectingWidget::onReconnect() { MTP::restart(); } -MainWindow::MainWindow() : Platform::MainWindow(), _body(this) { +MainWindow::MainWindow() { icon16 = icon256.scaledToWidth(16, Qt::SmoothTransformation); icon32 = icon256.scaledToWidth(32, Qt::SmoothTransformation); icon64 = icon256.scaledToWidth(64, Qt::SmoothTransformation); @@ -105,15 +104,9 @@ MainWindow::MainWindow() : Platform::MainWindow(), _body(this) { } }); - if (objectName().isEmpty()) { - setObjectName(qsl("MainWindow")); - } - resize(st::windowDefWidth, st::windowDefHeight); + resize(st::windowDefaultWidth, st::windowDefaultHeight); setLocale(QLocale(QLocale::English, QLocale::UnitedStates)); - setCentralWidget(_body); - - QMetaObject::connectSlotsByName(this); _inactiveTimer.setSingleShot(true); connect(&_inactiveTimer, SIGNAL(timeout()), this, SLOT(onInactiveTimer())); @@ -162,26 +155,17 @@ void MainWindow::onStateChanged(Qt::WindowState state) { if (state == Qt::WindowMinimized && cWorkMode() == dbiwmTrayOnly) { App::wnd()->minimizeToTray(); } - psSavePosition(state); + savePosition(state); } void MainWindow::init() { - psInitFrameless(); - setWindowIcon(wndIcon); + Platform::MainWindow::init(); - _title = Platform::CreateTitleWidget(this); + setWindowIcon(wndIcon); Application::instance()->installEventFilter(this); connect(windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onStateChanged(Qt::WindowState))); connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWindowActiveChanged()), Qt::QueuedConnection); - - QPalette p(palette()); - p.setColor(QPalette::Window, st::windowBg->c); - setPalette(p); - - setMinimumWidth(st::windowMinWidth); - setMinimumHeight(st::windowMinHeight); - psInitSize(); } void MainWindow::onWindowActiveChanged() { @@ -241,9 +225,7 @@ void MainWindow::clearWidgets() { QPixmap MainWindow::grabInner() { QPixmap result; - if (_settings) { - result = myGrab(_settings); - } else if (_intro) { + if (_intro) { result = myGrab(_intro); } else if (_main) { result = myGrab(_main); @@ -274,13 +256,13 @@ void MainWindow::clearPasscode() { } void MainWindow::setupPasscode(bool anim) { - QPixmap bg = grabInner(); + auto bg = grabInner(); if (_passcode) { _passcode->stop_show(); _passcode.destroyDelayed(); } - _passcode.create(_body); + _passcode.create(bodyWidget()); updateControlsGeometry(); if (_main) _main->hide(); @@ -332,7 +314,7 @@ void MainWindow::setupIntro(bool anim) { QPixmap bg = anim ? grabInner() : QPixmap(); clearWidgets(); - _intro.create(_body); + _intro.create(bodyWidget()); updateControlsGeometry(); if (anim) { @@ -382,7 +364,7 @@ void MainWindow::sendServiceHistoryRequest() { void MainWindow::setupMain(bool anim, const MTPUser *self) { QPixmap bg = anim ? grabInner() : QPixmap(); clearWidgets(); - _main.create(_body); + _main.create(bodyWidget()); updateControlsGeometry(); if (anim) { @@ -418,7 +400,7 @@ void MainWindow::showSettings() { } if (!_layerBg) { - _layerBg.create(_body); + _layerBg.create(bodyWidget()); } _settings.create(this); connect(_settings, SIGNAL(destroyed(QObject*)), this, SLOT(onSettingsDestroyed(QObject*))); @@ -492,7 +474,7 @@ void MainWindow::showDocument(DocumentData *doc, HistoryItem *item) { void MainWindow::ui_showLayer(LayerWidget *box, ShowLayerOptions options) { if (box) { if (!_layerBg) { - _layerBg.create(_body); + _layerBg.create(bodyWidget()); } if (options.testFlag(KeepOtherLayers)) { if (options.testFlag(ShowAfterOtherLayers)) { @@ -532,7 +514,7 @@ bool MainWindow::ui_isMediaViewShown() { void MainWindow::ui_showMediaPreview(DocumentData *document) { if (!document || ((!document->isAnimation() || !document->loaded()) && !document->sticker())) return; if (!_mediaPreview) { - _mediaPreview.create(_body); + _mediaPreview.create(bodyWidget()); updateControlsGeometry(); } if (_mediaPreview->isHidden()) { @@ -544,7 +526,7 @@ void MainWindow::ui_showMediaPreview(DocumentData *document) { void MainWindow::ui_showMediaPreview(PhotoData *photo) { if (!photo) return; if (!_mediaPreview) { - _mediaPreview.create(_body); + _mediaPreview.create(bodyWidget()); updateControlsGeometry(); } if (_mediaPreview->isHidden()) { @@ -571,7 +553,7 @@ void MainWindow::showConnecting(const QString &text, const QString &reconnect) { if (_connecting) { _connecting->set(text, reconnect); } else { - _connecting.create(_body, text, reconnect); + _connecting.create(bodyWidget(), text, reconnect); updateControlsGeometry(); fixOrder(); } @@ -586,15 +568,13 @@ void MainWindow::hideConnecting() { void MainWindow::themeUpdated(const Window::Theme::BackgroundUpdate &data) { using Type = Window::Theme::BackgroundUpdate::Type; if (data.type == Type::TestingTheme) { - if (_title) _title->update(); if (!_testingThemeWarning) { - _testingThemeWarning.create(_body); + _testingThemeWarning.create(bodyWidget()); _testingThemeWarning->setGeometry(rect()); _testingThemeWarning->setHiddenCallback([this] { _testingThemeWarning.destroyDelayed(); }); } _testingThemeWarning->showAnimated(); } else if (data.type == Type::RevertingTheme || data.type == Type::ApplyingTheme) { - if (_title) _title->update(); _testingThemeWarning->hideAnimated(); } } @@ -659,44 +639,6 @@ void MainWindow::setInnerFocus() { } } -Window::HitTestResult MainWindow::hitTest(const QPoint &p) const { - int x(p.x()), y(p.y()), w(width()), h(height()); - - const int32 raw = psResizeRowWidth(); - if (!windowState().testFlag(Qt::WindowMaximized)) { - if (y < raw) { - if (x < raw) { - return Window::HitTestResult::TopLeft; - } else if (x > w - raw - 1) { - return Window::HitTestResult::TopRight; - } - return Window::HitTestResult::Top; - } else if (y > h - raw - 1) { - if (x < raw) { - return Window::HitTestResult::BottomLeft; - } else if (x > w - raw - 1) { - return Window::HitTestResult::BottomRight; - } - return Window::HitTestResult::Bottom; - } else if (x < raw) { - return Window::HitTestResult::Left; - } else if (x > w - raw - 1) { - return Window::HitTestResult::Right; - } - } - auto titleTest = _title ? _title->hitTest(p - _title->geometry().topLeft()) : Window::HitTestResult::None; - if (titleTest != Window::HitTestResult::None) { - return titleTest; - } else if (x >= 0 && y >= 0 && x < w && y < h) { - return Window::HitTestResult::Client; - } - return Window::HitTestResult::None; -} - -QRect MainWindow::iconRect() const { - return _title ? _title->iconRect() : QRect(); -} - bool MainWindow::eventFilter(QObject *obj, QEvent *e) { switch (e->type()) { case QEvent::MouseButtonPress: @@ -757,7 +699,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { case QEvent::Move: case QEvent::Resize: if (obj == this) { - psUpdatedPosition(); + positionUpdated(); } break; } @@ -821,13 +763,17 @@ void MainWindow::updateTrayMenu(bool force) { void MainWindow::onShowAddContact() { if (isHidden()) showFromTray(); - if (_main) _main->showAddContact(); + if (App::self()) { + Ui::showLayer(new AddContactBox(), KeepOtherLayers); + } } void MainWindow::onShowNewGroup() { if (isHidden()) showFromTray(); - if (_main) Ui::showLayer(new GroupInfoBox(CreatingGroupGroup, false), KeepOtherLayers); + if (App::self()) { + Ui::showLayer(new GroupInfoBox(CreatingGroupGroup, false), KeepOtherLayers); + } } void MainWindow::onShowNewChannel() { @@ -906,7 +852,6 @@ void MainWindow::layerFinishedHide(LayerStackWidget *was) { } void MainWindow::fixOrder() { - if (_title) _title->raise(); if (_layerBg) _layerBg->raise(); if (_mediaPreview) _mediaPreview->raise(); if (_connecting) _connecting->raise(); @@ -980,7 +925,7 @@ void MainWindow::closeEvent(QCloseEvent *e) { } void MainWindow::resizeEvent(QResizeEvent *e) { - if (!_title) return; + Platform::MainWindow::resizeEvent(e); Adaptive::Layout layout = Adaptive::OneColumnLayout; if (width() > st::adaptiveWideWidth) { @@ -993,17 +938,11 @@ void MainWindow::resizeEvent(QResizeEvent *e) { Adaptive::Changed().notify(true); } - auto bodyTop = 0; - if (_title) { - _title->setGeometry(0, bodyTop, width(), st::titleHeight); - bodyTop += _title->height(); - } - _body->setGeometry(0, bodyTop, width(), height() - bodyTop); updateControlsGeometry(); } void MainWindow::updateControlsGeometry() { - auto body = _body->rect(); + auto body = bodyWidget()->rect(); if (_passcode) _passcode->setGeometry(body); if (_main) _main->setGeometry(body); if (_intro) _intro->setGeometry(body); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 7578e6a7d..39874f2c5 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -23,7 +23,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "pspecific.h" #include "ui/effects/rect_shadow.h" #include "platform/platform_main_window.h" -#include "window/window_title.h" #include "core/single_timer.h" class MediaView; @@ -71,7 +70,7 @@ private: class MediaPreviewWidget; -class MainWindow : public Platform::MainWindow, private base::Subscriber { +class MainWindow : public Platform::MainWindow { Q_OBJECT public: @@ -97,9 +96,6 @@ public: void mtpStateChanged(int32 dc, int32 state); - Window::HitTestResult hitTest(const QPoint &p) const; - QRect iconRect() const; - IntroWidget *introWidget(); MainWidget *mainWidget(); PasscodeWidget *passcodeWidget(); @@ -233,14 +229,10 @@ private: void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color); QImage icon16, icon32, icon64, iconbig16, iconbig32, iconbig64; - QWidget *centralwidget; - typedef QPair DelayedServiceMsg; QVector _delayedServiceMsgs; mtpRequestId _serviceHistoryRequest = 0; - Window::TitleWidget *_title = nullptr; - ChildWidget _body; ChildWidget _passcode = { nullptr }; ChildWidget _intro = { nullptr }; ChildWidget _main = { nullptr }; diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 85e9d2aa6..41804f51e 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -125,11 +125,11 @@ mediaPlayerNextDisabledIcon: icon { }; mediaPlayerPreviousButton: IconButton(mediaPlayerNextButton) { icon: icon { - { "player_previous", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, + { "player_next-flip_horizontal", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, }; } mediaPlayerPreviousDisabledIcon: icon { - { "player_previous", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, + { "player_next-flip_horizontal", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, }; mediaPlayerClose: IconButton(mediaPlayerRepeatButton) { width: 37px; @@ -178,10 +178,10 @@ mediaPlayerPanelNextDisabledIcon: icon { { "player_panel_next", mediaPlayerInactiveFg, point(10px, 10px) }, }; mediaPlayerPanelPreviousButton: IconButton(mediaPlayerPanelNextButton) { - icon: icon {{ "player_panel_previous", mediaPlayerActiveFg, point(10px, 10px) }}; + icon: icon {{ "player_panel_next-flip_horizontal", mediaPlayerActiveFg, point(10px, 10px) }}; } mediaPlayerPanelPreviousDisabledIcon: icon { - { "player_panel_previous", mediaPlayerInactiveFg, point(10px, 10px) }, + { "player_panel_next-flip_horizontal", mediaPlayerInactiveFg, point(10px, 10px) }, }; mediaPlayerPanelPadding: 16px; diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 23d8280a4..4390bd489 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -81,7 +81,7 @@ mediaviewVolumeOnIconOver: icon {{ "media_volume", mediaviewPlaybackActiveOver, mediaviewVolumeIconTop: 8px; mediaviewControllerRadius: 25px; -mediaviewLeft: icon {{ "mediaview_previous", #ffffff }}; +mediaviewLeft: icon {{ "mediaview_next-flip_horizontal", #ffffff }}; mediaviewRight: icon {{ "mediaview_next", #ffffff }}; mediaviewClose: icon {{ "mediaview_close", #ffffff }}; mediaviewSave: icon {{ "mediaview_download", #ffffff }}; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 3a952395f..0b648782e 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -54,7 +54,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD , _history(App::history(_peer->id)) , _channel(peerToChannel(_peer->id)) , _rowWidth(st::msgMinWidth) -, _search(this, st::dlgFilter, lang(lng_dlg_filter)) +, _search(this, st::dialogsFilter, lang(lng_dlg_filter)) , _cancelSearch(this, st::dialogsCancelSearch) , _itemsToBeLoaded(LinksOverviewPerPage * 2) , _width(st::windowMinWidth) { diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 167d9202f..10e61acf9 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -431,89 +431,6 @@ void MainWindow::LibsLoaded() { #endif // !TDESKTOP_DISABLE_UNITY_INTEGRATION } -void MainWindow::psInitSize() { - TWindowPos pos(cWindowPos()); - QRect avail(QDesktopWidget().availableGeometry()); - bool maximized = false; - QRect geom(avail.x() + (avail.width() - st::windowDefWidth) / 2, avail.y() + (avail.height() - st::windowDefHeight) / 2, st::windowDefWidth, st::windowDefHeight); - if (pos.w && pos.h) { - QList screens = Application::screens(); - for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { - QByteArray name = (*i)->name().toUtf8(); - if (pos.moncrc == hashCrc32(name.constData(), name.size())) { - QRect screen((*i)->geometry()); - int32 w = screen.width(), h = screen.height(); - if (w >= st::windowMinWidth && h >= st::windowMinHeight) { - if (pos.w > w) pos.w = w; - if (pos.h > h) pos.h = h; - pos.x += screen.x(); - pos.y += screen.y(); - if (pos.x < screen.x() + screen.width() - 10 && pos.y < screen.y() + screen.height() - 10) { - geom = QRect(pos.x, pos.y, pos.w, pos.h); - } - } - break; - } - } - - if (pos.y < 0) pos.y = 0; - maximized = pos.maximized; - } - setGeometry(geom); -} - -void MainWindow::psInitFrameless() { - psUpdatedPositionTimer.setSingleShot(true); - connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); -} - -void MainWindow::psSavePosition(Qt::WindowState state) { - if (state == Qt::WindowActive) state = windowHandle()->windowState(); - if (state == Qt::WindowMinimized || !posInited) return; - - TWindowPos pos(cWindowPos()), curPos = pos; - - if (state == Qt::WindowMaximized) { - curPos.maximized = 1; - } else { - QRect r(geometry()); - curPos.x = r.x(); - curPos.y = r.y(); - curPos.w = r.width(); - curPos.h = r.height(); - curPos.maximized = 0; - } - - int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2, d = 0; - QScreen *chosen = 0; - QList screens = Application::screens(); - for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { - int dx = (*i)->geometry().x() + (*i)->geometry().width() / 2 - px; if (dx < 0) dx = -dx; - int dy = (*i)->geometry().y() + (*i)->geometry().height() / 2 - py; if (dy < 0) dy = -dy; - if (!chosen || dx + dy < d) { - d = dx + dy; - chosen = *i; - } - } - if (chosen) { - curPos.x -= chosen->geometry().x(); - curPos.y -= chosen->geometry().y(); - QByteArray name = chosen->name().toUtf8(); - curPos.moncrc = hashCrc32(name.constData(), name.size()); - } - - if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { - if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { - cSetWindowPos(curPos); - Local::writeSettings(); - } - } -} - -void MainWindow::psUpdatedPosition() { - psUpdatedPositionTimer.start(SaveWindowPositionTimeout); -} - void MainWindow::psCreateTrayIcon() { if (!noQtTrayIcon) { cSetSupportTray(QSystemTrayIcon::isSystemTrayAvailable()); @@ -641,7 +558,7 @@ void MainWindow::psFirstShow() { show(); } - posInited = true; + setPositionInited(); } void MainWindow::psInitSysMenu() { diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 63551b249..bff10d103 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -30,18 +30,10 @@ class MainWindow : public Window::MainWindow { public: MainWindow(); - int32 psResizeRowWidth() const { - return 0;//st::wndResizeAreaWidth; - } - - void psInitFrameless(); - void psInitSize(); - void psFirstShow(); void psInitSysMenu(); void psUpdateSysMenu(Qt::WindowState state); void psUpdateMargins(); - void psUpdatedPosition(); void psFlash(); void psNotifySettingGot(); @@ -51,10 +43,6 @@ public: void psRefreshTaskbarIcon() { } - bool psPosInited() const { - return posInited; - } - void psUpdateCounter(); bool psHasNativeNotifications(); @@ -66,7 +54,6 @@ public: ~MainWindow(); public slots: - void psSavePosition(Qt::WindowState state = Qt::WindowActive); void psShowTrayMenu(); void psStatusIconCheck(); @@ -76,7 +63,6 @@ protected: bool psHasTrayIcon() const; - bool posInited = false; QSystemTrayIcon *trayIcon = nullptr; QMenu *trayIconMenu = nullptr; QImage icon256, iconbig256; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index e82f2b27d..15aec9e32 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -38,18 +38,10 @@ class MainWindow : public Window::MainWindow { public: MainWindow(); - int32 psResizeRowWidth() const { - return 0;//st::wndResizeAreaWidth; - } - - void psInitFrameless(); - void psInitSize(); - void psFirstShow(); void psInitSysMenu(); void psUpdateSysMenu(Qt::WindowState state); void psUpdateMargins(); - void psUpdatedPosition(); void psFlash(); @@ -58,10 +50,6 @@ public: void psRefreshTaskbarIcon() { } - bool psPosInited() const { - return posInited; - } - bool psFilterNativeEvent(void *event); bool eventFilter(QObject *obj, QEvent *evt) override; @@ -79,7 +67,6 @@ public: ~MainWindow(); public slots: - void psSavePosition(Qt::WindowState state = Qt::WindowActive); void psShowTrayMenu(); void psMacUndo(); @@ -103,7 +90,6 @@ protected: void psMacUpdateMenu(); - bool posInited; QSystemTrayIcon *trayIcon = nullptr; QMenu *trayIconMenu = nullptr; QImage icon256, iconbig256; @@ -118,6 +104,8 @@ protected: QTimer psUpdatedPositionTimer; private: + void createGlobalMenu(); + MacPrivate _private; mutable bool psIdle; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 0de7a6a3f..ca658345a 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -52,8 +52,7 @@ void MacPrivate::darkModeChanged() { } MainWindow::MainWindow() -: posInited(false) -, icon256(qsl(":/gui/art/icon256.png")) +: icon256(qsl(":/gui/art/icon256.png")) , iconbig256(qsl(":/gui/art/iconbig256.png")) , wndIcon(QPixmap::fromImage(iconbig256, Qt::ColorOnly)) { QImage tray(qsl(":/gui/art/osxtray.png")); @@ -190,89 +189,11 @@ void MainWindow::psUpdateCounter() { } } -void MainWindow::psInitSize() { - TWindowPos pos(cWindowPos()); - QRect avail(QDesktopWidget().availableGeometry()); - bool maximized = false; - QRect geom(avail.x() + (avail.width() - st::windowDefWidth) / 2, avail.y() + (avail.height() - st::windowDefHeight) / 2, st::windowDefWidth, st::windowDefHeight); - if (pos.w && pos.h) { - QList screens = Application::screens(); - for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { - QByteArray name = (*i)->name().toUtf8(); - if (pos.moncrc == hashCrc32(name.constData(), name.size())) { - QRect screen((*i)->geometry()); - int32 w = screen.width(), h = screen.height(); - if (w >= st::windowMinWidth && h >= st::windowMinHeight) { - if (pos.w > w) pos.w = w; - if (pos.h > h) pos.h = h; - pos.x += screen.x(); - pos.y += screen.y(); - if (pos.x < screen.x() + screen.width() - 10 && pos.y < screen.y() + screen.height() - 10) { - geom = QRect(pos.x, pos.y, pos.w, pos.h); - } - } - break; - } - } - - if (pos.y < 0) pos.y = 0; - maximized = pos.maximized; - } - setGeometry(geom); -} - void MainWindow::psInitFrameless() { psUpdatedPositionTimer.setSingleShot(true); connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); } -void MainWindow::psSavePosition(Qt::WindowState state) { - if (state == Qt::WindowActive) state = windowHandle()->windowState(); - if (state == Qt::WindowMinimized || !posInited) return; - - TWindowPos pos(cWindowPos()), curPos = pos; - - if (state == Qt::WindowMaximized) { - curPos.maximized = 1; - } else { - QRect r(geometry()); - curPos.x = r.x(); - curPos.y = r.y(); - curPos.w = r.width(); - curPos.h = r.height(); - curPos.maximized = 0; - } - - int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2, d = 0; - QScreen *chosen = 0; - QList screens = Application::screens(); - for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { - int dx = (*i)->geometry().x() + (*i)->geometry().width() / 2 - px; if (dx < 0) dx = -dx; - int dy = (*i)->geometry().y() + (*i)->geometry().height() / 2 - py; if (dy < 0) dy = -dy; - if (!chosen || dx + dy < d) { - d = dx + dy; - chosen = *i; - } - } - if (chosen) { - curPos.x -= chosen->geometry().x(); - curPos.y -= chosen->geometry().y(); - QByteArray name = chosen->name().toUtf8(); - curPos.moncrc = hashCrc32(name.constData(), name.size()); - } - - if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { - if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { - cSetWindowPos(curPos); - Local::writeSettings(); - } - } -} - -void MainWindow::psUpdatedPosition() { - psUpdatedPositionTimer.start(SaveWindowPositionTimeout); -} - void MainWindow::psFirstShow() { psUpdateMargins(); @@ -296,9 +217,14 @@ void MainWindow::psFirstShow() { show(); } - posInited = true; + setPositionInited(); - // init global menu + createGlobalMenu(); + + psMacUpdateMenu(); +} + +void MainWindow::createGlobalMenu() { auto main = psMainMenu.addMenu(qsl("Telegram")); auto about = main->addAction(lng_mac_menu_about_telegram(lt_telegram, qsl("Telegram"))); connect(about, SIGNAL(triggered()), base::lambda_slot(about, [] { @@ -339,8 +265,6 @@ void MainWindow::psFirstShow() { psNewChannel = window->addAction(lang(lng_mac_menu_new_channel), App::wnd(), SLOT(onShowNewChannel())); window->addSeparator(); psShowTelegram = window->addAction(lang(lng_mac_menu_show), App::wnd(), SLOT(showFromTray())); - - psMacUpdateMenu(); } namespace { @@ -398,23 +322,23 @@ void MainWindow::psUpdateMargins() { } void MainWindow::psMacUpdateMenu() { - if (!posInited) return; + if (!positionInited()) return; QWidget *focused = QApplication::focusWidget(); bool isLogged = !!App::self(), canUndo = false, canRedo = false, canCut = false, canCopy = false, canPaste = false, canDelete = false, canSelectAll = false; - if (QLineEdit *edit = qobject_cast(focused)) { + if (auto edit = qobject_cast(focused)) { canCut = canCopy = canDelete = edit->hasSelectedText(); canSelectAll = !edit->text().isEmpty(); canUndo = edit->isUndoAvailable(); canRedo = edit->isRedoAvailable(); canPaste = !Application::clipboard()->text().isEmpty(); - } else if (FlatTextarea *edit = qobject_cast(focused)) { + } else if (auto edit = qobject_cast(focused)) { canCut = canCopy = canDelete = edit->textCursor().hasSelection(); canSelectAll = !edit->isEmpty(); canUndo = edit->isUndoAvailable(); canRedo = edit->isRedoAvailable(); canPaste = !Application::clipboard()->text().isEmpty(); - } else if (HistoryInner *list = qobject_cast(focused)) { + } else if (auto list = qobject_cast(focused)) { canCopy = list->canCopySelected(); canDelete = list->canDeleteSelected(); } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index b44401412..e328e4dcb 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -194,7 +194,7 @@ public: max_w = avail.width(); accumulate_max(max_w, st::windowMinWidth); max_h = avail.height(); - accumulate_max(max_h, st::windowMinHeight); + accumulate_max(max_h, st::titleHeight + st::windowMinHeight); HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); HWND hwnd = App::wnd() ? App::wnd()->psHwnd() : 0; @@ -350,7 +350,7 @@ public: } return; } - if (!App::wnd()->psPosInited()) return; + if (!App::wnd()->positionInited()) return; int x = _x, y = _y, w = _w, h = _h; if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE) || !(pos->flags & SWP_NOREPOSITION))) { @@ -644,6 +644,17 @@ void MainWindow::psShowTrayMenu() { trayIconMenu->popup(QCursor::pos()); } +int32 MainWindow::screenNameChecksum(const QString &name) const { + constexpr int DeviceNameSize = base::array_size(MONITORINFOEX().szDevice); + wchar_t buffer[DeviceNameSize] = { 0 }; + if (name.size() < DeviceNameSize) { + name.toWCharArray(buffer); + } else { + memcpy(buffer, name.toStdWString().data(), sizeof(buffer)); + } + return hashCrc32(buffer, sizeof(buffer)); +} + void MainWindow::psRefreshTaskbarIcon() { QWidget *w = new QWidget(this); w->setWindowFlags(static_cast(Qt::Tool) | Qt::FramelessWindowHint); @@ -754,58 +765,7 @@ void MainWindow::psUpdateCounter() { SetWindowPos(ps_hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } -namespace { -HMONITOR enumMonitor = 0; -RECT enumMonitorWork; - -BOOL CALLBACK _monitorEnumProc( - _In_ HMONITOR hMonitor, - _In_ HDC hdcMonitor, - _In_ LPRECT lprcMonitor, - _In_ LPARAM dwData -) { - MONITORINFOEX info; - info.cbSize = sizeof(info); - GetMonitorInfo(hMonitor, &info); - if (dwData == hashCrc32(info.szDevice, sizeof(info.szDevice))) { - enumMonitor = hMonitor; - enumMonitorWork = info.rcWork; - return FALSE; - } - return TRUE; -} -} // namespace - -void MainWindow::psInitSize() { - TWindowPos pos(cWindowPos()); - QRect avail(Sandbox::availableGeometry()); - bool maximized = false; - QRect geom(avail.x() + (avail.width() - st::windowDefWidth) / 2, avail.y() + (avail.height() - st::windowDefHeight) / 2, st::windowDefWidth, st::windowDefHeight); - if (pos.w && pos.h) { - if (pos.y < 0) pos.y = 0; - enumMonitor = 0; - EnumDisplayMonitors(0, 0, &_monitorEnumProc, pos.moncrc); - if (enumMonitor) { - int32 w = enumMonitorWork.right - enumMonitorWork.left, h = enumMonitorWork.bottom - enumMonitorWork.top; - if (w >= st::windowMinWidth && h >= st::windowMinHeight) { - if (pos.w > w) pos.w = w; - if (pos.h > h) pos.h = h; - pos.x += enumMonitorWork.left; - pos.y += enumMonitorWork.top; - if (pos.x < enumMonitorWork.right - 10 && pos.y < enumMonitorWork.bottom - 10) { - geom = QRect(pos.x, pos.y, pos.w, pos.h); - } - } - } - maximized = pos.maximized; - } - setGeometry(geom); -} - -void MainWindow::psInitFrameless() { - psUpdatedPositionTimer.setSingleShot(true); - connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); - +void MainWindow::initHook() { auto platformInterface = QGuiApplication::platformNativeInterface(); ps_hWnd = static_cast(platformInterface->nativeResourceForWindow(QByteArrayLiteral("handle"), windowHandle())); @@ -816,53 +776,9 @@ void MainWindow::psInitFrameless() { Dlls::WTSRegisterSessionNotification(ps_hWnd, NOTIFY_FOR_THIS_SESSION); } -// RegisterApplicationRestart(NULL, 0); - psInitSysMenu(); } -void MainWindow::psSavePosition(Qt::WindowState state) { - if (state == Qt::WindowActive) state = windowHandle()->windowState(); - if (state == Qt::WindowMinimized || !posInited) return; - - TWindowPos pos(cWindowPos()), curPos = pos; - - if (state == Qt::WindowMaximized) { - curPos.maximized = 1; - } else { - RECT w; - GetWindowRect(ps_hWnd, &w); - curPos.x = w.left; - curPos.y = w.top; - curPos.w = w.right - w.left; - curPos.h = w.bottom - w.top; - curPos.maximized = 0; - } - - HMONITOR hMonitor = MonitorFromWindow(ps_hWnd, MONITOR_DEFAULTTONEAREST); - if (hMonitor) { - MONITORINFOEX info; - info.cbSize = sizeof(info); - GetMonitorInfo(hMonitor, &info); - if (!curPos.maximized) { - curPos.x -= info.rcWork.left; - curPos.y -= info.rcWork.top; - } - curPos.moncrc = hashCrc32(info.szDevice, sizeof(info.szDevice)); - } - - if (curPos.w >= st::windowMinWidth && curPos.h >= st::windowMinHeight) { - if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { - cSetWindowPos(curPos); - Local::writeSettings(); - } - } -} - -void MainWindow::psUpdatedPosition() { - psUpdatedPositionTimer.start(SaveWindowPositionTimeout); -} - bool MainWindow::psHasNativeNotifications() { return Notifications::supported(); } @@ -894,7 +810,7 @@ void MainWindow::psFirstShow() { show(); } - posInited = true; + setPositionInited(); if (showShadows) { shadowsUpdate(ShadowsChange::Moved | ShadowsChange::Resized | ShadowsChange::Shown); } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 0aad21bc1..e7e232926 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -35,12 +35,6 @@ class MainWindow : public Window::MainWindow { public: MainWindow(); - int32 psResizeRowWidth() const { - return 0;//st::wndResizeAreaWidth; - } - - void psInitFrameless(); - void psInitSize(); HWND psHwnd() const; HMENU psMenu() const; @@ -48,7 +42,6 @@ public: void psInitSysMenu(); void psUpdateSysMenu(Qt::WindowState state); void psUpdateMargins(); - void psUpdatedPosition(); void psFlash(); void psNotifySettingGot(); @@ -57,10 +50,6 @@ public: void psRefreshTaskbarIcon(); - bool psPosInited() const { - return posInited; - } - void psUpdateCounter(); bool psHasNativeNotifications(); @@ -99,15 +88,16 @@ public: ~MainWindow(); public slots: - void psSavePosition(Qt::WindowState state = Qt::WindowActive); void psShowTrayMenu(); protected: + void initHook() override; + int32 screenNameChecksum(const QString &name) const override; + bool psHasTrayIcon() const { return trayIcon; } - bool posInited = false; QSystemTrayIcon *trayIcon = nullptr; Ui::PopupMenu *trayIconMenu = nullptr; QImage icon256, iconbig256; diff --git a/Telegram/SourceFiles/platform/win/window_title_win.cpp b/Telegram/SourceFiles/platform/win/window_title_win.cpp index 61c348fd5..e542e965d 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.cpp +++ b/Telegram/SourceFiles/platform/win/window_title_win.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "platform/win/window_title_win.h" #include "ui/buttons/icon_button.h" +#include "ui/widgets/shadow.h" #include "styles/style_window.h" namespace Platform { @@ -30,7 +31,8 @@ TitleWidget::TitleWidget(QWidget *parent) : Window::TitleWidget(parent) , _minimize(this, st::titleButtonMinimize) , _maximizeRestore(this, st::titleButtonMaximize) , _close(this, st::titleButtonClose) -, _maximized(parent->window()->windowState() & Qt::WindowMaximized) { +, _maximized(parent->window()->windowState() & Qt::WindowMaximized) +, _shadow(this, st::titleShadow) { _minimize->setClickedCallback([this]() { window()->setWindowState(Qt::WindowMinimized); _minimize->clearState(); @@ -65,6 +67,7 @@ void TitleWidget::updateControlsPosition() { void TitleWidget::resizeEvent(QResizeEvent *e) { updateControlsPosition(); + _shadow->setGeometry(0, height() - st::lineWidth, width(), st::lineWidth); } void TitleWidget::updateControlsVisibility() { diff --git a/Telegram/SourceFiles/platform/win/window_title_win.h b/Telegram/SourceFiles/platform/win/window_title_win.h index 1fd97d0bf..49a4f11d2 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.h +++ b/Telegram/SourceFiles/platform/win/window_title_win.h @@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class IconButton; +class PlainShadow; } // namespace Ui namespace Platform { @@ -51,6 +52,7 @@ private: ChildWidget _minimize; ChildWidget _maximizeRestore; ChildWidget _close; + ChildWidget _shadow; bool _maximized = false; diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp index b3202da59..5137ca0a1 100644 --- a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp +++ b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp @@ -155,7 +155,7 @@ bool EventFilter::mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa } emit App::wnd()->windowHandle()->windowStateChanged(state); } else { - App::wnd()->psUpdatedPosition(); + App::wnd()->positionUpdated(); } App::wnd()->psUpdateMargins(); MainWindow::ShadowsChanges changes = (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXIMIZED) ? ShadowsChange::Hidden : (ShadowsChange::Resized | ShadowsChange::Shown); @@ -172,7 +172,7 @@ bool EventFilter::mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa case WM_MOVE: { App::wnd()->shadowsUpdate(ShadowsChange::Moved); - App::wnd()->psUpdatedPosition(); + App::wnd()->positionUpdated(); } return false; case WM_NCHITTEST: { @@ -185,7 +185,6 @@ bool EventFilter::mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa switch (res) { case Window::HitTestResult::Client: case Window::HitTestResult::SysButton: *result = HTCLIENT; break; - case Window::HitTestResult::Icon: *result = HTCAPTION; break; case Window::HitTestResult::Caption: *result = HTCAPTION; break; case Window::HitTestResult::Top: *result = HTTOP; break; case Window::HitTestResult::TopRight: *result = HTTOPRIGHT; break; @@ -204,42 +203,6 @@ bool EventFilter::mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa SendMessage(hWnd, WM_SYSCOMMAND, SC_MOUSEMENU, lParam); } return true; - case WM_NCLBUTTONDOWN: { - POINTS p = MAKEPOINTS(lParam); - RECT r; - GetWindowRect(hWnd, &r); - auto res = App::wnd()->hitTest(QPoint(p.x - r.left + App::wnd()->deltaLeft(), p.y - r.top + App::wnd()->deltaTop())); - switch (res) { - case Window::HitTestResult::Icon: - if (menuHidden && getms() < menuHidden + 10) { - menuHidden = 0; - if (getms() < menuShown + GetDoubleClickTime()) { - App::wnd()->close(); - } - } else { - QRect icon = App::wnd()->iconRect(); - p.x = r.left - App::wnd()->deltaLeft() + icon.left(); - p.y = r.top - App::wnd()->deltaTop() + icon.top() + icon.height(); - App::wnd()->psUpdateSysMenu(App::wnd()->windowHandle()->windowState()); - menuShown = getms(); - menuHidden = 0; - TrackPopupMenu(App::wnd()->psMenu(), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, p.x, p.y, 0, hWnd, 0); - menuHidden = getms(); - } - return true; - }; - } return false; - - case WM_NCLBUTTONDBLCLK: { - POINTS p = MAKEPOINTS(lParam); - RECT r; - GetWindowRect(hWnd, &r); - auto res = App::wnd()->hitTest(QPoint(p.x - r.left + App::wnd()->deltaLeft(), p.y - r.top + App::wnd()->deltaTop())); - switch (res) { - case Window::HitTestResult::Icon: App::wnd()->close(); return true; - }; - } return false; - case WM_SYSCOMMAND: { if (wParam == SC_MOUSEMENU) { POINTS p = MAKEPOINTS(lParam); diff --git a/Telegram/SourceFiles/pspecific_win.cpp b/Telegram/SourceFiles/pspecific_win.cpp index 78fe71759..7558b4c5a 100644 --- a/Telegram/SourceFiles/pspecific_win.cpp +++ b/Telegram/SourceFiles/pspecific_win.cpp @@ -924,7 +924,7 @@ void psUpdateOverlayed(TWidget *widget) { if (!wv) widget->setAttribute(Qt::WA_WState_Visible, true); widget->update(); QEvent e(QEvent::UpdateRequest); - widget->event(&e); + QGuiApplication::sendEvent(widget, &e); if (!wm) widget->setAttribute(Qt::WA_Mapped, false); if (!wv) widget->setAttribute(Qt::WA_WState_Visible, false); } diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index b56e2f95b..32ad78e0f 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -74,10 +74,11 @@ settingsButtonTop: 75px; settingsButtonSkip: 10px; settingsPrimaryButton: defaultActiveButton; settingsSecondaryButton: defaultLightButton; -settingsEditButton: IconButton(dialogsNewChatButton) { +settingsEditButton: IconButton { width: 24px; height: 34px; + icon: icon {{ "settings_edit_name", #b7b7b7 }}; iconPosition: point(3px, 9px); iconPositionDown: point(3px, 10px); } diff --git a/Telegram/SourceFiles/stickers/stickers.style b/Telegram/SourceFiles/stickers/stickers.style index 397d149d2..91ba059a5 100644 --- a/Telegram/SourceFiles/stickers/stickers.style +++ b/Telegram/SourceFiles/stickers/stickers.style @@ -136,8 +136,8 @@ emojiColorsSepColor: #d5d5d5; emojiSwitchSkip: 27px; emojiSwitchImgSkip: 21px; emojiSwitchColor: #42a8db; -emojiSwitchStickers: icon {{ "emoji_switch_right", emojiSwitchColor }}; -emojiSwitchEmoji: icon {{ "emoji_switch_left", emojiSwitchColor }}; +emojiSwitchStickers: icon {{ "emoji_switch", emojiSwitchColor }}; +emojiSwitchEmoji: icon {{ "emoji_switch-flip_horizontal", emojiSwitchColor }}; stickerPanSize: size(64px, 64px); stickerPanPadding: 11px; diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h index b8bb58a7c..9970640fc 100644 --- a/Telegram/SourceFiles/ui/twidget.h +++ b/Telegram/SourceFiles/ui/twidget.h @@ -138,9 +138,6 @@ class TWidget : public QWidget { public: TWidget(QWidget *parent = nullptr) : QWidget(parent) { } - bool event(QEvent *e) override { - return QWidget::event(e); - } virtual void grabStart() { } virtual void grabFinish() { @@ -238,39 +235,39 @@ private: // A simple wrap around T* to explicitly state ownership template -class ChildWidget { +class ChildObject { public: - ChildWidget(std_::nullptr_t) : _widget(nullptr) { + ChildObject(std_::nullptr_t) : _object(nullptr) { } // No default constructor, but constructors with at least // one argument are simply make functions. template - ChildWidget(Parent &&parent, Args&&... args) : _widget(new T(std_::forward(parent), std_::forward(args)...)) { + ChildObject(Parent &&parent, Args&&... args) : _object(new T(std_::forward(parent), std_::forward(args)...)) { } - ChildWidget(const ChildWidget &other) = delete; - ChildWidget &operator=(const ChildWidget &other) = delete; + ChildObject(const ChildObject &other) = delete; + ChildObject &operator=(const ChildObject &other) = delete; - ChildWidget &operator=(std_::nullptr_t) { - _widget = nullptr; + ChildObject &operator=(std_::nullptr_t) { + _object = nullptr; return *this; } - ChildWidget &operator=(T *widget) { - _widget = widget; + ChildObject &operator=(T *object) { + _object = object; return *this; } T *operator->() const { - return _widget; + return _object; } T &operator*() const { - return *_widget; + return *_object; } // So we can pass this pointer to methods like connect(). T *ptr() const { - return _widget; + return _object; } operator T*() const { return ptr(); @@ -279,28 +276,30 @@ public: // Use that instead "= new T(parent, ...)" template void create(Parent &&parent, Args&&... args) { - delete _widget; - _widget = new T(std_::forward(parent), std_::forward(args)...); + delete _object; + _object = new T(std_::forward(parent), std_::forward(args)...); } void destroy() { - if (_widget) { - delete _widget; - _widget = nullptr; - } + delete base::take(_object); } void destroyDelayed() { - if (_widget) { - _widget->hide(); - _widget->deleteLater(); - _widget = nullptr; + if (_object) { + if (auto widget = base::up_cast(_object)) { + widget->hide(); + } + _object->deleteLater(); + _object = nullptr; } } private: - T *_widget; + T *_object; }; +template +using ChildWidget = ChildObject; + void sendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton button, const QPoint &globalPoint); inline void sendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton button) { diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp index a13a453d2..938a86bf8 100644 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp @@ -68,6 +68,10 @@ QAction *DropdownMenu::addAction(const QString &text, const QObject *receiver, c return _menu->addAction(text, receiver, member, icon, iconOver); } +QAction *DropdownMenu::addAction(const QString &text, base::lambda_unique callback, const style::icon *icon, const style::icon *iconOver) { + return _menu->addAction(text, std_::move(callback), icon, iconOver); +} + QAction *DropdownMenu::addSeparator() { return _menu->addSeparator(); } diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h index 3d0092c14..6d63733ab 100644 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h @@ -31,6 +31,7 @@ public: DropdownMenu(QWidget *parent, const style::DropdownMenu &st = st::defaultDropdownMenu); QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); + QAction *addAction(const QString &text, base::lambda_unique callback, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); QAction *addSeparator(); void clearActions(); diff --git a/Telegram/SourceFiles/ui/widgets/menu.cpp b/Telegram/SourceFiles/ui/widgets/menu.cpp index baa2c2da1..d98fb8e31 100644 --- a/Telegram/SourceFiles/ui/widgets/menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/menu.cpp @@ -50,9 +50,15 @@ void Menu::init() { } QAction *Menu::addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon, const style::icon *iconOver) { - auto action = new QAction(text, this); + auto action = addAction(new QAction(text, this), icon, iconOver); connect(action, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection); - return addAction(action, icon, iconOver); + return action; +} + +QAction *Menu::addAction(const QString &text, base::lambda_unique callback, const style::icon *icon, const style::icon *iconOver) { + auto action = addAction(new QAction(text, this), icon, iconOver); + connect(action, SIGNAL(triggered(bool)), base::lambda_slot(action, std_::move(callback)), SLOT(action()), Qt::QueuedConnection); + return action; } QAction *Menu::addAction(QAction *action, const style::icon *icon, const style::icon *iconOver) { diff --git a/Telegram/SourceFiles/ui/widgets/menu.h b/Telegram/SourceFiles/ui/widgets/menu.h index 51e706001..a468dd88f 100644 --- a/Telegram/SourceFiles/ui/widgets/menu.h +++ b/Telegram/SourceFiles/ui/widgets/menu.h @@ -32,6 +32,7 @@ public: Menu(QWidget *parent, QMenu *menu, const style::Menu &st = st::defaultMenu); QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); + QAction *addAction(const QString &text, base::lambda_unique callback, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); QAction *addSeparator(); void clearActions(); diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index 03e267598..6dc106def 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -84,6 +84,10 @@ QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, cons return _menu->addAction(text, receiver, member, icon, iconOver); } +QAction *PopupMenu::addAction(const QString &text, base::lambda_unique callback, const style::icon *icon, const style::icon *iconOver) { + return _menu->addAction(text, std_::move(callback), icon, iconOver); +} + QAction *PopupMenu::addSeparator() { return _menu->addSeparator(); } diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.h b/Telegram/SourceFiles/ui/widgets/popup_menu.h index 9699cd6dc..6863a311e 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.h +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.h @@ -29,6 +29,7 @@ public: PopupMenu(QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu); QAction *addAction(const QString &text, const QObject *receiver, const char* member, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); + QAction *addAction(const QString &text, base::lambda_unique callback, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr); QAction *addSeparator(); void clearActions(); diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index b144ffa49..40604159d 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -190,6 +190,9 @@ defaultMaskButton: MaskButton { widgetSlideDuration: 200; widgetFadeDuration: 200; +fieldSearchIcon: icon {{ "box_search", #aaaaaa, point(9px, 8px) }}; +boxFieldSearchIcon: icon {{ "box_search", #aaaaaa, point(10px, 9px) }}; + discreteSliderHeight: 39px; discreteSliderTop: 5px; discreteSliderSkip: 3px; diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 0ae9526ba..02aef33b2 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -21,9 +21,147 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "window/main_window.h" +#include "localstorage.h" +#include "styles/style_window.h" +#include "platform/platform_window_title.h" +#include "window/window_theme.h" + namespace Window { -MainWindow::MainWindow() { +MainWindow::MainWindow() : QMainWindow() +, _positionUpdatedTimer(this) +, _body(this) { + setCentralWidget(_body); + subscribe(Theme::Background(), [this](const Theme::BackgroundUpdate &data) { + using Type = Theme::BackgroundUpdate::Type; + if (data.type == Type::TestingTheme || data.type == Type::RevertingTheme || data.type == Type::ApplyingTheme) { + if (_title) { + _title->update(); + } + } + }); +} + +void MainWindow::init() { + _positionUpdatedTimer->setSingleShot(true); + connect(_positionUpdatedTimer, SIGNAL(timeout()), this, SLOT(savePositionByTimer())); + + _title = Platform::CreateTitleWidget(this); + + auto p = palette(); + p.setColor(QPalette::Window, st::windowBg->c); + setPalette(p); + + initSize(); + + initHook(); +} + +HitTestResult MainWindow::hitTest(const QPoint &p) const { + auto titleResult = _title ? _title->hitTest(p - _title->geometry().topLeft()) : Window::HitTestResult::None; + if (titleResult != Window::HitTestResult::None) { + return titleResult; + } else if (rect().contains(p)) { + return Window::HitTestResult::Client; + } + return Window::HitTestResult::None; +} + +void MainWindow::initSize() { + setMinimumWidth(st::windowMinWidth); + setMinimumHeight((_title ? _title->height() : 0) + st::windowMinHeight); + + auto pos = cWindowPos(); + auto avail = QDesktopWidget().availableGeometry(); + bool maximized = false; + auto geom = QRect(avail.x() + (avail.width() - st::windowDefaultWidth) / 2, avail.y() + (avail.height() - st::windowDefaultHeight) / 2, st::windowDefaultWidth, st::windowDefaultHeight); + if (pos.w && pos.h) { + for (auto screen : QGuiApplication::screens()) { + if (pos.moncrc == screenNameChecksum(screen->name())) { + auto screenGeometry = screen->geometry(); + auto w = screenGeometry.width(), h = screenGeometry.height(); + if (w >= st::windowMinWidth && h >= st::windowMinHeight) { + if (pos.w > w) pos.w = w; + if (pos.h > h) pos.h = h; + pos.x += screenGeometry.x(); + pos.y += screenGeometry.y(); + if (pos.x + st::windowMinWidth <= screenGeometry.x() + screenGeometry.width() && + pos.y + st::windowMinHeight <= screenGeometry.y() + screenGeometry.height()) { + geom = QRect(pos.x, pos.y, pos.w, pos.h); + } + } + break; + } + } + + if (pos.y < 0) pos.y = 0; + maximized = pos.maximized; + } + setGeometry(geom); +} + +void MainWindow::positionUpdated() { + _positionUpdatedTimer->start(SaveWindowPositionTimeout); +} + +int32 MainWindow::screenNameChecksum(const QString &name) const { + auto bytes = name.toUtf8(); + return hashCrc32(bytes.constData(), bytes.size()); +} + +void MainWindow::setPositionInited() { + _positionInited = true; +} + +void MainWindow::resizeEvent(QResizeEvent *e) { + auto bodyTop = 0; + if (_title) { + _title->setGeometry(0, bodyTop, width(), st::titleHeight); + bodyTop += _title->height(); + } + _body->setGeometry(0, bodyTop, width(), height() - bodyTop); +} + +void MainWindow::savePosition(Qt::WindowState state) { + if (state == Qt::WindowActive) state = windowHandle()->windowState(); + if (state == Qt::WindowMinimized || !positionInited()) return; + + TWindowPos pos(cWindowPos()), curPos = pos; + + if (state == Qt::WindowMaximized) { + curPos.maximized = 1; + } else { + QRect r(geometry()); + curPos.x = r.x(); + curPos.y = r.y(); + curPos.w = r.width(); + curPos.h = r.height(); + curPos.maximized = 0; + } + + int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2; + int minDelta = 0; + QScreen *chosen = 0; + auto screens = QGuiApplication::screens(); + for (auto screen : QGuiApplication::screens()) { + auto delta = (screen->geometry().center() - QPoint(px, py)).manhattanLength(); + if (!chosen || delta < minDelta) { + minDelta = delta; + chosen = screen; + } + } + if (chosen) { + curPos.x -= chosen->geometry().x(); + curPos.y -= chosen->geometry().y(); + curPos.moncrc = screenNameChecksum(chosen->name()); + } + + if (curPos.w >= st::windowMinWidth && curPos.h >= st::windowMinHeight) { + if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { + cSetWindowPos(curPos); + Local::writeSettings(); + } + } } MainWindow::~MainWindow() { diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index ae15bd5f9..aeffea703 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -20,20 +20,63 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include "window/window_title.h" + namespace Window { -class MainWindow : public QMainWindow { +class TitleWidget; + +class MainWindow : public QMainWindow, protected base::Subscriber { + Q_OBJECT + public: MainWindow(); + void init(); + HitTestResult hitTest(const QPoint &p) const; + + bool positionInited() const { + return _positionInited; + } + void positionUpdated(); + virtual void closeWithoutDestroy(); virtual ~MainWindow(); protected: + void resizeEvent(QResizeEvent *e) override; + + QWidget *bodyWidget() { // temp + return _body; + } + void savePosition(Qt::WindowState state = Qt::WindowActive); + + virtual void initHook() { + } + virtual void stateChangedHook(Qt::WindowState state) { } + // This one is overriden in Windows for historical reasons. + virtual int32 screenNameChecksum(const QString &name) const; + + void setPositionInited(); + +private slots: + void savePositionByTimer() { + savePosition(); + } + +private: + void initSize(); + + ChildObject _positionUpdatedTimer; + bool _positionInited = false; + + ChildWidget _title = { nullptr }; + ChildWidget _body; + }; } // namespace Window diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 50f584808..16348efff 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -446,6 +446,7 @@ void Widget::addToHeight(int add) { auto newHeight = height() + add; auto newPosition = computePosition(newHeight); updateGeometry(newPosition.x(), newPosition.y(), width(), newHeight); + psUpdateOverlayed(this); } void Widget::updateGeometry(int x, int y, int width, int height) { diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 262520044..50bd74cad 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -24,8 +24,8 @@ using "ui/widgets/widgets.style"; windowMinWidth: 380px; windowMinHeight: 480px; -windowDefWidth: 800px; -windowDefHeight: 600px; +windowDefaultWidth: 800px; +windowDefaultHeight: 600px; windowShadow: icon {{ "window_shadow", windowShadowFg }}; windowShadowShift: 1px; diff --git a/Telegram/SourceFiles/window/window_title.h b/Telegram/SourceFiles/window/window_title.h index 41916387a..15dad581d 100644 --- a/Telegram/SourceFiles/window/window_title.h +++ b/Telegram/SourceFiles/window/window_title.h @@ -26,7 +26,6 @@ enum class HitTestResult { None = 0, Client, SysButton, - Icon, Caption, Top, TopRight, From e693a98bd4518cec09db355fe53610d08f0e106f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 4 Nov 2016 22:50:35 +0300 Subject: [PATCH 016/100] Passcode lock button added to DialogsWidget. --- Telegram/Resources/colors.palette | 10 +- Telegram/Resources/icons/dialogs_lock.png | Bin 0 -> 285 bytes Telegram/Resources/icons/dialogs_lock@2x.png | Bin 0 -> 408 bytes Telegram/Resources/icons/dialogs_unlock.png | Bin 0 -> 287 bytes .../Resources/icons/dialogs_unlock@2x.png | Bin 0 -> 430 bytes Telegram/Resources/sample.tdesktop-theme | 7 +- Telegram/SourceFiles/apiwrap.cpp | 4 +- Telegram/SourceFiles/app.cpp | 2 +- Telegram/SourceFiles/application.cpp | 6 +- Telegram/SourceFiles/boxes/boxes.style | 7 +- Telegram/SourceFiles/data/data_drafts.h | 4 +- Telegram/SourceFiles/dialogs/dialogs.style | 7 +- Telegram/SourceFiles/dialogswidget.cpp | 46 +- Telegram/SourceFiles/dialogswidget.h | 5 +- Telegram/SourceFiles/historywidget.cpp | 436 +++++++++--------- Telegram/SourceFiles/historywidget.h | 16 +- Telegram/SourceFiles/intro/intro.style | 24 +- Telegram/SourceFiles/intro/introcode.cpp | 179 +++---- Telegram/SourceFiles/intro/introcode.h | 46 +- Telegram/SourceFiles/intro/introphone.cpp | 174 ++++--- Telegram/SourceFiles/intro/introphone.h | 32 +- Telegram/SourceFiles/intro/intropwdcheck.cpp | 212 +++++---- Telegram/SourceFiles/intro/intropwdcheck.h | 29 +- Telegram/SourceFiles/intro/introsignup.cpp | 122 ++--- Telegram/SourceFiles/intro/introsignup.h | 26 +- Telegram/SourceFiles/intro/introstart.cpp | 24 +- Telegram/SourceFiles/intro/introstart.h | 14 +- Telegram/SourceFiles/intro/introwidget.cpp | 2 +- Telegram/SourceFiles/mainwindow.cpp | 27 +- Telegram/SourceFiles/mainwindow.h | 6 +- Telegram/SourceFiles/passcodewidget.cpp | 67 ++- Telegram/SourceFiles/passcodewidget.h | 14 +- Telegram/SourceFiles/shortcuts.cpp | 2 +- Telegram/SourceFiles/structs.h | 31 +- Telegram/SourceFiles/window/main_window.cpp | 4 +- Telegram/SourceFiles/window/window_theme.cpp | 4 +- 36 files changed, 812 insertions(+), 777 deletions(-) create mode 100644 Telegram/Resources/icons/dialogs_lock.png create mode 100644 Telegram/Resources/icons/dialogs_lock@2x.png create mode 100644 Telegram/Resources/icons/dialogs_unlock.png create mode 100644 Telegram/Resources/icons/dialogs_unlock@2x.png diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index da55dde33..8ed27047e 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -33,9 +33,6 @@ imageBg: #000000; imageBgTransparent: #ffffff; // widgets -cancelIconFg: #a2a2a2; -cancelIconFgOver: #808080; - activeButtonBg: windowActiveFill; activeButtonBgOver: #46b4eb | activeButtonBg; activeButtonFg: #ffffff; @@ -48,7 +45,9 @@ lightButtonBgOver: #f2f7fa | lightButtonBg; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; -menuIconFg: #808080 | windowSubTextFg; +menuIconFg: windowSubTextFg; + +dialogsMenuIconFg: menuIconFg; // custom title bar for Windows titleBg: windowOverBg; @@ -69,6 +68,9 @@ trayCounterFgMacInvert: #ffffff01; // layers layerBg: #0000007f; +cancelIconFg: #a2a2a2; +cancelIconFgOver: #808080; + // boxes boxBg: windowBg; boxTextFg: windowTextFg; diff --git a/Telegram/Resources/icons/dialogs_lock.png b/Telegram/Resources/icons/dialogs_lock.png new file mode 100644 index 0000000000000000000000000000000000000000..5067efdfe7cca0a4774cb00b6f738847bb3f51f4 GIT binary patch literal 285 zcmV+&0pk9NP)20002wNkl?0004BNkl1Viz7k2j+X z)ibp8iSMoYIozVEJHbN+`0>93yh`z3ZB#0iz-TlA-EJ4uYBg{;9Kd`&2jlS=T(4JT z$Y>(v84V^Eiv?&j8ZY&2HXG3CbdVvVsn2Mb$zXE3-Gaej0M_d@Xf~T*I-P#15B-@j zn);0Hb~{k7*Fme*0;|>P+xm994VKF#*zfnqjec;u-9VvG0L9|3V{yOVzpW2-p)b_s zMgxz>=jD?m`K!Ln7|nymCJ!2$r>8!=8)vf_D3{AoHojagpx5i+;~n}t-pAz*XU1si-}SM`jL|%3Y<>bJQ)Hk4KC;OG000020002yNklnosap;rg8Z^x z9yP7CqN6NvX7$ZH!7_ruN@4Z*Qp{f9+lyJ^5jw8-FQc3`* z>Un6+8Gvb;HokKXfSmJrXlo4sW6Z`k#sJV-y9*tzwY!`5G|%&XP@i9NR9^#o;-H=fx~}Ufz_Kh|f3prD1WGA~ l)~S?22;unO|B3!&rmr#?0004XNklKOTpE=^lq%jr6e6C&BS~iZsX6r^start(); - } + Window::Theme::Background()->start(); connect(&_webPagesTimer, SIGNAL(timeout()), this, SLOT(resolveWebPages())); connect(&_draftsSaveTimer, SIGNAL(timeout()), this, SLOT(saveDraftsToCloud())); diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 8974fe8ba..f5cce4723 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -199,7 +199,7 @@ namespace { if (auto w = wnd()) { w->tempDirDelete(Local::ClearManagerAll); w->notifyClearFast(); - w->setupIntro(true); + w->setupIntro(); } MTP::setAuthedId(0); Local::reset(); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 0a50e4629..159a844b7 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -750,12 +750,12 @@ AppClass::AppClass() : QObject() { DEBUG_LOG(("Application Info: showing.")); if (state == Local::ReadMapPassNeeded) { - _window->setupPasscode(false); + _window->setupPasscode(); } else { if (MTP::authedId()) { - _window->setupMain(false); + _window->setupMain(); } else { - _window->setupIntro(false); + _window->setupIntro(); } } _window->firstShow(); diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index ac9b071f3..ff7d14d7b 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -246,13 +246,8 @@ passcodeHeaderFont: font(19px); passcodeHeaderHeight: 80px; passcodeInput: flatInput(inpIntroPhone) { } -passcodeSubmit: flatButton(introNextButton) { - textTop: 15px; - overTextTop: 15px; - downTextTop: 16px; +passcodeSubmit: RoundButton(introNextButton) { width: 225px; - font: font(19px); - overFont: font(19px); } passcodeSubmitSkip: 40px; passcodePadding: margins(0px, 22px, 0px, 3px); diff --git a/Telegram/SourceFiles/data/data_drafts.h b/Telegram/SourceFiles/data/data_drafts.h index b40aa1c51..858207248 100644 --- a/Telegram/SourceFiles/data/data_drafts.h +++ b/Telegram/SourceFiles/data/data_drafts.h @@ -35,8 +35,8 @@ struct Draft { , previewCancelled(previewCancelled) , saveRequestId(saveRequestId) { } - Draft(const FlatTextarea &field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId = 0) - : textWithTags(field.getTextWithTags()) + Draft(const FlatTextarea *field, MsgId msgId, bool previewCancelled, mtpRequestId saveRequestId = 0) + : textWithTags(field->getTextWithTags()) , msgId(msgId) , cursor(field) , previewCancelled(previewCancelled) { diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index cf7470bef..cc84abd45 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -87,10 +87,15 @@ dialogsMenuToggle: IconButton { width: 32px; height: 32px; - icon: icon {{ "dialogs_menu", #999999 }}; + icon: icon {{ "dialogs_menu", dialogsMenuIconFg }}; iconPosition: point(6px, 6px); iconPositionDown: point(6px, 6px); } +dialogsLock: IconButton(dialogsMenuToggle) { + icon: icon {{ "dialogs_lock", dialogsMenuIconFg }}; +} +dialogsUnlockIcon: icon {{ "dialogs_unlock", dialogsMenuIconFg }}; + dialogsFilter: flatInput(inpDefGray) { font: font(fsize); bgColor: #f2f2f2; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index c08c99a85..0342e855f 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1028,10 +1028,6 @@ void DialogsInner::dialogsReceived(const QVector &added) { } } Notify::unreadCounterUpdated(); - if (!_sel && !shownDialogs()->isEmpty()) { - _sel = *shownDialogs()->cbegin(); - _importantSwitchSel = false; - } refresh(); } @@ -1776,6 +1772,7 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) , _mainMenuToggle(this, st::dialogsMenuToggle) , _filter(this, st::dialogsFilter, lang(lng_dlg_filter)) , _cancelSearch(this, st::dialogsCancelSearch) +, _lockUnlock(this, st::dialogsLock) , _scroll(this, st::dialogsScroll) , _inner(&_scroll, parent) , _a_show(animation(this, &DialogsWidget::step_show)) { @@ -1793,8 +1790,15 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) connect(_filter, SIGNAL(cancelled()), this, SLOT(onCancel())); connect(_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); connect(_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int))); - connect(_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); + _cancelSearch->setClickedCallback([this] { onCancelSearch(); }); + _lockUnlock->setVisible(Global::LocalPasscode()); + subscribe(Global::RefLocalPasscodeChanged(), [this] { updateLockUnlockVisibility(); }); + _lockUnlock->setClickedCallback([this] { + _lockUnlock->setIcon(&st::dialogsUnlockIcon); + App::wnd()->setupPasscode(); + _lockUnlock->setIcon(nullptr); + }); _mainMenuToggle->setClickedCallback([this] { showMainMenu(); }); _chooseByDragTimer.setSingleShot(true); @@ -1851,8 +1855,10 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window: _a_show.stop(); _scroll.hide(); + _mainMenuToggle->hide(); _filter->hide(); _cancelSearch->hide(); + _lockUnlock->hide(); int delta = st::slideShift; if (direction == Window::SlideDirection::FromLeft) { @@ -1880,7 +1886,9 @@ void DialogsWidget::step_show(float64 ms, bool timer) { _cacheUnder = _cacheOver = QPixmap(); _scroll.show(); + _mainMenuToggle->show(); _filter->show(); + updateLockUnlockVisibility(); _a_show.stop(); onFilterUpdate(); @@ -2440,17 +2448,15 @@ void DialogsWidget::onCompleteHashtag(QString tag) { } void DialogsWidget::resizeEvent(QResizeEvent *e) { - int32 w = width(); - _filter->setGeometryToLeft(st::dialogsFilterPadding.x() * 2 + _mainMenuToggle->width(), st::dialogsFilterPadding.y(), w - 3 * st::dialogsFilterPadding.x() - _mainMenuToggle->width(), _filter->height()); - _mainMenuToggle->moveToLeft(st::dialogsFilterPadding.x(), _filter->y()); - _cancelSearch->moveToRight(st::dialogsFilterPadding.x(), _filter->y()); + updateControlsGeometry(); + _scroll.move(0, _filter->height() + 2 * st::dialogsFilterPadding.y()); updateMainMenuGeometry(); int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0; int32 newScrollY = _scroll.scrollTop() + addToY; - _scroll.resize(w, height() - _filter->y() - _filter->height() - st::dialogsFilterPadding.y() - st::dialogsPadding.y()); + _scroll.resize(width(), height() - _filter->y() - _filter->height() - st::dialogsFilterPadding.y() - st::dialogsPadding.y()); if (addToY) { _scroll.scrollToY(newScrollY); } else { @@ -2458,6 +2464,26 @@ void DialogsWidget::resizeEvent(QResizeEvent *e) { } } +void DialogsWidget::updateLockUnlockVisibility() { + if (!_a_show.animating()) { + _lockUnlock->setVisible(Global::LocalPasscode()); + } + updateControlsGeometry(); +} + +void DialogsWidget::updateControlsGeometry() { + auto filterLeft = st::dialogsFilterPadding.x() * 2 + _mainMenuToggle->width(); + auto filterWidth = width() - 2 * st::dialogsFilterPadding.x(); + filterWidth -= st::dialogsFilterPadding.x() + _mainMenuToggle->width(); + if (Global::LocalPasscode()) { + filterWidth -= st::dialogsFilterPadding.x() + _lockUnlock->width(); + } + _filter->setGeometryToLeft(filterLeft, st::dialogsFilterPadding.y(), filterWidth, _filter->height()); + _mainMenuToggle->moveToLeft(st::dialogsFilterPadding.x(), _filter->y()); + _lockUnlock->moveToRight(st::dialogsFilterPadding.x(), _filter->y()); + _cancelSearch->moveToLeft(filterLeft + filterWidth - _cancelSearch->width(), _filter->y()); +} + void DialogsWidget::updateMainMenuGeometry() { if (!_mainMenu) return; diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 68a5a2b30..7778e6ab0 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -233,7 +233,7 @@ private: }; -class DialogsWidget : public TWidget, public RPCSender { +class DialogsWidget : public TWidget, public RPCSender, private base::Subscriber { Q_OBJECT public: @@ -315,6 +315,8 @@ public slots: private: void showMainMenu(); + void updateLockUnlockVisibility(); + void updateControlsGeometry(); void updateMainMenuGeometry(); bool _dragInScroll = false; @@ -338,6 +340,7 @@ private: ChildWidget _mainMenu = { nullptr }; ChildWidget _filter; ChildWidget _cancelSearch; + ChildWidget _lockUnlock; ScrollArea _scroll; DialogsInner _inner; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index b28f1914f..6768ad035 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2380,41 +2380,41 @@ ReportSpamPanel::ReportSpamPanel(HistoryWidget *parent) : TWidget(parent), _report(this, lang(lng_report_spam), st::reportSpamHide), _hide(this, lang(lng_report_spam_hide), st::reportSpamHide), _clear(this, lang(lng_profile_delete_conversation)) { - resize(parent->width(), _hide.height() + st::lineWidth); + resize(parent->width(), _hide->height() + st::lineWidth); - connect(&_report, SIGNAL(clicked()), this, SIGNAL(reportClicked())); - connect(&_hide, SIGNAL(clicked()), this, SIGNAL(hideClicked())); - connect(&_clear, SIGNAL(clicked()), this, SIGNAL(clearClicked())); + connect(_report, SIGNAL(clicked()), this, SIGNAL(reportClicked())); + connect(_hide, SIGNAL(clicked()), this, SIGNAL(hideClicked())); + connect(_clear, SIGNAL(clicked()), this, SIGNAL(clearClicked())); - _clear.hide(); + _clear->hide(); } void ReportSpamPanel::resizeEvent(QResizeEvent *e) { - _report.resize(width() - (_hide.width() + st::reportSpamSeparator) * 2, _report.height()); - _report.moveToLeft(_hide.width() + st::reportSpamSeparator, 0); - _hide.moveToRight(0, 0); - _clear.move((width() - _clear.width()) / 2, height() - _clear.height() - ((height() - st::msgFont->height - _clear.height()) / 2)); + _report->resize(width() - (_hide->width() + st::reportSpamSeparator) * 2, _report->height()); + _report->moveToLeft(_hide->width() + st::reportSpamSeparator, 0); + _hide->moveToRight(0, 0); + _clear->move((width() - _clear->width()) / 2, height() - _clear->height() - ((height() - st::msgFont->height - _clear->height()) / 2)); } void ReportSpamPanel::paintEvent(QPaintEvent *e) { Painter p(this); p.fillRect(QRect(0, 0, width(), height() - st::lineWidth), st::reportSpamBg); p.fillRect(Adaptive::OneColumn() ? 0 : st::lineWidth, height() - st::lineWidth, width() - (Adaptive::OneColumn() ? 0 : st::lineWidth), st::lineWidth, st::shadowColor->b); - if (!_clear.isHidden()) { + if (!_clear->isHidden()) { p.setPen(st::reportSpamFg); p.setFont(st::msgFont); - p.drawText(QRect(_report.x(), (_clear.y() - st::msgFont->height) / 2, _report.width(), st::msgFont->height), lang(lng_report_spam_thanks), style::al_top); + p.drawText(QRect(_report->x(), (_clear->y() - st::msgFont->height) / 2, _report->width(), st::msgFont->height), lang(lng_report_spam_thanks), style::al_top); } } void ReportSpamPanel::setReported(bool reported, PeerData *onPeer) { if (reported) { - _report.hide(); - _clear.setText(lang(onPeer->isChannel() ? (onPeer->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel) : lng_profile_delete_conversation)); - _clear.show(); + _report->hide(); + _clear->setText(lang(onPeer->isChannel() ? (onPeer->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel) : lng_profile_delete_conversation)); + _clear->show(); } else { - _report.show(); - _clear.hide(); + _report->show(); + _clear->hide(); } update(); } @@ -2699,8 +2699,8 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString } void HistoryHider::init() { - connect(&_send, SIGNAL(clicked()), this, SLOT(forward())); - connect(&_cancel, SIGNAL(clicked()), this, SLOT(startHide())); + connect(_send, SIGNAL(clicked()), this, SLOT(forward())); + connect(_cancel, SIGNAL(clicked()), this, SLOT(startHide())); _chooseWidth = st::forwardFont->width(lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose)); @@ -2788,8 +2788,8 @@ void HistoryHider::startHide() { if (_offered) _cacheForAnim = myGrab(this, _box); if (_forwardRequest) MTP::cancel(_forwardRequest); a_opacity.start(0); - _send.hide(); - _cancel.hide(); + _send->hide(); + _cancel->hide(); _a_appearance.start(); } } @@ -2824,18 +2824,18 @@ void HistoryHider::resizeEvent(QResizeEvent *e) { int32 w = st::boxWidth, h = st::boxPadding.top() + st::boxPadding.bottom(); if (_offered) { if (!_hiding) { - _send.show(); - _cancel.show(); + _send->show(); + _cancel->show(); } - h += st::boxTextFont->height + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom(); + h += st::boxTextFont->height + st::boxButtonPadding.top() + _send->height() + st::boxButtonPadding.bottom(); } else { h += st::forwardFont->height; - _send.hide(); - _cancel.hide(); + _send->hide(); + _cancel->hide(); } _box = QRect((width() - w) / 2, (height() - h) / 2, w, h); - _send.moveToRight(width() - (_box.x() + _box.width()) + st::boxButtonPadding.right(), _box.y() + h - st::boxButtonPadding.bottom() - _send.height()); - _cancel.moveToRight(width() - (_box.x() + _box.width()) + st::boxButtonPadding.right() + _send.width() + st::boxButtonPadding.left(), _send.y()); + _send->moveToRight(width() - (_box.x() + _box.width()) + st::boxButtonPadding.right(), _box.y() + h - st::boxButtonPadding.bottom() - _send->height()); + _cancel->moveToRight(width() - (_box.x() + _box.width()) + st::boxButtonPadding.right() + _send->width() + st::boxButtonPadding.left(), _send->y()); } bool HistoryHider::offerPeer(PeerId peer) { @@ -3030,25 +3030,25 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_reportSpamPanel, SIGNAL(clearClicked()), this, SLOT(onReportSpamClear())); connect(_historyToEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); connect(_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel())); - connect(&_send, SIGNAL(clicked()), this, SLOT(onSend())); - connect(&_unblock, SIGNAL(clicked()), this, SLOT(onUnblock())); - connect(&_botStart, SIGNAL(clicked()), this, SLOT(onBotStart())); - connect(&_joinChannel, SIGNAL(clicked()), this, SLOT(onJoinChannel())); - connect(&_muteUnmute, SIGNAL(clicked()), this, SLOT(onMuteUnmute())); + connect(_send, SIGNAL(clicked()), this, SLOT(onSend())); + connect(_unblock, SIGNAL(clicked()), this, SLOT(onUnblock())); + connect(_botStart, SIGNAL(clicked()), this, SLOT(onBotStart())); + connect(_joinChannel, SIGNAL(clicked()), this, SLOT(onJoinChannel())); + connect(_muteUnmute, SIGNAL(clicked()), this, SLOT(onMuteUnmute())); connect(_silent, SIGNAL(clicked()), this, SLOT(onBroadcastSilentChange())); connect(_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect())); connect(_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect())); - connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); - connect(&_field, SIGNAL(cancelled()), this, SLOT(onCancel())); - connect(&_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed())); - connect(&_field, SIGNAL(resized()), this, SLOT(onFieldResize())); - connect(&_field, SIGNAL(focused()), this, SLOT(onFieldFocused())); - connect(&_field, SIGNAL(changed()), this, SLOT(onTextChange())); - connect(&_field, SIGNAL(spacedReturnedPasted()), this, SLOT(onPreviewParse())); - connect(&_field, SIGNAL(linksChanged()), this, SLOT(onPreviewCheck())); + connect(_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); + connect(_field, SIGNAL(cancelled()), this, SLOT(onCancel())); + connect(_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed())); + connect(_field, SIGNAL(resized()), this, SLOT(onFieldResize())); + connect(_field, SIGNAL(focused()), this, SLOT(onFieldFocused())); + connect(_field, SIGNAL(changed()), this, SLOT(onTextChange())); + connect(_field, SIGNAL(spacedReturnedPasted()), this, SLOT(onPreviewParse())); + 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(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*))); @@ -3078,9 +3078,9 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_saveDraftTimer, SIGNAL(timeout()), this, SLOT(onDraftSave())); _saveCloudDraftTimer.setSingleShot(true); connect(&_saveCloudDraftTimer, SIGNAL(timeout()), this, SLOT(onCloudDraftSave())); - connect(_field.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onDraftSaveDelayed())); - connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onDraftSaveDelayed())); - connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onCheckFieldAutocomplete()), Qt::QueuedConnection); + connect(_field->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onDraftSaveDelayed())); + connect(_field, SIGNAL(cursorPositionChanged()), this, SLOT(onDraftSaveDelayed())); + connect(_field, SIGNAL(cursorPositionChanged()), this, SLOT(onCheckFieldAutocomplete()), Qt::QueuedConnection); _fieldBarCancel->hide(); @@ -3101,16 +3101,16 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(_fieldAutocomplete, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); connect(_fieldAutocomplete, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onStickerSend(DocumentData*))); connect(_fieldAutocomplete, SIGNAL(moderateKeyActivate(int,bool*)), this, SLOT(onModerateKeyActivate(int,bool*))); - _field.installEventFilter(_fieldAutocomplete); - _field.setTagMimeProcessor(std_::make_unique()); + _field->installEventFilter(_fieldAutocomplete); + _field->setTagMimeProcessor(std_::make_unique()); updateFieldSubmitSettings(); - _field.hide(); - _send.hide(); - _unblock.hide(); - _botStart.hide(); - _joinChannel.hide(); - _muteUnmute.hide(); + _field->hide(); + _send->hide(); + _unblock->hide(); + _botStart->hide(); + _joinChannel->hide(); + _muteUnmute->hide(); _reportSpamPanel.move(0, 0); _reportSpamPanel.hide(); @@ -3177,23 +3177,23 @@ void HistoryWidget::onMentionInsert(UserData *user) { } else { replacement = '@' + user->username; } - _field.insertTag(replacement, entityTag); + _field->insertTag(replacement, entityTag); } void HistoryWidget::onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::ChooseMethod method) { // Send bot command at once, if it was not inserted by pressing Tab. if (str.at(0) == '/' && method != FieldAutocomplete::ChooseMethod::ByTab) { App::sendBotCommand(_peer, nullptr, str); - setFieldText(_field.getTextWithTagsPart(_field.textCursor().position())); + setFieldText(_field->getTextWithTagsPart(_field->textCursor().position())); } else { - _field.insertTag(str); + _field->insertTag(str); } } void HistoryWidget::updateInlineBotQuery() { UserData *bot = nullptr; QString inlineBotUsername; - QString query = _field.getInlineBotQuery(&bot, &inlineBotUsername); + QString query = _field->getInlineBotQuery(&bot, &inlineBotUsername); if (inlineBotUsername != _inlineBotUsername) { _inlineBotUsername = inlineBotUsername; if (_inlineBotResolveRequestId) { @@ -3239,7 +3239,7 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) { void HistoryWidget::updateStickersByEmoji() { int len = 0; if (!_editMsgId) { - auto &text = _field.getTextWithTags().text; + auto &text = _field->getTextWithTags().text; if (auto emoji = emojiFromText(text, &len)) { if (text.size() > len) { len = 0; @@ -3264,13 +3264,13 @@ void HistoryWidget::onTextChange() { } if (cHasAudioCapture()) { - if (!_field.hasSendText() && !readyToForward() && !_editMsgId) { + if (!_field->hasSendText() && !readyToForward() && !_editMsgId) { _previewCancelled = false; - _send.hide(); + _send->hide(); updateMouseTracking(); mouseMoveEvent(0); - } else if (!_field.isHidden() && _send.isHidden()) { - _send.show(); + } else if (!_field->isHidden() && _send->isHidden()) { + _send->show(); updateMouseTracking(); _a_record.stop(); _inRecord = _inField = false; @@ -3292,7 +3292,7 @@ void HistoryWidget::onTextChange() { void HistoryWidget::onDraftSaveDelayed() { if (!_peer || !(_textUpdateEvents.testFlag(TextUpdateEvent::SaveDraft))) return; - if (!_field.textCursor().anchor() && !_field.textCursor().position() && !_field.verticalScrollBar()->value()) { + if (!_field->textCursor().anchor() && !_field->textCursor().position() && !_field->verticalScrollBar()->value()) { if (!Local::hasDraftCursors(_peer->id)) { return; } @@ -3320,7 +3320,7 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() { if (_editMsgId) { _history->setEditDraft(std_::make_unique(_field, _editMsgId, _previewCancelled, _saveEditMsgRequestId)); } else { - if (_replyToId || !_field.isEmpty()) { + if (_replyToId || !_field->isEmpty()) { _history->setLocalDraft(std_::make_unique(_field, _replyToId, _previewCancelled)); } else { _history->clearLocalDraft(); @@ -3350,14 +3350,14 @@ void HistoryWidget::writeDrafts(Data::Draft **localDraft, Data::Draft **editDraf storedLocalDraft = Local::MessageDraft((*localDraft)->msgId, (*localDraft)->textWithTags, (*localDraft)->previewCancelled); } } else { - storedLocalDraft = Local::MessageDraft(_replyToId, _field.getTextWithTags(), _previewCancelled); + storedLocalDraft = Local::MessageDraft(_replyToId, _field->getTextWithTags(), _previewCancelled); } if (editDraft) { if (*editDraft) { storedEditDraft = Local::MessageDraft((*editDraft)->msgId, (*editDraft)->textWithTags, (*editDraft)->previewCancelled); } } else if (_editMsgId) { - storedEditDraft = Local::MessageDraft(_editMsgId, _field.getTextWithTags(), _previewCancelled); + storedEditDraft = Local::MessageDraft(_editMsgId, _field->getTextWithTags(), _previewCancelled); } Local::writeDrafts(_peer->id, storedLocalDraft, storedEditDraft); if (_migrated) { @@ -3480,7 +3480,7 @@ void HistoryWidget::setInnerFocus() { if (_selCount || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { _list->setFocus(); } else { - _field.setFocus(); + _field->setFocus(); } } } @@ -4084,7 +4084,7 @@ void HistoryWidget::applyDraft(bool parseLinks) { auto draft = _history ? _history->draft() : nullptr; if (!draft || !canWriteMessage()) { clearFieldText(); - _field.setFocus(); + _field->setFocus(); _replyEditMsg = nullptr; _editMsgId = _replyToId = 0; return; @@ -4092,7 +4092,7 @@ void HistoryWidget::applyDraft(bool parseLinks) { _textUpdateEvents = 0; setFieldText(draft->textWithTags); - _field.setFocus(); + _field->setFocus(); draft->cursor.applyTo(_field); _textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping; _previewCancelled = draft->previewCancelled; @@ -4226,7 +4226,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re _canSendMessages = canSendMessages(_peer); if (_peer && _peer->isChannel()) { _peer->asChannel()->updateFull(); - _joinChannel.setText(lang(_peer->isMegagroup() ? lng_group_invite_join : lng_channel_join)); + _joinChannel->setText(lang(_peer->isMegagroup() ? lng_group_invite_join : lng_channel_join)); } _unblockRequest = _reportSpamRequest = 0; @@ -4360,13 +4360,13 @@ void HistoryWidget::updateFieldSubmitSettings() { } else if (cCtrlEnter()) { settings = FlatTextarea::SubmitSettings::CtrlEnter; } - _field.setSubmitSettings(settings); + _field->setSubmitSettings(settings); } void HistoryWidget::updateNotifySettings() { if (!_peer || !_peer->isChannel()) return; - _muteUnmute.setText(lang(_history->mute() ? lng_channel_unmute : lng_channel_mute)); + _muteUnmute->setText(lang(_history->mute() ? lng_channel_unmute : lng_channel_mute)); if (_peer->notify != UnknownNotifySettings) { _silent->setChecked(_peer->notify != EmptyNotifySettings && (_peer->notify->flags & MTPDpeerNotifySettings::Flag::f_silent)); if (_silent->isHidden() && hasSilentToggle()) { @@ -4496,14 +4496,14 @@ void HistoryWidget::updateControlsVisibility() { _reportSpamPanel.hide(); _scroll.hide(); _kbScroll.hide(); - _send.hide(); + _send->hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); - _unblock.hide(); - _botStart.hide(); - _joinChannel.hide(); - _muteUnmute.hide(); + _unblock->hide(); + _botStart->hide(); + _joinChannel->hide(); + _muteUnmute->hide(); _fieldAutocomplete->hide(); - _field.hide(); + _field->hide(); _fieldBarCancel->hide(); _attachDocument->hide(); _attachPhoto->hide(); @@ -4538,32 +4538,32 @@ void HistoryWidget::updateControlsVisibility() { } if (isBlocked() || isJoinChannel() || isMuteUnmute()) { if (isBlocked()) { - _joinChannel.hide(); - _muteUnmute.hide(); - if (_unblock.isHidden()) { - _unblock.clearState(); - _unblock.show(); + _joinChannel->hide(); + _muteUnmute->hide(); + if (_unblock->isHidden()) { + _unblock->clearState(); + _unblock->show(); } } else if (isJoinChannel()) { - _unblock.hide(); - _muteUnmute.hide(); - if (_joinChannel.isHidden()) { - _joinChannel.clearState(); - _joinChannel.show(); + _unblock->hide(); + _muteUnmute->hide(); + if (_joinChannel->isHidden()) { + _joinChannel->clearState(); + _joinChannel->show(); } } else if (isMuteUnmute()) { - _unblock.hide(); - _joinChannel.hide(); - if (_muteUnmute.isHidden()) { - _muteUnmute.clearState(); - _muteUnmute.show(); + _unblock->hide(); + _joinChannel->hide(); + if (_muteUnmute->isHidden()) { + _muteUnmute->clearState(); + _muteUnmute->show(); } } _kbShown = false; _fieldAutocomplete->hide(); - _send.hide(); + _send->hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); - _botStart.hide(); + _botStart->hide(); _attachDocument->hide(); _attachPhoto->hide(); _silent->hide(); @@ -4577,25 +4577,25 @@ void HistoryWidget::updateControlsVisibility() { _botCommandStart->hide(); _attachType->hide(); _emojiPan->hide(); - if (!_field.isHidden()) { - _field.hide(); + if (!_field->isHidden()) { + _field->hide(); resizeEvent(0); update(); } } else if (_canSendMessages) { onCheckFieldAutocomplete(); if (isBotStart()) { - _unblock.hide(); - _joinChannel.hide(); - _muteUnmute.hide(); - if (_botStart.isHidden()) { - _botStart.clearState(); - _botStart.show(); + _unblock->hide(); + _joinChannel->hide(); + _muteUnmute->hide(); + if (_botStart->isHidden()) { + _botStart->clearState(); + _botStart->show(); } _kbShown = false; - _send.hide(); + _send->hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); - _field.hide(); + _field->hide(); _attachEmoji->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); @@ -4606,25 +4606,25 @@ void HistoryWidget::updateControlsVisibility() { _kbScroll.hide(); _fieldBarCancel->hide(); } else { - _unblock.hide(); - _botStart.hide(); - _joinChannel.hide(); - _muteUnmute.hide(); - if (cHasAudioCapture() && !_field.hasSendText() && !readyToForward()) { - _send.hide(); + _unblock->hide(); + _botStart->hide(); + _joinChannel->hide(); + _muteUnmute->hide(); + if (cHasAudioCapture() && !_field->hasSendText() && !readyToForward()) { + _send->hide(); mouseMoveEvent(0); } else { if (_inlineBotCancel) { _inlineBotCancel->show(); - _send.hide(); + _send->hide(); } else { - _send.show(); + _send->show(); } _a_record.stop(); _inRecord = _inField = false; } if (_recording) { - _field.hide(); + _field->hide(); _attachEmoji->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); @@ -4638,7 +4638,7 @@ void HistoryWidget::updateControlsVisibility() { _kbScroll.hide(); } } else { - _field.show(); + _field->show(); if (_kbShown) { _kbScroll.show(); _attachEmoji->hide(); @@ -4693,12 +4693,12 @@ void HistoryWidget::updateControlsVisibility() { } } else { _fieldAutocomplete->hide(); - _send.hide(); + _send->hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); - _unblock.hide(); - _botStart.hide(); - _joinChannel.hide(); - _muteUnmute.hide(); + _unblock->hide(); + _botStart->hide(); + _joinChannel->hide(); + _muteUnmute->hide(); _attachDocument->hide(); _attachPhoto->hide(); _silent->hide(); @@ -4713,8 +4713,8 @@ void HistoryWidget::updateControlsVisibility() { _attachType->hide(); _emojiPan->hide(); _kbScroll.hide(); - if (!_field.isHidden()) { - _field.hide(); + if (!_field->isHidden()) { + _field->hide(); resizeEvent(0); update(); } @@ -4723,7 +4723,7 @@ void HistoryWidget::updateControlsVisibility() { } void HistoryWidget::updateMouseTracking() { - bool trackMouse = !_fieldBarCancel->isHidden() || _pinnedBar || (cHasAudioCapture() && _send.isHidden() && !_field.isHidden()); + bool trackMouse = !_fieldBarCancel->isHidden() || _pinnedBar || (cHasAudioCapture() && _send->isHidden() && !_field->isHidden()); setMouseTracking(trackMouse); } @@ -5132,7 +5132,7 @@ void HistoryWidget::preloadHistoryIfNeeded() { } void HistoryWidget::onInlineBotCancel() { - auto &textWithTags = _field.getTextWithTags(); + auto &textWithTags = _field->getTextWithTags(); if (textWithTags.text.size() > _inlineBotUsername.size() + 2) { setFieldText({ '@' + _inlineBotUsername + ' ', TextWithTags::Tags() }, TextUpdateEvent::SaveDraft, FlatTextarea::AddToUndoHistory); } else { @@ -5159,14 +5159,14 @@ void HistoryWidget::saveEditMsg() { WebPageId webPageId = _previewCancelled ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id : 0); - auto &textWithTags = _field.getTextWithTags(); + auto &textWithTags = _field->getTextWithTags(); auto prepareFlags = itemTextOptions(_history, App::self()).flags; EntitiesInText sendingEntities, leftEntities = entitiesFromTextTags(textWithTags.tags); QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities); if (!textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { - _field.selectAll(); - _field.setFocus(); + _field->selectAll(); + _field->setFocus(); return; } else if (!leftText.isEmpty()) { Ui::showLayer(new InformBox(lang(lng_edit_too_long))); @@ -5217,8 +5217,8 @@ bool HistoryWidget::saveEditMsgFail(History *history, const RPCError &error, mtp } else if (err == qstr("MESSAGE_NOT_MODIFIED")) { cancelEdit(); } else if (err == qstr("MESSAGE_EMPTY")) { - _field.selectAll(); - _field.setFocus(); + _field->selectAll(); + _field->setFocus(); } else { Ui::showLayer(new InformBox(lang(lng_edit_error))); } @@ -5246,7 +5246,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { MainWidget::MessageToSend message; message.history = _history; - message.textWithTags = _field.getTextWithTags(); + message.textWithTags = _field->getTextWithTags(); message.replyTo = replyTo; message.silent = _silent->checked(); message.webPageId = webPageId; @@ -5261,7 +5261,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { if (replyTo < 0) cancelReply(lastKeyboardUsed); if (_previewData && _previewData->pendingTill) previewCancel(); - _field.setFocus(); + _field->setFocus(); if (!_keyboard.hasMarkup() && _keyboard.forceReply() && !_kbReplyTo) onKbToggle(); } @@ -5467,14 +5467,14 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: _botKeyboardShow->hide(); _botKeyboardHide->hide(); _botCommandStart->hide(); - _field.hide(); + _field->hide(); _fieldBarCancel->hide(); - _send.hide(); + _send->hide(); if (_inlineBotCancel) _inlineBotCancel->hide(); - _unblock.hide(); - _botStart.hide(); - _joinChannel.hide(); - _muteUnmute.hide(); + _unblock->hide(); + _botStart->hide(); + _joinChannel->hide(); + _muteUnmute->hide(); if (_pinnedBar) { _pinnedBar->shadow->hide(); _pinnedBar->cancel->hide(); @@ -5549,7 +5549,7 @@ void HistoryWidget::animStop() { void HistoryWidget::step_record(float64 ms, bool timer) { float64 dt = ms / st::btnSend.duration; - if (dt >= 1 || !_send.isHidden() || isBotStart() || isBlocked()) { + if (dt >= 1 || !_send->isHidden() || isBotStart() || isBlocked()) { _a_record.stop(); a_recordDown.finish(); a_recordCancel.finish(); @@ -5561,7 +5561,7 @@ void HistoryWidget::step_record(float64 ms, bool timer) { if (_recording) { updateField(); } else { - update(_send.geometry()); + update(_send->geometry()); } } } @@ -5664,13 +5664,13 @@ void HistoryWidget::leaveEvent(QEvent *e) { void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { QPoint pos(e ? e->pos() : mapFromGlobal(QCursor::pos())); - bool inRecord = _send.geometry().contains(pos); + bool inRecord = _send->geometry().contains(pos); bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); - bool inReplyEdit = QRect(st::historyReplySkip, _field.y() - st::sendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId()); + bool inReplyEdit = QRect(st::historyReplySkip, _field->y() - st::sendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId()); bool inPinnedMsg = QRect(0, 0, width(), st::historyReplyHeight).contains(pos) && _pinnedBar; if (inRecord != _inRecord) { _inRecord = inRecord; - update(_send.geometry()); + update(_send->geometry()); } if (inField != _inField && _recording) { _inField = inField; @@ -5695,7 +5695,7 @@ void HistoryWidget::leaveToChildEvent(QEvent *e, QWidget *child) { // e -- from void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) { if (_replyForwardPressed) { _replyForwardPressed = false; - update(0, _field.y() - st::sendPadding - st::historyReplyHeight, width(), st::historyReplyHeight); + update(0, _field->y() - st::sendPadding - st::historyReplyHeight, width(), st::historyReplyHeight); } if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { _attachDrag = DragStateNone; @@ -5760,7 +5760,7 @@ void HistoryWidget::sendBotCommand(PeerData *peer, UserData *bot, const QString } } - _field.setFocus(); + _field->setFocus(); } void HistoryWidget::app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col) { @@ -5863,7 +5863,7 @@ bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) { toInsert += ' '; if (!insertingInlineBot) { - auto &textWithTags = _field.getTextWithTags(); + auto &textWithTags = _field->getTextWithTags(); if (specialGif) { if (textWithTags.text.trimmed() == '@' + cInlineGifBotUsername() && textWithTags.text.at(0) == '@') { clearFieldText(TextUpdateEvent::SaveDraft, FlatTextarea::AddToUndoHistory); @@ -5872,7 +5872,7 @@ bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) { TextWithTags textWithTagsToSet; QRegularExpressionMatch m = QRegularExpression(qsl("^/[A-Za-z_0-9]{0,64}(@[A-Za-z_0-9]{0,32})?(\\s|$)")).match(textWithTags.text); if (m.hasMatch()) { - textWithTagsToSet = _field.getTextWithTagsPart(m.capturedLength()); + textWithTagsToSet = _field->getTextWithTagsPart(m.capturedLength()); } else { textWithTagsToSet = textWithTags; } @@ -5880,16 +5880,16 @@ bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) { for (auto &tag : textWithTagsToSet.tags) { tag.offset += toInsert.size(); } - _field.setTextWithTags(textWithTagsToSet); + _field->setTextWithTags(textWithTagsToSet); - QTextCursor cur(_field.textCursor()); + QTextCursor cur(_field->textCursor()); cur.movePosition(QTextCursor::End); - _field.setTextCursor(cur); + _field->setTextCursor(cur); } } else { - if (!specialGif || _field.isEmpty()) { + if (!specialGif || _field->isEmpty()) { setFieldText({ toInsert, TextWithTags::Tags() }, TextUpdateEvent::SaveDraft, FlatTextarea::AddToUndoHistory); - _field.setFocus(); + _field->setFocus(); return true; } } @@ -5953,7 +5953,7 @@ DragState HistoryWidget::getDragState(const QMimeData *d) { } void HistoryWidget::updateDragAreas() { - _field.setAcceptDrops(!_attachDrag); + _field->setAcceptDrops(!_attachDrag); switch (_attachDrag) { case DragStateNone: _attachDragDocument->otherLeave(); @@ -6008,7 +6008,7 @@ void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result) UserData *bot = nullptr; QString inlineBotUsername; - auto query = _field.getInlineBotQuery(&bot, &inlineBotUsername); + auto query = _field->getInlineBotQuery(&bot, &inlineBotUsername); if (inlineBotUsername == _inlineBotUsername) { if (bot == LookingUpInlineBot) { bot = resolvedBot; @@ -6055,7 +6055,7 @@ bool HistoryWidget::updateCmdStartShown() { bool cmdStartShown = false; if (_history && _peer && ((_peer->isChat() && _peer->asChat()->botStatus > 0) || (_peer->isMegagroup() && _peer->asChannel()->mgInfo->botStatus > 0) || (_peer->isUser() && _peer->asUser()->botInfo))) { if (!isBotStart() && !isBlocked() && !_keyboard.hasMarkup() && !_keyboard.forceReply()) { - if (!_field.hasSendText()) { + if (!_field->hasSendText()) { cmdStartShown = true; } } @@ -6141,7 +6141,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbScroll.hide(); _kbShown = false; - _field.setMaxHeight(st::historyComposeFieldMaxHeight); + _field->setMaxHeight(st::historyComposeFieldMaxHeight); _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_editMsgId && !_replyToId) { @@ -6164,7 +6164,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbScroll.hide(); _kbShown = false; - _field.setMaxHeight(st::historyComposeFieldMaxHeight); + _field->setMaxHeight(st::historyComposeFieldMaxHeight); _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) { @@ -6183,7 +6183,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbShown = true; int32 maxh = qMin(_keyboard.height(), st::historyComposeFieldMaxHeight - (st::historyComposeFieldMaxHeight / 2)); - _field.setMaxHeight(st::historyComposeFieldMaxHeight - maxh); + _field->setMaxHeight(st::historyComposeFieldMaxHeight - maxh); _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId) { @@ -6426,7 +6426,7 @@ void HistoryWidget::updateOnlineDisplayTimer() { void HistoryWidget::moveFieldControls() { int w = width(), h = height(), right = w, bottom = h, keyboardHeight = 0; - int maxKeyboardHeight = st::historyComposeFieldMaxHeight - _field.height(); + int maxKeyboardHeight = st::historyComposeFieldMaxHeight - _field->height(); _keyboard.resizeToWidth(width(), maxKeyboardHeight); if (_kbShown) { keyboardHeight = qMin(_keyboard.height(), maxKeyboardHeight); @@ -6441,10 +6441,10 @@ void HistoryWidget::moveFieldControls() { int buttonsBottom = bottom - _attachDocument->height(); _attachDocument->move(0, buttonsBottom); _attachPhoto->move(0, buttonsBottom); - _field.move(_attachDocument->width(), bottom - _field.height() - st::sendPadding); - _send.move(right - _send.width(), buttonsBottom); - if (_inlineBotCancel) _inlineBotCancel->move(_send.pos()); - right -= _send.width(); + _field->move(_attachDocument->width(), bottom - _field->height() - st::sendPadding); + _send->move(right - _send->width(), buttonsBottom); + if (_inlineBotCancel) _inlineBotCancel->move(_send->pos()); + right -= _send->width(); _attachEmoji->move(right - _attachEmoji->width(), buttonsBottom); _botKeyboardHide->move(right - _botKeyboardHide->width(), buttonsBottom); right -= _attachEmoji->width(); @@ -6453,27 +6453,27 @@ void HistoryWidget::moveFieldControls() { _silent->move(right - _silent->width(), buttonsBottom); right = w; - _fieldBarCancel->move(right - _fieldBarCancel->width(), _field.y() - st::sendPadding - _fieldBarCancel->height()); + _fieldBarCancel->move(right - _fieldBarCancel->width(), _field->y() - st::sendPadding - _fieldBarCancel->height()); _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()); - _joinChannel.setGeometry(0, bottom - _joinChannel.height(), w, _joinChannel.height()); - _muteUnmute.setGeometry(0, bottom - _muteUnmute.height(), w, _muteUnmute.height()); + _botStart->setGeometry(0, bottom - _botStart->height(), w, _botStart->height()); + _unblock->setGeometry(0, bottom - _unblock->height(), w, _unblock->height()); + _joinChannel->setGeometry(0, bottom - _joinChannel->height(), w, _joinChannel->height()); + _muteUnmute->setGeometry(0, bottom - _muteUnmute->height(), w, _muteUnmute->height()); } void HistoryWidget::updateFieldSize() { bool kbShowShown = _history && !_kbShown && _keyboard.hasMarkup(); int fieldWidth = width() - _attachDocument->width(); - fieldWidth -= _send.width(); + fieldWidth -= _send->width(); fieldWidth -= _attachEmoji->width(); if (kbShowShown) fieldWidth -= _botKeyboardShow->width(); if (_cmdStartShown) fieldWidth -= _botCommandStart->width(); if (hasSilentToggle()) fieldWidth -= _silent->width(); - if (_field.width() != fieldWidth) { - _field.resize(fieldWidth, _field.height()); + if (_field->width() != fieldWidth) { + _field->resize(fieldWidth, _field->height()); } else { moveFieldControls(); } @@ -6483,7 +6483,7 @@ void HistoryWidget::clearInlineBot() { if (_inlineBot) { _inlineBot = nullptr; inlineBotChanged(); - _field.finishPlaceholder(); + _field->finishPlaceholder(); } _emojiPan->clearInlineBot(); onCheckFieldAutocomplete(); @@ -6494,7 +6494,7 @@ void HistoryWidget::inlineBotChanged() { if (isInlineBot && !_inlineBotCancel) { _inlineBotCancel = std_::make_unique(this, st::historyInlineBotCancel); connect(_inlineBotCancel.get(), SIGNAL(clicked()), this, SLOT(onInlineBotCancel())); - _inlineBotCancel->setGeometry(_send.geometry()); + _inlineBotCancel->setGeometry(_send->geometry()); _attachEmoji->raise(); updateFieldSubmitSettings(); updateControlsVisibility(); @@ -6521,7 +6521,7 @@ void HistoryWidget::onCheckFieldAutocomplete() { bool start = false; bool isInlineBot = _inlineBot && (_inlineBot != LookingUpInlineBot); - QString query = isInlineBot ? QString() : _field.getMentionHashtagBotCommandPart(start); + QString query = isInlineBot ? QString() : _field->getMentionHashtagBotCommandPart(start); if (!query.isEmpty()) { if (query.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtagsAndBots(); if (query.at(0) == '@' && cRecentInlineBots().isEmpty()) Local::readRecentHashtagsAndBots(); @@ -6532,15 +6532,15 @@ void HistoryWidget::onCheckFieldAutocomplete() { void HistoryWidget::updateFieldPlaceholder() { if (_editMsgId) { - _field.setPlaceholder(lang(lng_edit_message_text)); - _send.setText(lang(lng_settings_save)); + _field->setPlaceholder(lang(lng_edit_message_text)); + _send->setText(lang(lng_settings_save)); } else { if (_inlineBot && _inlineBot != LookingUpInlineBot) { - _field.setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); + _field->setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); } else { - _field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_silent->checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_message_ph)); + _field->setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_silent->checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_message_ph)); } - _send.setText(lang(lng_send_button)); + _send->setText(lang(lng_send_button)); } } @@ -6665,7 +6665,7 @@ void HistoryWidget::cancelSendFile(const FileLoadResultPtr &file) { _confirmWithTextId = 0; } if (!file->originalText.isEmpty()) { - _field.textCursor().insertText(file->originalText); + _field->textCursor().insertText(file->originalText); } } @@ -6909,10 +6909,10 @@ void HistoryWidget::peerMessagesUpdated(PeerId peer) { updateBotKeyboard(); if (!_scroll.isHidden()) { bool unblock = isBlocked(), botStart = isBotStart(), joinChannel = isJoinChannel(), muteUnmute = isMuteUnmute(); - bool upd = (_unblock.isHidden() == unblock); - if (!upd && !unblock) upd = (_botStart.isHidden() == botStart); - if (!upd && !unblock && !botStart) upd = (_joinChannel.isHidden() == joinChannel); - if (!upd && !unblock && !botStart && !joinChannel) upd = (_muteUnmute.isHidden() == muteUnmute); + bool upd = (_unblock->isHidden() == unblock); + if (!upd && !unblock) upd = (_botStart->isHidden() == botStart); + if (!upd && !unblock && !botStart) upd = (_joinChannel->isHidden() == joinChannel); + if (!upd && !unblock && !botStart && !joinChannel) upd = (_muteUnmute->isHidden() == muteUnmute); if (upd) { updateControlsVisibility(); updateControlsGeometry(); @@ -7102,10 +7102,10 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh int newScrollHeight = height(); if (isBlocked() || isBotStart() || isJoinChannel() || isMuteUnmute()) { - newScrollHeight -= _unblock.height(); + newScrollHeight -= _unblock->height(); } else { if (_canSendMessages) { - newScrollHeight -= (_field.height() + 2 * st::sendPadding); + newScrollHeight -= (_field->height() + 2 * st::sendPadding); } if (_editMsgId || replyToId() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) { newScrollHeight -= st::historyReplyHeight; @@ -7312,7 +7312,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { if (_keyboard.singleUse() && _keyboard.hasMarkup() && _keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _history->lastKeyboardUsed) { _history->lastKeyboardHiddenId = _history->lastKeyboardId; } - if (!isBotStart() && !isBlocked() && _canSendMessages && (wasVisible || (_replyToId && _replyEditMsg) || (!_field.hasSendText() && !kbWasHidden()))) { + if (!isBotStart() && !isBlocked() && _canSendMessages && (wasVisible || (_replyToId && _replyEditMsg) || (!_field->hasSendText() && !kbWasHidden()))) { if (!_a_show.animating()) { if (hasMarkup) { _kbScroll.show(); @@ -7327,7 +7327,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _botCommandStart->hide(); } int32 maxh = hasMarkup ? qMin(_keyboard.height(), st::historyComposeFieldMaxHeight - (st::historyComposeFieldMaxHeight / 2)) : 0; - _field.setMaxHeight(st::historyComposeFieldMaxHeight - maxh); + _field->setMaxHeight(st::historyComposeFieldMaxHeight - maxh); _kbShown = hasMarkup; _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard.forceReply()) ? App::histItemById(_keyboard.forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { @@ -7344,7 +7344,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _botKeyboardShow->show(); _botCommandStart->hide(); } - _field.setMaxHeight(st::historyComposeFieldMaxHeight); + _field->setMaxHeight(st::historyComposeFieldMaxHeight); _kbShown = false; _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { @@ -7360,7 +7360,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _botKeyboardShow->hide(); _botCommandStart->show(); } - _field.setMaxHeight(st::historyComposeFieldMaxHeight); + _field->setMaxHeight(st::historyComposeFieldMaxHeight); _kbShown = false; _kbReplyTo = 0; if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId && !_editMsgId) { @@ -7408,7 +7408,7 @@ void HistoryWidget::updateToEndVisibility() { } void HistoryWidget::mousePressEvent(QMouseEvent *e) { - _replyForwardPressed = QRect(0, _field.y() - st::sendPadding - st::historyReplyHeight, st::historyReplySkip, st::historyReplyHeight).contains(e->pos()); + _replyForwardPressed = QRect(0, _field->y() - st::sendPadding - st::historyReplyHeight, st::historyReplySkip, st::historyReplyHeight).contains(e->pos()); if (_replyForwardPressed && !_fieldBarCancel->isHidden()) { updateField(); } else if (_inRecord && cHasAudioCapture()) { @@ -7449,7 +7449,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { } else if (e->key() == Qt::Key_Up) { if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { if (_history && _history->lastSentMsg && _history->lastSentMsg->canEdit(::date(unixtime()))) { - if (_field.isEmpty() && !_editMsgId && !_replyToId) { + if (_field->isEmpty() && !_editMsgId && !_replyToId) { App::contextItem(_history->lastSentMsg); onEditMessage(); return; @@ -7545,7 +7545,7 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot hideSelectorControlsAnimated(); - _field.setFocus(); + _field->setFocus(); } HistoryWidget::PinnedBar::PinnedBar(MsgId msgId, HistoryWidget *parent) @@ -7711,7 +7711,7 @@ bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti hideSelectorControlsAnimated(); - _field.setFocus(); + _field->setFocus(); return true; } @@ -7756,13 +7756,13 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption) hideSelectorControlsAnimated(); - _field.setFocus(); + _field->setFocus(); } void HistoryWidget::setFieldText(const TextWithTags &textWithTags, TextUpdateEvents events, FlatTextarea::UndoHistoryAction undoHistoryAction) { _textUpdateEvents = events; - _field.setTextWithTags(textWithTags, undoHistoryAction); - _field.moveCursor(QTextCursor::End); + _field->setTextWithTags(textWithTags, undoHistoryAction); + _field->moveCursor(QTextCursor::End); _textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping; _previewCancelled = false; @@ -7775,7 +7775,7 @@ void HistoryWidget::setFieldText(const TextWithTags &textWithTags, TextUpdateEve } void HistoryWidget::onReplyToMessage() { - HistoryItem *to = App::contextItem(); + auto to = App::contextItem(); if (!to || to->id <= 0 || !_canSendMessages) return; if (to->history() == _migrated) { @@ -7811,7 +7811,7 @@ void HistoryWidget::onReplyToMessage() { updateBotKeyboard(); - if (!_field.isHidden()) _fieldBarCancel->show(); + if (!_field->isHidden()) _fieldBarCancel->show(); updateMouseTracking(); updateReplyToName(); resizeEvent(0); @@ -7822,7 +7822,7 @@ void HistoryWidget::onReplyToMessage() { _saveDraftStart = getms(); onDraftSave(); - _field.setFocus(); + _field->setFocus(); } void HistoryWidget::onEditMessage() { @@ -7836,7 +7836,7 @@ void HistoryWidget::onEditMessage() { delete box; if (!_editMsgId) { - if (_replyToId || !_field.isEmpty()) { + if (_replyToId || !_field->isEmpty()) { _history->setLocalDraft(std_::make_unique(_field, _replyToId, _previewCancelled)); } else { _history->clearLocalDraft(); @@ -7864,7 +7864,7 @@ void HistoryWidget::onEditMessage() { updateBotKeyboard(); - if (!_field.isHidden()) _fieldBarCancel->show(); + if (!_field->isHidden()) _fieldBarCancel->show(); updateFieldPlaceholder(); updateMouseTracking(); updateReplyToName(); @@ -7875,7 +7875,7 @@ void HistoryWidget::onEditMessage() { _saveDraftStart = getms(); onDraftSave(); - _field.setFocus(); + _field->setFocus(); } } @@ -8089,12 +8089,12 @@ void HistoryWidget::previewCancel() { void HistoryWidget::onPreviewParse() { if (_previewCancelled) return; - _field.parseLinks(); + _field->parseLinks(); } void HistoryWidget::onPreviewCheck() { if (_previewCancelled) return; - QStringList linksList = _field.linksList(); + QStringList linksList = _field->linksList(); QString newLinks = linksList.join(' '); if (newLinks != _previewLinks) { MTP::cancel(_previewRequest); @@ -8207,7 +8207,7 @@ void HistoryWidget::onCancel() { auto editText = textApplyEntities(original.text, original.entities); auto editTags = textTagsFromEntities(original.entities); TextWithTags editData = { editText, editTags }; - if (_replyEditMsg && editData != _field.getTextWithTags()) { + if (_replyEditMsg && editData != _field->getTextWithTags()) { auto box = new ConfirmBox(lang(lng_cancel_edit_post_sure), lang(lng_cancel_edit_post_yes), st::defaultBoxButton, lang(lng_cancel_edit_post_no)); connect(box, SIGNAL(confirmed()), this, SLOT(onFieldBarCancel())); Ui::showLayer(box); @@ -8239,7 +8239,7 @@ void HistoryWidget::onFullPeerUpdated(PeerData *data) { if (updateCmdStartShown()) { updateControlsVisibility(); updateControlsGeometry(); - } else if (!_scroll.isHidden() && _unblock.isHidden() == isBlocked()) { + } else if (!_scroll.isHidden() && _unblock->isHidden() == isBlocked()) { updateControlsVisibility(); updateControlsGeometry(); } @@ -8274,7 +8274,7 @@ void HistoryWidget::peerUpdated(PeerData *data) { } } if (!_a_show.animating()) { - if (_unblock.isHidden() == isBlocked() || (!isBlocked() && _joinChannel.isHidden() == isJoinChannel())) { + if (_unblock->isHidden() == isBlocked() || (!isBlocked() && _joinChannel->isHidden() == isJoinChannel())) { resize = true; } bool newCanSendMessages = canSendMessages(_peer); @@ -8365,7 +8365,7 @@ void HistoryWidget::onListEscapePressed() { } void HistoryWidget::onListEnterPressed() { - if (!_botStart.isHidden()) { + if (!_botStart->isHidden()) { onBotStart(); } } @@ -8422,7 +8422,7 @@ void HistoryWidget::updateTopBarSelection() { if (_selCount || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { _list->setFocus(); } else { - _field.setFocus(); + _field->setFocus(); } } App::main()->topBar()->update(); @@ -8453,7 +8453,7 @@ void HistoryWidget::updateReplyEditTexts(bool force) { updateBotKeyboard(); - if (!_field.isHidden() || _recording) { + if (!_field->isHidden() || _recording) { _fieldBarCancel->show(); updateMouseTracking(); } @@ -8489,7 +8489,7 @@ void HistoryWidget::updateField() { } void HistoryWidget::drawField(Painter &p, const QRect &rect) { - int32 backy = _field.y() - st::sendPadding, backh = _field.height() + 2 * st::sendPadding; + int32 backy = _field->y() - st::sendPadding, backh = _field->height() + 2 * st::sendPadding; Text *from = 0, *text = 0; bool serviceColor = false, hasForward = readyToForward(); ImagePtr preview; @@ -8639,10 +8639,10 @@ void HistoryWidget::drawRecordButton(Painter &p) { } return &st::historyRecordVoice; }; - fastIcon()->paintInCenter(p, _send.geometry()); + fastIcon()->paintInCenter(p, _send->geometry()); if (down > 0. && down < 1.) { p.setOpacity(down); - st::historyRecordVoiceActive.paintInCenter(p, _send.geometry()); + st::historyRecordVoiceActive.paintInCenter(p, _send->geometry()); p.setOpacity(1.); } } @@ -8662,8 +8662,8 @@ void HistoryWidget::drawRecording(Painter &p) { p.setPen(st::historyRecordDurationFg); p.drawText(_attachPhoto->x() + _attachEmoji->width(), _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration); - int32 left = _attachPhoto->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send.width() - st::historyRecordVoice.width()) / 2); - int32 right = width() - _send.width(); + int32 left = _attachPhoto->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send->width() - st::historyRecordVoice.width()) / 2); + int32 right = width() - _send->width(); p.setPen(a_recordCancel.current()); p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, lang(lng_record_cancel)); @@ -8763,9 +8763,9 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { } if (_list) { - if (!_field.isHidden() || _recording) { + if (!_field->isHidden() || _recording) { drawField(p, r); - if (_send.isHidden()) { + if (_send->isHidden()) { drawRecordButton(p); if (_recording) drawRecording(p); } @@ -8775,12 +8775,12 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { } if (_scroll.isHidden()) { p.setClipRect(_scroll.geometry()); - HistoryLayout::paintEmpty(p, width(), height() - _field.height() - 2 * st::sendPadding); + HistoryLayout::paintEmpty(p, width(), height() - _field->height() - 2 * st::sendPadding); } } else { style::font font(st::msgServiceFont); int32 w = font->width(lang(lng_willbe_history)) + st::msgPadding.left() + st::msgPadding.right(), h = font->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + 2; - QRect tr((width() - w) / 2, (height() - _field.height() - 2 * st::sendPadding - h) / 2, w, h); + QRect tr((width() - w) / 2, (height() - _field->height() - 2 * st::sendPadding - h) / 2, w, h); HistoryLayout::ServiceMessagePainter::paintBubble(p, tr.x(), tr.y(), tr.width(), tr.height()); p.setPen(st::msgServiceColor->p); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 8b508b9c7..2ba364b92 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -351,8 +351,9 @@ protected: void paintEvent(QPaintEvent *e) override; private: - FlatButton _report, _hide; - LinkButton _clear; + ChildWidget _report; + ChildWidget _hide; + ChildWidget _clear; }; @@ -489,7 +490,8 @@ private: QString _shareUrl, _shareText; QString _botAndQuery; - BoxButton _send, _cancel; + ChildWidget _send; + ChildWidget _cancel; PeerData *_offered = nullptr; anim::fvalue a_opacity; @@ -1098,7 +1100,11 @@ private: ReportSpamPanel _reportSpamPanel; - FlatButton _send, _unblock, _botStart, _joinChannel, _muteUnmute; + ChildWidget _send; + ChildWidget _unblock; + ChildWidget _botStart; + ChildWidget _joinChannel; + ChildWidget _muteUnmute; mtpRequestId _unblockRequest = 0; mtpRequestId _reportSpamRequest = 0; ChildWidget _attachDocument; @@ -1109,7 +1115,7 @@ private: ChildWidget _botCommandStart; ChildWidget _silent; bool _cmdStartShown = false; - MessageField _field; + ChildWidget _field; Animation _a_record, _a_recording; bool _recording = false; bool _inRecord = false; diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index a751a9088..1a7d836f0 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -74,28 +74,14 @@ introTextSize: size(400px, 93px); introCallSkip: 15px; introPwdTextSize: size(400px, 73px); -introNextButton: flatButton { - duration: 200; - cursor: cursor(pointer); - - color: #ffffff; - overColor: #ffffff; - downColor: #ffffff; - bgColor: #2fa9e2; - overBgColor: #279ad0; - downBgColor: #279ad0; - - textTop: 16px; - overTextTop: 16px; - downTextTop: 17px; - - font: font(17px); - overFont: font(17px); - +introNextButton: RoundButton(defaultActiveButton) { width: 300px; height: 56px; - radius: buttonRadius; + textTop: 16px; + downTextTop: 17px; + + font: font(17px); } introPhoneTop: 8px; diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index b60211d97..0c403eb5a 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "intro/introsignup.h" #include "intro/intropwdcheck.h" +#include "ui/buttons/round_button.h" #include "styles/style_intro.h" CodeInput::CodeInput(QWidget *parent, const style::flatInput &st, const QString &ph) : FlatInput(parent, st, ph) { @@ -75,26 +76,28 @@ void CodeInput::correctValue(const QString &was, QString &now) { IntroCode::IntroCode(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) , _a_error(animation(this, &IntroCode::step_error)) -, next(this, lang(lng_intro_next), st::introNextButton) +, _next(this, lang(lng_intro_next), st::introNextButton) , _desc(st::introTextSize.width()) , _noTelegramCode(this, lang(lng_code_no_telegram), st::introLink) , _noTelegramCodeRequestId(0) -, code(this, st::inpIntroCode, lang(lng_code_ph)) -, sentRequest(0) -, callStatus(intro()->getCallStatus()) { +, _code(this, st::inpIntroCode, lang(lng_code_ph)) +, _callTimer(this) +, _callStatus(intro()->getCallStatus()) +, _checkRequest(this) { setGeometry(parent->innerRect()); - connect(&next, SIGNAL(clicked()), this, SLOT(onSubmitCode())); - connect(&code, SIGNAL(changed()), this, SLOT(onInputChange())); - connect(&callTimer, SIGNAL(timeout()), this, SLOT(onSendCall())); - connect(&checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); - connect(&_noTelegramCode, SIGNAL(clicked()), this, SLOT(onNoTelegramCode())); + _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); + connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitCode())); + connect(_code, SIGNAL(changed()), this, SLOT(onInputChange())); + connect(_callTimer, SIGNAL(timeout()), this, SLOT(onSendCall())); + connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); + connect(_noTelegramCode, SIGNAL(clicked()), this, SLOT(onNoTelegramCode())); updateDescText(); if (!intro()->codeByTelegram()) { - if (callStatus.type == IntroWidget::CallWaiting) { - callTimer.start(1000); + if (_callStatus.type == IntroWidget::CallWaiting) { + _callTimer->start(1000); } } } @@ -102,13 +105,13 @@ IntroCode::IntroCode(IntroWidget *parent) : IntroStep(parent) void IntroCode::updateDescText() { _desc.setRichText(st::introFont, lang(intro()->codeByTelegram() ? lng_code_telegram : lng_code_desc)); if (intro()->codeByTelegram()) { - _noTelegramCode.show(); - callTimer.stop(); + _noTelegramCode->show(); + _callTimer->stop(); } else { - _noTelegramCode.hide(); - callStatus = intro()->getCallStatus(); - if (callStatus.type == IntroWidget::CallWaiting && !callTimer.isActive()) { - callTimer.start(1000); + _noTelegramCode->hide(); + _callStatus = intro()->getCallStatus(); + if (_callStatus.type == IntroWidget::CallWaiting && !_callTimer->isActive()) { + _callTimer->start(1000); } } update(); @@ -122,21 +125,21 @@ void IntroCode::paintEvent(QPaintEvent *e) { p.setClipRect(e->rect()); } bool codeByTelegram = intro()->codeByTelegram(); - if (trivial || e->rect().intersects(textRect)) { + if (trivial || e->rect().intersects(_textRect)) { p.setFont(st::introHeaderFont->f); - p.drawText(textRect, intro()->getPhone(), style::al_top); + p.drawText(_textRect, intro()->getPhone(), style::al_top); p.setFont(st::introFont->f); - _desc.draw(p, textRect.x(), textRect.y() + textRect.height() - 2 * st::introFont->height, textRect.width(), style::al_top); + _desc.draw(p, _textRect.x(), _textRect.y() + _textRect.height() - 2 * st::introFont->height, _textRect.width(), style::al_top); } if (codeByTelegram) { } else { QString callText; - switch (callStatus.type) { + switch (_callStatus.type) { case IntroWidget::CallWaiting: { - if (callStatus.timeout >= 3600) { - callText = lng_code_call(lt_minutes, qsl("%1:%2").arg(callStatus.timeout / 3600).arg((callStatus.timeout / 60) % 60, 2, 10, QChar('0')), lt_seconds, qsl("%1").arg(callStatus.timeout % 60, 2, 10, QChar('0'))); + if (_callStatus.timeout >= 3600) { + callText = lng_code_call(lt_minutes, qsl("%1:%2").arg(_callStatus.timeout / 3600).arg((_callStatus.timeout / 60) % 60, 2, 10, QChar('0')), lt_seconds, qsl("%1").arg(_callStatus.timeout % 60, 2, 10, QChar('0'))); } else { - callText = lng_code_call(lt_minutes, QString::number(callStatus.timeout / 60), lt_seconds, qsl("%1").arg(callStatus.timeout % 60, 2, 10, QChar('0'))); + callText = lng_code_call(lt_minutes, QString::number(_callStatus.timeout / 60), lt_seconds, qsl("%1").arg(_callStatus.timeout % 60, 2, 10, QChar('0'))); } } break; @@ -149,32 +152,32 @@ void IntroCode::paintEvent(QPaintEvent *e) { } break; } if (!callText.isEmpty()) { - p.drawText(QRect(textRect.left(), code.y() + code.height() + st::introCallSkip, st::introTextSize.width(), st::introErrorHeight), callText, style::al_center); + p.drawText(QRect(_textRect.left(), _code->y() + _code->height() + st::introCallSkip, st::introTextSize.width(), st::introErrorHeight), callText, style::al_center); } } - if (_a_error.animating() || error.length()) { + if (_a_error.animating() || _error.length()) { p.setOpacity(a_errorAlpha.current()); p.setFont(st::introErrorFont); p.setPen(st::introErrorFg); - p.drawText(QRect(textRect.left(), next.y() + next.height() + st::introErrorTop, st::introTextSize.width(), st::introErrorHeight), error, style::al_center); + p.drawText(QRect(_textRect.left(), _next->y() + _next->height() + st::introErrorTop, st::introTextSize.width(), st::introErrorHeight), _error, style::al_center); } } void IntroCode::resizeEvent(QResizeEvent *e) { if (e->oldSize().width() != width()) { - next.move((width() - next.width()) / 2, st::introBtnTop); - code.move((width() - code.width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); + _next->move((width() - _next->width()) / 2, st::introBtnTop); + _code->move((width() - _code->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); } - textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); - _noTelegramCode.move(textRect.left() + (st::introTextSize.width() - _noTelegramCode.width()) / 2, code.y() + code.height() + st::introCallSkip + (st::introErrorHeight - _noTelegramCode.height()) / 2); + _textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); + _noTelegramCode->move(_textRect.left() + (st::introTextSize.width() - _noTelegramCode->width()) / 2, _code->y() + _code->height() + st::introCallSkip + (st::introErrorHeight - _noTelegramCode->height()) / 2); } -void IntroCode::showError(const QString &err) { - if (!err.isEmpty()) code.notaBene(); - if (!_a_error.animating() && err == error) return; +void IntroCode::showError(const QString &error) { + if (!error.isEmpty()) _code->notaBene(); + if (!_a_error.animating() && error == _error) return; - if (err.length()) { - error = err; + if (error.length()) { + _error = error; a_errorAlpha.start(1); } else { a_errorAlpha.start(0); @@ -189,7 +192,7 @@ void IntroCode::step_error(float64 ms, bool timer) { _a_error.stop(); a_errorAlpha.finish(); if (!a_errorAlpha.current()) { - error.clear(); + _error.clear(); } } else { a_errorAlpha.update(dt, anim::linear); @@ -199,59 +202,57 @@ void IntroCode::step_error(float64 ms, bool timer) { void IntroCode::activate() { IntroStep::activate(); - code.setFocus(); + _code->setFocus(); } void IntroCode::finished() { IntroStep::finished(); - error.clear(); + _error.clear(); a_errorAlpha = anim::fvalue(0); - sentCode.clear(); - code.setDisabled(false); + _sentCode.clear(); + _code->setDisabled(false); - callTimer.stop(); - code.setText(QString()); + _callTimer->stop(); + _code->setText(QString()); rpcClear(); } void IntroCode::cancelled() { - if (sentRequest) { - MTP::cancel(sentRequest); - sentRequest = 0; + if (_sentRequest) { + MTP::cancel(base::take(_sentRequest)); } MTP::send(MTPauth_CancelCode(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()))); } void IntroCode::stopCheck() { - checkRequest.stop(); + _checkRequest->stop(); } void IntroCode::onCheckRequest() { - int32 status = MTP::state(sentRequest); + int32 status = MTP::state(_sentRequest); if (status < 0) { int32 leftms = -status; if (leftms >= 1000) { - if (sentRequest) { - MTP::cancel(sentRequest); - sentRequest = 0; - sentCode.clear(); + if (_sentRequest) { + MTP::cancel(base::take(_sentRequest)); + _sentCode.clear(); } - if (!code.isEnabled()) { - code.setDisabled(false); - code.setFocus(); + if (!_code->isEnabled()) { + _code->setDisabled(false); + _code->setFocus(); } } } - if (!sentRequest && status == MTP::RequestSent) { + if (!_sentRequest && status == MTP::RequestSent) { stopCheck(); } } void IntroCode::codeSubmitDone(const MTPauth_Authorization &result) { stopCheck(); - sentRequest = 0; - code.setDisabled(false); + _sentRequest = 0; + _code->setDisabled(false); const auto &d(result.c_auth_authorization()); if (d.vuser.type() != mtpc_user || !d.vuser.c_user().is_self()) { // wtf? showError(lang(lng_server_error)); @@ -264,34 +265,34 @@ void IntroCode::codeSubmitDone(const MTPauth_Authorization &result) { bool IntroCode::codeSubmitFail(const RPCError &error) { if (MTP::isFloodError(error)) { stopCheck(); - sentRequest = 0; + _sentRequest = 0; showError(lang(lng_flood_error)); - code.setDisabled(false); - code.setFocus(); + _code->setDisabled(false); + _code->setFocus(); return true; } if (MTP::isDefaultHandledError(error)) return false; stopCheck(); - sentRequest = 0; - code.setDisabled(false); + _sentRequest = 0; + _code->setDisabled(false); const QString &err = error.type(); if (err == qstr("PHONE_NUMBER_INVALID") || err == qstr("PHONE_CODE_EXPIRED")) { // show error intro()->onBack(); return true; } else if (err == qstr("PHONE_CODE_EMPTY") || err == qstr("PHONE_CODE_INVALID")) { showError(lang(lng_bad_code)); - code.notaBene(); + _code->notaBene(); return true; } else if (err == qstr("PHONE_NUMBER_UNOCCUPIED")) { // success, need to signUp - intro()->setCode(sentCode); + intro()->setCode(_sentCode); intro()->replaceStep(new IntroSignup(intro())); return true; } else if (err == qstr("SESSION_PASSWORD_NEEDED")) { - intro()->setCode(sentCode); - code.setDisabled(false); - checkRequest.start(1000); - sentRequest = MTP::send(MTPaccount_GetPassword(), rpcDone(&IntroCode::gotPassword), rpcFail(&IntroCode::codeSubmitFail)); + intro()->setCode(_sentCode); + _code->setDisabled(false); + _checkRequest->start(1000); + _sentRequest = MTP::send(MTPaccount_GetPassword(), rpcDone(&IntroCode::gotPassword), rpcFail(&IntroCode::codeSubmitFail)); return true; } if (cDebug()) { // internal server error @@ -299,43 +300,43 @@ bool IntroCode::codeSubmitFail(const RPCError &error) { } else { showError(lang(lng_server_error)); } - code.setFocus(); + _code->setFocus(); return false; } void IntroCode::onInputChange() { showError(QString()); - if (code.text().length() == 5) onSubmitCode(); + if (_code->text().length() == 5) onSubmitCode(); } void IntroCode::onSendCall() { - if (callStatus.type == IntroWidget::CallWaiting) { - if (--callStatus.timeout <= 0) { - callStatus.type = IntroWidget::CallCalling; - callTimer.stop(); + if (_callStatus.type == IntroWidget::CallWaiting) { + if (--_callStatus.timeout <= 0) { + _callStatus.type = IntroWidget::CallCalling; + _callTimer->stop(); MTP::send(MTPauth_ResendCode(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash())), rpcDone(&IntroCode::callDone)); } else { - intro()->setCallStatus(callStatus); + intro()->setCallStatus(_callStatus); } } update(); } void IntroCode::callDone(const MTPauth_SentCode &v) { - if (callStatus.type == IntroWidget::CallCalling) { - callStatus.type = IntroWidget::CallCalled; - intro()->setCallStatus(callStatus); + if (_callStatus.type == IntroWidget::CallCalling) { + _callStatus.type = IntroWidget::CallCalled; + intro()->setCallStatus(_callStatus); update(); } } void IntroCode::gotPassword(const MTPaccount_Password &result) { stopCheck(); - sentRequest = 0; - code.setDisabled(false); + _sentRequest = 0; + _code->setDisabled(false); switch (result.type()) { case mtpc_account_noPassword: // should not happen - code.setFocus(); + _code->setFocus(); break; case mtpc_account_password: { @@ -349,20 +350,20 @@ void IntroCode::gotPassword(const MTPaccount_Password &result) { } void IntroCode::onSubmitCode() { - if (sentRequest) return; + if (_sentRequest) return; - code.setDisabled(true); + _code->setDisabled(true); setFocus(); showError(QString()); - checkRequest.start(1000); + _checkRequest->start(1000); - sentCode = code.text(); + _sentCode = _code->text(); intro()->setPwdSalt(QByteArray()); intro()->setHasRecovery(false); intro()->setPwdHint(QString()); - sentRequest = MTP::send(MTPauth_SignIn(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(sentCode)), rpcDone(&IntroCode::codeSubmitDone), rpcFail(&IntroCode::codeSubmitFail)); + _sentRequest = MTP::send(MTPauth_SignIn(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(_sentCode)), rpcDone(&IntroCode::codeSubmitDone), rpcFail(&IntroCode::codeSubmitFail)); } void IntroCode::onNoTelegramCode() { @@ -395,7 +396,7 @@ void IntroCode::noTelegramCodeDone(const MTPauth_SentCode &result) { bool IntroCode::noTelegramCodeFail(const RPCError &error) { if (MTP::isFloodError(error)) { showError(lang(lng_flood_error)); - code.setFocus(); + _code->setFocus(); return true; } if (MTP::isDefaultHandledError(error)) return false; @@ -405,7 +406,7 @@ bool IntroCode::noTelegramCodeFail(const RPCError &error) { } else { showError(lang(lng_server_error)); } - code.setFocus(); + _code->setFocus(); return false; } diff --git a/Telegram/SourceFiles/intro/introcode.h b/Telegram/SourceFiles/intro/introcode.h index ab62224ca..49e049bcd 100644 --- a/Telegram/SourceFiles/intro/introcode.h +++ b/Telegram/SourceFiles/intro/introcode.h @@ -25,6 +25,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/flatinput.h" #include "intro/introwidget.h" +namespace Ui { +class RoundButton; +} // namespace Ui + class CodeInput final : public FlatInput { Q_OBJECT @@ -43,7 +47,6 @@ class IntroCode final : public IntroStep { Q_OBJECT public: - IntroCode(IntroWidget *parent); void paintEvent(QPaintEvent *e) override; @@ -65,7 +68,6 @@ public: void updateDescText(); public slots: - void onSubmitCode(); void onNoTelegramCode(); void onInputChange(); @@ -73,32 +75,32 @@ public slots: void onCheckRequest(); private: - void showError(const QString &err); void callDone(const MTPauth_SentCode &v); void gotPassword(const MTPaccount_Password &result); - void stopCheck(); - - QString error; - anim::fvalue a_errorAlpha; - Animation _a_error; - - FlatButton next; - - Text _desc; - LinkButton _noTelegramCode; - mtpRequestId _noTelegramCodeRequestId; - QRect textRect; - void noTelegramCodeDone(const MTPauth_SentCode &result); bool noTelegramCodeFail(const RPCError &result); - CodeInput code; - QString sentCode; - mtpRequestId sentRequest; - QTimer callTimer; - IntroWidget::CallStatus callStatus; + void stopCheck(); + + QString _error; + anim::fvalue a_errorAlpha; + Animation _a_error; + + ChildWidget _next; + + Text _desc; + ChildWidget _noTelegramCode; + mtpRequestId _noTelegramCodeRequestId; + QRect _textRect; + + ChildWidget _code; + QString _sentCode; + mtpRequestId _sentRequest = 0; + ChildObject _callTimer; + IntroWidget::CallStatus _callStatus; + + ChildObject _checkRequest; - QTimer checkRequest; }; diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index b9aaf1f99..9f68aacd5 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "intro/introcode.h" #include "styles/style_intro.h" +#include "ui/buttons/round_button.h" namespace { class SignUpClickHandler : public LeftButtonClickHandler { @@ -46,38 +47,37 @@ namespace { IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) , _a_error(animation(this, &IntroPhone::step_error)) -, changed(false) -, next(this, lang(lng_intro_next), st::introNextButton) -, country(this, st::introCountry) -, phone(this, st::inpIntroPhone) -, code(this, st::inpIntroCountryCode) +, _next(this, lang(lng_intro_next), st::introNextButton) +, _country(this, st::introCountry) +, _phone(this, st::inpIntroPhone) +, _code(this, st::inpIntroCountryCode) , _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), FlatLabel::InitType::Rich, st::introErrorLabel, st::introErrorLabelTextStyle) -, _showSignup(false) -, sentRequest(0) { +, _checkRequest(this) { setVisible(false); setGeometry(parent->innerRect()); - connect(&next, SIGNAL(clicked()), this, SLOT(onSubmitPhone())); - connect(&phone, SIGNAL(voidBackspace(QKeyEvent*)), &code, SLOT(startErasing(QKeyEvent*))); - connect(&country, SIGNAL(codeChanged(const QString &)), &code, SLOT(codeSelected(const QString &))); - connect(&code, SIGNAL(codeChanged(const QString &)), &country, SLOT(onChooseCode(const QString &))); - connect(&code, SIGNAL(codeChanged(const QString &)), &phone, SLOT(onChooseCode(const QString &))); - connect(&country, SIGNAL(codeChanged(const QString &)), &phone, SLOT(onChooseCode(const QString &))); - connect(&code, SIGNAL(addedToNumber(const QString &)), &phone, SLOT(addedToNumber(const QString &))); - connect(&phone, SIGNAL(changed()), this, SLOT(onInputChange())); - connect(&code, SIGNAL(changed()), this, SLOT(onInputChange())); + _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); + connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitPhone())); + connect(_phone, SIGNAL(voidBackspace(QKeyEvent*)), _code, SLOT(startErasing(QKeyEvent*))); + connect(_country, SIGNAL(codeChanged(const QString &)), _code, SLOT(codeSelected(const QString &))); + connect(_code, SIGNAL(codeChanged(const QString &)), _country, SLOT(onChooseCode(const QString &))); + connect(_code, SIGNAL(codeChanged(const QString &)), _phone, SLOT(onChooseCode(const QString &))); + connect(_country, SIGNAL(codeChanged(const QString &)), _phone, SLOT(onChooseCode(const QString &))); + connect(_code, SIGNAL(addedToNumber(const QString &)), _phone, SLOT(addedToNumber(const QString &))); + connect(_phone, SIGNAL(changed()), this, SLOT(onInputChange())); + connect(_code, SIGNAL(changed()), this, SLOT(onInputChange())); connect(intro(), SIGNAL(countryChanged()), this, SLOT(countryChanged())); - connect(&checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); + connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); - _signup.setLink(1, MakeShared(this)); - _signup.hide(); + _signup->setLink(1, MakeShared(this)); + _signup->hide(); - _signupCache = myGrab(&_signup); + _signupCache = myGrab(_signup); - if (!country.onChooseCountry(intro()->currentCountry())) { - country.onChooseCountry(qsl("US")); + if (!_country->onChooseCountry(intro()->currentCountry())) { + _country->onChooseCountry(qsl("US")); } - changed = false; + _changed = false; } void IntroPhone::paintEvent(QPaintEvent *e) { @@ -87,52 +87,52 @@ void IntroPhone::paintEvent(QPaintEvent *e) { if (!trivial) { p.setClipRect(e->rect()); } - if (trivial || e->rect().intersects(textRect)) { + if (trivial || e->rect().intersects(_textRect)) { p.setFont(st::introHeaderFont->f); - p.drawText(textRect, lang(lng_phone_title), style::al_top); + p.drawText(_textRect, lang(lng_phone_title), style::al_top); p.setFont(st::introFont->f); - p.drawText(textRect, lang(lng_phone_desc), style::al_bottom); + p.drawText(_textRect, lang(lng_phone_desc), style::al_bottom); } - if (_a_error.animating() || error.length()) { - int32 errorY = _showSignup ? ((phone.y() + phone.height() + next.y() - st::introErrorFont->height) / 2) : (next.y() + next.height() + st::introErrorTop); + if (_a_error.animating() || _error.length()) { + int32 errorY = _showSignup ? ((_phone->y() + _phone->height() + _next->y() - st::introErrorFont->height) / 2) : (_next->y() + _next->height() + st::introErrorTop); p.setOpacity(a_errorAlpha.current()); p.setFont(st::introErrorFont); p.setPen(st::introErrorFg); - p.drawText(QRect(textRect.x(), errorY, textRect.width(), st::introErrorFont->height), error, style::al_top); + p.drawText(QRect(_textRect.x(), errorY, _textRect.width(), st::introErrorFont->height), _error, style::al_top); - if (_signup.isHidden() && _showSignup) { - p.drawPixmap(_signup.x(), _signup.y(), _signupCache); + if (_signup->isHidden() && _showSignup) { + p.drawPixmap(_signup->x(), _signup->y(), _signupCache); } } } void IntroPhone::resizeEvent(QResizeEvent *e) { if (e->oldSize().width() != width()) { - next.move((width() - next.width()) / 2, st::introBtnTop); - country.move((width() - country.width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); - int phoneTop = country.y() + country.height() + st::introPhoneTop; - phone.move((width() - country.width()) / 2 + country.width() - st::inpIntroPhone.width, phoneTop); - code.move((width() - country.width()) / 2, phoneTop); + _next->move((width() - _next->width()) / 2, st::introBtnTop); + _country->move((width() - _country->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); + int phoneTop = _country->y() + _country->height() + st::introPhoneTop; + _phone->move((width() - _country->width()) / 2 + _country->width() - st::inpIntroPhone.width, phoneTop); + _code->move((width() - _country->width()) / 2, phoneTop); } - _signup.move((width() - _signup.width()) / 2, next.y() + next.height() + st::introErrorTop - ((st::introErrorLabelTextStyle.lineHeight - st::introErrorFont->height) / 2)); - textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); + _signup->move((width() - _signup->width()) / 2, _next->y() + _next->height() + st::introErrorTop - ((st::introErrorLabelTextStyle.lineHeight - st::introErrorFont->height) / 2)); + _textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); } -void IntroPhone::showError(const QString &err, bool signUp) { - if (!err.isEmpty()) { - phone.notaBene(); +void IntroPhone::showError(const QString &error, bool signUp) { + if (!error.isEmpty()) { + _phone->notaBene(); _showSignup = signUp; } - if (!_a_error.animating() && err == error) return; + if (!_a_error.animating() && error == _error) return; - if (err.length()) { - error = err; + if (error.length()) { + _error = error; a_errorAlpha.start(1); } else { a_errorAlpha.start(0); } - _signup.hide(); + _signup->hide(); _a_error.start(); } @@ -143,10 +143,10 @@ void IntroPhone::step_error(float64 ms, bool timer) { _a_error.stop(); a_errorAlpha.finish(); if (!a_errorAlpha.current()) { - error.clear(); - _signup.hide(); - } else if (!error.isEmpty() && _showSignup) { - _signup.show(); + _error.clear(); + _signup->hide(); + } else if (!_error.isEmpty() && _showSignup) { + _signup->show(); } } else { a_errorAlpha.update(dt, anim::linear); @@ -155,65 +155,64 @@ void IntroPhone::step_error(float64 ms, bool timer) { } void IntroPhone::countryChanged() { - if (!changed) { + if (!_changed) { selectCountry(intro()->currentCountry()); } } void IntroPhone::onInputChange() { - changed = true; + _changed = true; showError(QString()); } void IntroPhone::disableAll() { - next.setDisabled(true); - phone.setDisabled(true); - country.setDisabled(true); - code.setDisabled(true); + _next->setDisabled(true); + _phone->setDisabled(true); + _country->setDisabled(true); + _code->setDisabled(true); setFocus(); } void IntroPhone::enableAll(bool failed) { - next.setDisabled(false); - phone.setDisabled(false); - country.setDisabled(false); - code.setDisabled(false); - if (failed) phone.setFocus(); + _next->setDisabled(false); + _phone->setDisabled(false); + _country->setDisabled(false); + _code->setDisabled(false); + if (failed) _phone->setFocus(); } void IntroPhone::onSubmitPhone() { - if (sentRequest || isHidden()) return; + if (_sentRequest || isHidden()) return; if (!App::isValidPhone(fullNumber())) { showError(lang(lng_bad_phone)); - phone.setFocus(); + _phone->setFocus(); return; } disableAll(); showError(QString()); - checkRequest.start(1000); + _checkRequest->start(1000); - sentPhone = fullNumber(); - sentRequest = MTP::send(MTPauth_CheckPhone(MTP_string(sentPhone)), rpcDone(&IntroPhone::phoneCheckDone), rpcFail(&IntroPhone::phoneSubmitFail)); + _sentPhone = fullNumber(); + _sentRequest = MTP::send(MTPauth_CheckPhone(MTP_string(_sentPhone)), rpcDone(&IntroPhone::phoneCheckDone), rpcFail(&IntroPhone::phoneSubmitFail)); } void IntroPhone::stopCheck() { - checkRequest.stop(); + _checkRequest->stop(); } void IntroPhone::onCheckRequest() { - int32 status = MTP::state(sentRequest); + int32 status = MTP::state(_sentRequest); if (status < 0) { int32 leftms = -status; if (leftms >= 1000) { - MTP::cancel(sentRequest); - sentRequest = 0; - if (!phone.isEnabled()) enableAll(true); + MTP::cancel(base::take(_sentRequest)); + if (!_phone->isEnabled()) enableAll(true); } } - if (!sentRequest && status == MTP::RequestSent) { + if (!_sentRequest && status == MTP::RequestSent) { stopCheck(); } } @@ -226,20 +225,20 @@ void IntroPhone::phoneCheckDone(const MTPauth_CheckedPhone &result) { disableAll(); showError(QString()); - checkRequest.start(1000); + _checkRequest->start(1000); MTPauth_SendCode::Flags flags = 0; - sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail)); + _sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail)); } else { showError(lang(lng_bad_phone_noreg), true); enableAll(true); - sentRequest = 0; + _sentRequest = 0; } } void IntroPhone::phoneSubmitDone(const MTPauth_SentCode &result) { stopCheck(); - sentRequest = 0; + _sentRequest = 0; enableAll(true); if (result.type() != mtpc_auth_sentCode) { @@ -254,7 +253,7 @@ void IntroPhone::phoneSubmitDone(const MTPauth_SentCode &result) { case mtpc_auth_sentCodeTypeCall: intro()->setCodeByTelegram(false); break; case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break; } - intro()->setPhone(sentPhone, d.vphone_code_hash.c_string().v.c_str(), d.is_phone_registered()); + intro()->setPhone(_sentPhone, d.vphone_code_hash.c_string().v.c_str(), d.is_phone_registered()); if (d.has_next_type() && d.vnext_type.type() == mtpc_auth_codeTypeCall) { intro()->setCallStatus({ IntroWidget::CallWaiting, d.has_timeout() ? d.vtimeout.v : 60 }); } else { @@ -267,16 +266,16 @@ void IntroPhone::toSignUp() { disableAll(); showError(QString()); - checkRequest.start(1000); + _checkRequest->start(1000); MTPauth_SendCode::Flags flags = 0; - sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail)); + _sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail)); } bool IntroPhone::phoneSubmitFail(const RPCError &error) { if (MTP::isFloodError(error)) { stopCheck(); - sentRequest = 0; + _sentRequest = 0; showError(lang(lng_flood_error)); enableAll(true); return true; @@ -284,7 +283,7 @@ bool IntroPhone::phoneSubmitFail(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; stopCheck(); - sentRequest = 0; + _sentRequest = 0; const QString &err = error.type(); if (err == qstr("PHONE_NUMBER_INVALID")) { // show error showError(lang(lng_bad_phone)); @@ -301,32 +300,31 @@ bool IntroPhone::phoneSubmitFail(const RPCError &error) { } QString IntroPhone::fullNumber() const { - return code.text() + phone.text(); + return _code->text() + _phone->text(); } void IntroPhone::selectCountry(const QString &c) { - country.onChooseCountry(c); + _country->onChooseCountry(c); } void IntroPhone::activate() { IntroStep::activate(); - phone.setFocus(); + _phone->setFocus(); } void IntroPhone::finished() { IntroStep::finished(); - checkRequest.stop(); + _checkRequest->stop(); rpcClear(); - error.clear(); + _error.clear(); a_errorAlpha = anim::fvalue(0); enableAll(true); } void IntroPhone::cancelled() { - if (sentRequest) { - MTP::cancel(sentRequest); - sentRequest = 0; + if (_sentRequest) { + MTP::cancel(base::take(_sentRequest)); } } diff --git a/Telegram/SourceFiles/intro/introphone.h b/Telegram/SourceFiles/intro/introphone.h index 8c53bc6b4..cf4786e73 100644 --- a/Telegram/SourceFiles/intro/introphone.h +++ b/Telegram/SourceFiles/intro/introphone.h @@ -25,11 +25,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/flatlabel.h" #include "intro/introwidget.h" +namespace Ui { +class RoundButton; +} // namespace Ui + class IntroPhone final : public IntroStep { Q_OBJECT public: - IntroPhone(IntroWidget *parent); void paintEvent(QPaintEvent *e) override; @@ -51,14 +54,12 @@ public: void toSignUp(); public slots: - void countryChanged(); void onInputChange(); void onSubmitPhone(); void onCheckRequest(); private: - QString fullNumber() const; void disableAll(); void enableAll(bool failed); @@ -66,25 +67,26 @@ private: void showError(const QString &err, bool signUp = false); - QString error; + QString _error; anim::fvalue a_errorAlpha; Animation _a_error; - bool changed; - FlatButton next; + bool _changed = false; + ChildWidget _next; - QRect textRect; + QRect _textRect; - CountryInput country; - PhonePartInput phone; - CountryCodeInput code; + ChildWidget _country; + ChildWidget _phone; + ChildWidget _code; - FlatLabel _signup; + ChildWidget _signup; QPixmap _signupCache; - bool _showSignup; + bool _showSignup = false; - QString sentPhone; - mtpRequestId sentRequest; + QString _sentPhone; + mtpRequestId _sentRequest = 0; + + ChildObject _checkRequest; - QTimer checkRequest; }; diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index c603206aa..d04c3dadd 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "lang.h" #include "application.h" #include "intro/introsignup.h" +#include "ui/buttons/round_button.h" IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) @@ -40,27 +41,28 @@ IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent) , _toRecover(this, lang(lng_signin_recover)) , _toPassword(this, lang(lng_signin_try_password)) , _reset(this, lang(lng_signin_reset_account), st::btnRedLink) -, sentRequest(0) { +, _checkRequest(this) { setVisible(false); setGeometry(parent->innerRect()); - connect(&_next, SIGNAL(clicked()), this, SLOT(onSubmitPwd())); - connect(&checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); - connect(&_toRecover, SIGNAL(clicked()), this, SLOT(onToRecover())); - connect(&_toPassword, SIGNAL(clicked()), this, SLOT(onToPassword())); - connect(&_pwdField, SIGNAL(changed()), this, SLOT(onInputChange())); - connect(&_codeField, SIGNAL(changed()), this, SLOT(onInputChange())); - connect(&_reset, SIGNAL(clicked()), this, SLOT(onReset())); + _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); + connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitPwd())); + connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); + connect(_toRecover, SIGNAL(clicked()), this, SLOT(onToRecover())); + connect(_toPassword, SIGNAL(clicked()), this, SLOT(onToPassword())); + connect(_pwdField, SIGNAL(changed()), this, SLOT(onInputChange())); + connect(_codeField, SIGNAL(changed()), this, SLOT(onInputChange())); + connect(_reset, SIGNAL(clicked()), this, SLOT(onReset())); - _pwdField.setEchoMode(QLineEdit::Password); + _pwdField->setEchoMode(QLineEdit::Password); if (!_hint.isEmpty()) { _hintText.setText(st::introFont, lng_signin_hint(lt_password_hint, _hint)); } - _codeField.hide(); - _toPassword.hide(); - _toRecover.show(); - _reset.hide(); + _codeField->hide(); + _toPassword->hide(); + _toRecover->show(); + _reset->hide(); setMouseTracking(true); } @@ -72,26 +74,26 @@ void IntroPwdCheck::paintEvent(QPaintEvent *e) { if (!trivial) { p.setClipRect(e->rect()); } - if (trivial || e->rect().intersects(textRect)) { + if (trivial || e->rect().intersects(_textRect)) { p.setFont(st::introHeaderFont->f); - p.drawText(textRect, lang(lng_signin_title), style::al_top); + p.drawText(_textRect, lang(lng_signin_title), style::al_top); p.setFont(st::introFont->f); - p.drawText(textRect, lang(_pwdField.isHidden() ? lng_signin_recover_desc : lng_signin_desc), style::al_bottom); + p.drawText(_textRect, lang(_pwdField->isHidden() ? lng_signin_recover_desc : lng_signin_desc), style::al_bottom); } - if (_pwdField.isHidden()) { + if (_pwdField->isHidden()) { if (!_emailPattern.isEmpty()) { - p.drawText(QRect(textRect.x(), _pwdField.y() + _pwdField.height() + st::introFinishSkip, textRect.width(), st::introFont->height), _emailPattern, style::al_top); + p.drawText(QRect(_textRect.x(), _pwdField->y() + _pwdField->height() + st::introFinishSkip, _textRect.width(), st::introFont->height), _emailPattern, style::al_top); } } else if (!_hint.isEmpty()) { - _hintText.drawElided(p, _pwdField.x(), _pwdField.y() + _pwdField.height() + st::introFinishSkip, _pwdField.width(), 1, style::al_top); + _hintText.drawElided(p, _pwdField->x(), _pwdField->y() + _pwdField->height() + st::introFinishSkip, _pwdField->width(), 1, style::al_top); } - if (_a_error.animating() || error.length()) { + if (_a_error.animating() || _error.length()) { p.setOpacity(a_errorAlpha.current()); - QRect errRect((width() - st::introErrorWidth) / 2, (_pwdField.y() + _pwdField.height() + st::introFinishSkip + st::introFont->height + _next.y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); + QRect errRect((width() - st::introErrorWidth) / 2, (_pwdField->y() + _pwdField->height() + st::introFinishSkip + st::introFont->height + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); p.setFont(st::introErrorFont); p.setPen(st::introErrorFg); - p.drawText(errRect, error, QTextOption(style::al_center)); + p.drawText(errRect, _error, QTextOption(style::al_center)); p.setOpacity(1); } @@ -99,21 +101,21 @@ void IntroPwdCheck::paintEvent(QPaintEvent *e) { void IntroPwdCheck::resizeEvent(QResizeEvent *e) { if (e->oldSize().width() != width()) { - _next.move((width() - _next.width()) / 2, st::introBtnTop); - _pwdField.move((width() - _pwdField.width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); - _codeField.move((width() - _codeField.width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); - _toRecover.move(_next.x() + (_pwdField.width() - _toRecover.width()) / 2, _next.y() + _next.height() + st::introFinishSkip); - _toPassword.move(_next.x() + (_pwdField.width() - _toPassword.width()) / 2, _next.y() + _next.height() + st::introFinishSkip); - _reset.move((width() - _reset.width()) / 2, _toRecover.y() + _toRecover.height() + st::introFinishSkip); + _next->move((width() - _next->width()) / 2, st::introBtnTop); + _pwdField->move((width() - _pwdField->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); + _codeField->move((width() - _codeField->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top); + _toRecover->move(_next->x() + (_pwdField->width() - _toRecover->width()) / 2, _next->y() + _next->height() + st::introFinishSkip); + _toPassword->move(_next->x() + (_pwdField->width() - _toPassword->width()) / 2, _next->y() + _next->height() + st::introFinishSkip); + _reset->move((width() - _reset->width()) / 2, _toRecover->y() + _toRecover->height() + st::introFinishSkip); } - textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); + _textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); } -void IntroPwdCheck::showError(const QString &err) { - if (!_a_error.animating() && err == error) return; +void IntroPwdCheck::showError(const QString &error) { + if (!_a_error.animating() && error == _error) return; - if (err.length()) { - error = err; + if (error.length()) { + _error = error; a_errorAlpha.start(1); } else { a_errorAlpha.start(0); @@ -128,7 +130,7 @@ void IntroPwdCheck::step_error(float64 ms, bool timer) { _a_error.stop(); a_errorAlpha.finish(); if (!a_errorAlpha.current()) { - error.clear(); + _error.clear(); } } else { a_errorAlpha.update(dt, anim::linear); @@ -138,51 +140,49 @@ void IntroPwdCheck::step_error(float64 ms, bool timer) { void IntroPwdCheck::activate() { IntroStep::activate(); - if (_pwdField.isHidden()) { - _codeField.setFocus(); + if (_pwdField->isHidden()) { + _codeField->setFocus(); } else { - _pwdField.setFocus(); + _pwdField->setFocus(); } } void IntroPwdCheck::cancelled() { - if (sentRequest) { - MTP::cancel(sentRequest); - sentRequest = 0; + if (_sentRequest) { + MTP::cancel(base::take(_sentRequest)); } } void IntroPwdCheck::stopCheck() { - checkRequest.stop(); + _checkRequest->stop(); } void IntroPwdCheck::onCheckRequest() { - int32 status = MTP::state(sentRequest); + int32 status = MTP::state(_sentRequest); if (status < 0) { int32 leftms = -status; if (leftms >= 1000) { - MTP::cancel(sentRequest); - sentRequest = 0; - if (!_pwdField.isEnabled()) { - _pwdField.setDisabled(false); - _codeField.setDisabled(false); + MTP::cancel(base::take(_sentRequest)); + if (!_pwdField->isEnabled()) { + _pwdField->setDisabled(false); + _codeField->setDisabled(false); activate(); } } } - if (!sentRequest && status == MTP::RequestSent) { + if (!_sentRequest && status == MTP::RequestSent) { stopCheck(); } } void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) { - sentRequest = 0; + _sentRequest = 0; stopCheck(); if (recover) { cSetPasswordRecovered(true); } - _pwdField.setDisabled(false); - _codeField.setDisabled(false); + _pwdField->setDisabled(false); + _codeField->setDisabled(false); const auto &d(result.c_auth_authorization()); if (d.vuser.type() != mtpc_user || !d.vuser.c_user().is_self()) { // wtf? showError(lang(lng_server_error)); @@ -193,25 +193,25 @@ void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &res bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) { if (MTP::isFloodError(error)) { - sentRequest = 0; + _sentRequest = 0; stopCheck(); - _codeField.setDisabled(false); + _codeField->setDisabled(false); showError(lang(lng_flood_error)); - _pwdField.setDisabled(false); - _pwdField.notaBene(); + _pwdField->setDisabled(false); + _pwdField->notaBene(); return true; } if (MTP::isDefaultHandledError(error)) return false; - sentRequest = 0; + _sentRequest = 0; stopCheck(); - _pwdField.setDisabled(false); - _codeField.setDisabled(false); + _pwdField->setDisabled(false); + _codeField->setDisabled(false); const QString &err = error.type(); if (err == qstr("PASSWORD_HASH_INVALID")) { showError(lang(lng_signin_bad_password)); - _pwdField.selectAll(); - _pwdField.notaBene(); + _pwdField->selectAll(); + _pwdField->notaBene(); return true; } else if (err == qstr("PASSWORD_EMPTY")) { intro()->onBack(); @@ -221,22 +221,22 @@ bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) { } else { showError(lang(lng_server_error)); } - _pwdField.setFocus(); + _pwdField->setFocus(); return false; } bool IntroPwdCheck::codeSubmitFail(const RPCError &error) { if (MTP::isFloodError(error)) { showError(lang(lng_flood_error)); - _codeField.notaBene(); + _codeField->notaBene(); return true; } if (MTP::isDefaultHandledError(error)) return false; - sentRequest = 0; + _sentRequest = 0; stopCheck(); - _pwdField.setDisabled(false); - _codeField.setDisabled(false); + _pwdField->setDisabled(false); + _codeField->setDisabled(false); const QString &err = error.type(); if (err == qstr("PASSWORD_EMPTY")) { intro()->onBack(); @@ -250,8 +250,8 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) { return true; } else if (err == qstr("CODE_INVALID")) { showError(lang(lng_signin_wrong_code)); - _codeField.selectAll(); - _codeField.notaBene(); + _codeField->selectAll(); + _codeField->notaBene(); return true; } if (cDebug()) { // internal server error @@ -259,22 +259,22 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) { } else { showError(lang(lng_server_error)); } - _codeField.setFocus(); + _codeField->setFocus(); return false; } void IntroPwdCheck::recoverStarted(const MTPauth_PasswordRecovery &result) { - _emailPattern = st::introFont->elided(lng_signin_recover_hint(lt_recover_email, qs(result.c_auth_passwordRecovery().vemail_pattern)), textRect.width()); + _emailPattern = st::introFont->elided(lng_signin_recover_hint(lt_recover_email, qs(result.c_auth_passwordRecovery().vemail_pattern)), _textRect.width()); update(); } bool IntroPwdCheck::recoverStartFail(const RPCError &error) { stopCheck(); - _pwdField.setDisabled(false); - _codeField.setDisabled(false); - _pwdField.show(); - _codeField.hide(); - _pwdField.setFocus(); + _pwdField->setDisabled(false); + _codeField->setDisabled(false); + _pwdField->show(); + _codeField->hide(); + _pwdField->setFocus(); update(); showError(QString()); return true; @@ -282,17 +282,16 @@ bool IntroPwdCheck::recoverStartFail(const RPCError &error) { void IntroPwdCheck::onToRecover() { if (_hasRecovery) { - if (sentRequest) { - MTP::cancel(sentRequest); - sentRequest = 0; + if (_sentRequest) { + MTP::cancel(base::take(_sentRequest)); } showError(QString()); - _toRecover.hide(); - _toPassword.show(); - _pwdField.hide(); - _pwdField.setText(QString()); - _codeField.show(); - _codeField.setFocus(); + _toRecover->hide(); + _toPassword->show(); + _pwdField->hide(); + _pwdField->setText(QString()); + _codeField->show(); + _codeField->setFocus(); if (_emailPattern.isEmpty()) { MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&IntroPwdCheck::recoverStarted), rpcFail(&IntroPwdCheck::recoverStartFail)); } @@ -311,36 +310,35 @@ void IntroPwdCheck::onToPassword() { } void IntroPwdCheck::onToReset() { - if (sentRequest) { - MTP::cancel(sentRequest); - sentRequest = 0; + if (_sentRequest) { + MTP::cancel(base::take(_sentRequest)); } - _toRecover.show(); - _toPassword.hide(); - _pwdField.show(); - _codeField.hide(); - _codeField.setText(QString()); - _pwdField.setFocus(); - _reset.show(); + _toRecover->show(); + _toPassword->hide(); + _pwdField->show(); + _codeField->hide(); + _codeField->setText(QString()); + _pwdField->setFocus(); + _reset->show(); update(); } void IntroPwdCheck::onReset() { - if (sentRequest) return; + if (_sentRequest) return; ConfirmBox *box = new ConfirmBox(lang(lng_signin_sure_reset), lang(lng_signin_reset), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onResetSure())); Ui::showLayer(box); } void IntroPwdCheck::onResetSure() { - if (sentRequest) return; - sentRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&IntroPwdCheck::deleteDone), rpcFail(&IntroPwdCheck::deleteFail)); + if (_sentRequest) return; + _sentRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&IntroPwdCheck::deleteDone), rpcFail(&IntroPwdCheck::deleteFail)); } bool IntroPwdCheck::deleteFail(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; - sentRequest = 0; + _sentRequest = 0; auto type = error.type(); if (type.startsWith(qstr("2FA_CONFIRM_WAIT_"))) { @@ -376,27 +374,27 @@ void IntroPwdCheck::onInputChange() { } void IntroPwdCheck::onSubmitPwd(bool force) { - if (sentRequest) return; - if (_pwdField.isHidden()) { - if (!force && !_codeField.isEnabled()) return; - QString code = _codeField.text().trimmed(); + if (_sentRequest) return; + if (_pwdField->isHidden()) { + if (!force && !_codeField->isEnabled()) return; + QString code = _codeField->text().trimmed(); if (code.isEmpty()) { - _codeField.notaBene(); + _codeField->notaBene(); return; } - sentRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&IntroPwdCheck::pwdSubmitDone, true), rpcFail(&IntroPwdCheck::codeSubmitFail)); + _sentRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&IntroPwdCheck::pwdSubmitDone, true), rpcFail(&IntroPwdCheck::codeSubmitFail)); } else { - if (!force && !_pwdField.isEnabled()) return; + if (!force && !_pwdField->isEnabled()) return; - _pwdField.setDisabled(true); + _pwdField->setDisabled(true); setFocus(); showError(QString()); - QByteArray pwdData = _salt + _pwdField.text().toUtf8() + _salt, pwdHash(32, Qt::Uninitialized); + QByteArray pwdData = _salt + _pwdField->text().toUtf8() + _salt, pwdHash(32, Qt::Uninitialized); hashSha256(pwdData.constData(), pwdData.size(), pwdHash.data()); - sentRequest = MTP::send(MTPauth_CheckPassword(MTP_bytes(pwdHash)), rpcDone(&IntroPwdCheck::pwdSubmitDone, false), rpcFail(&IntroPwdCheck::pwdSubmitFail)); + _sentRequest = MTP::send(MTPauth_CheckPassword(MTP_bytes(pwdHash)), rpcDone(&IntroPwdCheck::pwdSubmitDone, false), rpcFail(&IntroPwdCheck::pwdSubmitFail)); } } diff --git a/Telegram/SourceFiles/intro/intropwdcheck.h b/Telegram/SourceFiles/intro/intropwdcheck.h index 580e0080b..01c37b7cf 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.h +++ b/Telegram/SourceFiles/intro/intropwdcheck.h @@ -20,16 +20,17 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include -#include "ui/flatbutton.h" #include "ui/flatinput.h" #include "intro/introwidget.h" +namespace Ui { +class RoundButton; +} // namespace Ui + class IntroPwdCheck final : public IntroStep { Q_OBJECT public: - IntroPwdCheck(IntroWidget *parent); void paintEvent(QPaintEvent *e) override; @@ -49,7 +50,6 @@ public: void recoverStarted(const MTPauth_PasswordRecovery &result); public slots: - void onSubmitPwd(bool force = false); void onToRecover(); void onToPassword(); @@ -60,32 +60,35 @@ public slots: void onResetSure(); private: - - void showError(const QString &err); + void showError(const QString &error); void stopCheck(); void deleteDone(const MTPBool &result); bool deleteFail(const RPCError &error); - QString error; + QString _error; anim::fvalue a_errorAlpha; Animation _a_error; - FlatButton _next; + ChildWidget _next; - QRect textRect; + QRect _textRect; QByteArray _salt; bool _hasRecovery; QString _hint, _emailPattern; - FlatInput _pwdField, _codeField; - LinkButton _toRecover, _toPassword, _reset; - mtpRequestId sentRequest; + ChildWidget _pwdField; + ChildWidget _codeField; + ChildWidget _toRecover; + ChildWidget _toPassword; + ChildWidget _reset; + mtpRequestId _sentRequest = 0; Text _hintText; QByteArray _pwdSalt; - QTimer checkRequest; + ChildObject _checkRequest; + }; diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index 4fa4f795b..0f674e5d5 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -27,25 +27,27 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/photocropbox.h" #include "lang.h" #include "application.h" +#include "ui/buttons/round_button.h" IntroSignup::IntroSignup(IntroWidget *parent) : IntroStep(parent) , a_errorAlpha(0) , a_photoOver(0) , _a_error(animation(this, &IntroSignup::step_error)) , _a_photo(animation(this, &IntroSignup::step_photo)) -, next(this, lang(lng_intro_finish), st::introNextButton) -, first(this, st::inpIntroName, lang(lng_signup_firstname)) -, last(this, st::inpIntroName, lang(lng_signup_lastname)) -, sentRequest(0) -, _invertOrder(langFirstNameGoesSecond()) { +, _next(this, lang(lng_intro_finish), st::introNextButton) +, _first(this, st::inpIntroName, lang(lng_signup_firstname)) +, _last(this, st::inpIntroName, lang(lng_signup_lastname)) +, _invertOrder(langFirstNameGoesSecond()) +, _checkRequest(this) { setVisible(false); setGeometry(parent->innerRect()); - connect(&next, SIGNAL(clicked()), this, SLOT(onSubmitName())); - connect(&checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); + _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); + connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitName())); + connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); if (_invertOrder) { - setTabOrder(&last, &first); + setTabOrder(_last, _first); } setMouseTracking(true); @@ -102,20 +104,20 @@ void IntroSignup::paintEvent(QPaintEvent *e) { if (!trivial) { p.setClipRect(e->rect()); } - if (trivial || e->rect().intersects(textRect)) { + if (trivial || e->rect().intersects(_textRect)) { p.setFont(st::introHeaderFont->f); - p.drawText(textRect, lang(lng_signup_title), style::al_top); + p.drawText(_textRect, lang(lng_signup_title), style::al_top); p.setFont(st::introFont->f); - p.drawText(textRect, lang(lng_signup_desc), style::al_bottom); + p.drawText(_textRect, lang(lng_signup_desc), style::al_bottom); } if (_a_error.animating() || error.length()) { p.setOpacity(a_errorAlpha.current()); QRect errRect; if (_invertOrder) { - errRect = QRect((width() - st::introErrorWidth) / 2, (first.y() + first.height() + next.y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); + errRect = QRect((width() - st::introErrorWidth) / 2, (_first->y() + _first->height() + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); } else { - errRect = QRect((width() - st::introErrorWidth) / 2, (last.y() + last.height() + next.y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); + errRect = QRect((width() - st::introErrorWidth) / 2, (_last->y() + _last->height() + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight); } p.setFont(st::introErrorFont); p.setPen(st::introErrorFg); @@ -147,19 +149,19 @@ void IntroSignup::paintEvent(QPaintEvent *e) { } void IntroSignup::resizeEvent(QResizeEvent *e) { - _phLeft = (width() - next.width()) / 2; + _phLeft = (width() - _next->width()) / 2; _phTop = st::introTextTop + st::introTextSize.height() + st::introCountry.top; if (e->oldSize().width() != width()) { - next.move((width() - next.width()) / 2, st::introBtnTop); + _next->move((width() - _next->width()) / 2, st::introBtnTop); if (_invertOrder) { - last.move((width() - next.width()) / 2 + next.width() - last.width(), _phTop); - first.move((width() - next.width()) / 2 + next.width() - first.width(), last.y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop); + _last->move((width() - _next->width()) / 2 + _next->width() - _last->width(), _phTop); + _first->move((width() - _next->width()) / 2 + _next->width() - _first->width(), _last->y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop); } else { - first.move((width() - next.width()) / 2 + next.width() - first.width(), _phTop); - last.move((width() - next.width()) / 2 + next.width() - last.width(), first.y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop); + _first->move((width() - _next->width()) / 2 + _next->width() - _first->width(), _phTop); + _last->move((width() - _next->width()) / 2 + _next->width() - _last->width(), _first->y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop); } } - textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); + _textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); } void IntroSignup::showError(const QString &err) { @@ -204,42 +206,40 @@ void IntroSignup::step_photo(float64 ms, bool timer) { void IntroSignup::activate() { IntroStep::activate(); if (_invertOrder) { - last.setFocus(); + _last->setFocus(); } else { - first.setFocus(); + _first->setFocus(); } } void IntroSignup::cancelled() { - if (sentRequest) { - MTP::cancel(sentRequest); - sentRequest = 0; + if (_sentRequest) { + MTP::cancel(base::take(_sentRequest)); } } void IntroSignup::stopCheck() { - checkRequest.stop(); + _checkRequest->stop(); } void IntroSignup::onCheckRequest() { - int32 status = MTP::state(sentRequest); + int32 status = MTP::state(_sentRequest); if (status < 0) { int32 leftms = -status; if (leftms >= 1000) { - MTP::cancel(sentRequest); - sentRequest = 0; - if (!first.isEnabled()) { - first.setDisabled(false); - last.setDisabled(false); + MTP::cancel(base::take(_sentRequest)); + if (!_first->isEnabled()) { + _first->setDisabled(false); + _last->setDisabled(false); if (_invertOrder) { - first.setFocus(); + _first->setFocus(); } else { - last.setFocus(); + _last->setFocus(); } } } } - if (!sentRequest && status == MTP::RequestSent) { + if (!_sentRequest && status == MTP::RequestSent) { stopCheck(); } } @@ -252,8 +252,8 @@ void IntroSignup::onPhotoReady(const QImage &img) { void IntroSignup::nameSubmitDone(const MTPauth_Authorization &result) { stopCheck(); - first.setDisabled(false); - last.setDisabled(false); + _first->setDisabled(false); + _last->setDisabled(false); const auto &d(result.c_auth_authorization()); if (d.vuser.type() != mtpc_user || !d.vuser.c_user().is_self()) { // wtf? showError(lang(lng_server_error)); @@ -265,21 +265,21 @@ void IntroSignup::nameSubmitDone(const MTPauth_Authorization &result) { bool IntroSignup::nameSubmitFail(const RPCError &error) { if (MTP::isFloodError(error)) { stopCheck(); - first.setDisabled(false); - last.setDisabled(false); + _first->setDisabled(false); + _last->setDisabled(false); showError(lang(lng_flood_error)); if (_invertOrder) { - first.setFocus(); + _first->setFocus(); } else { - last.setFocus(); + _last->setFocus(); } return true; } if (MTP::isDefaultHandledError(error)) return false; stopCheck(); - first.setDisabled(false); - last.setDisabled(false); + _first->setDisabled(false); + _last->setDisabled(false); const QString &err = error.type(); if (err == qstr("PHONE_NUMBER_INVALID") || err == qstr("PHONE_CODE_EXPIRED") || err == qstr("PHONE_CODE_EMPTY") || err == qstr("PHONE_CODE_INVALID") || @@ -288,11 +288,11 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) { return true; } else if (err == "FIRSTNAME_INVALID") { showError(lang(lng_bad_name)); - first.setFocus(); + _first->setFocus(); return true; } else if (err == "LASTNAME_INVALID") { showError(lang(lng_bad_name)); - last.setFocus(); + _last->setFocus(); return true; } if (cDebug()) { // internal server error @@ -301,9 +301,9 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) { showError(lang(lng_server_error)); } if (_invertOrder) { - last.setFocus(); + _last->setFocus(); } else { - first.setFocus(); + _first->setFocus(); } return false; } @@ -314,33 +314,33 @@ void IntroSignup::onInputChange() { void IntroSignup::onSubmitName(bool force) { if (_invertOrder) { - if ((last.hasFocus() || last.text().trimmed().length()) && !first.text().trimmed().length()) { - first.setFocus(); + if ((_last->hasFocus() || _last->text().trimmed().length()) && !_first->text().trimmed().length()) { + _first->setFocus(); return; - } else if (!last.text().trimmed().length()) { - last.setFocus(); + } else if (!_last->text().trimmed().length()) { + _last->setFocus(); return; } } else { - if ((first.hasFocus() || first.text().trimmed().length()) && !last.text().trimmed().length()) { - last.setFocus(); + if ((_first->hasFocus() || _first->text().trimmed().length()) && !_last->text().trimmed().length()) { + _last->setFocus(); return; - } else if (!first.text().trimmed().length()) { - first.setFocus(); + } else if (!_first->text().trimmed().length()) { + _first->setFocus(); return; } } - if (!force && !first.isEnabled()) return; + if (!force && !_first->isEnabled()) return; - first.setDisabled(true); - last.setDisabled(true); + _first->setDisabled(true); + _last->setDisabled(true); setFocus(); showError(QString()); - firstName = first.text().trimmed(); - lastName = last.text().trimmed(); - sentRequest = MTP::send(MTPauth_SignUp(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(intro()->getCode()), MTP_string(firstName), MTP_string(lastName)), rpcDone(&IntroSignup::nameSubmitDone), rpcFail(&IntroSignup::nameSubmitFail)); + _firstName = _first->text().trimmed(); + _lastName = _last->text().trimmed(); + _sentRequest = MTP::send(MTPauth_SignUp(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(intro()->getCode()), MTP_string(_firstName), MTP_string(_lastName)), rpcDone(&IntroSignup::nameSubmitDone), rpcFail(&IntroSignup::nameSubmitFail)); } void IntroSignup::onSubmit() { diff --git a/Telegram/SourceFiles/intro/introsignup.h b/Telegram/SourceFiles/intro/introsignup.h index 2d9a00c21..3b7535ec8 100644 --- a/Telegram/SourceFiles/intro/introsignup.h +++ b/Telegram/SourceFiles/intro/introsignup.h @@ -20,16 +20,18 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -#include #include "ui/flatbutton.h" #include "ui/flatinput.h" #include "intro/introwidget.h" +namespace Ui { +class RoundButton; +} // namespace Ui + class IntroSignup final : public IntroStep { Q_OBJECT public: - IntroSignup(IntroWidget *parent); void paintEvent(QPaintEvent *e) override; @@ -48,14 +50,12 @@ public: bool nameSubmitFail(const RPCError &error); public slots: - void onSubmitName(bool force = false); void onInputChange(); void onCheckRequest(); void onPhotoReady(const QImage &img); private: - void showError(const QString &err); void stopCheck(); @@ -64,20 +64,22 @@ private: Animation _a_error; Animation _a_photo; - FlatButton next; + ChildWidget _next; - QRect textRect; + QRect _textRect; - bool _photoOver; + bool _photoOver = false; QImage _photoBig; QPixmap _photoSmall; int32 _phLeft, _phTop; - FlatInput first, last; - QString firstName, lastName; - mtpRequestId sentRequest; + ChildWidget _first; + ChildWidget _last; + QString _firstName, _lastName; + mtpRequestId _sentRequest = 0; - bool _invertOrder; + bool _invertOrder = false; + + ChildObject _checkRequest; - QTimer checkRequest; }; diff --git a/Telegram/SourceFiles/intro/introstart.cpp b/Telegram/SourceFiles/intro/introstart.cpp index 60a7117b6..6c24ab9e8 100644 --- a/Telegram/SourceFiles/intro/introstart.cpp +++ b/Telegram/SourceFiles/intro/introstart.cpp @@ -25,36 +25,38 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "application.h" #include "intro/introphone.h" #include "langloaderplain.h" +#include "ui/buttons/round_button.h" IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent) , _intro(this, lang(lng_intro), FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle) , _changeLang(this, QString()) , _next(this, lang(lng_start_msgs), st::introNextButton) { - _changeLang.hide(); + _changeLang->hide(); if (cLang() == languageDefault) { int32 l = Sandbox::LangSystem(); if (l != languageDefault) { LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[l].c_str() + qsl(".strings"), langLoaderRequest(lng_switch_to_this)); QString text = loader.found().value(lng_switch_to_this); if (!text.isEmpty()) { - _changeLang.setText(text); + _changeLang->setText(text); parent->langChangeTo(l); - _changeLang.show(); + _changeLang->show(); } } } else { - _changeLang.setText(langOriginal(lng_switch_to_this)); + _changeLang->setText(langOriginal(lng_switch_to_this)); parent->langChangeTo(languageDefault); - _changeLang.show(); + _changeLang->show(); } _headerWidth = st::introHeaderFont->width(qsl("Telegram Desktop")); setGeometry(parent->innerRect()); - connect(&_next, SIGNAL(clicked()), parent, SLOT(onStepSubmit())); + _next->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); + connect(_next, SIGNAL(clicked()), parent, SLOT(onStepSubmit())); - connect(&_changeLang, SIGNAL(clicked()), parent, SLOT(onChangeLang())); + connect(_changeLang, SIGNAL(clicked()), parent, SLOT(onChangeLang())); setMouseTracking(true); } @@ -66,7 +68,7 @@ void IntroStart::paintEvent(QPaintEvent *e) { if (!trivial) { p.setClipRect(e->rect()); } - int32 hy = _intro.y() - st::introHeaderFont->height - st::introHeaderSkip + st::introHeaderFont->ascent; + int32 hy = _intro->y() - st::introHeaderFont->height - st::introHeaderSkip + st::introHeaderFont->ascent; p.setFont(st::introHeaderFont); p.setPen(st::introHeaderFg); @@ -77,9 +79,9 @@ void IntroStart::paintEvent(QPaintEvent *e) { void IntroStart::resizeEvent(QResizeEvent *e) { if (e->oldSize().width() != width()) { - _next.move((width() - _next.width()) / 2, st::introBtnTop); - _intro.move((width() - _intro.width()) / 2, _next.y() - _intro.height() - st::introSkip); - _changeLang.move((width() - _changeLang.width()) / 2, _next.y() + _next.height() + _changeLang.height()); + _next->move((width() - _next->width()) / 2, st::introBtnTop); + _intro->move((width() - _intro->width()) / 2, _next->y() - _intro->height() - st::introSkip); + _changeLang->move((width() - _changeLang->width()) / 2, _next->y() + _next->height() + _changeLang->height()); } } diff --git a/Telegram/SourceFiles/intro/introstart.h b/Telegram/SourceFiles/intro/introstart.h index c9665476e..9ad5733b6 100644 --- a/Telegram/SourceFiles/intro/introstart.h +++ b/Telegram/SourceFiles/intro/introstart.h @@ -23,9 +23,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/introwidget.h" #include "ui/flatlabel.h" +namespace Ui { +class RoundButton; +} // namespace Ui + class IntroStart final : public IntroStep { public: - IntroStart(IntroWidget *parent); void paintEvent(QPaintEvent *e) override; @@ -34,11 +37,12 @@ public: void onSubmit() override; private: + ChildWidget _intro; - FlatLabel _intro; + ChildWidget _changeLang; - LinkButton _changeLang; + ChildWidget _next; + + int32 _headerWidth = 0; - FlatButton _next; - int32 _headerWidth; }; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 5a225e54a..58c63c048 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -338,7 +338,7 @@ void IntroWidget::resizeEvent(QResizeEvent *e) { } void IntroWidget::finish(const MTPUser &user, const QImage &photo) { - App::wnd()->setupMain(true, &user); + App::wnd()->setupMain(&user); if (!photo.isNull()) { App::app()->uploadProfilePhoto(photo, MTP::authedId()); } diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index c91a2b906..37a9c943c 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -255,9 +255,9 @@ void MainWindow::clearPasscode() { } } -void MainWindow::setupPasscode(bool anim) { - auto bg = grabInner(); - +void MainWindow::setupPasscode() { + auto animated = (_main || _intro); + auto bg = animated ? grabInner() : QPixmap(); if (_passcode) { _passcode->stop_show(); _passcode.destroyDelayed(); @@ -270,7 +270,7 @@ void MainWindow::setupPasscode(bool anim) { _settings.destroyDelayed(); } if (_intro) _intro->hide(); - if (anim) { + if (animated) { _passcode->animShow(bg); } else { setInnerFocus(); @@ -294,14 +294,14 @@ void MainWindow::checkAutoLock() { App::app()->checkLocalTime(); uint64 ms = getms(true), idle = psIdleTime(), should = Global::AutoLock() * 1000ULL; if (idle >= should || (_shouldLockAt > 0 && ms > _shouldLockAt + 3000ULL)) { - setupPasscode(true); + setupPasscode(); } else { _shouldLockAt = ms + (should - idle); _autoLockTimer.start(should - idle); } } -void MainWindow::setupIntro(bool anim) { +void MainWindow::setupIntro() { cSetContactsReceived(false); cSetDialogsReceived(false); if (_intro && !_intro->isHidden() && !_main) return; @@ -311,14 +311,17 @@ void MainWindow::setupIntro(bool anim) { } Ui::hideSettingsAndLayer(true); - QPixmap bg = anim ? grabInner() : QPixmap(); + auto animated = (_main || _passcode); + auto bg = animated ? grabInner() : QPixmap(); clearWidgets(); _intro.create(bodyWidget()); updateControlsGeometry(); - if (anim) { + if (animated) { _intro->animShow(bg); + } else { + setInnerFocus(); } fixOrder(); @@ -361,13 +364,15 @@ void MainWindow::sendServiceHistoryRequest() { _serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(1), MTP_int(0), MTP_int(0)), _main->rpcDone(&MainWidget::serviceHistoryDone), _main->rpcFail(&MainWidget::serviceHistoryFail)); } -void MainWindow::setupMain(bool anim, const MTPUser *self) { - QPixmap bg = anim ? grabInner() : QPixmap(); +void MainWindow::setupMain(const MTPUser *self) { + auto animated = (_intro || _passcode); + auto bg = animated ? grabInner() : QPixmap(); + clearWidgets(); _main.create(bodyWidget()); updateControlsGeometry(); - if (anim) { + if (animated) { _main->animShow(bg); } else { _main->activate(); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 39874f2c5..e9f5995e4 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -85,11 +85,11 @@ public: void inactivePress(bool inactive); bool inactivePress() const; - void setupPasscode(bool anim); + void setupPasscode(); void clearPasscode(); void checkAutoLockIn(int msec); - void setupIntro(bool anim); - void setupMain(bool anim, const MTPUser *user = 0); + void setupIntro(); + void setupMain(const MTPUser *user = 0); void serviceNotification(const QString &msg, const MTPMessageMedia &media = MTP_messageMediaEmpty(), bool force = false); void sendServiceHistoryRequest(); void showDelayedServiceMsgs(); diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index a31ed5301..3e63025cc 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "application.h" #include "ui/text/text.h" +#include "ui/buttons/round_button.h" #include "styles/style_boxes.h" PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) @@ -33,32 +34,32 @@ PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) , _passcode(this, st::passcodeInput) , _submit(this, lang(lng_passcode_submit), st::passcodeSubmit) , _logout(this, lang(lng_passcode_logout)) { - _passcode.setEchoMode(QLineEdit::Password); - connect(&_submit, SIGNAL(clicked()), this, SLOT(onSubmit())); + _passcode->setEchoMode(QLineEdit::Password); + connect(_passcode, SIGNAL(changed()), this, SLOT(onChanged())); + connect(_passcode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - connect(&_passcode, SIGNAL(changed()), this, SLOT(onChanged())); - connect(&_passcode, SIGNAL(submitted(bool)), this, SLOT(onSubmit())); - - connect(&_logout, SIGNAL(clicked()), App::wnd(), SLOT(onLogout())); + _submit->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); + _submit->setClickedCallback([this] { onSubmit(); }); + _logout->setClickedCallback([] { App::wnd()->onLogout(); }); show(); - _passcode.setFocus(); + _passcode->setFocus(); } void PasscodeWidget::onSubmit() { - if (_passcode.text().isEmpty()) { - _passcode.notaBene(); + if (_passcode->text().isEmpty()) { + _passcode->notaBene(); return; } if (!passcodeCanTry()) { _error = lang(lng_flood_error); - _passcode.notaBene(); + _passcode->notaBene(); update(); return; } if (App::main()) { - if (Local::checkPasscode(_passcode.text().toUtf8())) { + if (Local::checkPasscode(_passcode->text().toUtf8())) { cSetPasscodeBadTries(0); App::wnd()->clearPasscode(); } else { @@ -68,14 +69,14 @@ void PasscodeWidget::onSubmit() { return; } } else { - if (Local::readMap(_passcode.text().toUtf8()) != Local::ReadMapPassNeeded) { + if (Local::readMap(_passcode->text().toUtf8()) != Local::ReadMapPassNeeded) { cSetPasscodeBadTries(0); MTP::start(); if (MTP::authedId()) { - App::wnd()->setupMain(true); + App::wnd()->setupMain(); } else { - App::wnd()->setupIntro(true); + App::wnd()->setupIntro(); } App::app()->checkMapVersion(); @@ -90,8 +91,8 @@ void PasscodeWidget::onSubmit() { void PasscodeWidget::onError() { _error = lang(lng_passcode_wrong); - _passcode.selectAll(); - _passcode.notaBene(); + _passcode->selectAll(); + _passcode->notaBene(); update(); } @@ -151,15 +152,15 @@ void PasscodeWidget::stop_show() { } void PasscodeWidget::showAll() { - _passcode.show(); - _submit.show(); - _logout.show(); + _passcode->show(); + _submit->show(); + _logout->show(); } void PasscodeWidget::hideAll() { - _passcode.hide(); - _submit.hide(); - _logout.hide(); + _passcode->hide(); + _submit->hide(); + _logout->hide(); } void PasscodeWidget::paintEvent(QPaintEvent *e) { @@ -186,32 +187,22 @@ void PasscodeWidget::paintEvent(QPaintEvent *e) { p.setFont(st::passcodeHeaderFont); p.setPen(st::windowTextFg); - p.drawText(QRect(0, _passcode.y() - st::passcodeHeaderHeight, width(), st::passcodeHeaderHeight), lang(lng_passcode_enter), style::al_center); + p.drawText(QRect(0, _passcode->y() - st::passcodeHeaderHeight, width(), st::passcodeHeaderHeight), lang(lng_passcode_enter), style::al_center); if (!_error.isEmpty()) { p.setFont(st::boxTextFont); p.setPen(st::boxTextFgError); - p.drawText(QRect(0, _passcode.y() + _passcode.height(), width(), st::passcodeSubmitSkip), _error, style::al_center); + p.drawText(QRect(0, _passcode->y() + _passcode->height(), width(), st::passcodeSubmitSkip), _error, style::al_center); } } } void PasscodeWidget::resizeEvent(QResizeEvent *e) { - _passcode.move((width() - _passcode.width()) / 2, (height() / 3)); - _submit.move(_passcode.x(), _passcode.y() + _passcode.height() + st::passcodeSubmitSkip); - _logout.move(_passcode.x() + (_passcode.width() - _logout.width()) / 2, _submit.y() + _submit.height() + st::linkFont->ascent); -} - -void PasscodeWidget::mousePressEvent(QMouseEvent *e) { - -} - -void PasscodeWidget::keyPressEvent(QKeyEvent *e) { + _passcode->move((width() - _passcode->width()) / 2, (height() / 3)); + _submit->move(_passcode->x(), _passcode->y() + _passcode->height() + st::passcodeSubmitSkip); + _logout->move(_passcode->x() + (_passcode->width() - _logout->width()) / 2, _submit->y() + _submit->height() + st::linkFont->ascent); } void PasscodeWidget::setInnerFocus() { - _passcode.setFocus(); -} - -PasscodeWidget::~PasscodeWidget() { + _passcode->setFocus(); } diff --git a/Telegram/SourceFiles/passcodewidget.h b/Telegram/SourceFiles/passcodewidget.h index 3f7e6d36a..6b682ddcc 100644 --- a/Telegram/SourceFiles/passcodewidget.h +++ b/Telegram/SourceFiles/passcodewidget.h @@ -20,6 +20,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +namespace Ui { +class RoundButton; +} // namespace Ui + class PasscodeWidget : public TWidget { Q_OBJECT @@ -32,13 +36,9 @@ public: void step_show(float64 ms, bool timer); void stop_show(); - ~PasscodeWidget(); - protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; public slots: void onError(); @@ -54,9 +54,9 @@ private: anim::ivalue a_coordUnder, a_coordOver; anim::fvalue a_shadow; - FlatInput _passcode; - FlatButton _submit; - LinkButton _logout; + ChildWidget _passcode; + ChildWidget _submit; + ChildWidget _logout; QString _error; }; diff --git a/Telegram/SourceFiles/shortcuts.cpp b/Telegram/SourceFiles/shortcuts.cpp index 643f0634d..729f4d601 100644 --- a/Telegram/SourceFiles/shortcuts.cpp +++ b/Telegram/SourceFiles/shortcuts.cpp @@ -38,7 +38,7 @@ bool lock_telegram() { w->passcodeWidget()->onSubmit(); return true; } else if (Global::LocalPasscode()) { - w->setupPasscode(true); + w->setupPasscode(); return true; } } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 0af012287..432f67c49 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1394,29 +1394,32 @@ QString saveFileName(const QString &title, const QString &filter, const QString MsgId clientMsgId(); struct MessageCursor { - MessageCursor() : position(0), anchor(0), scroll(QFIXED_MAX) { - } + MessageCursor() = default; MessageCursor(int position, int anchor, int scroll) : position(position), anchor(anchor), scroll(scroll) { } - MessageCursor(const QTextEdit &edit) { + MessageCursor(const QTextEdit *edit) { fillFrom(edit); } - void fillFrom(const QTextEdit &edit) { - QTextCursor c = edit.textCursor(); + void fillFrom(const QTextEdit *edit) { + QTextCursor c = edit->textCursor(); position = c.position(); anchor = c.anchor(); - QScrollBar *s = edit.verticalScrollBar(); + QScrollBar *s = edit->verticalScrollBar(); scroll = (s && (s->value() != s->maximum())) ? s->value() : QFIXED_MAX; } - void applyTo(QTextEdit &edit) { - QTextCursor c = edit.textCursor(); - c.setPosition(anchor, QTextCursor::MoveAnchor); - c.setPosition(position, QTextCursor::KeepAnchor); - edit.setTextCursor(c); - QScrollBar *s = edit.verticalScrollBar(); - if (s) s->setValue(scroll); + void applyTo(QTextEdit *edit) { + auto cursor = edit->textCursor(); + cursor.setPosition(anchor, QTextCursor::MoveAnchor); + cursor.setPosition(position, QTextCursor::KeepAnchor); + edit->setTextCursor(cursor); + if (auto scrollbar = edit->verticalScrollBar()) { + scrollbar->setValue(scroll); + } } - int position, anchor, scroll; + int position = 0; + int anchor = 0; + int scroll = QFIXED_MAX; + }; inline bool operator==(const MessageCursor &a, const MessageCursor &b) { diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 02aef33b2..2e0b1c931 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -126,12 +126,12 @@ void MainWindow::savePosition(Qt::WindowState state) { if (state == Qt::WindowActive) state = windowHandle()->windowState(); if (state == Qt::WindowMinimized || !positionInited()) return; - TWindowPos pos(cWindowPos()), curPos = pos; + auto pos = cWindowPos(), curPos = pos; if (state == Qt::WindowMaximized) { curPos.maximized = 1; } else { - QRect r(geometry()); + auto r = geometry(); curPos.x = r.x(); curPos.y = r.y(); curPos.w = r.width(); diff --git a/Telegram/SourceFiles/window/window_theme.cpp b/Telegram/SourceFiles/window/window_theme.cpp index 67ced2a08..6a107cb8c 100644 --- a/Telegram/SourceFiles/window/window_theme.cpp +++ b/Telegram/SourceFiles/window/window_theme.cpp @@ -419,7 +419,9 @@ void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) { void ChatBackground::start() { if (_id == internal::kUninitializedBackground) { - setImage(kThemeBackground); + if (!Local::readBackground()) { + setImage(kThemeBackground); + } } } From d12177befd8dcae9b4f18aeddfe045270f8bdf1f Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 5 Nov 2016 11:36:24 +0300 Subject: [PATCH 017/100] Update Telegram button added to DialogsWidget/Intro. Three dot menu. --- Telegram/Resources/basic.style | 3 +- Telegram/Resources/basic_types.style | 2 - Telegram/Resources/colors.palette | 48 ++- Telegram/Resources/icons/title_menu_dots.png | Bin 0 -> 130 bytes .../Resources/icons/title_menu_dots@2x.png | Bin 0 -> 212 bytes Telegram/Resources/langs/lang.strings | 1 + Telegram/Resources/sample.tdesktop-theme | 44 ++- Telegram/SourceFiles/boxes/abstractbox.cpp | 10 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 3 +- Telegram/SourceFiles/boxes/members_box.cpp | 3 +- Telegram/SourceFiles/boxes/sharebox.cpp | 3 +- Telegram/SourceFiles/dialogs/dialogs.style | 94 +++-- .../SourceFiles/dialogs/dialogs_layout.cpp | 56 +-- Telegram/SourceFiles/dialogs/dialogs_layout.h | 2 +- Telegram/SourceFiles/dialogswidget.cpp | 348 +++++++----------- Telegram/SourceFiles/dialogswidget.h | 28 +- .../history/field_autocomplete.cpp | 8 - .../SourceFiles/history/field_autocomplete.h | 2 - Telegram/SourceFiles/history/history.style | 35 +- Telegram/SourceFiles/history/history_item.cpp | 6 +- Telegram/SourceFiles/history/history_item.h | 2 +- .../history/history_media_types.cpp | 4 +- .../SourceFiles/history/history_message.cpp | 2 +- Telegram/SourceFiles/historywidget.cpp | 13 +- Telegram/SourceFiles/historywidget.h | 1 - Telegram/SourceFiles/intro/intro.style | 6 +- Telegram/SourceFiles/intro/introwidget.cpp | 41 ++- Telegram/SourceFiles/intro/introwidget.h | 19 +- Telegram/SourceFiles/mainwidget.cpp | 91 ++++- Telegram/SourceFiles/mainwidget.h | 2 + Telegram/SourceFiles/mediaview.cpp | 6 +- Telegram/SourceFiles/mediaview.h | 33 +- Telegram/SourceFiles/overviewwidget.cpp | 1 - .../settings/settings_advanced_widget.cpp | 7 +- .../settings/settings_advanced_widget.h | 2 + .../settings/settings_inner_widget.h | 3 - .../SourceFiles/settings/settings_widget.cpp | 10 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 10 - Telegram/SourceFiles/structs.h | 3 + Telegram/SourceFiles/ui/flatbutton.cpp | 12 +- Telegram/SourceFiles/ui/scrollarea.cpp | 1 + Telegram/SourceFiles/ui/scrollarea.h | 21 +- .../SourceFiles/ui/widgets/dropdown_menu.cpp | 5 +- .../SourceFiles/ui/widgets/dropdown_menu.h | 5 + .../SourceFiles/ui/widgets/inner_dropdown.cpp | 4 +- .../SourceFiles/ui/widgets/inner_dropdown.h | 8 +- Telegram/SourceFiles/window/main_window.h | 7 +- .../window/notifications_manager_default.cpp | 6 +- .../SourceFiles/window/top_bar_widget.cpp | 47 ++- Telegram/SourceFiles/window/top_bar_widget.h | 18 +- 50 files changed, 624 insertions(+), 462 deletions(-) create mode 100644 Telegram/Resources/icons/title_menu_dots.png create mode 100644 Telegram/Resources/icons/title_menu_dots@2x.png diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 36f48aa70..234e9f2dc 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -404,7 +404,6 @@ noContactsColor: #777777; topBarHeight: 54px; topBarDuration: 200; -topBarForward: icon {{ "title_back-flip_horizontal", #a3a3a3 }}; topBarBackward: icon {{ "title_back", #a3a3a3 }}; topBarForwardAlpha: 0.6; topBarBack: icon {{ "title_back", #259fd8 }}; @@ -504,6 +503,8 @@ msgReplyBarSize: size(2px, 36px); msgReplyBarSkip: 10px; msgInReplyBarColor: #2fa9e2; msgInReplyBarSelColor: #2fa9e2; +msgOutReplyBarColor: historyOutIconFg; +msgOutReplyBarSelColor: historyOutIconFgSelected; msgImgReplyBarColor: #ffffff; msgBotKbDuration: 200; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index 2ca21038a..3e343c13b 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -58,8 +58,6 @@ flatButton { overFont: font; duration: int; cursor: cursor; - - radius: pixels; } flatInput { diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 8ed27047e..24ac18d9f 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -22,9 +22,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org // basic windowBg: #ffffff; // white: fallback for background windowTextFg: #000000; // black: fallback for text color -windowSubTextFg: #8a8a8a; // gray: fallback for subtext color +windowSubTextFg: #999999; // gray: fallback for subtext color windowActiveFill: #40ace3; // bright blue: fallback for blue filled active areas -windowOverBg: #f3f3f3; // light blue: fallback for over background +windowOverBg: #f0f0f0; // light gray: fallback for over background windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over color windowActiveTextFg: #1485c2; // online blue: fallback for active color windowShadowFg: #000000; // black: fallback for shadow color @@ -47,8 +47,6 @@ lightButtonFgOver: lightButtonFg; menuIconFg: windowSubTextFg; -dialogsMenuIconFg: menuIconFg; - // custom title bar for Windows titleBg: windowOverBg; titleShadow: #00000003; @@ -106,6 +104,45 @@ notificationSampleNameFg: #939393 | windowSubTextFg; introHeaderFg: windowTextFg; introErrorFg: windowTextFg; +// dialogs +dialogsMenuIconFg: menuIconFg; + +dialogsBg: windowBg; +dialogsNameFg: #373737 | windowTextFg; +dialogsChatIconFg: dialogsNameFg; +dialogsDateFg: windowSubTextFg; +dialogsTextFg: windowSubTextFg; +dialogsTextFgService: #4981af; +dialogsDraftFg: #dd4b39; +dialogsVerifiedIconBg: #4abcf1; +dialogsVerifiedIconFg: #ffffff; +dialogsSendingIconFg: #c1c1c1; +dialogsSentIconFg: #5dc452; + +dialogsBgOver: windowOverBg; +dialogsNameFgOver: windowTextFg; +dialogsChatIconFgOver: dialogsNameFgOver; +dialogsDateFgOver: #8a8a8a | dialogsDateFg; +dialogsTextFgOver: dialogsTextFg; +dialogsTextFgServiceOver: dialogsTextFgService; +dialogsDraftFgOver: dialogsDraftFg; +dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; +dialogsVerifiedIconFgOver: dialogsVerifiedIconFg; +dialogsSendingIconFgOver: dialogsSendingIconFg; +dialogsSentIconFgOver: dialogsSentIconFg; + +dialogsBgActive: dialogsBgOver; +dialogsNameFgActive: dialogsNameFgOver; +dialogsChatIconFgActive: dialogsNameFgActive; +dialogsDateFgActive: dialogsDateFgOver; +dialogsTextFgActive: dialogsTextFgOver; +dialogsTextFgServiceActive: dialogsTextFgServiceOver; +dialogsDraftFgActive: dialogsDraftFgOver; +dialogsVerifiedIconBgActive: dialogsVerifiedIconBgOver; +dialogsVerifiedIconFgActive: dialogsVerifiedIconFgOver; +dialogsSendingIconFgActive: dialogsSendingIconFgOver; +dialogsSentIconFgActive: dialogsSentIconFgOver; + // history topBarBg: windowBg; @@ -132,6 +169,9 @@ historyCaptionInFg: historyTextInFg; historyCaptionOutFg: historyTextOutFg; historyFileNameInFg: historyTextInFg; historyFileNameOutFg: historyTextOutFg; +historyOutIconFg: dialogsSentIconFg; +historyOutIconFgSelected: #4da79f; +historyIconFgInverted: #ffffff; historySystemBg: #89a0b47f; historySystemBgSelected: #bbc8d4a2; diff --git a/Telegram/Resources/icons/title_menu_dots.png b/Telegram/Resources/icons/title_menu_dots.png new file mode 100644 index 0000000000000000000000000000000000000000..228a2a3bc94874a0a14043e8cd58a36c70508ebd GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^96&6>!3HEZNY`WoDNj!q#}JK)Z~G5&9xxDb@_qhW z;m==of$1BTvlMy0Unuu=p#=jAyY2D<=3Ua;5~WqlH5ihtgC`mNJ>PlN!Dy0_X6RO_ eWB(s564!UGXcGMu@Te4MD1)b~pUXO@geCx6A}XT* literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/title_menu_dots@2x.png b/Telegram/Resources/icons/title_menu_dots@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0020d07f11493d55e521effaba2075a58de38e3e GIT binary patch literal 212 zcmV;_04x8AP)MxB;LObS>nZhhI%8{WgOcIzg><_}JY)-oN#vs>30c zQoawdNdSbqhr0)WQE^<&@-LgN^0MG$VUCl9ZC80&aI!GR$-=g)Ji`;3%+|&vp`VTb O0000Q<@% literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index c7b5a66fc..6ca0bc71d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -212,6 +212,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_dlg_search_chat" = "Search in this chat"; "lng_dlg_search_channel" = "Search in this channel"; "lng_dlg_search_for_messages" = "Search for messages"; +"lng_update_telegram" = "Update Telegram"; "lng_settings_save" = "Save"; "lng_settings_upload" = "Set Profile Photo"; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index 2c23392a5..56cddb35c 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -23,9 +23,9 @@ windowBg: #ffffff; windowTextFg: #000000; -windowSubTextFg: #8a8a8a; +windowSubTextFg: #999999; windowActiveFill: #40ace3; -windowOverBg: #f3f3f3; +windowOverBg: #f0f0f0; windowSubTextFgOver: #7c99b2; windowActiveTextFg: #1485c2; windowShadowFg: #000000; @@ -42,7 +42,6 @@ lightButtonBgOver: #f2f7fa; // lightButtonBg; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; menuIconFg: windowSubTextFg; -dialogsMenuIconFg: menuIconFg; titleBg: windowOverBg; titleShadow: #00000003; titleButtonFg: #ababab; @@ -82,10 +81,44 @@ notificationSampleTextFg: #d7d7d7; // windowSubTextFg; notificationSampleNameFg: #939393; // windowSubTextFg; introHeaderFg: windowTextFg; introErrorFg: windowTextFg; +dialogsMenuIconFg: menuIconFg; +dialogsBg: windowBg; +dialogsNameFg: #373737; // windowTextFg; +dialogsChatIconFg: dialogsNameFg; +dialogsDateFg: windowSubTextFg; +dialogsTextFg: windowSubTextFg; +dialogsTextFgService: #4981af; +dialogsDraftFg: #dd4b39; +dialogsVerifiedIconBg: #4abcf1; +dialogsVerifiedIconFg: #ffffff; +dialogsSendingIconFg: #c1c1c1; +dialogsSentIconFg: #5dc452; +dialogsBgOver: windowOverBg; +dialogsNameFgOver: windowTextFg; +dialogsChatIconFgOver: dialogsNameFgOver; +dialogsDateFgOver: #8a8a8a; // dialogsDateFg; +dialogsTextFgOver: dialogsTextFg; +dialogsTextFgServiceOver: dialogsTextFgService; +dialogsDraftFgOver: dialogsDraftFg; +dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; +dialogsVerifiedIconFgOver: dialogsVerifiedIconFg; +dialogsSendingIconFgOver: dialogsSendingIconFg; +dialogsSentIconFgOver: dialogsSentIconFg; +dialogsBgActive: dialogsBgOver; +dialogsNameFgActive: dialogsNameFgOver; +dialogsChatIconFgActive: dialogsNameFgActive; +dialogsDateFgActive: dialogsDateFgOver; +dialogsTextFgActive: dialogsTextFgOver; +dialogsTextFgServiceActive: dialogsTextFgServiceOver; +dialogsDraftFgActive: dialogsDraftFgOver; +dialogsVerifiedIconBgActive: dialogsVerifiedIconBgOver; +dialogsVerifiedIconFgActive: dialogsVerifiedIconFgOver; +dialogsSendingIconFgActive: dialogsSendingIconFgOver; +dialogsSentIconFgActive: dialogsSentIconFgOver; topBarBg: windowBg; emojiPanBg: windowBg; emojiPanCategories: #f7f7f7; // windowBg; -emojiPanHeaderFg: #999999; // windowSubTextFg; +emojiPanHeaderFg: windowSubTextFg; emojiPanHeaderBg: #fffffff2; // emojiPanBg; historyComposeAreaBg: windowBg; historyComposeIconFg: #cccccc; @@ -103,6 +136,9 @@ historyCaptionInFg: historyTextInFg; historyCaptionOutFg: historyTextOutFg; historyFileNameInFg: historyTextInFg; historyFileNameOutFg: historyTextOutFg; +historyOutIconFg: dialogsSentIconFg; +historyOutIconFgSelected: #4da79f; +historyIconFgInverted: #ffffff; historySystemBg: #89a0b47f; historySystemBgSelected: #bbc8d4a2; historySystemFg: #ffffff; diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index bf2cd382b..4aeb35860 100644 --- a/Telegram/SourceFiles/boxes/abstractbox.cpp +++ b/Telegram/SourceFiles/boxes/abstractbox.cpp @@ -35,7 +35,7 @@ void BlueTitleShadow::paintEvent(QPaintEvent *e) { st::boxBlueTitleShadow.fill(p, QRect(r.left(), 0, r.width(), height())); } -AbstractBox::AbstractBox(int w) : LayerWidget() { +AbstractBox::AbstractBox(int w) : LayerWidget(App::wnd()->bodyWidget()) { setAttribute(Qt::WA_OpaquePaintEvent); resize(w, 0); } @@ -64,8 +64,9 @@ void AbstractBox::resizeEvent(QResizeEvent *e) { } void AbstractBox::parentResized() { - int32 newHeight = countHeight(); - setGeometry((App::wnd()->width() - width()) / 2, (App::wnd()->height() - newHeight) / 2, width(), newHeight); + auto newHeight = countHeight(); + auto parentSize = parentWidget()->size(); + setGeometry((parentSize.width() - width()) / 2, (parentSize.height() - newHeight) / 2, width(), newHeight); update(); } @@ -123,7 +124,7 @@ void AbstractBox::resizeMaxHeight(int32 newWidth, int32 maxHeight) { } int AbstractBox::countHeight() const { - return qMin(_maxHeight, App::wnd()->height() - 2 * st::boxVerticalMargin); + return qMin(_maxHeight, parentWidget()->height() - 2 * st::boxVerticalMargin); } void AbstractBox::onClose() { @@ -163,7 +164,6 @@ void ScrollableBox::init(TWidget *inner, int bottomSkip, int topSkip) { _bottomSkip = bottomSkip; _topSkip = topSkip; _scroll->setOwnedWidget(inner); - _scroll->setFocusPolicy(Qt::NoFocus); updateScrollGeometry(); } diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index a59f7608b..4504d8a2f 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -854,7 +854,8 @@ void ContactsBox::Inner::peerUpdated(PeerData *peer) { } void ContactsBox::Inner::loadProfilePhotos(int32 yFrom) { - int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5; + if (!parentWidget()) return; + int32 yTo = yFrom + parentWidget()->height() * 5; MTP::clearLoaderPriorities(); if (yTo < 0) return; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 9292d671b..00c654e3c 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -310,7 +310,8 @@ void MembersBox::Inner::selectSkipPage(int32 h, int32 dir) { } void MembersBox::Inner::loadProfilePhotos(int32 yFrom) { - int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5; + if (!parentWidget()) return; + int32 yTo = yFrom + parentWidget()->height() * 5; MTP::clearLoaderPriorities(); if (yTo < 0) return; diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index fd5482118..7ce9e5405 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -427,13 +427,14 @@ int ShareBox::Inner::chatIndex(PeerData *peer) const { } void ShareBox::Inner::loadProfilePhotos(int yFrom) { + if (!parentWidget()) return; if (yFrom < 0) { yFrom = 0; } if (auto part = (yFrom % _rowHeight)) { yFrom -= part; } - int yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5 * _columnCount; + int yTo = yFrom + parentWidget()->height() * 5 * _columnCount; if (!yTo) { return; } diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index cc84abd45..38f8fdd2e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -33,18 +33,9 @@ dialogsUnreadFont: font(12px bold); dialogsUnreadHeight: 19px; dialogsUnreadPadding: 5px; -dialogsBg: windowBg; -dialogsBgOver: #f5f5f5; -dialogsBgActive: #6a91b1; dialogsTextFont: font(fsize); -dialogsTextFg: #888888; -dialogsTextFgService: #4981af; -dialogsTextFgActive: #ffffff; dialogsDateFont: font(13px); -dialogsDateFgActive: #ffffff; -dialogsDateFg: #a8a8a8; dialogsDateSkip: 5px; -dialogsNameFg: #000000; dialogsNameTop: 2px; dialogsRowHeight: 62px; @@ -70,17 +61,25 @@ dialogsTextStyle: textStyle(defaultTextStyle) { linkFgDown: dialogsTextFgService; linkFlagsOver: font(fsize); } -dialogsTextStyleDraft: textStyle(dialogsTextStyle) { - linkFg: #dd4b39; - linkFgDown: #dd4b39; +dialogsTextStyleOver: textStyle(dialogsTextStyle) { + linkFg: dialogsTextFgServiceOver; + linkFgDown: dialogsTextFgServiceOver; } dialogsTextStyleActive: textStyle(dialogsTextStyle) { - linkFg: dialogsTextFgActive; - linkFgDown: dialogsTextFgActive; + linkFg: dialogsTextFgServiceActive; + linkFgDown: dialogsTextFgServiceActive; +} +dialogsTextStyleDraft: textStyle(dialogsTextStyle) { + linkFg: dialogsDraftFg; + linkFgDown: dialogsDraftFg; +} +dialogsTextStyleDraftOver: textStyle(dialogsTextStyle) { + linkFg: dialogsDraftFgOver; + linkFgDown: dialogsDraftFgOver; } dialogsTextStyleDraftActive: textStyle(dialogsTextStyle) { - linkFg: #c6e1f7; - linkFgDown: #c6e1f7; + linkFg: dialogsDraftFgActive; + linkFgDown: dialogsDraftFgActive; } dialogsMenuToggle: IconButton { @@ -127,30 +126,61 @@ dialogsMenuSettings: icon {{ "menu_settings", menuIconFg }}; dialogsMenuHelp: icon {{ "menu_help", menuIconFg }}; dialogsChatTypeSkip: 22px; -dialogsChatIcon: icon {{ "dialogs_chat", #373737, point(1px, 4px) }}; -dialogsChatActiveIcon: icon {{ "dialogs_chat", #ffffff, point(1px, 4px) }}; -dialogsChannelIcon: icon {{ "dialogs_channel", #373737, point(3px, 4px) }}; -dialogsChannelActiveIcon: icon {{ "dialogs_channel", #ffffff, point(3px, 4px) }}; +dialogsChatIcon: icon {{ "dialogs_chat", dialogsChatIconFg, point(1px, 4px) }}; +dialogsChatIconOver: icon {{ "dialogs_chat", dialogsChatIconFgOver, point(1px, 4px) }}; +dialogsChatIconActive: icon {{ "dialogs_chat", dialogsChatIconFgActive, point(1px, 4px) }}; +dialogsChannelIcon: icon {{ "dialogs_channel", dialogsChatIconFg, point(3px, 4px) }}; +dialogsChannelIconOver: icon {{ "dialogs_channel", dialogsChatIconFgOver, point(3px, 4px) }}; +dialogsChannelIconActive: icon {{ "dialogs_channel", dialogsChatIconFgActive, point(3px, 4px) }}; -dialogsSentStateFg: #5dc452; dialogsSendStateSkip: 20px; -dialogsSendingIcon: icon {{ "dialogs_sending", #c1c1c1, point(8px, 4px) }}; -dialogsSendingActiveIcon: icon {{ "dialogs_sending", #ffffff99, point(8px, 4px) }}; -dialogsSentIcon: icon {{ "dialogs_sent", dialogsSentStateFg, point(10px, 4px) }}; -dialogsSentActiveIcon: icon {{ "dialogs_sent", #ffffff, point(10px, 4px) }}; -dialogsReceivedIcon: icon {{ "dialogs_received", dialogsSentStateFg, point(5px, 4px) }}; -dialogsReceivedActiveIcon: icon {{ "dialogs_received", #ffffff, point(5px, 4px) }}; +dialogsSendingIcon: icon {{ "dialogs_sending", dialogsSendingIconFg, point(8px, 4px) }}; +dialogsSendingIconOver: icon {{ "dialogs_sending", dialogsSendingIconFgOver, point(8px, 4px) }}; +dialogsSendingIconActive: icon {{ "dialogs_sending", dialogsSendingIconFgActive, point(8px, 4px) }}; +dialogsSentIcon: icon {{ "dialogs_sent", dialogsSentIconFg, point(10px, 4px) }}; +dialogsSentIconOver: icon {{ "dialogs_sent", dialogsSentIconFgOver, point(10px, 4px) }}; +dialogsSentIconActive: icon {{ "dialogs_sent", dialogsSentIconFgActive, point(10px, 4px) }}; +dialogsReceivedIcon: icon {{ "dialogs_received", dialogsSentIconFg, point(5px, 4px) }}; +dialogsReceivedIconOver: icon {{ "dialogs_received", dialogsSentIconFgOver, point(5px, 4px) }}; +dialogsReceivedIconActive: icon {{ "dialogs_received", dialogsSentIconFgActive, point(5px, 4px) }}; dialogsVerifiedIcon: icon { - { "dialogs_verified_star", #4abcf1, point(4px, 2px) }, - { "dialogs_verified_check", #ffffff, point(7px, 7px) }, + { "dialogs_verified_star", dialogsVerifiedIconBg, point(4px, 2px) }, + { "dialogs_verified_check", dialogsVerifiedIconFg, point(7px, 7px) }, }; -dialogsVerifiedActiveIcon: icon { - { "dialogs_verified_star", #ffffff, point(4px, 2px) }, - { "dialogs_verified_check", #6a91b1, point(7px, 7px) }, +dialogsVerifiedIconOver: icon { + { "dialogs_verified_star", dialogsVerifiedIconBgOver, point(4px, 2px) }, + { "dialogs_verified_check", dialogsVerifiedIconFgOver, point(7px, 7px) }, +}; +dialogsVerifiedIconActive: icon { + { "dialogs_verified_star", dialogsVerifiedIconBgActive, point(4px, 2px) }, + { "dialogs_verified_check", dialogsVerifiedIconFgActive, point(7px, 7px) }, }; historySendingIcon: icon {{ "dialogs_sending", #98d292, point(5px, 5px) }}; historySendingInvertedIcon: icon {{ "dialogs_sending", #ffffffc8, point(5px, 5px) }}; historyViewsSendingIcon: icon {{ "dialogs_sending", #a0adb5, point(3px, 0px) }}; historyViewsSendingInvertedIcon: icon {{ "dialogs_sending", #ffffffc8, point(3px, 0px) }}; + +dialogsUpdateButton: flatButton { + duration: 0; + cursor: cursor(pointer); + + color: activeButtonFg; + overColor: activeButtonFgOver; + downColor: activeButtonFgOver; + + bgColor: activeButtonBg; + overBgColor: activeButtonBgOver; + downBgColor: activeButtonBgOver; + + width: -34px; + height: 46px; + + textTop: 14px; + overTextTop: 14px; + downTextTop: 15px; + + font: semiboldFont; + overFont: semiboldFont; +} diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 6f3bbf1e4..bae24e096 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -35,9 +35,12 @@ namespace { // Show all dates that are in the last 20 hours in time format. constexpr int kRecentlyInSeconds = 20 * 3600; -void paintRowDate(Painter &p, const QDateTime &date, QRect &rectForName, bool active) { - QDateTime now(QDateTime::currentDateTime()), lastTime(date); - QDate nowDate(now.date()), lastDate(lastTime.date()); +void paintRowDate(Painter &p, const QDateTime &date, QRect &rectForName, bool active, bool selected) { + auto now = QDateTime::currentDateTime(); + auto lastTime = date; + auto nowDate = now.date(); + auto lastDate = lastTime.date(); + QString dt; bool wasSameDay = (lastDate == nowDate); bool wasRecently = qAbs(lastTime.secsTo(now)) < kRecentlyInSeconds; @@ -51,7 +54,7 @@ void paintRowDate(Painter &p, const QDateTime &date, QRect &rectForName, bool ac int32 dtWidth = st::dialogsDateFont->width(dt); rectForName.setWidth(rectForName.width() - dtWidth - st::dialogsDateSkip); p.setFont(st::dialogsDateFont); - p.setPen(active ? st::dialogsDateFgActive : st::dialogsDateFg); + p.setPen(active ? st::dialogsDateFgActive : (selected ? st::dialogsDateFgOver : st::dialogsDateFg)); p.drawText(rectForName.left() + rectForName.width() + st::dialogsDateSkip, rectForName.top() + st::msgNameFont->height - st::msgDateFont->descent, dt); } @@ -68,57 +71,56 @@ void paintRow(Painter &p, History *history, HistoryItem *item, Data::Draft *draf int32 namewidth = w - nameleft - st::dialogsPadding.x(); QRect rectForName(nameleft, st::dialogsPadding.y() + st::dialogsNameTop, namewidth, st::msgNameFont->height); - if (auto chatTypeIcon = ChatTypeIcon(history->peer, active)) { + if (auto chatTypeIcon = ChatTypeIcon(history->peer, active, selected)) { chatTypeIcon->paint(p, rectForName.topLeft(), w); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); } int texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; if (draft) { - paintRowDate(p, date, rectForName, active); + paintRowDate(p, date, rectForName, active, selected); p.setFont(st::dialogsTextFont); - p.setPen(active ? st::dialogsTextFgActive : st::dialogsTextFgService); if (history->typing.isEmpty() && history->sendActions.isEmpty()) { if (history->cloudDraftTextCache.isEmpty()) { auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft))); auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, textClean(draft->textWithTags.text)); history->cloudDraftTextCache.setText(st::dialogsTextFont, draftText, _textDlgOptions); } - textstyleSet(&(active ? st::dialogsTextStyleDraftActive : st::dialogsTextStyleDraft)); - p.setFont(st::dialogsTextFont); - p.setPen(active ? st::dialogsTextFgActive : st::dialogsTextFg); + p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); + textstyleSet(&(active ? st::dialogsTextStyleDraftActive : (selected ? st::dialogsTextStyleDraftOver : st::dialogsTextStyleDraft))); history->cloudDraftTextCache.drawElided(p, nameleft, texttop, namewidth, 1); textstyleRestore(); } else { + p.setPen(active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService)); history->typingText.drawElided(p, nameleft, texttop, namewidth); } } else if (!item) { p.setFont(st::dialogsTextFont); - p.setPen(active ? st::dialogsTextFgActive : st::dialogsTextFgService); + p.setPen(active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService)); if (history->typing.isEmpty() && history->sendActions.isEmpty()) { p.drawText(nameleft, texttop + st::msgNameFont->ascent, lang(lng_empty_history)); } else { history->typingText.drawElided(p, nameleft, texttop, namewidth); } } else if (!item->isEmpty()) { - paintRowDate(p, date, rectForName, active); + paintRowDate(p, date, rectForName, active, selected); paintItemCallback(nameleft, namewidth, item); } - auto sendStateIcon = ([draft, item, active]() -> const style::icon* { + auto sendStateIcon = ([draft, item, active, selected]() -> const style::icon* { if (draft) { if (draft->saveRequestId) { - return &(active ? st::dialogsSendingActiveIcon : st::dialogsSendingIcon); + return &(active ? st::dialogsSendingIconActive : (selected ? st::dialogsSendingIconOver : st::dialogsSendingIcon)); } } else if (item && !item->isEmpty() && item->needCheck()) { if (item->id > 0) { if (item->unread()) { - return &(active ? st::dialogsSentActiveIcon : st::dialogsSentIcon); + return &(active ? st::dialogsSentIconActive : (selected ? st::dialogsSentIconOver : st::dialogsSentIcon)); } - return &(active ? st::dialogsReceivedActiveIcon : st::dialogsReceivedIcon); + return &(active ? st::dialogsReceivedIconActive : (selected ? st::dialogsReceivedIconOver : st::dialogsReceivedIcon)); } - return &(active ? st::dialogsSendingActiveIcon : st::dialogsSendingIcon); + return &(active ? st::dialogsSendingIconActive : (selected ? st::dialogsSendingIconOver : st::dialogsSendingIcon)); } return nullptr; })(); @@ -128,12 +130,12 @@ void paintRow(Painter &p, History *history, HistoryItem *item, Data::Draft *draf } if (history->peer->isUser() && history->peer->isVerified()) { - auto icon = &(active ? st::dialogsVerifiedActiveIcon : st::dialogsVerifiedIcon); + auto icon = &(active ? st::dialogsVerifiedIconActive : (selected ? st::dialogsVerifiedIconOver : st::dialogsVerifiedIcon)); rectForName.setWidth(rectForName.width() - icon->width()); icon->paint(p, rectForName.topLeft() + QPoint(qMin(history->peer->dialogName().maxWidth(), rectForName.width()), 0), w); } - p.setPen(active ? st::dialogsTextFgActive : st::dialogsNameFg); + p.setPen(active ? st::dialogsNameFgActive : (selected ? st::dialogsNameFgOver : st::dialogsNameFg)); history->peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } @@ -167,11 +169,11 @@ QImage colorizeCircleHalf(UnreadBadgeSizeData *data, int size, int half, int xof } // namepsace -const style::icon *ChatTypeIcon(PeerData *peer, bool active) { +const style::icon *ChatTypeIcon(PeerData *peer, bool active, bool selected) { if (peer->isChat() || peer->isMegagroup()) { - return &(active ? st::dialogsChatActiveIcon : st::dialogsChatIcon); + return &(active ? st::dialogsChatIconActive : (selected ? st::dialogsChatIconOver : st::dialogsChatIcon)); } else if (peer->isChannel()) { - return &(active ? st::dialogsChannelActiveIcon : st::dialogsChannelIcon); + return &(active ? st::dialogsChannelIconActive : (selected ? st::dialogsChannelIconOver : st::dialogsChannelIcon)); } return nullptr; } @@ -263,7 +265,7 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele if (item && cloudDraft && unreadCount > 0) { cloudDraft = nullptr; // Draw item, if draft is older. } - paintRow(p, history, item, cloudDraft, displayDate(), w, active, selected, onlyBackground, [&p, w, active, history, unreadCount](int nameleft, int namewidth, HistoryItem *item) { + paintRow(p, history, item, cloudDraft, displayDate(), w, active, selected, onlyBackground, [&p, w, active, selected, history, unreadCount](int nameleft, int namewidth, HistoryItem *item) { int availableWidth = namewidth; int texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; if (unreadCount) { @@ -280,9 +282,9 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele availableWidth -= unreadWidth + st::dialogsUnreadPadding; } if (history->typing.isEmpty() && history->sendActions.isEmpty()) { - item->drawInDialog(p, QRect(nameleft, texttop, availableWidth, st::dialogsTextFont->height), active, history->textCachedFor, history->lastItemTextCache); + item->drawInDialog(p, QRect(nameleft, texttop, availableWidth, st::dialogsTextFont->height), active, selected, history->textCachedFor, history->lastItemTextCache); } else { - p.setPen(active ? st::dialogsTextFgActive : st::dialogsTextFgService); + p.setPen(active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService)); history->typingText.drawElided(p, nameleft, texttop, availableWidth); } }); @@ -291,9 +293,9 @@ void RowPainter::paint(Painter &p, const Row *row, int w, bool active, bool sele void RowPainter::paint(Painter &p, const FakeRow *row, int w, bool active, bool selected, bool onlyBackground) { auto item = row->item(); auto history = item->history(); - paintRow(p, history, item, nullptr, item->date, w, active, selected, onlyBackground, [&p, row, active](int nameleft, int namewidth, HistoryItem *item) { + paintRow(p, history, item, nullptr, item->date, w, active, selected, onlyBackground, [&p, row, active, selected](int nameleft, int namewidth, HistoryItem *item) { int lastWidth = namewidth, texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; - item->drawInDialog(p, QRect(nameleft, texttop, lastWidth, st::dialogsTextFont->height), active, row->_cacheFor, row->_cache); + item->drawInDialog(p, QRect(nameleft, texttop, lastWidth, st::dialogsTextFont->height), active, selected, row->_cacheFor, row->_cache); }); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h index 6e81e317b..d5765bc60 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.h +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -27,7 +27,7 @@ class FakeRow; namespace Layout { -const style::icon *ChatTypeIcon(PeerData *peer, bool active); +const style::icon *ChatTypeIcon(PeerData *peer, bool active, bool selected); class RowPainter { public: diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 0342e855f..48ae1070c 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -40,6 +40,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "localstorage.h" #include "apiwrap.h" #include "ui/widgets/dropdown_menu.h" +#include "autoupdater.h" DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(parent) , dialogs(std_::make_unique(Dialogs::SortMode::Date)) @@ -259,12 +260,12 @@ void DialogsInner::peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool a QRect rectForName(nameleft, st::dialogsPadding.y() + st::dialogsNameTop, namewidth, st::msgNameFont->height); // draw chat icon - if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(peer, active)) { + if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(peer, active, selected)) { chatTypeIcon->paint(p, rectForName.topLeft(), w); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); } if (peer->isVerified()) { - auto icon = &(active ? st::dialogsVerifiedActiveIcon : st::dialogsVerifiedIcon); + auto icon = &(active ? st::dialogsVerifiedIconActive : (selected ? st::dialogsVerifiedIconOver : st::dialogsVerifiedIcon)); rectForName.setWidth(rectForName.width() - icon->width()); icon->paint(p, rectForName.topLeft() + QPoint(qMin(peer->dialogName().maxWidth(), rectForName.width()), 0), w); } @@ -304,7 +305,7 @@ void DialogsInner::searchInPeerPaint(Painter &p, int32 w, bool onlyBackground) c int32 namewidth = w - nameleft - st::dialogsPadding.x() * 2 - st::dialogsCancelSearch.width; QRect rectForName(nameleft, st::dialogsPadding.y() + st::dialogsNameTop, namewidth, st::msgNameFont->height); - if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(_searchInPeer, false)) { + if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(_searchInPeer, false, false)) { chatTypeIcon->paint(p, rectForName.topLeft(), w); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); } @@ -616,12 +617,11 @@ void DialogsInner::clearSelection() { void DialogsInner::contextMenuEvent(QContextMenuEvent *e) { if (_menu) { _menu->deleteLater(); - _menu = 0; + _menu = nullptr; } if (_menuPeer) { updateSelectedRow(_menuPeer); - _menuPeer = 0; - disconnect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData*))); + _menuPeer = nullptr; } if (e->reason() == QContextMenuEvent::Mouse) { @@ -630,7 +630,7 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) { onUpdateSelected(true); } - History *history = 0; + History *history = nullptr; if (_state == DefaultState) { if (_sel) history = _sel->history(); } else if (_state == FilteredState || _state == SearchedState) { @@ -642,109 +642,19 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) { _menuPeer = history->peer; _menu = new Ui::PopupMenu(); - _menu->addAction(lang((_menuPeer->isChat() || _menuPeer->isMegagroup()) ? lng_context_view_group : (_menuPeer->isUser() ? lng_context_view_profile : lng_context_view_channel)), this, SLOT(onContextProfile()))->setEnabled(true); - _menu->addAction(lang(menuPeerMuted() ? lng_enable_notifications_from_tray : lng_disable_notifications_from_tray), this, SLOT(onContextToggleNotifications()))->setEnabled(true); - _menu->addAction(lang(lng_profile_search_messages), this, SLOT(onContextSearch()))->setEnabled(true); - if (_menuPeer->isUser()) { - _menu->addAction(lang(lng_profile_clear_history), this, SLOT(onContextClearHistory()))->setEnabled(true); - _menu->addAction(lang(lng_profile_delete_conversation), this, SLOT(onContextDeleteAndLeave()))->setEnabled(true); - if (_menuPeer->asUser()->access != UserNoAccess && _menuPeer != App::self()) { - _menu->addAction(lang(_menuPeer->asUser()->isBlocked() ? (_menuPeer->asUser()->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (_menuPeer->asUser()->botInfo ? lng_profile_block_bot : lng_profile_block_user)), this, SLOT(onContextToggleBlock()))->setEnabled(true); - connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData*))); - } - } else if (_menuPeer->isChat()) { - _menu->addAction(lang(lng_profile_clear_history), this, SLOT(onContextClearHistory()))->setEnabled(true); - _menu->addAction(lang(lng_profile_clear_and_exit), this, SLOT(onContextDeleteAndLeave()))->setEnabled(true); - } else if (_menuPeer->isChannel() && _menuPeer->asChannel()->amIn() && !_menuPeer->asChannel()->amCreator()) { - _menu->addAction(lang(_menuPeer->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel), this, SLOT(onContextDeleteAndLeave()))->setEnabled(true); - } - + App::main()->fillPeerMenu(_menuPeer, [this](const QString &text, base::lambda_unique callback) { + return _menu->addAction(text, std_::move(callback)); + }); connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroyed(QObject*))); _menu->popup(e->globalPos()); e->accept(); } -bool DialogsInner::menuPeerMuted() { - return _menuPeer && _menuPeer->notify != EmptyNotifySettings && _menuPeer->notify != UnknownNotifySettings && _menuPeer->notify->mute >= unixtime(); -} - -void DialogsInner::onContextProfile() { - if (!_menuPeer) return; - Ui::showPeerProfile(_menuPeer); -} - -void DialogsInner::onContextToggleNotifications() { - if (!_menuPeer) return; - App::main()->updateNotifySetting(_menuPeer, menuPeerMuted() ? NotifySettingSetNotify : NotifySettingSetMuted); -} - -void DialogsInner::onContextSearch() { - if (!_menuPeer) return; - App::main()->searchInPeer(_menuPeer); -} - -void DialogsInner::onContextClearHistory() { - if (!_menuPeer || _menuPeer->isChannel()) return; - - _menuActionPeer = _menuPeer; - ConfirmBox *box = new ConfirmBox(_menuPeer->isUser() ? lng_sure_delete_history(lt_contact, _menuPeer->name) : lng_sure_delete_group_history(lt_group, _menuPeer->name), lang(lng_box_delete), st::attentionBoxButton); - connect(box, SIGNAL(confirmed()), this, SLOT(onContextClearHistorySure())); - Ui::showLayer(box); -} - -void DialogsInner::onContextClearHistorySure() { - if (!_menuActionPeer || _menuActionPeer->isChannel()) return; - Ui::hideLayer(); - App::main()->clearHistory(_menuActionPeer); -} - -void DialogsInner::onContextDeleteAndLeave() { - if (!_menuPeer) return; - - _menuActionPeer = _menuPeer; - ConfirmBox *box = new ConfirmBox(_menuPeer->isUser() ? lng_sure_delete_history(lt_contact, _menuPeer->name) : (_menuPeer->isChat() ? lng_sure_delete_and_exit(lt_group, _menuPeer->name) : lang(_menuPeer->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel)), lang(_menuPeer->isUser() ? lng_box_delete : lng_box_leave), _menuPeer->isChannel() ? st::defaultBoxButton : st::attentionBoxButton); - connect(box, SIGNAL(confirmed()), this, SLOT(onContextDeleteAndLeaveSure())); - Ui::showLayer(box); -} - -void DialogsInner::onContextDeleteAndLeaveSure() { - if (!_menuActionPeer) return; - - Ui::hideLayer(); - Ui::showChatsList(); - if (_menuActionPeer->isUser()) { - App::main()->deleteConversation(_menuActionPeer); - } else if (_menuActionPeer->isChat()) { - MTP::send(MTPmessages_DeleteChatUser(_menuActionPeer->asChat()->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, _menuActionPeer), App::main()->rpcFail(&MainWidget::leaveChatFailed, _menuActionPeer)); - } else if (_menuActionPeer->isChannel()) { - if (_menuActionPeer->migrateFrom()) { - App::main()->deleteConversation(_menuActionPeer->migrateFrom()); - } - MTP::send(MTPchannels_LeaveChannel(_menuActionPeer->asChannel()->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived)); - } -} - -void DialogsInner::onContextToggleBlock() { - if (!_menuPeer || !_menuPeer->isUser()) return; - if (_menuPeer->asUser()->isBlocked()) { - MTP::send(MTPcontacts_Unblock(_menuPeer->asUser()->inputUser), rpcDone(&DialogsInner::contextBlockDone, qMakePair(_menuPeer->asUser(), false))); - } else { - MTP::send(MTPcontacts_Block(_menuPeer->asUser()->inputUser), rpcDone(&DialogsInner::contextBlockDone, qMakePair(_menuPeer->asUser(), true))); - } -} - -void DialogsInner::contextBlockDone(QPair data, const MTPBool &result) { - data.first->setBlockStatus(data.second ? UserData::BlockStatus::Blocked : UserData::BlockStatus::NotBlocked); - emit App::main()->peerUpdated(data.first); -} - void DialogsInner::onMenuDestroyed(QObject *obj) { if (_menu == obj) { - _menu = 0; + _menu = nullptr; if (_menuPeer) { - updateSelectedRow(_menuPeer); - _menuPeer = 0; - disconnect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData*))); + updateSelectedRow(base::take(_menuPeer)); } lastMousePos = QCursor::pos(); if (rect().contains(mapFromGlobal(lastMousePos))) { @@ -935,18 +845,6 @@ void DialogsInner::clearSearchResults(bool clearPeople) { _lastSearchId = _lastSearchMigratedId = 0; } -void DialogsInner::updateNotifySettings(PeerData *peer) { - if (_menu && _menuPeer == peer && _menu->actions().size() > 1) { - _menu->actions().at(1)->setText(lang(menuPeerMuted() ? lng_enable_notifications_from_tray : lng_disable_notifications_from_tray)); - } -} - -void DialogsInner::peerUpdated(PeerData *peer) { - if (_menu && _menuPeer == peer && _menuPeer->isUser() && _menu->actions().size() > 5) { - _menu->actions().at(5)->setText(lang(_menuPeer->asUser()->isBlocked() ? (_menuPeer->asUser()->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (_menuPeer->asUser()->botInfo ? lng_profile_block_bot : lng_profile_block_user))); - } -} - PeerData *DialogsInner::updateFromParentDrag(QPoint globalPos) { lastMousePos = globalPos; _selByMouse = true; @@ -1774,23 +1672,29 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) , _cancelSearch(this, st::dialogsCancelSearch) , _lockUnlock(this, st::dialogsLock) , _scroll(this, st::dialogsScroll) -, _inner(&_scroll, parent) +, _inner(this, parent) , _a_show(animation(this, &DialogsWidget::step_show)) { - _scroll.setWidget(&_inner); - _scroll.setFocusPolicy(Qt::NoFocus); - connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int))); - connect(&_inner, SIGNAL(dialogMoved(int,int)), this, SLOT(onDialogMoved(int,int))); - connect(&_inner, SIGNAL(searchMessages()), this, SLOT(onNeedSearchMessages())); - connect(&_inner, SIGNAL(searchResultChosen()), this, SLOT(onCancel())); - connect(&_inner, SIGNAL(completeHashtag(QString)), this, SLOT(onCompleteHashtag(QString))); - connect(&_inner, SIGNAL(refreshHashtags()), this, SLOT(onFilterCursorMoved())); - connect(&_inner, SIGNAL(cancelSearchInPeer()), this, SLOT(onCancelSearchInPeer())); - connect(&_scroll, SIGNAL(geometryChanged()), &_inner, SLOT(onParentGeometryChanged())); - connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onListScroll())); + _scroll->setOwnedWidget(_inner); + connect(_inner, SIGNAL(mustScrollTo(int,int)), _scroll, SLOT(scrollToY(int,int))); + connect(_inner, SIGNAL(dialogMoved(int,int)), this, SLOT(onDialogMoved(int,int))); + connect(_inner, SIGNAL(searchMessages()), this, SLOT(onNeedSearchMessages())); + connect(_inner, SIGNAL(searchResultChosen()), this, SLOT(onCancel())); + connect(_inner, SIGNAL(completeHashtag(QString)), this, SLOT(onCompleteHashtag(QString))); + connect(_inner, SIGNAL(refreshHashtags()), this, SLOT(onFilterCursorMoved())); + connect(_inner, SIGNAL(cancelSearchInPeer()), this, SLOT(onCancelSearchInPeer())); + connect(_scroll, SIGNAL(geometryChanged()), _inner, SLOT(onParentGeometryChanged())); + connect(_scroll, SIGNAL(scrolled()), this, SLOT(onListScroll())); connect(_filter, SIGNAL(cancelled()), this, SLOT(onCancel())); connect(_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate())); connect(_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int))); +#ifndef TDESKTOP_DISABLE_AUTOUPDATE + Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onCheckUpdateStatus())); + Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onCheckUpdateStatus())); + Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onCheckUpdateStatus())); + onCheckUpdateStatus(); +#endif // !TDESKTOP_DISABLE_AUTOUPDATE + _cancelSearch->setClickedCallback([this] { onCancelSearch(); }); _lockUnlock->setVisible(Global::LocalPasscode()); subscribe(Global::RefLocalPasscodeChanged(), [this] { updateLockUnlockVisibility(); }); @@ -1814,34 +1718,52 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) _cancelSearch->hide(); } +#ifndef TDESKTOP_DISABLE_AUTOUPDATE +void DialogsWidget::onCheckUpdateStatus() { + if (Sandbox::updatingState() == Application::UpdatingReady) { + if (_updateTelegram) return; + _updateTelegram.create(this, lang(lng_update_telegram).toUpper(), st::dialogsUpdateButton); + _updateTelegram->show(); + _updateTelegram->setClickedCallback([] { + checkReadyUpdate(); + App::restart(); + }); + } else { + if (!_updateTelegram) return; + _updateTelegram.destroy(); + } + updateControlsGeometry(); +} +#endif // TDESKTOP_DISABLE_AUTOUPDATE + void DialogsWidget::activate() { _filter->setFocus(); - _inner.activate(); + _inner->activate(); } void DialogsWidget::createDialog(History *history) { - bool creating = !history->inChatList(Dialogs::Mode::All); - _inner.createDialog(history); + auto creating = !history->inChatList(Dialogs::Mode::All); + _inner->createDialog(history); if (creating && history->peer->migrateFrom()) { - if (History *h = App::historyLoaded(history->peer->migrateFrom()->id)) { - if (h->inChatList(Dialogs::Mode::All)) { - removeDialog(h); + if (auto migrated = App::historyLoaded(history->peer->migrateFrom()->id)) { + if (migrated->inChatList(Dialogs::Mode::All)) { + removeDialog(migrated); } } } } void DialogsWidget::dlgUpdated(Dialogs::Mode list, Dialogs::Row *row) { - _inner.dlgUpdated(list, row); + _inner->dlgUpdated(list, row); } void DialogsWidget::dlgUpdated(History *row, MsgId msgId) { - _inner.dlgUpdated(row, msgId); + _inner->dlgUpdated(row, msgId); } void DialogsWidget::dialogsToUp() { if (_filter->getLastText().trimmed().isEmpty()) { - _scroll.scrollToY(0); + _scroll->scrollToY(0); } } @@ -1854,7 +1776,7 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window: _a_show.stop(); - _scroll.hide(); + _scroll->hide(); _mainMenuToggle->hide(); _filter->hide(); _cancelSearch->hide(); @@ -1885,7 +1807,7 @@ void DialogsWidget::step_show(float64 ms, bool timer) { _cacheUnder = _cacheOver = QPixmap(); - _scroll.show(); + _scroll->show(); _mainMenuToggle->show(); _filter->show(); updateLockUnlockVisibility(); @@ -1909,21 +1831,17 @@ void DialogsWidget::onCancel() { } } -void DialogsWidget::updateNotifySettings(PeerData *peer) { - _inner.updateNotifySettings(peer); -} - void DialogsWidget::notify_userIsContactChanged(UserData *user, bool fromThisApp) { if (fromThisApp) { _filter->setText(QString()); _filter->updatePlaceholder(); onFilterUpdate(); } - _inner.notify_userIsContactChanged(user, fromThisApp); + _inner->notify_userIsContactChanged(user, fromThisApp); } void DialogsWidget::notify_historyMuteUpdated(History *history) { - _inner.notify_historyMuteUpdated(history); + _inner->notify_historyMuteUpdated(history); } void DialogsWidget::unreadCountsReceived(const QVector &dialogs) { @@ -1961,7 +1879,7 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque } if (dialogsList) { unreadCountsReceived(*dialogsList); - _inner.dialogsReceived(*dialogsList); + _inner->dialogsReceived(*dialogsList); onListScroll(); TimeId lastDate = 0; @@ -2080,7 +1998,7 @@ void DialogsWidget::onNeedSearchMessages() { } void DialogsWidget::onChooseByDrag() { - _inner.choosePeer(); + _inner->choosePeer(); } void DialogsWidget::showMainMenu() { @@ -2112,7 +2030,7 @@ void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) { onCancelSearch(); _searchInPeer = inPeer->migrateTo() ? inPeer->migrateTo() : inPeer; _searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0; - _inner.searchInPeer(_searchInPeer); + _inner->searchInPeer(_searchInPeer); } _filter->setText(query); _filter->updatePlaceholder(); @@ -2120,16 +2038,16 @@ void DialogsWidget::searchMessages(const QString &query, PeerData *inPeer) { _searchTimer.stop(); onSearchMessages(); - _inner.saveRecentHashtags(query); + _inner->saveRecentHashtags(query); } } void DialogsWidget::onSearchMore() { if (!_searchRequest) { if (!_searchFull) { - int32 offsetDate = _inner.lastSearchDate(); - PeerData *offsetPeer = _inner.lastSearchPeer(); - MsgId offsetId = _inner.lastSearchId(); + int32 offsetDate = _inner->lastSearchDate(); + PeerData *offsetPeer = _inner->lastSearchPeer(); + MsgId offsetId = _inner->lastSearchId(); if (_searchInPeer) { MTPmessages_Search::Flags flags = 0; _searchRequest = MTP::send(MTPmessages_Search(MTP_flags(flags), _searchInPeer->input, MTP_string(_searchQuery), MTP_inputMessagesFilterEmpty(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(offsetId), MTP_int(SearchPerPage)), rpcDone(&DialogsWidget::searchReceived, offsetId ? DialogsSearchPeerFromOffset : DialogsSearchPeerFromStart), rpcFail(&DialogsWidget::searchFailed, offsetId ? DialogsSearchPeerFromOffset : DialogsSearchPeerFromStart)); @@ -2140,7 +2058,7 @@ void DialogsWidget::onSearchMore() { _searchQueries.insert(_searchRequest, _searchQuery); } } else if (_searchInMigrated && !_searchFullMigrated) { - MsgId offsetMigratedId = _inner.lastSearchMigratedId(); + MsgId offsetMigratedId = _inner->lastSearchMigratedId(); MTPmessages_Search::Flags flags = 0; _searchRequest = MTP::send(MTPmessages_Search(MTP_flags(flags), _searchInMigrated->input, MTP_string(_searchQuery), MTP_inputMessagesFilterEmpty(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(offsetMigratedId), MTP_int(SearchPerPage)), rpcDone(&DialogsWidget::searchReceived, offsetMigratedId ? DialogsSearchMigratedFromOffset : DialogsSearchMigratedFromStart), rpcFail(&DialogsWidget::searchFailed, offsetMigratedId ? DialogsSearchMigratedFromOffset : DialogsSearchMigratedFromStart)); } @@ -2150,7 +2068,7 @@ void DialogsWidget::onSearchMore() { void DialogsWidget::loadDialogs() { if (_dialogsRequest) return; if (_dialogsFull) { - _inner.addAllSavedPeers(); + _inner->addAllSavedPeers(); cSetDialogsReceived(true); return; } @@ -2164,7 +2082,7 @@ void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &contacts) { if (contacts.type() == mtpc_contacts_contacts) { const auto &d(contacts.c_contacts_contacts()); App::feedUsers(d.vusers); - _inner.contactsReceived(d.vcontacts.c_vector().v); + _inner->contactsReceived(d.vcontacts.c_vector().v); } if (App::main()) App::main()->contactsReceived(); } @@ -2176,7 +2094,7 @@ bool DialogsWidget::contactsFailed(const RPCError &error) { } void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessages_Messages &result, mtpRequestId req) { - if (_inner.state() == DialogsInner::FilteredState || _inner.state() == DialogsInner::SearchedState) { + if (_inner->state() == DialogsInner::FilteredState || _inner->state() == DialogsInner::SearchedState) { if (type == DialogsSearchFromStart || type == DialogsSearchPeerFromStart) { SearchQueries::iterator i = _searchQueries.find(req); if (i != _searchQueries.cend()) { @@ -2193,7 +2111,7 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa App::feedUsers(d.vusers); App::feedChats(d.vchats); auto &msgs(d.vmessages.c_vector().v); - if (!_inner.searchReceived(msgs, type, msgs.size())) { + if (!_inner->searchReceived(msgs, type, msgs.size())) { if (type == DialogsSearchMigratedFromStart || type == DialogsSearchMigratedFromOffset) { _searchFullMigrated = true; } else { @@ -2207,7 +2125,7 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa App::feedUsers(d.vusers); App::feedChats(d.vchats); auto &msgs(d.vmessages.c_vector().v); - if (!_inner.searchReceived(msgs, type, d.vcount.v)) { + if (!_inner->searchReceived(msgs, type, d.vcount.v)) { if (type == DialogsSearchMigratedFromStart || type == DialogsSearchMigratedFromOffset) { _searchFullMigrated = true; } else { @@ -2226,7 +2144,7 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa App::feedUsers(d.vusers); App::feedChats(d.vchats); auto &msgs(d.vmessages.c_vector().v); - if (!_inner.searchReceived(msgs, type, d.vcount.v)) { + if (!_inner->searchReceived(msgs, type, d.vcount.v)) { if (type == DialogsSearchMigratedFromStart || type == DialogsSearchMigratedFromOffset) { _searchFullMigrated = true; } else { @@ -2244,7 +2162,7 @@ void DialogsWidget::searchReceived(DialogsSearchRequestType type, const MTPmessa void DialogsWidget::peopleReceived(const MTPcontacts_Found &result, mtpRequestId req) { auto q = _peopleQuery; - if (_inner.state() == DialogsInner::FilteredState || _inner.state() == DialogsInner::SearchedState) { + if (_inner->state() == DialogsInner::FilteredState || _inner->state() == DialogsInner::SearchedState) { auto i = _peopleQueries.find(req); if (i != _peopleQueries.cend()) { q = i.value(); @@ -2258,7 +2176,7 @@ void DialogsWidget::peopleReceived(const MTPcontacts_Found &result, mtpRequestId auto &d = result.c_contacts_found(); App::feedUsers(d.vusers); App::feedChats(d.vchats); - _inner.peopleReceived(q, d.vresults.c_vector().v); + _inner->peopleReceived(q, d.vresults.c_vector().v); } break; } @@ -2302,7 +2220,7 @@ void DialogsWidget::dragEnterEvent(QDragEnterEvent *e) { if (_dragForward) { e->setDropAction(Qt::CopyAction); e->accept(); - updateDragInScroll(_scroll.geometry().contains(e->pos())); + updateDragInScroll(_scroll->geometry().contains(e->pos())); } else if (App::main() && App::main()->getDragState(e->mimeData()) != DragStateNone) { e->setDropAction(Qt::CopyAction); e->accept(); @@ -2311,13 +2229,13 @@ void DialogsWidget::dragEnterEvent(QDragEnterEvent *e) { } void DialogsWidget::dragMoveEvent(QDragMoveEvent *e) { - if (_scroll.geometry().contains(e->pos())) { + if (_scroll->geometry().contains(e->pos())) { if (_dragForward) { updateDragInScroll(true); } else { _chooseByDragTimer.start(ChoosePeerByDragTimeout); } - PeerData *p = _inner.updateFromParentDrag(mapToGlobal(e->pos())); + PeerData *p = _inner->updateFromParentDrag(mapToGlobal(e->pos())); if (p) { e->setDropAction(Qt::CopyAction); } else { @@ -2325,7 +2243,7 @@ void DialogsWidget::dragMoveEvent(QDragMoveEvent *e) { } } else { if (_dragForward) updateDragInScroll(false); - _inner.leaveEvent(0); + _inner->leaveEvent(0); e->setDropAction(Qt::IgnoreAction); } e->accept(); @@ -2337,7 +2255,7 @@ void DialogsWidget::dragLeaveEvent(QDragLeaveEvent *e) { } else { _chooseByDragTimer.stop(); } - _inner.leaveEvent(0); + _inner->leaveEvent(0); e->accept(); } @@ -2354,8 +2272,8 @@ void DialogsWidget::updateDragInScroll(bool inScroll) { void DialogsWidget::dropEvent(QDropEvent *e) { _chooseByDragTimer.stop(); - if (_scroll.geometry().contains(e->pos())) { - PeerData *p = _inner.updateFromParentDrag(mapToGlobal(e->pos())); + if (_scroll->geometry().contains(e->pos())) { + PeerData *p = _inner->updateFromParentDrag(mapToGlobal(e->pos())); if (p) { e->acceptProposedAction(); App::main()->onFilesOrForwardDrop(p->id, e->mimeData()); @@ -2366,12 +2284,12 @@ void DialogsWidget::dropEvent(QDropEvent *e) { void DialogsWidget::onListScroll() { // if (!App::self()) return; - _inner.loadPeerPhotos(_scroll.scrollTop()); - if (_inner.state() == DialogsInner::SearchedState || (_inner.state() == DialogsInner::FilteredState && _searchInMigrated && _searchFull && !_searchFullMigrated)) { - if (_scroll.scrollTop() > (_inner.searchList().size() + _inner.filteredList().size() + _inner.peopleList().size()) * st::dialogsRowHeight - PreloadHeightsCount * _scroll.height()) { + _inner->loadPeerPhotos(_scroll->scrollTop()); + if (_inner->state() == DialogsInner::SearchedState || (_inner->state() == DialogsInner::FilteredState && _searchInMigrated && _searchFull && !_searchFullMigrated)) { + if (_scroll->scrollTop() > (_inner->searchList().size() + _inner->filteredList().size() + _inner->peopleList().size()) * st::dialogsRowHeight - PreloadHeightsCount * _scroll->height()) { onSearchMore(); } - } else if (_scroll.scrollTop() > _inner.dialogsList()->size() * st::dialogsRowHeight - PreloadHeightsCount * _scroll.height()) { + } else if (_scroll->scrollTop() > _inner->dialogsList()->size() * st::dialogsRowHeight - PreloadHeightsCount * _scroll->height()) { loadDialogs(); } } @@ -2380,7 +2298,7 @@ void DialogsWidget::onFilterUpdate(bool force) { if (_a_show.animating() && !force) return; QString filterText = _filter->getLastText(); - _inner.onFilterUpdate(filterText, force); + _inner->onFilterUpdate(filterText, force); if (filterText.isEmpty()) { _searchCache.clear(); _searchQueries.clear(); @@ -2400,7 +2318,7 @@ void DialogsWidget::searchInPeer(PeerData *peer) { onCancelSearch(); _searchInPeer = peer ? (peer->migrateTo() ? peer->migrateTo() : peer) : 0; _searchInMigrated = _searchInPeer ? _searchInPeer->migrateFrom() : 0; - _inner.searchInPeer(_searchInPeer); + _inner->searchInPeer(_searchInPeer); onFilterUpdate(true); } @@ -2417,7 +2335,7 @@ void DialogsWidget::onFilterCursorMoved(int from, int to) { } if (!t.at(start).isLetterOrNumber() && t.at(start) != '_') break; } - _inner.onHashtagFilterUpdate(r); + _inner->onHashtagFilterUpdate(r); } void DialogsWidget::onCompleteHashtag(QString tag) { @@ -2449,19 +2367,7 @@ void DialogsWidget::onCompleteHashtag(QString tag) { void DialogsWidget::resizeEvent(QResizeEvent *e) { updateControlsGeometry(); - - _scroll.move(0, _filter->height() + 2 * st::dialogsFilterPadding.y()); - updateMainMenuGeometry(); - - int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0; - int32 newScrollY = _scroll.scrollTop() + addToY; - _scroll.resize(width(), height() - _filter->y() - _filter->height() - st::dialogsFilterPadding.y() - st::dialogsPadding.y()); - if (addToY) { - _scroll.scrollToY(newScrollY); - } else { - onListScroll(); - } } void DialogsWidget::updateLockUnlockVisibility() { @@ -2482,6 +2388,24 @@ void DialogsWidget::updateControlsGeometry() { _mainMenuToggle->moveToLeft(st::dialogsFilterPadding.x(), _filter->y()); _lockUnlock->moveToRight(st::dialogsFilterPadding.x(), _filter->y()); _cancelSearch->moveToLeft(filterLeft + filterWidth - _cancelSearch->width(), _filter->y()); + + auto addToScroll = App::main() ? App::main()->contentScrollAddToY() : 0; + auto newScrollTop = _scroll->scrollTop() + addToScroll; + auto scrollTop = _filter->height() + 2 * st::dialogsFilterPadding.y(); + auto scrollHeight = height() - scrollTop; + if (_updateTelegram) { + auto updateHeight = _updateTelegram->height(); + _updateTelegram->setGeometry(0, height() - updateHeight, width(), updateHeight); + scrollHeight -= updateHeight; + } else { + scrollHeight -= st::dialogsPadding.y(); + } + _scroll->setGeometry(0, scrollTop, width(), scrollHeight); + if (addToScroll) { + _scroll->scrollToY(newScrollTop); + } else { + onListScroll(); + } } void DialogsWidget::updateMainMenuGeometry() { @@ -2494,26 +2418,26 @@ void DialogsWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { e->ignore(); } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { - if (!_inner.choosePeer()) { - if (_inner.state() == DialogsInner::DefaultState || _inner.state() == DialogsInner::SearchedState || (_inner.state() == DialogsInner::FilteredState && _inner.hasFilteredResults())) { - _inner.selectSkip(1); - _inner.choosePeer(); + if (!_inner->choosePeer()) { + if (_inner->state() == DialogsInner::DefaultState || _inner->state() == DialogsInner::SearchedState || (_inner->state() == DialogsInner::FilteredState && _inner->hasFilteredResults())) { + _inner->selectSkip(1); + _inner->choosePeer(); } else { onSearchMessages(); } } } else if (e->key() == Qt::Key_Down) { - _inner.setMouseSel(false); - _inner.selectSkip(1); + _inner->setMouseSel(false); + _inner->selectSkip(1); } else if (e->key() == Qt::Key_Up) { - _inner.setMouseSel(false); - _inner.selectSkip(-1); + _inner->setMouseSel(false); + _inner->selectSkip(-1); } else if (e->key() == Qt::Key_PageDown) { - _inner.setMouseSel(false); - _inner.selectSkipPage(_scroll.height(), 1); + _inner->setMouseSel(false); + _inner->selectSkipPage(_scroll->height(), 1); } else if (e->key() == Qt::Key_PageUp) { - _inner.setMouseSel(false); - _inner.selectSkipPage(_scroll.height(), -1); + _inner->setMouseSel(false); + _inner->selectSkipPage(_scroll->height(), -1); } else { e->ignore(); } @@ -2540,43 +2464,43 @@ void DialogsWidget::paintEvent(QPaintEvent *e) { st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), _cacheOver.height() / retina)); return; } - QRect above(0, 0, width(), _scroll.y()); + QRect above(0, 0, width(), _scroll->y()); if (above.intersects(r)) { p.fillRect(above.intersected(r), st::dialogsBg); } - QRect below(0, _scroll.y() + qMin(_scroll.height(), _inner.height()), width(), height()); + QRect below(0, _scroll->y() + qMin(_scroll->height(), _inner->height()), width(), height()); if (below.intersects(r)) { p.fillRect(below.intersected(r), st::dialogsBg); } } void DialogsWidget::destroyData() { - _inner.destroyData(); + _inner->destroyData(); } void DialogsWidget::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const { - return _inner.peerBefore(inPeer, inMsg, outPeer, outMsg); + return _inner->peerBefore(inPeer, inMsg, outPeer, outMsg); } void DialogsWidget::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const { - return _inner.peerAfter(inPeer, inMsg, outPeer, outMsg); + return _inner->peerAfter(inPeer, inMsg, outPeer, outMsg); } void DialogsWidget::scrollToPeer(const PeerId &peer, MsgId msgId) { - _inner.scrollToPeer(peer, msgId); + _inner->scrollToPeer(peer, msgId); } void DialogsWidget::removeDialog(History *history) { - _inner.removeDialog(history); + _inner->removeDialog(history); onFilterUpdate(); } Dialogs::IndexedList *DialogsWidget::contactsList() { - return _inner.contactsList(); + return _inner->contactsList(); } Dialogs::IndexedList *DialogsWidget::dialogsList() { - return _inner.dialogsList(); + return _inner->dialogsList(); } bool DialogsWidget::onCancelSearch() { @@ -2590,10 +2514,10 @@ bool DialogsWidget::onCancelSearch() { Ui::showPeerHistory(_searchInPeer, ShowAtUnreadMsgId); } _searchInPeer = _searchInMigrated = 0; - _inner.searchInPeer(0); + _inner->searchInPeer(0); clearing = true; } - _inner.clearFilter(); + _inner->clearFilter(); _filter->clear(); _filter->updatePlaceholder(); onFilterUpdate(); @@ -2610,9 +2534,9 @@ void DialogsWidget::onCancelSearchInPeer() { Ui::showPeerHistory(_searchInPeer, ShowAtUnreadMsgId); } _searchInPeer = _searchInMigrated = 0; - _inner.searchInPeer(0); + _inner->searchInPeer(0); } - _inner.clearFilter(); + _inner->clearFilter(); _filter->clear(); _filter->updatePlaceholder(); onFilterUpdate(); @@ -2622,8 +2546,8 @@ void DialogsWidget::onCancelSearchInPeer() { } void DialogsWidget::onDialogMoved(int movedFrom, int movedTo) { - int32 st = _scroll.scrollTop(); + int32 st = _scroll->scrollTop(); if (st > movedTo && st < movedFrom) { - _scroll.scrollToY(st + st::dialogsRowHeight); + _scroll->scrollToY(st + st::dialogsRowHeight); } } diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 7778e6ab0..f2f735213 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -134,19 +134,8 @@ public slots: void onPeerPhotoChanged(PeerData *peer); void onDialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow); - void onContextProfile(); - void onContextToggleNotifications(); - void onContextSearch(); - void onContextClearHistory(); - void onContextClearHistorySure(); - void onContextDeleteAndLeave(); - void onContextDeleteAndLeaveSure(); - void onContextToggleBlock(); - void onMenuDestroyed(QObject*); - void peerUpdated(PeerData *peer); - signals: void mustScrollTo(int scrollToTop, int scrollToBottom); void dialogMoved(int movedFrom, int movedTo); @@ -173,8 +162,6 @@ private: void clearSelection(); void clearSearchResults(bool clearPeople = true); void updateSelectedRow(PeerData *peer = 0); - bool menuPeerMuted(); - void contextBlockDone(QPair data, const MTPBool &result); Dialogs::IndexedList *shownDialogs() const { return (Global::DialogsMode() == Dialogs::Mode::Important) ? importantDialogs.get() : dialogs.get(); @@ -227,7 +214,6 @@ private: PeerData *_searchInPeer = nullptr; PeerData *_searchInMigrated = nullptr; PeerData *_menuPeer = nullptr; - PeerData *_menuActionPeer = nullptr; Ui::PopupMenu *_menu = nullptr; @@ -283,10 +269,8 @@ public: void searchMessages(const QString &query, PeerData *inPeer = 0); void onSearchMore(); - void updateNotifySettings(PeerData *peer); - void rpcClear() override { - _inner.rpcClear(); + _inner->rpcClear(); RPCSender::rpcClear(); } @@ -313,6 +297,11 @@ public slots: void onChooseByDrag(); +#ifndef TDESKTOP_DISABLE_AUTOUPDATE +private slots: + void onCheckUpdateStatus(); +#endif // TDESKTOP_DISABLE_AUTOUPDATE + private: void showMainMenu(); void updateLockUnlockVisibility(); @@ -341,8 +330,9 @@ private: ChildWidget _filter; ChildWidget _cancelSearch; ChildWidget _lockUnlock; - ScrollArea _scroll; - DialogsInner _inner; + ChildWidget _scroll; + ChildWidget _inner; + ChildWidget _updateTelegram = { nullptr }; Animation _a_show; QPixmap _cacheUnder, _cacheOver; diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index b46b759a8..b4db5ee2f 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -33,19 +33,12 @@ FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent) , _inner(this, &_mrows, &_hrows, &_brows, &_srows) , a_opacity(0) , _a_appearance(animation(this, &FieldAutocomplete::step_appearance)) { - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); - connect(_inner, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod)), this, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(mustScrollTo(int, int)), _scroll, SLOT(scrollToY(int, int))); - setFocusPolicy(Qt::NoFocus); - _scroll->setFocusPolicy(Qt::NoFocus); - _scroll->viewport()->setFocusPolicy(Qt::NoFocus); - _inner->setGeometry(rect()); _scroll->setGeometry(rect()); @@ -402,7 +395,6 @@ void FieldAutocomplete::hideFast() { _a_appearance.stop(); } a_opacity = anim::fvalue(0, 0); - _hideTimer.stop(); hideFinish(); } diff --git a/Telegram/SourceFiles/history/field_autocomplete.h b/Telegram/SourceFiles/history/field_autocomplete.h index 751035ffc..8324ada7f 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.h +++ b/Telegram/SourceFiles/history/field_autocomplete.h @@ -130,8 +130,6 @@ private: anim::fvalue a_opacity; Animation _a_appearance; - QTimer _hideTimer; - friend class internal::FieldAutocompleteInner; }; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 6988e7ff9..b17f4e898 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -80,25 +80,22 @@ historyFileOutPlaySelected: icon {{ "history_file_play", msgOutBgSelected }}; historyFileInPlay: icon {{ "history_file_play", msgInBg }}; historyFileInPlaySelected: icon {{ "history_file_play", msgInBgSelected }}; -historyOutFg: dialogsSentStateFg; -historyOutSelectedFg: #4da79f; -historyInvertedFg: #ffffff; historySendStateSpace: 24px; historySendStatePosition: point(-17px, -19px); -historySentIcon: icon {{ "history_sent", historyOutFg, point(2px, 4px) }}; -historySentSelectedIcon: icon {{ "history_sent", historyOutSelectedFg, point(2px, 4px) }}; -historySentInvertedIcon: icon {{ "history_sent", historyInvertedFg, point(2px, 4px) }}; -historyReceivedIcon: icon {{ "history_received", historyOutFg, point(2px, 4px) }}; -historyReceivedSelectedIcon: icon {{ "history_received", historyOutSelectedFg, point(2px, 4px) }}; -historyReceivedInvertedIcon: icon {{ "history_received", historyInvertedFg, point(2px, 4px) }}; +historySentIcon: icon {{ "history_sent", historyOutIconFg, point(2px, 4px) }}; +historySentSelectedIcon: icon {{ "history_sent", historyOutIconFgSelected, point(2px, 4px) }}; +historySentInvertedIcon: icon {{ "history_sent", historyIconFgInverted, point(2px, 4px) }}; +historyReceivedIcon: icon {{ "history_received", historyOutIconFg, point(2px, 4px) }}; +historyReceivedSelectedIcon: icon {{ "history_received", historyOutIconFgSelected, point(2px, 4px) }}; +historyReceivedInvertedIcon: icon {{ "history_received", historyIconFgInverted, point(2px, 4px) }}; historyViewsSpace: 11px; historyViewsWidth: 20px; historyViewsTop: -15px; historyViewsInIcon: icon {{ "history_views", #a0acb6 }}; historyViewsInSelectedIcon: icon {{ "history_views", #6a9bc5 }}; -historyViewsOutIcon: icon {{ "history_views", historyOutFg }}; -historyViewsOutSelectedIcon: icon {{ "history_views", historyOutSelectedFg }}; +historyViewsOutIcon: icon {{ "history_views", historyOutIconFg }}; +historyViewsOutSelectedIcon: icon {{ "history_views", historyOutIconFgSelected }}; historyViewsInvertedIcon: icon {{ "history_views", #ffffffe6 }}; historyPeer1NameFg: #c03d33; // red @@ -279,16 +276,20 @@ historyInlineBotCancel: MaskButton(historyReplyCancel) { height: 46px; } -topBarSearch: MaskButton { +topBarSearch: IconButton { width: 44px; height: topBarHeight; - bg: topBarBg; - icon: icon {{ "title_search-invert", topBarBg }}; - - iconBg: #c7c7c7; - iconBgOver: #a3a3a3; + icon: icon {{ "title_search", #a8a8a8 }}; + iconOver: icon {{ "title_search", #a3a3a3 }}; iconPosition: point(13px, 18px); iconPositionDown: point(13px, 18px); } +topBarMenuToggle: IconButton(topBarSearch) { + icon: icon {{ "title_menu_dots", #a8a8a8 }}; + iconOver: icon {{ "title_menu_dots", #a3a3a3 }}; + + iconPosition: point(18px, 17px); + iconPositionDown: point(18px, 17px); +} diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index e3adc56e4..6eccdee61 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -859,15 +859,15 @@ QString HistoryItem::inDialogsText() const { return plainText; } -void HistoryItem::drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const { +void HistoryItem::drawInDialog(Painter &p, const QRect &r, bool active, bool selected, const HistoryItem *&cacheFor, Text &cache) const { if (cacheFor != this) { cacheFor = this; cache.setText(st::dialogsTextFont, inDialogsText(), _textDlgOptions); } if (r.width()) { - textstyleSet(&(act ? st::dialogsTextStyleActive : st::dialogsTextStyle)); + textstyleSet(&(active ? st::dialogsTextStyleActive : (selected ? st::dialogsTextStyleOver : st::dialogsTextStyle))); p.setFont(st::dialogsTextFont); - p.setPen(act ? st::dialogsTextFgActive : st::dialogsTextFg); + p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); cache.drawElided(p, r.left(), r.top(), r.width(), r.height() / st::dialogsTextFont->height); textstyleRestore(); } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 0c61b2a87..4ffdaf629 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -659,7 +659,7 @@ public: virtual void setViewsCount(int32 count) { } virtual void setId(MsgId newId); - void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const; + void drawInDialog(Painter &p, const QRect &r, bool active, bool selected, const HistoryItem *&cacheFor, Text &cache) const; bool emptyText() const { return _text.isEmpty(); diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 0f8be9e4e..759cb3f60 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -2649,7 +2649,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u bool out = _parent->out(), isPost = _parent->isPost(), outbg = out && !isPost; bool selected = (selection == FullSelection); - auto &barfg = selected ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor); + auto &barfg = selected ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor); auto &semibold = selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg); auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); @@ -3048,7 +3048,7 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint bool out = _parent->out(), isPost = _parent->isPost(), outbg = out && !isPost; bool selected = (selection == FullSelection); - auto &barfg = selected ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor); + auto &barfg = selected ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor); auto &semibold = selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg); auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 99e12e1d4..0d32d39bc 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -245,7 +245,7 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in const style::color *bar = &st::msgImgReplyBarColor; if (flags & PaintInBubble) { - bar = &((flags & PaintSelected) ? (outbg ? st::historyOutSelectedFg : st::msgInReplyBarSelColor) : (outbg ? st::historyOutFg : st::msgInReplyBarColor)); + bar = &((flags & PaintSelected) ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor)); } QRect rbar(rtlrect(x + st::msgReplyBarPos.x(), y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), w + 2 * x)); p.fillRect(rbar, *bar); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 6768ad035..d4c86564f 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3019,8 +3019,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _fileLoader(this, FileLoaderQueueStopTimeout) , _a_show(animation(this, &HistoryWidget::step_show)) , _topShadow(this, st::shadowColor) { - _scroll.setFocusPolicy(Qt::NoFocus); - setAcceptDrops(true); subscribe(FileDownload::ImageLoaded(), [this] { update(); }); @@ -3086,8 +3084,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _scroll.hide(); - _kbScroll.setFocusPolicy(Qt::NoFocus); - _kbScroll.viewport()->setFocusPolicy(Qt::NoFocus); _kbScroll.setWidget(&_keyboard); _kbScroll.hide(); @@ -6279,9 +6275,6 @@ void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { if (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) { p.setOpacity(st::topBarForwardAlpha + (1 - st::topBarForwardAlpha) * over); st::topBarBackward.paint(p, (st::topBarArrowPadding.left() - st::topBarBackward.width()) / 2, (st::topBarHeight - st::topBarBackward.height()) / 2, width()); - } else { - p.setOpacity(st::topBarForwardAlpha + (1 - st::topBarForwardAlpha) * over); - st::topBarForward.paint(p, width() - (st::topBarArrowPadding.left() + st::topBarForward.width()) / 2, (st::topBarHeight - st::topBarForward.height()) / 2, width()); } } @@ -6318,7 +6311,7 @@ void HistoryWidget::onMembersDropdownShow() { _membersDropdown->setMaxHeight(countMembersDropdownHeightMax()); _membersDropdown->moveToLeft(0, 0); - connect(_membersDropdown, SIGNAL(beforeHidden()), this, SLOT(onMembersDropdownHidden())); + _membersDropdown->setHiddenCallback([this] { _membersDropdown.destroyDelayed(); }); } _membersDropdown->otherEnter(); } @@ -6327,10 +6320,6 @@ void HistoryWidget::onModerateKeyActivate(int index, bool *outHandled) { *outHandled = _keyboard.isHidden() ? false : _keyboard.moderateKeyActivate(index); } -void HistoryWidget::onMembersDropdownHidden() { - _membersDropdown.destroyDelayed(); -} - void HistoryWidget::topBarClick() { if (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) { App::main()->showBackFromStack(); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 2ba364b92..45536b036 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -855,7 +855,6 @@ private slots: void onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::ChooseMethod method); void onMentionInsert(UserData *user); void onInlineBotCancel(); - void onMembersDropdownHidden(); void onMembersDropdownShow(); void onModerateKeyActivate(int index, bool *outHandled); diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 1a7d836f0..3117d1930 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -44,12 +44,12 @@ introCountry: countryInput { } introBtnTop: 288px; -introSkip: 45px; +introSkip: 25px; introFinishSkip: 15px; introPhotoSize: 98px; introHeaderFont: font(24px); introHeaderSkip: 14px; -introIconSkip: 54px; +introIconSkip: 50px; introFont: font(16px); introLink: linkButton(btnDefLink) { font: introFont; @@ -61,7 +61,7 @@ introLabel: flatLabel(labelDefFlat) { } introStepSize: size(400px, 200px); -introSize: size(400px, 400px); +introSize: size(400px, 460px); introSlideShift: 500px; // intro hiding animation introSlideDuration: 200; introSlideDelta: 0; // between hide start and show start diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 58c63c048..d29791b21 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -35,20 +35,25 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/buttons/icon_button.h" #include "ui/effects/widget_fade_wrap.h" #include "styles/style_intro.h" +#include "autoupdater.h" IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent) , _a_stage(animation(this, &IntroWidget::step_stage)) , _a_show(animation(this, &IntroWidget::step_show)) -, _back(this, new Ui::IconButton(this, st::introBackButton), base::lambda_unique(), st::introSlideDuration) { +, _back(this, new Ui::IconButton(this, st::introBackButton), base::lambda_unique(), st::introSlideDuration) +, _settings(this, lang(lng_menu_settings), st::defaultBoxButton) { _back->entity()->setClickedCallback([this] { onBack(); }); _back->hideFast(); + _settings->setClickedCallback([] { App::wnd()->showSettings(); }); + _countryForReg = psCurrentCountry(); MTP::send(MTPhelp_GetNearestDc(), rpcDone(&IntroWidget::gotNearestDC)); _stepHistory.push_back(new IntroStart(this)); _back->raise(); + _settings->raise(); show(); setFocus(); @@ -58,10 +63,32 @@ IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent) _back->moveToLeft(st::introBackPosition.x(), st::introBackPosition.y()); #ifndef TDESKTOP_DISABLE_AUTOUPDATE + Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onCheckUpdateStatus())); + Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onCheckUpdateStatus())); + Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onCheckUpdateStatus())); Sandbox::startUpdateCheck(); + onCheckUpdateStatus(); #endif // !TDESKTOP_DISABLE_AUTOUPDATE } +#ifndef TDESKTOP_DISABLE_AUTOUPDATE +void IntroWidget::onCheckUpdateStatus() { + if (Sandbox::updatingState() == Application::UpdatingReady) { + if (_update) return; + _update.create(this, lang(lng_menu_update).toUpper(), st::defaultBoxButton); + _update->show(); + _update->setClickedCallback([] { + checkReadyUpdate(); + App::restart(); + }); + } else { + if (!_update) return; + _update.destroy(); + } + updateControlsGeometry(); +} +#endif // TDESKTOP_DISABLE_AUTOUPDATE + void IntroWidget::langChangeTo(int32 langId) { _langChangeTo = langId; } @@ -136,6 +163,10 @@ void IntroWidget::historyMove(MoveType type) { void IntroWidget::pushStep(IntroStep *step, MoveType type) { _stepHistory.push_back(step); _back->raise(); + _settings->raise(); + if (_update) { + _update->raise(); + } _stepHistory.back()->hide(); historyMove(type); @@ -335,6 +366,14 @@ void IntroWidget::resizeEvent(QResizeEvent *e) { for (auto step : _stepHistory) { step->setGeometry(r); } + updateControlsGeometry(); +} + +void IntroWidget::updateControlsGeometry() { + _settings->moveToLeft(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _settings->height()); + if (_update) { + _update->moveToLeft(st::boxButtonPadding.right() + _settings->width() + st::boxButtonPadding.left(), _settings->y()); + } } void IntroWidget::finish(const MTPUser &user, const QImage &photo) { diff --git a/Telegram/SourceFiles/intro/introwidget.h b/Telegram/SourceFiles/intro/introwidget.h index 9837539f4..24b6bd452 100644 --- a/Telegram/SourceFiles/intro/introwidget.h +++ b/Telegram/SourceFiles/intro/introwidget.h @@ -35,10 +35,6 @@ class IntroWidget : public TWidget, public RPCSender { public: IntroWidget(QWidget *window); - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; - void animShow(const QPixmap &bgAnimCache, bool back = false); void step_show(float64 ms, bool timer); void stop_show(); @@ -87,7 +83,12 @@ public: pushStep(step, MoveReplace); } - ~IntroWidget() override; + ~IntroWidget(); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void keyPressEvent(QKeyEvent *e) override; public slots: void onStepSubmit(); @@ -97,7 +98,13 @@ public slots: signals: void countryChanged(); +#ifndef TDESKTOP_DISABLE_AUTOUPDATE +private slots: + void onCheckUpdateStatus(); +#endif // TDESKTOP_DISABLE_AUTOUPDATE + private: + void updateControlsGeometry(); QPixmap grabStep(int skip = 0); int _langChangeTo = 0; @@ -145,6 +152,8 @@ private: QString _firstname, _lastname; ChildWidget> _back; + ChildWidget _settings; + ChildWidget _update = { nullptr }; float64 _backFrom = 0.; float64 _backTo = 0.; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index db1c94f6f..a2bf332d2 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -83,7 +83,7 @@ MainWidget::MainWidget(QWidget *parent) : TWidget(parent) updateScrollColors(); connect(_dialogs, SIGNAL(cancelled()), this, SLOT(dialogsCancelled())); - connect(this, SIGNAL(dialogsUpdated()), this, SLOT(onListScroll())); + connect(this, SIGNAL(dialogsUpdated()), _dialogs, SLOT(onListScroll())); connect(_history, SIGNAL(cancelled()), _dialogs, SLOT(activate())); connect(this, SIGNAL(peerPhotoChanged(PeerData*)), this, SIGNAL(dialogsUpdated())); connect(&noUpdatesTimer, SIGNAL(timeout()), this, SLOT(mtpPing())); @@ -1993,6 +1993,94 @@ void MainWidget::scheduleViewIncrement(HistoryItem *item) { j.value().insert(item->id, true); } +void MainWidget::fillPeerMenu(PeerData *peer, base::lambda_unique handler)> callback) { + callback(lang((peer->isChat() || peer->isMegagroup()) ? lng_context_view_group : (peer->isUser() ? lng_context_view_profile : lng_context_view_channel)), [peer] { + Ui::showPeerProfile(peer); + }); + auto muteSubscription = MakeShared(); + auto muteAction = callback(lang(peer->isMuted() ? lng_enable_notifications_from_tray : lng_disable_notifications_from_tray), [peer, muteSubscription] { + App::main()->updateNotifySetting(peer, peer->isMuted() ? NotifySettingSetNotify : NotifySettingSetMuted); + }); + auto muteChangedHandler = Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::NotificationsEnabled, [muteAction, peer](const Notify::PeerUpdate &update) { + if (update.peer != peer) return; + muteAction->setText(lang(peer->isMuted() ? lng_enable_notifications_from_tray : lng_disable_notifications_from_tray)); + }); + *muteSubscription = Notify::PeerUpdated().add_subscription(std_::move(muteChangedHandler)); + + callback(lang(lng_profile_search_messages), [peer] { + App::main()->searchInPeer(peer); + }); + + auto clearHistoryHandler = [peer] { + auto box = new ConfirmBox(peer->isUser() ? lng_sure_delete_history(lt_contact, peer->name) : lng_sure_delete_group_history(lt_group, peer->name), lang(lng_box_delete), st::attentionBoxButton); + box->setConfirmedCallback([peer] { + if (!App::main()) return; + + Ui::hideLayer(); + App::main()->clearHistory(peer); + }); + Ui::showLayer(box); + }; + auto deleteAndLeaveHandler = [peer] { + auto warningText = peer->isUser() ? lng_sure_delete_history(lt_contact, peer->name) : + peer->isChat() ? lng_sure_delete_and_exit(lt_group, peer->name) : + lang(peer->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel); + auto confirmText = lang(peer->isUser() ? lng_box_delete : lng_box_leave); + auto &confirmStyle = peer->isChannel() ? st::defaultBoxButton : st::attentionBoxButton; + auto box = new ConfirmBox(warningText, confirmText, confirmStyle); + box->setConfirmedCallback([peer] { + if (!App::main()) return; + + Ui::hideLayer(); + Ui::showChatsList(); + if (peer->isUser()) { + App::main()->deleteConversation(peer); + } else if (peer->isChat()) { + MTP::send(MTPmessages_DeleteChatUser(peer->asChat()->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, peer), App::main()->rpcFail(&MainWidget::leaveChatFailed, peer)); + } else if (peer->isChannel()) { + if (peer->migrateFrom()) { + App::main()->deleteConversation(peer->migrateFrom()); + } + MTP::send(MTPchannels_LeaveChannel(peer->asChannel()->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived)); + } + }); + Ui::showLayer(box); + }; + if (auto user = peer->asUser()) { + callback(lang(lng_profile_clear_history), std_::move(clearHistoryHandler)); + callback(lang(lng_profile_delete_conversation), std_::move(deleteAndLeaveHandler)); + if (user->access != UserNoAccess && user != App::self()) { + auto blockSubscription = MakeShared(); + auto blockAction = callback(lang(user->isBlocked() ? (user->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (user->botInfo ? lng_profile_block_bot : lng_profile_block_user)), [user, blockSubscription] { + auto willBeBlocked = !user->isBlocked(); + auto handler = ::rpcDone([user, willBeBlocked](const MTPBool &result) { + user->setBlockStatus(willBeBlocked ? UserData::BlockStatus::Blocked : UserData::BlockStatus::NotBlocked); + emit App::main()->peerUpdated(user); + }); + if (willBeBlocked) { + MTP::send(MTPcontacts_Block(user->inputUser), std_::move(handler)); + } else { + MTP::send(MTPcontacts_Unblock(user->inputUser), std_::move(handler)); + } + }); + auto blockChangedHandler = Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::UserIsBlocked, [blockAction, peer](const Notify::PeerUpdate &update) { + if (update.peer != peer) return; + blockAction->setText(lang(peer->asUser()->isBlocked() ? (peer->asUser()->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (peer->asUser()->botInfo ? lng_profile_block_bot : lng_profile_block_user))); + }); + *blockSubscription = Notify::PeerUpdated().add_subscription(std_::move(blockChangedHandler)); + + if (user->blockStatus() == UserData::BlockStatus::Unknown) { + App::api()->requestFullPeer(user); + } + } + } else if (peer->isChat()) { + callback(lang(lng_profile_clear_history), std_::move(clearHistoryHandler)); + callback(lang(lng_profile_clear_and_exit), std_::move(deleteAndLeaveHandler)); + } else if (peer->isChannel() && peer->asChannel()->amIn() && !peer->asChannel()->amCreator()) { + callback(lang(peer->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel), std_::move(deleteAndLeaveHandler)); + } +} + void MainWidget::onViewsIncrement() { if (!App::main() || !MTP::authedId()) return; @@ -3817,7 +3905,6 @@ void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNoti if (_history->peer() == updatePeer) { _history->updateNotifySettings(); } - _dialogs->updateNotifySettings(updatePeer); if (changed) { Notify::peerUpdatedDelayed(updatePeer, Notify::PeerUpdate::Flag::NotificationsEnabled); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 8840579ef..5da1b0b7e 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -364,6 +364,8 @@ public: void scheduleViewIncrement(HistoryItem *item); + void fillPeerMenu(PeerData *peer, base::lambda_unique handler)> callback); + void gotRangeDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff); void onSelfParticipantUpdated(ChannelData *channel); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index b43afe02f..d28788425 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -134,7 +134,7 @@ MediaView::MediaView() : TWidget(App::wnd()) connect(&_docSaveAs, SIGNAL(clicked()), this, SLOT(onSaveAs())); connect(&_docCancel, SIGNAL(clicked()), this, SLOT(onSaveCancel())); - connect(_dropdown, SIGNAL(beforeHidden()), this, SLOT(onDropdownHidden())); + _dropdown->setHiddenCallback([this] { dropdownHidden(); }); } void MediaView::moveToScreen() { @@ -142,7 +142,7 @@ void MediaView::moveToScreen() { windowHandle()->setScreen(App::wnd()->windowHandle()->screen()); } - QPoint wndCenter(App::wnd()->x() + App::wnd()->width() / 2, App::wnd()->y() + App::wnd()->height() / 2); + auto wndCenter = App::wnd()->geometry().center(); QRect avail = Sandbox::screenGeometry(wndCenter); if (avail != geometry()) { setGeometry(avail); @@ -679,7 +679,7 @@ void MediaView::onHideControls(bool force) { if (!_a_state.animating()) _a_state.start(); } -void MediaView::onDropdownHidden() { +void MediaView::dropdownHidden() { setFocus(); _ignoringDropdown = true; _lastMouseMovePos = mapFromGlobal(QCursor::pos()); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index ed7872c63..b54900c1e 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -80,9 +80,23 @@ public: void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; +protected: + void paintEvent(QPaintEvent *e) override; + + void keyPressEvent(QKeyEvent *e) override; + void wheelEvent(QWheelEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseDoubleClickEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void contextMenuEvent(QContextMenuEvent *e) override; + void touchEvent(QTouchEvent *e); + + bool event(QEvent *e) override; + bool eventFilter(QObject *obj, QEvent *e) override; + private slots: void onHideControls(bool force = false); - void onDropdownHidden(); void onScreenResized(int screen); @@ -105,22 +119,6 @@ private slots: void updateImage(); -protected: - void paintEvent(QPaintEvent *e) override; - - void keyPressEvent(QKeyEvent *e) override; - void wheelEvent(QWheelEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseDoubleClickEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - void contextMenuEvent(QContextMenuEvent *e) override; - void touchEvent(QTouchEvent *e); - - bool event(QEvent *e) override; - bool eventFilter(QObject *obj, QEvent *e) override; - -private slots: void onVideoPauseResume(); void onVideoSeekProgress(int64 positionMs); void onVideoSeekFinished(int64 positionMs); @@ -129,6 +127,7 @@ private slots: void onVideoPlayProgress(const AudioMsgId &audioId); private: + void dropdownHidden(); void updateDocSize(); void updateControls(); void updateActions(); diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 0b648782e..ffa8c8da3 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1879,7 +1879,6 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp , _inner(this, &_scroll, peer, type) , _a_show(animation(this, &OverviewWidget::step_show)) , _topShadow(this, st::shadowColor) { - _scroll.setFocusPolicy(Qt::NoFocus); _scroll.setWidget(&_inner); _scroll.move(0, 0); _inner.move(0, 0); diff --git a/Telegram/SourceFiles/settings/settings_advanced_widget.cpp b/Telegram/SourceFiles/settings/settings_advanced_widget.cpp index 86437d389..9502a6b14 100644 --- a/Telegram/SourceFiles/settings/settings_advanced_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_advanced_widget.cpp @@ -63,7 +63,8 @@ void AdvancedWidget::createControls() { if (self()) { addChildRow(_askQuestion, marginSmall, lang(lng_settings_ask_question), SLOT(onAskQuestion())); } - addChildRow(_telegramFAQ, marginLarge, lang(lng_settings_faq), SLOT(onTelegramFAQ())); + addChildRow(_telegramFAQ, marginSmall, lang(lng_settings_faq), SLOT(onTelegramFAQ())); + addChildRow(_about, marginLarge, lang(lng_menu_about), SLOT(onAbout())); if (self()) { style::margins marginLogout(0, 0, 0, 2 * st::settingsLargeSkip); addChildRow(_logOut, marginLogout, lang(lng_settings_logout), SLOT(onLogOut())); @@ -123,6 +124,10 @@ void AdvancedWidget::onTelegramFAQ() { QDesktopServices::openUrl(telegramFaqLink()); } +void AdvancedWidget::onAbout() { + Ui::showLayer(new AboutBox()); +} + void AdvancedWidget::onLogOut() { App::wnd()->onLogout(); } diff --git a/Telegram/SourceFiles/settings/settings_advanced_widget.h b/Telegram/SourceFiles/settings/settings_advanced_widget.h index 81e8c5e38..a6c8b75a2 100644 --- a/Telegram/SourceFiles/settings/settings_advanced_widget.h +++ b/Telegram/SourceFiles/settings/settings_advanced_widget.h @@ -39,6 +39,7 @@ private slots: void onAskQuestion(); void onAskQuestionSure(); void onTelegramFAQ(); + void onAbout(); void onLogOut(); private: @@ -54,6 +55,7 @@ private: #endif // !TDESKTOP_DISABLE_NETWORK_PROXY ChildWidget _askQuestion = { nullptr }; ChildWidget _telegramFAQ = { nullptr }; + ChildWidget _about = { nullptr }; ChildWidget _logOut = { nullptr }; mtpRequestId _supportGetRequest = 0; diff --git a/Telegram/SourceFiles/settings/settings_inner_widget.h b/Telegram/SourceFiles/settings/settings_inner_widget.h index 0a5fcf557..7091124e2 100644 --- a/Telegram/SourceFiles/settings/settings_inner_widget.h +++ b/Telegram/SourceFiles/settings/settings_inner_widget.h @@ -42,9 +42,6 @@ public: void showFinished(); -signals: - void heightUpdated(); - private slots: void onBlockHeightUpdated(); diff --git a/Telegram/SourceFiles/settings/settings_widget.cpp b/Telegram/SourceFiles/settings/settings_widget.cpp index 0bad833c0..c3ae485e8 100644 --- a/Telegram/SourceFiles/settings/settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_widget.cpp @@ -142,7 +142,8 @@ Widget::Widget(QWidget *parent) : LayerWidget(parent) } void Widget::parentResized() { - int windowWidth = App::wnd()->width(); + auto parentSize = parentWidget()->size(); + int windowWidth = parentSize.width(); int newWidth = st::settingsMaxWidth; int newContentLeft = st::settingsMaxPadding; if (windowWidth <= st::settingsMaxWidth) { @@ -177,8 +178,9 @@ void Widget::onInnerHeightUpdated() { void Widget::resizeUsingInnerHeight(int newWidth, int newContentLeft) { if (!App::wnd()) return; - int windowWidth = App::wnd()->width(); - int windowHeight = App::wnd()->height(); + auto parentSize = parentWidget()->size(); + int windowWidth = parentSize.width(); + int windowHeight = parentSize.height(); int maxHeight = st::settingsFixedBarHeight + _inner->height(); int newHeight = maxHeight; if (newHeight > windowHeight || newWidth >= windowWidth) { @@ -189,7 +191,7 @@ void Widget::resizeUsingInnerHeight(int newWidth, int newContentLeft) { _contentLeft = newContentLeft; } - setGeometry((App::wnd()->width() - newWidth) / 2, (App::wnd()->height() - newHeight) / 2, newWidth, newHeight); + setGeometry((windowWidth - newWidth) / 2, (windowHeight - newHeight) / 2, newWidth, newHeight); update(); } diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index abe0d7143..3576aeedb 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -49,7 +49,6 @@ EmojiColorPicker::EmojiColorPicker() : TWidget() memset(_hovers, 0, sizeof(_hovers)); setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); int32 w = st::emojiPanSize.width() * (EmojiColorsCount + 1) + 4 * st::emojiColorsPadding + st::emojiColorsSep + st::defaultDropdownShadow.width() * 2; int32 h = 2 * st::emojiColorsPadding + st::emojiPanSize.height() + st::defaultDropdownShadow.height() * 2; @@ -299,7 +298,6 @@ EmojiPanInner::EmojiPanInner() : TWidget() resize(st::emojiPanWidth - st::emojiScroll.width, countHeight()); setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); setAttribute(Qt::WA_OpaquePaintEvent); _picker.hide(); @@ -804,7 +802,6 @@ StickerPanInner::StickerPanInner() : TWidget() setMaxHeight(st::emojiPanMaxHeight - st::emojiCategory.height); setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); setAttribute(Qt::WA_OpaquePaintEvent); connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); @@ -2431,7 +2428,6 @@ EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool , _delete(special ? 0 : new Ui::IconButton(this, st::hashtagClose)) { // Stickers::NoneSetId if in emoji resize(st::emojiPanWidth, st::emojiPanHeader); setMouseTracking(true); - setFocusPolicy(Qt::NoFocus); setText(text); if (_delete) { _delete->hide(); @@ -2569,12 +2565,6 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) , 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::defaultDropdownPadding.left() + st::emojiPanWidth + st::defaultDropdownPadding.right(); _height = st::defaultDropdownPadding.top() + _contentHeight + st::defaultDropdownPadding.bottom(); _bottom = 0; diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 432f67c49..4cfaa501c 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -266,6 +266,9 @@ public: } bool isVerified() const; bool isMegagroup() const; + bool isMuted() const { + return (notify != EmptyNotifySettings) && (notify != UnknownNotifySettings) && (notify->mute >= unixtime()); + } bool canWrite() const; UserData *asUser(); const UserData *asUser() const; diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 0b83cb649..108b77a0b 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -107,17 +107,7 @@ void FlatButton::paintEvent(QPaintEvent *e) { auto &bg = (_state & StateOver) ? ((_state & StateDown) ? _st.downBgColor : _st.overBgColor) : _st.bgColor; auto &fg = (_state & StateOver) ? ((_state & StateDown) ? _st.downColor : _st.overColor) : _st.color; p.setOpacity(_opacity); - if (_st.radius > 0) { - p.setRenderHint(QPainter::HighQualityAntialiasing); - p.setPen(Qt::NoPen); - if (animating) { - p.setBrush(a_bg.current()); - } else { - p.setBrush(bg); - } - p.drawRoundedRect(r, _st.radius, _st.radius); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); - } else if (animating) { + if (animating) { p.fillRect(r, a_bg.current()); } else { p.fillRect(r, bg); diff --git a/Telegram/SourceFiles/ui/scrollarea.cpp b/Telegram/SourceFiles/ui/scrollarea.cpp index 7aac81107..5dd8e58a7 100644 --- a/Telegram/SourceFiles/ui/scrollarea.cpp +++ b/Telegram/SourceFiles/ui/scrollarea.cpp @@ -328,6 +328,7 @@ ScrollArea::ScrollArea(QWidget *parent, const style::flatScroll &st, bool handle , _bottomShadow(this, &_st) , _touchEnabled(handleTouch) { setLayoutDirection(cLangDir()); + setFocusPolicy(Qt::NoFocus); connect(_verticalBar, SIGNAL(topShadowVisibility(bool)), _topShadow, SLOT(changeVisibility(bool))); connect(_verticalBar, SIGNAL(bottomShadowVisibility(bool)), _bottomShadow, SLOT(changeVisibility(bool))); diff --git a/Telegram/SourceFiles/ui/scrollarea.h b/Telegram/SourceFiles/ui/scrollarea.h index b09348c96..4f4a7e37f 100644 --- a/Telegram/SourceFiles/ui/scrollarea.h +++ b/Telegram/SourceFiles/ui/scrollarea.h @@ -106,11 +106,9 @@ class SplittedWidget : public TWidget { Q_OBJECT public: - - SplittedWidget(QWidget *parent) : TWidget(parent), _otherWidth(0) { + SplittedWidget(QWidget *parent) : TWidget(parent) { setAttribute(Qt::WA_OpaquePaintEvent); } - void paintEvent(QPaintEvent *e); // paintEvent done through paintRegion void setHeight(int32 newHeight) { resize(width(), newHeight); emit resizeOther(); @@ -122,18 +120,17 @@ public: void update(const QRegion&); public slots: - void update() { update(0, 0, fullWidth(), height()); } signals: - void resizeOther(); void updateOther(const QRect&); void updateOther(const QRegion&); protected: + void paintEvent(QPaintEvent *e) override; // paintEvent done through paintRegion int32 otherWidth() const { return _otherWidth; @@ -144,8 +141,7 @@ protected: virtual void paintRegion(Painter &p, const QRegion ®ion, bool paintingOther) = 0; private: - - int32 _otherWidth; + int32 _otherWidth = 0; void setOtherWidth(int32 otherWidth) { _otherWidth = otherWidth; } @@ -163,7 +159,6 @@ class ScrollArea : public QScrollArea { T_WIDGET public: - ScrollArea(QWidget *parent, const style::flatScroll &st = st::scrollDef, bool handleTouch = true); int scrollWidth() const; @@ -190,7 +185,6 @@ public: ~ScrollArea(); protected: - bool eventFilter(QObject *obj, QEvent *e) override; void resizeEvent(QResizeEvent *e) override; @@ -201,7 +195,6 @@ protected: void leaveEventHook(QEvent *e); public slots: - void scrollToY(int toTop, int toBottom = -1); void disableScroll(bool dis); void onScrolled(); @@ -215,18 +208,15 @@ public slots: void onVerticalScroll(); signals: - void scrolled(); void scrollStarted(); void scrollFinished(); void geometryChanged(); protected: - void scrollContentsBy(int dx, int dy) override; private: - bool touchScroll(const QPoint &delta); void touchScrollUpdated(const QPoint &screenPos); @@ -271,5 +261,8 @@ class SplittedWidgetOther : public TWidget { public: SplittedWidgetOther(ScrollArea *parent) : TWidget(parent) { } - void paintEvent(QPaintEvent *e); + +protected: + void paintEvent(QPaintEvent *e) override; + }; diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp index 938a86bf8..b3ba1c5d1 100644 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.cpp @@ -44,7 +44,7 @@ DropdownMenu::DropdownMenu(QWidget *parent, const style::DropdownMenu &st) : Inn //} void DropdownMenu::init() { - connect(this, SIGNAL(beforeHidden()), this, SLOT(onHidden())); + InnerDropdown::setHiddenCallback([this] { hideFinish(); }); setOwnedWidget(_menu); @@ -216,6 +216,9 @@ void DropdownMenu::childHiding(DropdownMenu *child) { void DropdownMenu::hideFinish() { _menu->clearSelection(); + if (_hiddenCallback) { + _hiddenCallback(); + } } // Not ready with submenus yet. diff --git a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h index 6d63733ab..80b106eec 100644 --- a/Telegram/SourceFiles/ui/widgets/dropdown_menu.h +++ b/Telegram/SourceFiles/ui/widgets/dropdown_menu.h @@ -35,6 +35,10 @@ public: QAction *addSeparator(); void clearActions(); + void setHiddenCallback(base::lambda_unique callback) { + _hiddenCallback = std_::move(callback); + } + using Actions = Ui::Menu::Actions; Actions &actions(); @@ -91,6 +95,7 @@ private: void showMenu(const QPoint &p, DropdownMenu *parent, TriggeredSource source); const style::DropdownMenu &_st; + base::lambda_unique _hiddenCallback; ChildWidget _menu; diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index d7f5cd622..0805d1a17 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -188,7 +188,9 @@ void InnerDropdown::hideFinished() { _cache = QPixmap(); _ignoreShowEvents = false; if (!isHidden()) { - emit beforeHidden(); + if (_hiddenCallback) { + _hiddenCallback(); + } hide(); } } diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h index 9b251042a..b955f43f5 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.h +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.h @@ -49,6 +49,10 @@ public: void showFast(); void hideFast(); + void setHiddenCallback(base::lambda_unique callback) { + _hiddenCallback = std_::move(callback); + } + bool isHiding() const { return _hiding && _a_appearance.animating(); } @@ -60,9 +64,6 @@ public: }; void hideAnimated(HideOption option = HideOption::Default); -signals: - void beforeHidden(); - protected: void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; @@ -102,6 +103,7 @@ private: QTimer _hideTimer; bool _ignoreShowEvents = false; + base::lambda_unique _hiddenCallback; RectShadow _shadow; ChildWidget _scroll; diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index aeffea703..319b52163 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -44,12 +44,13 @@ public: virtual ~MainWindow(); + QWidget *bodyWidget() { + return _body; + } + protected: void resizeEvent(QResizeEvent *e) override; - QWidget *bodyWidget() { // temp - return _body; - } void savePosition(Qt::WindowState state = Qt::WindowActive); virtual void initHook() { diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 16348efff..c12e34f7d 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -638,7 +638,7 @@ void Notification::updateNotifyDisplay() { QRect rectForName(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyTextTop, itemWidth, st::msgNameFont->height); if (!options.hideNameAndPhoto) { - if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(_history->peer, false)) { + if (auto chatTypeIcon = Dialogs::Layout::ChatTypeIcon(_history->peer, false, false)) { chatTypeIcon->paint(p, rectForName.topLeft(), w); rectForName.setLeft(rectForName.left() + st::dialogsChatTypeSkip); } @@ -649,8 +649,8 @@ void Notification::updateNotifyDisplay() { Text itemTextCache(itemWidth); QRect r(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyItemTop + st::msgNameFont->height, itemWidth, 2 * st::dialogsTextFont->height); if (_item) { - bool active = false; - _item->drawInDialog(p, r, active, textCachedFor, itemTextCache); + auto active = false, selected = false; + _item->drawInDialog(p, r, active, selected, textCachedFor, itemTextCache); } else if (_forwardedCount > 1) { p.setFont(st::dialogsTextFont); if (_author) { diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 28900ff04..6ba138981 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -25,11 +25,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" #include "mainwidget.h" +#include "mainwindow.h" #include "shortcuts.h" #include "lang.h" #include "ui/buttons/peer_avatar_button.h" #include "ui/buttons/round_button.h" #include "ui/buttons/icon_button.h" +#include "ui/widgets/dropdown_menu.h" #include "ui/flatbutton.h" namespace Window { @@ -41,16 +43,18 @@ TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w) , _delete(this, lang(lng_selected_delete), st::defaultActiveButton) , _info(this, nullptr, st::infoButton) , _mediaType(this, lang(lng_media_type), st::topBarButton) -, _search(this, st::topBarSearch) { +, _search(this, st::topBarSearch) +, _menuToggle(this, st::topBarMenuToggle) { _clearSelection->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); _forward->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); _delete->setTextTransform(Ui::RoundButton::TextTransform::ToUpper); - connect(_forward, SIGNAL(clicked()), this, SLOT(onForwardSelection())); - connect(_delete, SIGNAL(clicked()), this, SLOT(onDeleteSelection())); - connect(_clearSelection, SIGNAL(clicked()), this, SLOT(onClearSelection())); - connect(_info, SIGNAL(clicked()), this, SLOT(onInfoClicked())); - connect(_search, SIGNAL(clicked()), this, SLOT(onSearch())); + _forward->setClickedCallback([this] { onForwardSelection(); }); + _delete->setClickedCallback([this] { onDeleteSelection(); }); + _clearSelection->setClickedCallback([this] { onClearSelection(); }); + _info->setClickedCallback([this] { onInfoClicked(); }); + _search->setClickedCallback([this] { onSearch(); }); + _menuToggle->setClickedCallback([this] { showMenu(); }); subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); }); @@ -83,6 +87,22 @@ void TopBarWidget::onSearch() { } } +void TopBarWidget::showMenu() { + if (auto main = App::main()) { + if (auto peer = main->peer()) { + _menu.create(App::main()); + App::main()->fillPeerMenu(peer, [this](const QString &text, base::lambda_unique callback) { + return _menu->addAction(text, std_::move(callback)); + }); + _menu->setHiddenCallback([this] { + _menu.destroyDelayed(); + }); + _menu->moveToRight(0, 0); + _menu->showAnimated(); + } + } +} + void TopBarWidget::enterEvent(QEvent *e) { a_over.start(1); _a_appearance.start(); @@ -163,13 +183,11 @@ void TopBarWidget::mousePressEvent(QMouseEvent *e) { } void TopBarWidget::resizeEvent(QResizeEvent *e) { - int r = width(); - int buttonsLeft = st::topBarActionSkip + (Adaptive::OneColumn() ? 0 : st::lineWidth); int buttonsWidth = _forward->contentWidth() + _delete->contentWidth() + _clearSelection->width(); buttonsWidth += buttonsLeft + st::topBarActionSkip * 3; - int widthLeft = qMin(r - buttonsWidth, -2 * st::defaultActiveButton.width); + int widthLeft = qMin(width() - buttonsWidth, -2 * st::defaultActiveButton.width); _forward->setFullWidth(-(widthLeft / 2)); _delete->setFullWidth(-(widthLeft / 2)); @@ -181,9 +199,10 @@ void TopBarWidget::resizeEvent(QResizeEvent *e) { _delete->moveToLeft(buttonsLeft, buttonsTop); _clearSelection->moveToRight(st::topBarActionSkip, buttonsTop); - if (!_info->isHidden()) _info->move(r -= _info->width(), 0); - if (!_mediaType->isHidden()) _mediaType->move(r -= _mediaType->width(), 0); - _search->move(width() - (_info->isHidden() ? st::topBarArrowPadding.left() : _info->width()) - _search->width(), 0); + _info->moveToRight(0, 0); + _menuToggle->moveToRight(0, 0); + _mediaType->moveToRight(0, 0); + _search->moveToRight(_info->isHidden() ? _menuToggle->width() : _info->width(), 0); } void TopBarWidget::startAnim() { @@ -193,6 +212,7 @@ void TopBarWidget::startAnim() { _forward->hide(); _mediaType->hide(); _search->hide(); + _menuToggle->hide(); if (_membersShowArea) { _membersShowArea->hide(); } @@ -235,13 +255,16 @@ void TopBarWidget::showAll() { if (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) { _info->setPeer(h); _info->show(); + _menuToggle->hide(); } else { _info->hide(); + _menuToggle->show(); } _search->show(); } else { _search->hide(); _info->hide(); + _menuToggle->hide(); } if (_membersShowArea) { _membersShowArea->show(); diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index cd4c1711d..9a133a9ec 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -25,7 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Ui { class PeerAvatarButton; class RoundButton; -class MaskButton; +class IconButton; +class DropdownMenu; } // namespace Ui namespace Window { @@ -58,17 +59,17 @@ public: protected: bool eventFilter(QObject *obj, QEvent *e) override; -public slots: +signals: + void clicked(); + +private: void onForwardSelection(); void onDeleteSelection(); void onClearSelection(); void onInfoClicked(); void onSearch(); + void showMenu(); -signals: - void clicked(); - -private: void updateAdaptiveLayout(); MainWidget *main(); @@ -87,7 +88,10 @@ private: ChildWidget _info; ChildWidget _mediaType; - ChildWidget _search; + ChildWidget _search; + ChildWidget _menuToggle; + ChildWidget _menu = { nullptr }; + ChildWidget _membersShowArea = { nullptr }; }; From b3d1602354dfa52cb00c77b88bbf39e34bc6ef58 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 6 Nov 2016 20:23:13 +0300 Subject: [PATCH 018/100] Forward bar and unread counter added to top bar in OneColumn layout. --- Telegram/Resources/basic.style | 1 + Telegram/Resources/colors.palette | 2 + Telegram/Resources/icons/simple_close.png | Bin 178 -> 124 bytes Telegram/Resources/icons/simple_close@2x.png | Bin 354 -> 176 bytes Telegram/Resources/sample.tdesktop-theme | 1 + Telegram/SourceFiles/application.cpp | 4 +- Telegram/SourceFiles/dialogs/dialogs.style | 16 ++++++ Telegram/SourceFiles/dialogswidget.cpp | 49 ++++++++++++++++-- Telegram/SourceFiles/dialogswidget.h | 3 ++ Telegram/SourceFiles/facades.cpp | 4 ++ Telegram/SourceFiles/facades.h | 2 + Telegram/SourceFiles/historywidget.cpp | 9 ++-- Telegram/SourceFiles/historywidget.h | 4 +- Telegram/SourceFiles/mainwidget.cpp | 15 +++--- Telegram/SourceFiles/mainwidget.h | 2 +- Telegram/SourceFiles/mainwindow.cpp | 1 + Telegram/SourceFiles/mainwindow.h | 3 +- Telegram/SourceFiles/overviewwidget.cpp | 6 ++- Telegram/SourceFiles/overviewwidget.h | 2 +- .../platform/win/main_window_win.cpp | 4 +- Telegram/SourceFiles/profile/profile.style | 2 +- .../SourceFiles/profile/profile_fixed_bar.cpp | 20 ++++++- .../SourceFiles/window/top_bar_widget.cpp | 35 ++++++++++++- Telegram/SourceFiles/window/top_bar_widget.h | 4 ++ Telegram/SourceFiles/window/window.style | 5 ++ 25 files changed, 164 insertions(+), 30 deletions(-) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 234e9f2dc..152e9870a 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -817,6 +817,7 @@ contactsScroll: flatScroll(boxScroll) { simpleCloseIcon: icon {{ "simple_close", #c7c7c7 }}; simpleCloseIconOver: icon {{ "simple_close", #a3a3a3 }}; +dialogsForwardCancelIcon: icon {{ "simple_close", dialogsForwardFg }}; boxPhotoPadding: margins(28px, 28px, 28px, 18px); boxPhotoCompressedPadding: margins(0px, 2px, 0px, 22px); diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 24ac18d9f..dc3c4a898 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -143,6 +143,8 @@ dialogsVerifiedIconFgActive: dialogsVerifiedIconFgOver; dialogsSendingIconFgActive: dialogsSendingIconFgOver; dialogsSentIconFgActive: dialogsSentIconFgOver; +dialogsForwardFg: #ffffff; + // history topBarBg: windowBg; diff --git a/Telegram/Resources/icons/simple_close.png b/Telegram/Resources/icons/simple_close.png index 07e1c0c58feb02ec5be6fd3ef0923e5602afc8d1..10f8b030e8cd105c9c3ac7af355ac80689fada49 100644 GIT binary patch delta 94 zcmV-k0HOb~0ep}oSVKufK~xx(m5?zC05AvxLw|g~&2Q47o3&sC7jqC3*$xm%?EzNk50cHZVS(evxhX4Qo07*qoM6N<$f}K(% AyZ`_I delta 149 zcmV;G0BZkyvH_4Je|bqnK~xx(b2Z($F=SKp?0);3G;B zl86ZAyLw+*k(mHn(1-vaGly*&=BYpz+NEO$Hh@*=Pplp2#`pp=3ooTYNxV80n&x-X zfN|--IiGTCQ(DKSb+B8%Uh8r=vc6gu?9)+&eQ9su0qM))zz!?e00000NkvXXu0mjf DG6F)f delta 328 zcmV-O0k{6J0pbFXBYy!YNklU5QX6%FnuD0#5G4D6~{=Eb8xFvHD6?<3ZCj>?y4^X_^v-A%0O5gkeaUrf9AI zg%w3XP?lwOI2<^gPQ-Cc(=@bgy9>+noIpgfvMkw(e#1nB0Ds6tge~Y227EvOSJZ~h zKo@ik1Mq-(2k?ELBuR+lm@!hoDz*szMu%YlKq+O#0Q|3A;Pra(d_Jk`8l@CUDeAhm zVpb5~d_EIJ(R7ZYh|A?-yRr=a9Dp$fV~o>A&8GYf8{fp$Pd0em8X-!qvbyQWg3xm&xlIJ<_ aS%fzu*Eq?;HNflu0000updateUnreadCounter(); - } + Global::RefUnreadCounterUpdate().notify(true); } void AppClass::call_handleFileDialogQueue() { diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 38f8fdd2e..219b91c24 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -184,3 +184,19 @@ dialogsUpdateButton: flatButton { font: semiboldFont; overFont: semiboldFont; } + +dialogsForwardHeight: 32px; +dialogsForwardTextLeft: 35px; +dialogsForwardTextTop: 6px; +dialogsForwardCancel: IconButton { + width: 34px; + height: dialogsForwardHeight; + + icon: dialogsForwardCancelIcon; + iconOver: dialogsForwardCancelIcon; + + iconPosition: point(12px, 11px); + iconPositionDown: point(12px, 11px); +} +dialogsForwardFont: semiboldFont; +dialogsForwardBg: windowActiveFill; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 48ae1070c..ba87b6f03 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1695,6 +1695,8 @@ DialogsWidget::DialogsWidget(QWidget *parent) : TWidget(parent) onCheckUpdateStatus(); #endif // !TDESKTOP_DISABLE_AUTOUPDATE + subscribe(Adaptive::Changed(), [this] { updateForwardBar(); }); + _cancelSearch->setClickedCallback([this] { onCancelSearch(); }); _lockUnlock->setVisible(Global::LocalPasscode()); subscribe(Global::RefLocalPasscodeChanged(), [this] { updateLockUnlockVisibility(); }); @@ -1767,17 +1769,24 @@ void DialogsWidget::dialogsToUp() { } } +void DialogsWidget::showFast() { + show(); + updateForwardBar(); +} + void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms) { if (App::app()) App::app()->mtpPause(); _cacheUnder = params.oldContentCache; show(); + updateForwardBar(); _cacheOver = App::main()->grabForShowAnimation(params); _a_show.stop(); _scroll->hide(); _mainMenuToggle->hide(); + if (_forwardCancel) _forwardCancel->hide(); _filter->hide(); _cancelSearch->hide(); _lockUnlock->hide(); @@ -1809,6 +1818,7 @@ void DialogsWidget::step_show(float64 ms, bool timer) { _scroll->show(); _mainMenuToggle->show(); + if (_forwardCancel) _forwardCancel->show(); _filter->show(); updateLockUnlockVisibility(); _a_show.stop(); @@ -2378,20 +2388,26 @@ void DialogsWidget::updateLockUnlockVisibility() { } void DialogsWidget::updateControlsGeometry() { + auto filterTop = 0; + if (_forwardCancel) { + _forwardCancel->moveToLeft(0, filterTop); + filterTop += st::dialogsForwardHeight; + } auto filterLeft = st::dialogsFilterPadding.x() * 2 + _mainMenuToggle->width(); auto filterWidth = width() - 2 * st::dialogsFilterPadding.x(); filterWidth -= st::dialogsFilterPadding.x() + _mainMenuToggle->width(); if (Global::LocalPasscode()) { filterWidth -= st::dialogsFilterPadding.x() + _lockUnlock->width(); } - _filter->setGeometryToLeft(filterLeft, st::dialogsFilterPadding.y(), filterWidth, _filter->height()); + filterTop += st::dialogsFilterPadding.y(); + _filter->setGeometryToLeft(filterLeft, filterTop, filterWidth, _filter->height()); _mainMenuToggle->moveToLeft(st::dialogsFilterPadding.x(), _filter->y()); _lockUnlock->moveToRight(st::dialogsFilterPadding.x(), _filter->y()); _cancelSearch->moveToLeft(filterLeft + filterWidth - _cancelSearch->width(), _filter->y()); auto addToScroll = App::main() ? App::main()->contentScrollAddToY() : 0; auto newScrollTop = _scroll->scrollTop() + addToScroll; - auto scrollTop = _filter->height() + 2 * st::dialogsFilterPadding.y(); + auto scrollTop = filterTop + _filter->height() + st::dialogsFilterPadding.y(); auto scrollHeight = height() - scrollTop; if (_updateTelegram) { auto updateHeight = _updateTelegram->height(); @@ -2414,6 +2430,23 @@ void DialogsWidget::updateMainMenuGeometry() { _mainMenu->moveToLeft(st::dialogsMenuPosition.x(), st::dialogsMenuPosition.y()); } +void DialogsWidget::updateForwardBar() { + auto selecting = App::main()->selectingPeer(); + auto oneColumnSelecting = (Adaptive::OneColumn() && selecting); + if (!oneColumnSelecting == !_forwardCancel) { + return; + } + if (oneColumnSelecting) { + _forwardCancel.create(this, st::dialogsForwardCancel); + _forwardCancel->setClickedCallback([] { Global::RefPeerChooseCancel().notify(true); }); + if (!_a_show.animating()) _forwardCancel->show(); + } else { + _forwardCancel.destroyDelayed(); + } + updateControlsGeometry(); + update(); +} + void DialogsWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { e->ignore(); @@ -2464,11 +2497,19 @@ void DialogsWidget::paintEvent(QPaintEvent *e) { st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), _cacheOver.height() / retina)); return; } - QRect above(0, 0, width(), _scroll->y()); + auto aboveTop = 0; + if (_forwardCancel) { + p.fillRect(0, aboveTop, width(), st::dialogsForwardHeight, st::dialogsForwardBg); + p.setPen(st::dialogsForwardFg); + p.setFont(st::dialogsForwardFont); + p.drawTextLeft(st::dialogsForwardTextLeft, st::dialogsForwardTextTop, width(), lang(lng_forward_choose)); + aboveTop += st::dialogsForwardHeight; + } + auto above = QRect(0, aboveTop, width(), _scroll->y()); if (above.intersects(r)) { p.fillRect(above.intersected(r), st::dialogsBg); } - QRect below(0, _scroll->y() + qMin(_scroll->height(), _inner->height()), width(), height()); + auto below = QRect(0, _scroll->y() + qMin(_scroll->height(), _inner->height()), width(), height()); if (below.intersects(r)) { p.fillRect(below.intersected(r), st::dialogsBg); } diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index f2f735213..cac76a09b 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -253,6 +253,7 @@ public: return true; } void showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms); + void showFast(); void step_show(float64 ms, bool timer); void destroyData(); @@ -307,6 +308,7 @@ private: void updateLockUnlockVisibility(); void updateControlsGeometry(); void updateMainMenuGeometry(); + void updateForwardBar(); bool _dragInScroll = false; bool _dragForward = false; @@ -325,6 +327,7 @@ private: mtpRequestId _dialogsRequest = 0; mtpRequestId _contactsRequest = 0; + ChildWidget _forwardCancel = { nullptr }; ChildWidget _mainMenuToggle; ChildWidget _mainMenu = { nullptr }; ChildWidget _filter; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 8457a0775..6a8a88d83 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -666,6 +666,8 @@ struct Data { base::Observable LocalPasscodeChanged; base::Observable ItemRemoved; + base::Observable UnreadCounterUpdate; + base::Observable PeerChooseCancel; }; @@ -781,5 +783,7 @@ DefineVar(Global, bool, LocalPasscode); DefineRefVar(Global, base::Observable, LocalPasscodeChanged); DefineRefVar(Global, base::Observable, ItemRemoved); +DefineRefVar(Global, base::Observable, UnreadCounterUpdate); +DefineRefVar(Global, base::Observable, PeerChooseCancel); } // namespace Global diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index dc3eaed73..b95f128f4 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -355,6 +355,8 @@ DeclareVar(bool, LocalPasscode); DeclareRefVar(base::Observable, LocalPasscodeChanged); DeclareRefVar(base::Observable, ItemRemoved); +DeclareRefVar(base::Observable, UnreadCounterUpdate); +DeclareRefVar(base::Observable, PeerChooseCancel); } // namespace Global diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index d4c86564f..acc13358f 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2701,6 +2701,7 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString void HistoryHider::init() { connect(_send, SIGNAL(clicked()), this, SLOT(forward())); connect(_cancel, SIGNAL(clicked()), this, SLOT(startHide())); + subscribe(Global::RefPeerChooseCancel(), [this] { startHide(); }); _chooseWidth = st::forwardFont->width(lang(_botAndQuery.isEmpty() ? lng_forward_choose : lng_inline_switch_choose)); @@ -6240,7 +6241,7 @@ void HistoryWidget::onForwardHere() { App::forward(_peer->id, ForwardContextMessage); } -void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { +bool HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { if (_a_show.animating()) { int retina = cIntRetinaFactor(); if (a_coordOver.current() > 0) { @@ -6252,10 +6253,10 @@ void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina)); p.setOpacity(a_progress.current()); st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), st::topBarHeight)); - return; + return false; } - if (!_history) return; + if (!_history) return false; int increaseLeft = (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) ? (st::topBarArrowPadding.left() - st::topBarArrowPadding.right()) : 0; decreaseWidth += increaseLeft; @@ -6275,7 +6276,9 @@ void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { if (Adaptive::OneColumn() || !App::main()->stackIsEmpty()) { p.setOpacity(st::topBarForwardAlpha + (1 - st::topBarForwardAlpha) * over); st::topBarBackward.paint(p, (st::topBarArrowPadding.left() - st::topBarBackward.width()) / 2, (st::topBarHeight - st::topBarBackward.height()) / 2, width()); + p.setOpacity(1.); } + return true; } QRect HistoryWidget::getMembersShowAreaGeometry() const { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 45536b036..c479a6251 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -441,7 +441,7 @@ private: }; -class HistoryHider : public TWidget { +class HistoryHider : public TWidget, private base::Subscriber { Q_OBJECT public: @@ -559,7 +559,7 @@ public: void updateTopBarSelection(); - void paintTopBar(Painter &p, float64 over, int32 decreaseWidth); + bool paintTopBar(Painter &p, float64 over, int32 decreaseWidth); QRect getMembersShowAreaGeometry() const; void setMembersShowAreaActive(bool active); void topBarClick(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index a2bf332d2..3ae059064 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2264,7 +2264,7 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show if (!animationParams.oldContentCache.isNull()) { _dialogs->showAnimated(back ? Window::SlideDirection::FromLeft : Window::SlideDirection::FromRight, animationParams); } else { - _dialogs->show(); + _dialogs->showFast(); } } } else { @@ -2820,7 +2820,7 @@ void MainWidget::showAll() { } } if (selectingPeer()) { - _dialogs->show(); + _dialogs->showFast(); _history->hide(); if (_overview) _overview->hide(); if (_wideSection) _wideSection->hide(); @@ -2833,7 +2833,7 @@ void MainWidget::showAll() { _history->show(); _history->updateControlsGeometry(); } else { - _dialogs->show(); + _dialogs->showFast(); _history->hide(); } if (!selectingPeer()) { @@ -2854,7 +2854,7 @@ void MainWidget::showAll() { _forwardConfirm = 0; } } - _dialogs->show(); + _dialogs->showFast(); if (_overview) { _overview->show(); } else if (_wideSection) { @@ -2972,12 +2972,13 @@ bool MainWidget::needBackButton() { return _overview || _wideSection || _history->peer(); } -void MainWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { +bool MainWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { if (_overview) { - _overview->paintTopBar(p, over, decreaseWidth); + return _overview->paintTopBar(p, over, decreaseWidth); } else if (!_wideSection) { - _history->paintTopBar(p, over, decreaseWidth); + return _history->paintTopBar(p, over, decreaseWidth); } + return false; } QRect MainWidget::getMembersShowAreaGeometry() const { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 5da1b0b7e..1c8a279a5 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -144,7 +144,7 @@ public: bool needBackButton(); // Temporary methods, while top bar was not done inside HistoryWidget / OverviewWidget. - void paintTopBar(Painter &p, float64 over, int32 decreaseWidth); + bool paintTopBar(Painter &p, float64 over, int32 decreaseWidth); QRect getMembersShowAreaGeometry() const; void setMembersShowAreaActive(bool active); Window::TopBarWidget *topBar(); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 37a9c943c..da17f3560 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -103,6 +103,7 @@ MainWindow::MainWindow() { Notify::unreadCounterUpdated(); } }); + subscribe(Global::RefUnreadCounterUpdate(), [this] { updateUnreadCounter(); }); resize(st::windowDefaultWidth, st::windowDefaultHeight); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index e9f5995e4..b1d8fc839 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -144,8 +144,6 @@ public: bool isActive(bool cached = true) const; void hideMediaview(); - void updateUnreadCounter(); - QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon); bool contentOverlapped(const QRect &globalRect); @@ -217,6 +215,7 @@ private slots: void onWindowActiveChanged(); private: + void updateUnreadCounter(); void showConnecting(const QString &text, const QString &reconnect = QString()); void hideConnecting(); diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index ffa8c8da3..7b67d7544 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1969,7 +1969,7 @@ void OverviewWidget::scrollReset() { _scroll.scrollToY((type() == OverviewMusicFiles || type() == OverviewVoiceFiles) ? _scroll.scrollTopMax() : 0); } -void OverviewWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { +bool OverviewWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { if (_a_show.animating()) { int retina = cIntRetinaFactor(); if (a_coordOver.current() > 0) { @@ -1981,13 +1981,15 @@ void OverviewWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina)); p.setOpacity(a_progress.current()); st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), st::topBarHeight)); - return; + return false; } p.setOpacity(st::topBarBackAlpha + (1 - st::topBarBackAlpha) * over); st::topBarBack.paint(p, (st::topBarArrowPadding.left() - st::topBarBack.width()) / 2, (st::topBarHeight - st::topBarBack.height()) / 2, width()); p.setFont(st::topBarBackFont); p.setPen(st::topBarBackColor); p.drawText(st::topBarArrowPadding.left(), (st::topBarHeight - st::topBarBackFont->height) / 2 + st::topBarBackFont->ascent, _header); + p.setOpacity(1.); + return true; } void OverviewWidget::topBarClick() { diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 9b81e2ee7..aebbe33cb 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -279,7 +279,7 @@ public: void scrollBy(int32 add); void scrollReset(); - void paintTopBar(Painter &p, float64 over, int32 decreaseWidth); + bool paintTopBar(Painter &p, float64 over, int32 decreaseWidth); void topBarClick(); PeerData *peer() const; diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index e328e4dcb..cef2dfd05 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -724,8 +724,8 @@ void MainWindow::psUpdateWorkmode() { } void MainWindow::psUpdateCounter() { - int32 counter = App::histories().unreadBadge(); - bool muted = App::histories().unreadOnlyMuted(); + auto counter = App::histories().unreadBadge(); + auto muted = App::histories().unreadOnlyMuted(); auto iconSizeSmall = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); auto iconSizeBig = QSize(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); diff --git a/Telegram/SourceFiles/profile/profile.style b/Telegram/SourceFiles/profile/profile.style index 80c306a07..5c7ffca41 100644 --- a/Telegram/SourceFiles/profile/profile.style +++ b/Telegram/SourceFiles/profile/profile.style @@ -27,7 +27,7 @@ profileTopBarHeight: topBarHeight; profileTopBarBackIconPosition: point(12px, 20px); profileTopBarBackFont: font(14px); profileTopBarBackFg: #1485c2; -profileTopBarBackPosition: point(32px, 17px); +profileTopBarBackPosition: point(39px, 17px); profileFixedBarButton: topBarButton; profileMarginTop: 13px; diff --git a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp index c936b2296..584b480d7 100644 --- a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp +++ b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp @@ -22,19 +22,24 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "profile/profile_fixed_bar.h" #include "styles/style_profile.h" +#include "styles/style_window.h" #include "ui/buttons/round_button.h" #include "lang.h" #include "mainwidget.h" #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" #include "observer_peer.h" +#include "window/top_bar_widget.h" namespace Profile { -class BackButton final : public Button { +class BackButton final : public Button, private base::Subscriber { public: BackButton(QWidget *parent) : Button(parent) { setCursor(style::cur_pointer); + + subscribe(Adaptive::Changed(), [this] { updateAdaptiveLayout(); }); + updateAdaptiveLayout(); } protected: @@ -50,6 +55,8 @@ protected: p.setFont(st::profileTopBarBackFont); p.setPen(st::profileTopBarBackFg); p.drawTextLeft(st::profileTopBarBackPosition.x(), st::profileTopBarBackPosition.y(), width(), lang(lng_menu_back)); + + Window::TopBarWidget::paintUnreadCounter(p, width()); } void onStateChanged(int oldState, ButtonStateChangeSource source) override { if ((_state & Button::StateDown) && !(oldState & Button::StateDown)) { @@ -58,6 +65,17 @@ protected: } private: + void updateAdaptiveLayout() { + if (!Adaptive::OneColumn()) { + unsubscribe(base::take(_unreadCounterSubscription)); + } else if (!_unreadCounterSubscription) { + _unreadCounterSubscription = subscribe(Global::RefUnreadCounterUpdate(), [this] { + rtlupdate(0, 0, st::titleUnreadCounterRight, st::titleUnreadCounterTop); + }); + } + } + + int _unreadCounterSubscription = 0; }; diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 6ba138981..f58b25a74 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/top_bar_widget.h" #include "styles/style_history.h" +#include "styles/style_window.h" #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" #include "mainwidget.h" @@ -33,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/buttons/icon_button.h" #include "ui/widgets/dropdown_menu.h" #include "ui/flatbutton.h" +#include "dialogs/dialogs_layout.h" namespace Window { @@ -57,6 +59,11 @@ TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w) _menuToggle->setClickedCallback([this] { showMenu(); }); subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); }); + if (Adaptive::OneColumn()) { + _unreadCounterSubscription = subscribe(Global::RefUnreadCounterUpdate(), [this] { + rtlupdate(0, 0, st::titleUnreadCounterRight, st::titleUnreadCounterTop); + }); + } setCursor(style::cur_pointer); showAll(); @@ -171,8 +178,27 @@ void TopBarWidget::paintEvent(QPaintEvent *e) { if (!_search->isHidden()) { decreaseWidth += _search->width(); } - main()->paintTopBar(p, a_over.current(), decreaseWidth); + auto paintCounter = main()->paintTopBar(p, a_over.current(), decreaseWidth); p.restore(); + + if (paintCounter) { + paintUnreadCounter(p, width()); + } + } +} + +void TopBarWidget::paintUnreadCounter(Painter &p, int outerWidth) { + if (!Adaptive::OneColumn()) { + return; + } + if (auto counter = App::histories().unreadBadge()) { + auto counterText = (counter > 99) ? qsl("..%1").arg(counter % 100) : QString::number(counter); + Dialogs::Layout::UnreadBadgeStyle unreadSt; + unreadSt.muted = App::histories().unreadOnlyMuted(); + auto unreadRight = st::titleUnreadCounterRight; + if (rtl()) unreadRight = outerWidth - st::titleUnreadCounterRight; + auto unreadTop = st::titleUnreadCounterTop; + Dialogs::Layout::paintUnreadCount(p, counterText, unreadRight, unreadTop, unreadSt); } } @@ -316,6 +342,13 @@ void TopBarWidget::showSelected(uint32 selCount, bool canDelete) { void TopBarWidget::updateAdaptiveLayout() { updateMembersShowArea(); showAll(); + if (!Adaptive::OneColumn()) { + unsubscribe(base::take(_unreadCounterSubscription)); + } else if (!_unreadCounterSubscription) { + _unreadCounterSubscription = subscribe(Global::RefUnreadCounterUpdate(), [this] { + rtlupdate(0, 0, st::titleUnreadCounterRight, st::titleUnreadCounterTop); + }); + } } Ui::RoundButton *TopBarWidget::mediaTypeButton() { diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index 9a133a9ec..5fdd48fef 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -56,6 +56,8 @@ public: Ui::RoundButton *mediaTypeButton(); + static void paintUnreadCounter(Painter &p, int outerWidth); + protected: bool eventFilter(QObject *obj, QEvent *e) override; @@ -94,6 +96,8 @@ private: ChildWidget _membersShowArea = { nullptr }; + int _unreadCounterSubscription = 0; + }; } // namespace Window diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 50bd74cad..4c130b571 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -82,6 +82,11 @@ notifySendReply: IconButton { iconPositionDown: point(0px, 1px); } +titleUnreadCounterTop: 5px; +titleUnreadCounterRight: 35px; + +// Windows specific title + titleHeight: 21px; titleButtonMinimize: IconButton { width: 24px; From dac96bfc4aa28af62ef868c807936bd67229d3b9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 6 Nov 2016 21:08:52 +0300 Subject: [PATCH 019/100] Closed beta 10019002: redesign, fix emoji pan hide after inline bot. --- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 2 +- Telegram/SourceFiles/historywidget.cpp | 1 - Telegram/SourceFiles/layerwidget.cpp | 5 ++++- Telegram/SourceFiles/stickers/emoji_pan.cpp | 12 ++++++------ Telegram/SourceFiles/stickers/emoji_pan.h | 4 ++-- Telegram/build/version | 2 +- 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 2251dae34..288457f4d 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,1 - PRODUCTVERSION 0,10,19,1 + FILEVERSION 0,10,19,2 + PRODUCTVERSION 0,10,19,2 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.10.19.1" + VALUE "FileVersion", "0.10.19.2" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.1" + VALUE "ProductVersion", "0.10.19.2" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 22abe67ef..efe575eec 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,10,19,1 - PRODUCTVERSION 0,10,19,1 + FILEVERSION 0,10,19,2 + PRODUCTVERSION 0,10,19,2 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.10.19.1" + VALUE "FileVersion", "0.10.19.2" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.10.19.1" + VALUE "ProductVersion", "0.10.19.2" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 595eb1879..2c472b8fb 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/utils.h" -#define BETA_VERSION_MACRO (10019001ULL) +#define BETA_VERSION_MACRO (10019002ULL) constexpr int AppVersion = 10020; constexpr str_const AppVersionStr = "0.10.20"; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index acc13358f..20ff55111 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -7561,7 +7561,6 @@ void HistoryWidget::updatePinnedBar(bool force) { } } t_assert(_history != nullptr); - if (!_pinnedBar->msg) { _pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId); } diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index f625b1f49..f6de4cce5 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -501,6 +501,7 @@ void MediaPreviewWidget::showPreview(PhotoData *photo) { startShow(); _photo = photo; _document = nullptr; + fillEmojiString(); resetGifAndCache(); } @@ -548,7 +549,9 @@ void MediaPreviewWidget::fillEmojiString() { return result; }; - if (auto sticker = _document->sticker()) { + if (_photo) { + _emojiList.clear(); + } else if (auto sticker = _document->sticker()) { auto &inputSet = sticker->set; if (inputSet.type() == mtpc_inputStickerSetID) { _emojiList = getStickerEmojiList(inputSet.c_inputStickerSetID().vid.v); diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 3576aeedb..41ab8dfa5 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -2597,7 +2597,7 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) setCurrentTabIcon(dbietRecent); _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideAnimated())); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideByTimerOrLeave())); connect(&e_inner, SIGNAL(scrollToY(int)), &e_scroll, SLOT(scrollToY(int))); connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool))); @@ -2865,7 +2865,7 @@ bool EmojiPan::preventAutoHide() const { void EmojiPan::leaveEvent(QEvent *e) { if (preventAutoHide() || s_inner.inlineResultsShown()) return; if (_a_appearance.animating()) { - hideAnimated(); + hideByTimerOrLeave(); } else { _hideTimer.start(300); } @@ -2879,7 +2879,7 @@ void EmojiPan::otherEnter() { void EmojiPan::otherLeave() { if (preventAutoHide() || s_inner.inlineResultsShown()) return; if (_a_appearance.animating()) { - hideAnimated(); + hideByTimerOrLeave(); } else { _hideTimer.start(0); } @@ -3177,10 +3177,10 @@ void EmojiPan::step_appearance(float64 ms, bool timer) { if (timer) update(); } -void EmojiPan::hideAnimated() { +void EmojiPan::hideByTimerOrLeave() { if (isHidden() || preventAutoHide() || s_inner.inlineResultsShown()) return; - startHideAnimated(); + hideAnimated(); } void EmojiPan::prepareShowHideCache() { @@ -3193,7 +3193,7 @@ void EmojiPan::prepareShowHideCache() { } } -void EmojiPan::startHideAnimated() { +void EmojiPan::hideAnimated() { if (_hiding) return; prepareShowHideCache(); diff --git a/Telegram/SourceFiles/stickers/emoji_pan.h b/Telegram/SourceFiles/stickers/emoji_pan.h index 7545ae805..afa4e6374 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.h +++ b/Telegram/SourceFiles/stickers/emoji_pan.h @@ -536,13 +536,14 @@ public: return s_inner.inlineResultsShown(); } -public slots: void showAnimated(); void hideAnimated(); +public slots: void refreshStickers(); private slots: + void hideByTimerOrLeave(); void refreshSavedGifs(); void hideFinish(); @@ -595,7 +596,6 @@ private: void updateContentHeight(); void leaveToChildEvent(QEvent *e, QWidget *child); - void startHideAnimated(); void prepareShowHideCache(); void updateSelected(); diff --git a/Telegram/build/version b/Telegram/build/version index 121e18f19..8301c82a1 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019001 +BetaVersion 10019002 From 0326976473e54d2cd074aa9cc0460aeb8873f77c Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 6 Nov 2016 21:45:50 +0300 Subject: [PATCH 020/100] Beta version 10019002: fixed build for Xcode. --- Telegram/SourceFiles/mainwindow.h | 4 ++-- .../platform/linux/notifications_manager_linux.h | 8 ++++++++ Telegram/SourceFiles/platform/mac/main_window_mac.mm | 9 ++------- .../SourceFiles/platform/mac/notifications_manager_mac.h | 8 ++++++++ Telegram/SourceFiles/platform/platform_window_title.h | 2 +- Telegram/SourceFiles/platform/win/window_title_win.cpp | 4 ++-- 6 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index b1d8fc839..0fd6813a8 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -144,7 +144,7 @@ public: bool isActive(bool cached = true) const; void hideMediaview(); - QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon); + QImage iconWithCounter(int size, int count, const style::color &bg, const style::color &fg, bool smallIcon) override; bool contentOverlapped(const QRect &globalRect); bool contentOverlapped(QWidget *w, QPaintEvent *e) { @@ -225,7 +225,7 @@ private: QPixmap grabInner(); - void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color); + void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) override; QImage icon16, icon32, icon64, iconbig16, iconbig32, iconbig64; typedef QPair DelayedServiceMsg; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index c8e0583e2..8d0112450 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -28,6 +28,14 @@ namespace Notifications { inline void defaultNotificationShown(QWidget *widget) { } +inline bool skipAudio() { + return false; +} + +inline bool skipToast() { + return false; +} + class Manager : public Window::Notifications::NativeManager { public: Manager(); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index ca658345a..f165dc88a 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "historywidget.h" #include "localstorage.h" #include "window/notifications_manager_default.h" -#include "platform/mac/notifications_manager_mac.h" +#include "platform/platform_notifications_manager.h" #include "boxes/contactsbox.h" #include "boxes/aboutbox.h" @@ -189,11 +189,6 @@ void MainWindow::psUpdateCounter() { } } -void MainWindow::psInitFrameless() { - psUpdatedPositionTimer.setSingleShot(true); - connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); -} - void MainWindow::psFirstShow() { psUpdateMargins(); @@ -258,7 +253,7 @@ void MainWindow::createGlobalMenu() { if (!App::self()) return; Ui::showLayer(new ContactsBox()); - }, SLOT(action())); + }), SLOT(action())); psAddContact = window->addAction(lang(lng_mac_menu_add_contact), App::wnd(), SLOT(onShowAddContact())); window->addSeparator(); psNewGroup = window->addAction(lang(lng_mac_menu_new_group), App::wnd(), SLOT(onShowNewGroup())); diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h index 631021944..35c1d4bca 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h @@ -25,6 +25,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Platform { namespace Notifications { +inline bool skipAudio() { + return false; +} + +inline bool skipToast() { + return false; +} + class Manager : public Window::Notifications::NativeManager { public: Manager(); diff --git a/Telegram/SourceFiles/platform/platform_window_title.h b/Telegram/SourceFiles/platform/platform_window_title.h index b124c469f..6291c5d7e 100644 --- a/Telegram/SourceFiles/platform/platform_window_title.h +++ b/Telegram/SourceFiles/platform/platform_window_title.h @@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #else // Q_OS_WIN namespace Platform { -inline Window::TitleWidget *CreateTitleWidget() { +inline Window::TitleWidget *CreateTitleWidget(QWidget *parent) { return nullptr; } diff --git a/Telegram/SourceFiles/platform/win/window_title_win.cpp b/Telegram/SourceFiles/platform/win/window_title_win.cpp index e542e965d..a27c96f5d 100644 --- a/Telegram/SourceFiles/platform/win/window_title_win.cpp +++ b/Telegram/SourceFiles/platform/win/window_title_win.cpp @@ -31,8 +31,8 @@ TitleWidget::TitleWidget(QWidget *parent) : Window::TitleWidget(parent) , _minimize(this, st::titleButtonMinimize) , _maximizeRestore(this, st::titleButtonMaximize) , _close(this, st::titleButtonClose) -, _maximized(parent->window()->windowState() & Qt::WindowMaximized) -, _shadow(this, st::titleShadow) { +, _shadow(this, st::titleShadow) +, _maximized(parent->window()->windowState() & Qt::WindowMaximized) { _minimize->setClickedCallback([this]() { window()->setWindowState(Qt::WindowMinimized); _minimize->clearState(); From 23c2e5364a56d2a8e292f38c2d42e97da85ff1c9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Nov 2016 14:24:19 +0300 Subject: [PATCH 021/100] Removed anim::cvalue and ColorAnimation, all done by fvalue now. Also moved style::interpolate to anim::color/anim::pen/anim::brush. --- Telegram/Resources/basic.style | 12 +- Telegram/Resources/basic_types.style | 8 - Telegram/Resources/colors.palette | 17 +- Telegram/Resources/sample.tdesktop-theme | 17 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 12 +- Telegram/SourceFiles/boxes/sharebox.cpp | 16 +- Telegram/SourceFiles/boxes/sharebox.h | 2 +- .../SourceFiles/codegen/style/generator.cpp | 4 - .../SourceFiles/codegen/style/parsed_file.cpp | 27 -- .../SourceFiles/codegen/style/parsed_file.h | 2 - .../codegen/style/structure_types.cpp | 2 - .../codegen/style/structure_types.h | 1 - Telegram/SourceFiles/core/lambda_wrap.h | 15 +- Telegram/SourceFiles/dialogs/dialogs.style | 8 - .../SourceFiles/dialogs/dialogs_layout.cpp | 11 +- Telegram/SourceFiles/dialogs/dialogs_layout.h | 1 + Telegram/SourceFiles/dialogswidget.cpp | 6 +- Telegram/SourceFiles/history/history.style | 3 - .../SourceFiles/history/history_drag_area.cpp | 20 +- .../SourceFiles/history/history_drag_area.h | 2 +- .../history/history_media_types.cpp | 10 +- Telegram/SourceFiles/historywidget.cpp | 24 +- Telegram/SourceFiles/historywidget.h | 2 +- .../inline_bot_layout_internal.cpp | 8 +- Telegram/SourceFiles/intro/intro.style | 4 - Telegram/SourceFiles/intro/introwidget.cpp | 15 +- Telegram/SourceFiles/mainwidget.cpp | 6 +- Telegram/SourceFiles/mediaview.cpp | 12 +- .../SourceFiles/overview/overview_layout.cpp | 16 +- Telegram/SourceFiles/overviewwidget.cpp | 6 +- Telegram/SourceFiles/passcodewidget.cpp | 7 +- .../platform/win/main_window_win.cpp | 2 +- Telegram/SourceFiles/stdafx.h | 2 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 8 +- Telegram/SourceFiles/ui/animation.cpp | 42 +- Telegram/SourceFiles/ui/animation.h | 367 ++++++++---------- .../SourceFiles/ui/buttons/icon_button.cpp | 10 +- Telegram/SourceFiles/ui/buttons/icon_button.h | 2 +- .../ui/effects/round_image_checkbox.cpp | 2 +- Telegram/SourceFiles/ui/flatbutton.cpp | 33 +- Telegram/SourceFiles/ui/flatbutton.h | 2 +- Telegram/SourceFiles/ui/flatinput.cpp | 141 ++++--- Telegram/SourceFiles/ui/flatinput.h | 20 +- Telegram/SourceFiles/ui/flattextarea.cpp | 18 +- Telegram/SourceFiles/ui/flattextarea.h | 2 +- Telegram/SourceFiles/ui/scrollarea.cpp | 72 ++-- Telegram/SourceFiles/ui/scrollarea.h | 2 +- .../SourceFiles/ui/style/style_core_color.h | 23 -- .../SourceFiles/ui/style/style_core_types.h | 2 - .../SourceFiles/ui/widgets/media_slider.cpp | 24 +- .../SourceFiles/ui/widgets/multi_select.cpp | 2 +- .../window/notifications_manager_default.cpp | 2 +- .../SourceFiles/window/slide_animation.cpp | 6 +- Telegram/SourceFiles/window/slide_animation.h | 4 + Telegram/SourceFiles/window/window.style | 1 - 55 files changed, 483 insertions(+), 602 deletions(-) diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 152e9870a..8992a0b5a 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -63,7 +63,7 @@ defaultBoxButton: RoundButton { secondaryTextFg: #2f9fea; secondaryTextFgOver: #2f9fea; textBg: boxBg; - textBgOver: #edf7ff; + textBgOver: lightButtonBgOver; width: -24px; height: 36px; @@ -280,7 +280,6 @@ slideDuration: 240; slideShift: 100px; slideFadeOutBg: #0000003c; slideShadow: icon {{ "slide_shadow", #000000 }}; -slideFunction: transition(easeOutCirc); btnYesColor: #0080c0; btnYesHover: #0073ad; @@ -321,9 +320,6 @@ inpDefFlat: flatInput { phPos: point(2px, 0px); phShift: 50px; phDuration: 100; - phLeftFunc: transition(linear); - phAlphaFunc: transition(linear); - phColorFunc: transition(linear); } inpDefGray: flatInput(inpDefFlat) { @@ -691,11 +687,9 @@ btnSend: flatButton { color: btnYesColor; overColor: btnYesHover; - downColor: btnYesHover; bgColor: historySendBg; overBgColor: historySendBgOver; - downBgColor: historySendBgOver; width: -32px; height: 46px; @@ -710,7 +704,6 @@ btnSend: flatButton { btnUnblock: flatButton(btnSend) { color: #d15948; overColor: #d15948; - downColor: #db6352; } historyScroll: flatScroll(scrollDef) { @@ -737,11 +730,9 @@ reportSpamHide: flatButton { color: btnYesColor; overColor: btnYesHover; - downColor: btnYesHover; bgColor: transparent; overBgColor: transparent; - downBgColor: transparent; width: -40px; height: 46px; @@ -763,7 +754,6 @@ reportSpamButton: flatButton(reportSpamHide) { bgColor: #888888; overBgColor: #7b7b7b; - downBgColor: #7b7b7b; } reportSpamSeparator: 30px; reportSpamBg: #fffffff0; diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index 3e343c13b..fdf78866f 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -41,11 +41,9 @@ linkButton { flatButton { color: color; overColor: color; - downColor: color; bgColor: color; overBgColor: color; - downBgColor: color; width: pixels; height: pixels; @@ -84,9 +82,6 @@ flatInput { phAlign: align; phShift: pixels; phDuration: int; - phLeftFunc: transition; - phAlphaFunc: transition; - phColorFunc: transition; } flatTextarea { @@ -104,9 +99,6 @@ flatTextarea { phAlign: align; phShift: pixels; phDuration: int; - phLeftFunc: transition; - phAlphaFunc: transition; - phColorFunc: transition; } flatScroll { diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index dc3c4a898..d7fc7245a 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -34,14 +34,14 @@ imageBgTransparent: #ffffff; // widgets activeButtonBg: windowActiveFill; -activeButtonBgOver: #46b4eb | activeButtonBg; +activeButtonBgOver: #46b4eb; activeButtonFg: #ffffff; activeButtonFgOver: activeButtonFg; activeButtonSecondaryFg: #cceeff; activeButtonSecondaryFgOver: activeButtonSecondaryFg; lightButtonBg: windowBg; -lightButtonBgOver: #f2f7fa | lightButtonBg; +lightButtonBgOver: #edf7ff; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; @@ -74,7 +74,7 @@ boxBg: windowBg; boxTextFg: windowTextFg; boxTextFgGood: #4ab44a; boxTextFgError: #d84d4d; -boxTitleFg: #404040 | windowTextFg; +boxTitleFg: #404040; boxSearchBg: boxBg; boxSearchCancelIconFg: cancelIconFg; boxSearchCancelIconFgOver: cancelIconFgOver; @@ -108,7 +108,7 @@ introErrorFg: windowTextFg; dialogsMenuIconFg: menuIconFg; dialogsBg: windowBg; -dialogsNameFg: #373737 | windowTextFg; +dialogsNameFg: #373737; dialogsChatIconFg: dialogsNameFg; dialogsDateFg: windowSubTextFg; dialogsTextFg: windowSubTextFg; @@ -118,6 +118,9 @@ dialogsVerifiedIconBg: #4abcf1; dialogsVerifiedIconFg: #ffffff; dialogsSendingIconFg: #c1c1c1; dialogsSentIconFg: #5dc452; +dialogsUnreadBg: windowActiveFill; +dialogsUnreadBgMuted: #bbbbbb; +dialogsUnreadFg: #ffffff; dialogsBgOver: windowOverBg; dialogsNameFgOver: windowTextFg; @@ -130,6 +133,9 @@ dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; dialogsVerifiedIconFgOver: dialogsVerifiedIconFg; dialogsSendingIconFgOver: dialogsSendingIconFg; dialogsSentIconFgOver: dialogsSentIconFg; +dialogsUnreadBgOver: dialogsUnreadBg; +dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; +dialogsUnreadFgOver: dialogsUnreadFg; dialogsBgActive: dialogsBgOver; dialogsNameFgActive: dialogsNameFgOver; @@ -142,6 +148,9 @@ dialogsVerifiedIconBgActive: dialogsVerifiedIconBgOver; dialogsVerifiedIconFgActive: dialogsVerifiedIconFgOver; dialogsSendingIconFgActive: dialogsSendingIconFgOver; dialogsSentIconFgActive: dialogsSentIconFgOver; +dialogsUnreadBgActive: dialogsUnreadBgOver; +dialogsUnreadBgMutedActive: dialogsUnreadBgMutedOver; +dialogsUnreadFgActive: dialogsUnreadFgOver; dialogsForwardFg: #ffffff; diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index abd4bb402..6a55e0c44 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -32,13 +32,13 @@ windowShadowFg: #000000; imageBg: #000000; imageBgTransparent: #ffffff; activeButtonBg: windowActiveFill; -activeButtonBgOver: #46b4eb; // activeButtonBg; +activeButtonBgOver: #46b4eb; activeButtonFg: #ffffff; activeButtonFgOver: activeButtonFg; activeButtonSecondaryFg: #cceeff; activeButtonSecondaryFgOver: activeButtonSecondaryFg; lightButtonBg: windowBg; -lightButtonBgOver: #f2f7fa; // lightButtonBg; +lightButtonBgOver: #edf7ff; lightButtonFg: #2b99d5; lightButtonFgOver: lightButtonFg; menuIconFg: windowSubTextFg; @@ -61,7 +61,7 @@ boxBg: windowBg; boxTextFg: windowTextFg; boxTextFgGood: #4ab44a; boxTextFgError: #d84d4d; -boxTitleFg: #404040; // windowTextFg; +boxTitleFg: #404040; boxSearchBg: boxBg; boxSearchCancelIconFg: cancelIconFg; boxSearchCancelIconFgOver: cancelIconFgOver; @@ -83,7 +83,7 @@ introHeaderFg: windowTextFg; introErrorFg: windowTextFg; dialogsMenuIconFg: menuIconFg; dialogsBg: windowBg; -dialogsNameFg: #373737; // windowTextFg; +dialogsNameFg: #373737; dialogsChatIconFg: dialogsNameFg; dialogsDateFg: windowSubTextFg; dialogsTextFg: windowSubTextFg; @@ -93,6 +93,9 @@ dialogsVerifiedIconBg: #4abcf1; dialogsVerifiedIconFg: #ffffff; dialogsSendingIconFg: #c1c1c1; dialogsSentIconFg: #5dc452; +dialogsUnreadBg: windowActiveFill; +dialogsUnreadBgMuted: #bbbbbb; +dialogsUnreadFg: #ffffff; dialogsBgOver: windowOverBg; dialogsNameFgOver: windowTextFg; dialogsChatIconFgOver: dialogsNameFgOver; @@ -104,6 +107,9 @@ dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; dialogsVerifiedIconFgOver: dialogsVerifiedIconFg; dialogsSendingIconFgOver: dialogsSendingIconFg; dialogsSentIconFgOver: dialogsSentIconFg; +dialogsUnreadBgOver: dialogsUnreadBg; +dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; +dialogsUnreadFgOver: dialogsUnreadFg; dialogsBgActive: dialogsBgOver; dialogsNameFgActive: dialogsNameFgOver; dialogsChatIconFgActive: dialogsNameFgActive; @@ -115,6 +121,9 @@ dialogsVerifiedIconBgActive: dialogsVerifiedIconBgOver; dialogsVerifiedIconFgActive: dialogsVerifiedIconFgOver; dialogsSendingIconFgActive: dialogsSendingIconFgOver; dialogsSentIconFgActive: dialogsSentIconFgOver; +dialogsUnreadBgActive: dialogsUnreadBgOver; +dialogsUnreadBgMutedActive: dialogsUnreadBgMutedOver; +dialogsUnreadFgActive: dialogsUnreadFgOver; dialogsForwardFg: #ffffff; topBarBg: windowBg; emojiPanBg: windowBg; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 4504d8a2f..3f056af98 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -969,19 +969,11 @@ void ContactsBox::Inner::paintDialog(Painter &p, uint64 ms, PeerData *peer, Cont namew -= icon->width(); icon->paint(p, namex + qMin(data->name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width()); } - if (checkedRatio > 0) { - if (checkedRatio < 1) { - p.setPen(style::interpolate(st::contactsNameFg, st::contactsNameCheckedFg, checkedRatio)); - } else { - p.setPen(st::contactsNameCheckedFg); - } - } else { - p.setPen(st::contactsNameFg); - } + p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, checkedRatio)); data->name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width()); bool uname = (user || peer->isChannel()) && (data->statusText.at(0) == '@'); - p.setFont(st::contactsStatusFont->f); + p.setFont(st::contactsStatusFont); if (uname && !_lastQuery.isEmpty() && peer->userName().startsWith(_lastQuery, Qt::CaseInsensitive)) { int availw = width() - namex - st::contactsPadding.right(); QString first = '@' + peer->userName().mid(0, _lastQuery.size()), second = peer->userName().mid(_lastQuery.size()); diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 7ce9e5405..16a20cca7 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -485,16 +485,16 @@ ShareBox::Inner::Chat *ShareBox::Inner::getChat(Dialogs::Row *row) { void ShareBox::Inner::setActive(int active) { if (active != _active) { - auto changeNameFg = [this](int index, const style::color &from, const style::color &to) { + auto changeNameFg = [this](int index, float64 from, float64 to) { if (auto chat = getChatAtIndex(index)) { - chat->nameFg.start([this, peer = chat->peer] { + chat->nameActive.start([this, peer = chat->peer] { repaintChat(peer); - }, from->c, to->c, st::shareActivateDuration); + }, from, to, st::shareActivateDuration); } }; - changeNameFg(_active, st::shareNameActiveFg, st::shareNameFg); + changeNameFg(_active, 1., 0.); _active = active; - changeNameFg(_active, st::shareNameFg, st::shareNameActiveFg); + changeNameFg(_active, 0., 1.); } auto y = (_active < _columnCount) ? 0 : (_rowsTop + ((_active / _columnCount) * _rowHeight)); emit mustScrollTo(y, y + _rowHeight); @@ -509,11 +509,7 @@ void ShareBox::Inner::paintChat(Painter &p, uint64 ms, Chat *chat, int index) { auto photoTop = st::sharePhotoTop; chat->checkbox.paint(p, ms, x + photoLeft, y + photoTop, outerWidth); - if (chat->nameFg.animating()) { - p.setPen(chat->nameFg.current()); - } else { - p.setPen((index == _active) ? st::shareNameActiveFg : st::shareNameFg); - } + p.setPen(anim::pen(st::shareNameFg, st::shareNameActiveFg, chat->nameActive.current((index == _active) ? 1. : 0.))); auto nameWidth = (_rowWidth - st::shareColumnSkip); auto nameLeft = st::shareColumnSkip / 2; diff --git a/Telegram/SourceFiles/boxes/sharebox.h b/Telegram/SourceFiles/boxes/sharebox.h index 413344ca5..196a6912b 100644 --- a/Telegram/SourceFiles/boxes/sharebox.h +++ b/Telegram/SourceFiles/boxes/sharebox.h @@ -162,7 +162,7 @@ private: PeerData *peer; Ui::RoundImageCheckbox checkbox; Text name; - ColorAnimation nameFg; + FloatAnimation nameActive; }; void paintChat(Painter &p, uint64 ms, Chat *chat, int index); void updateChat(PeerData *peer); diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 0b1db3d34..379022939 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -284,7 +284,6 @@ QString Generator::typeToString(structure::Type type) const { case Tag::Color: return "style::color"; case Tag::Point: return "style::point"; case Tag::Size: return "style::size"; - case Tag::Transition: return "style::transition"; case Tag::Cursor: return "style::cursor"; case Tag::Align: return "style::align"; case Tag::Margins: return "style::margins"; @@ -306,7 +305,6 @@ QString Generator::typeToDefaultValue(structure::Type type) const { case Tag::Color: return "{ Qt::Uninitialized }"; case Tag::Point: return "{ 0, 0 }"; case Tag::Size: return "{ 0, 0 }"; - case Tag::Transition: return "anim::linear"; case Tag::Cursor: return "style::cur_default"; case Tag::Align: return "style::al_topleft"; case Tag::Margins: return "{ 0, 0, 0, 0 }"; @@ -357,7 +355,6 @@ QString Generator::valueAssignmentCode(structure::Value value) const { auto v(value.Size()); return QString("{ %1, %2 }").arg(pxValueName(v.width)).arg(pxValueName(v.height)); } break; - case Tag::Transition: return QString("anim::%1").arg(value.String().c_str()); case Tag::Cursor: return QString("style::cur_%1").arg(value.String().c_str()); case Tag::Align: return QString("style::al_%1").arg(value.String().c_str()); case Tag::Margins: { @@ -1114,7 +1111,6 @@ bool Generator::collectUniqueValues() { case Tag::Double: case Tag::String: case Tag::Color: - case Tag::Transition: case Tag::Cursor: case Tag::Align: break; case Tag::Pixels: pxValues_.insert(value.Int(), true); break; diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index a0e77fb5e..faf0b1464 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -120,7 +120,6 @@ std::string logType(const structure::Type &type) { { structure::TypeTag::Color , "color" }, { structure::TypeTag::Point , "point" }, { structure::TypeTag::Size , "size" }, - { structure::TypeTag::Transition, "transition" }, { structure::TypeTag::Cursor , "cursor" }, { structure::TypeTag::Align , "align" }, { structure::TypeTag::Margins , "margins" }, @@ -138,10 +137,6 @@ bool validateAnsiString(const QString &value) { return true; } -bool validateTransitionString(const QString &value) { - return QRegularExpression("^[a-zA-Z_]+$").match(value).hasMatch(); -} - bool validateCursorString(const QString &value) { return QRegularExpression("^[a-z_]+$").match(value).hasMatch(); } @@ -313,8 +308,6 @@ structure::Value ParsedFile::readValue() { return pointValue; } else if (auto sizeValue = readSizeValue()) { return sizeValue; - } else if (auto transitionValue = readTransitionValue()) { - return transitionValue; } else if (auto cursorValue = readCursorValue()) { return cursorValue; } else if (auto alignValue = readAlignValue()) { @@ -606,26 +599,6 @@ structure::Value ParsedFile::readSizeValue() { return {}; } -structure::Value ParsedFile::readTransitionValue() { - if (auto font = file_.getToken(BasicType::Name)) { - if (tokenValue(font) == "transition") { - assertNextToken(BasicType::LeftParenthesis); - - auto transition = tokenValue(assertNextToken(BasicType::Name)); - - assertNextToken(BasicType::RightParenthesis); - - if (validateTransitionString(transition)) { - return { structure::TypeTag::Transition, transition.toStdString() }; - } else { - logError(kErrorBadString) << "bad transition value"; - } - } - file_.putBack(); - } - return {}; -} - structure::Value ParsedFile::readCursorValue() { if (auto font = file_.getToken(BasicType::Name)) { if (tokenValue(font) == "cursor") { diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.h b/Telegram/SourceFiles/codegen/style/parsed_file.h index 1c2271fe4..820d1a6a8 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.h +++ b/Telegram/SourceFiles/codegen/style/parsed_file.h @@ -93,7 +93,6 @@ private: structure::Value readColorValue(); structure::Value readPointValue(); structure::Value readSizeValue(); - structure::Value readTransitionValue(); structure::Value readCursorValue(); structure::Value readAlignValue(); structure::Value readMarginsValue(); @@ -131,7 +130,6 @@ private: { "color" , { structure::TypeTag::Color } }, { "point" , { structure::TypeTag::Point } }, { "size" , { structure::TypeTag::Size } }, - { "transition", { structure::TypeTag::Transition } }, { "cursor" , { structure::TypeTag::Cursor } }, { "align" , { structure::TypeTag::Align } }, { "margins" , { structure::TypeTag::Margins } }, diff --git a/Telegram/SourceFiles/codegen/style/structure_types.cpp b/Telegram/SourceFiles/codegen/style/structure_types.cpp index e11042a34..f1c56f691 100644 --- a/Telegram/SourceFiles/codegen/style/structure_types.cpp +++ b/Telegram/SourceFiles/codegen/style/structure_types.cpp @@ -179,7 +179,6 @@ Value::Value(TypeTag type, int value) : Value(type, std::make_shared(value)) { if (type_.tag != TypeTag::String && - type_.tag != TypeTag::Transition && type_.tag != TypeTag::Cursor && type_.tag != TypeTag::Align) { type_.tag = TypeTag::Invalid; @@ -197,7 +196,6 @@ Value::Value(Type type, Qt::Initialization) : type_(type) { case TypeTag::Color: data_ = std::make_shared(data::color { 0, 0, 0, 255 }); break; case TypeTag::Point: data_ = std::make_shared(data::point { 0, 0 }); break; case TypeTag::Size: data_ = std::make_shared(data::size { 0, 0 }); break; - case TypeTag::Transition: data_ = std::make_shared("linear"); break; case TypeTag::Cursor: data_ = std::make_shared("default"); break; case TypeTag::Align: data_ = std::make_shared("topleft"); break; case TypeTag::Margins: data_ = std::make_shared(data::margins { 0, 0, 0, 0 }); break; diff --git a/Telegram/SourceFiles/codegen/style/structure_types.h b/Telegram/SourceFiles/codegen/style/structure_types.h index 53c5b595a..58d508769 100644 --- a/Telegram/SourceFiles/codegen/style/structure_types.h +++ b/Telegram/SourceFiles/codegen/style/structure_types.h @@ -48,7 +48,6 @@ enum class TypeTag { Color, Point, Size, - Transition, Cursor, Align, Margins, diff --git a/Telegram/SourceFiles/core/lambda_wrap.h b/Telegram/SourceFiles/core/lambda_wrap.h index cd1a15559..4e4abe2ae 100644 --- a/Telegram/SourceFiles/core/lambda_wrap.h +++ b/Telegram/SourceFiles/core/lambda_wrap.h @@ -52,8 +52,9 @@ struct lambda_wrap_helper_base { const call_type call; const destruct_type destruct; - static constexpr size_t kFullStorageSize = sizeof(void*) + 24U; + static constexpr size_t kFullStorageSize = 24U + sizeof(void*); static constexpr size_t kStorageSize = kFullStorageSize - sizeof(void*); + using alignment = uint64; template using IsLarge = std_::integral_constant) <= kStorageSize)>; @@ -144,7 +145,7 @@ struct lambda_wrap_helper_move_impl : new (lambda) JustLambda(static_cast(*source_lambda)); } static void construct_move_lambda_method(void *lambda, void *source) { - static_assert(alignof(JustLambda) <= alignof(void*), "Bad lambda alignment."); + static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment."); auto space = sizeof(JustLambda); auto aligned = std_::align(alignof(JustLambda), space, lambda, space); t_assert(aligned == lambda); @@ -219,7 +220,7 @@ struct lambda_wrap_helper_copy_impl : new (lambda) JustLambda(static_cast(*source_lambda)); } static void construct_copy_lambda_method(void *lambda, const void *source) { - static_assert(alignof(JustLambda) <= alignof(void*), "Bad lambda alignment."); + static_assert(alignof(JustLambda) <= alignof(typename Parent::alignment), "Bad lambda alignment."); auto space = sizeof(JustLambda); auto aligned = std_::align(alignof(JustLambda), space, lambda, space); t_assert(aligned == lambda); @@ -318,10 +319,12 @@ protected: lambda_unique(const BaseHelper *helper, const Private &) : helper_(helper) { } - const BaseHelper *helper_; - static_assert(BaseHelper::kStorageSize % sizeof(void*) == 0, "Bad pointer size."); - void *(storage_[BaseHelper::kStorageSize / sizeof(void*)]); + union { + void *(storage_[BaseHelper::kStorageSize / sizeof(void*)]); + typename BaseHelper::alignment alignment_; + }; + const BaseHelper *helper_; }; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 219b91c24..4d5ea7338 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -23,12 +23,6 @@ using "basic_types.style"; using "ui/widgets/widgets.style"; -dialogsUnreadFg: #ffffff; -dialogsUnreadFgActive: #5b94bf; -dialogsUnreadBg: windowActiveFill; -dialogsUnreadBgMuted: #bbbbbb; -dialogsUnreadBgActive: #ffffff; -dialogsUnreadBgMutedActive: #d3e2ee; dialogsUnreadFont: font(12px bold); dialogsUnreadHeight: 19px; dialogsUnreadPadding: 5px; @@ -168,11 +162,9 @@ dialogsUpdateButton: flatButton { color: activeButtonFg; overColor: activeButtonFgOver; - downColor: activeButtonFgOver; bgColor: activeButtonBg; overBgColor: activeButtonBgOver; - downBgColor: activeButtonBgOver; width: -34px; height: 46px; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index bae24e096..fc3dc862b 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -141,15 +141,17 @@ void paintRow(Painter &p, History *history, HistoryItem *item, Data::Draft *draf struct UnreadBadgeSizeData { QImage circle; - QPixmap left[4], right[4]; + QPixmap left[6], right[6]; }; class UnreadBadgeStyleData : public Data::AbstractStructure { public: UnreadBadgeSizeData sizes[UnreadBadgeSizesCount]; - const style::color *bg[4] = { + const style::color *bg[6] = { &st::dialogsUnreadBg, + &st::dialogsUnreadBgOver, &st::dialogsUnreadBgActive, &st::dialogsUnreadBgMuted, + &st::dialogsUnreadBgMutedOver, &st::dialogsUnreadBgMutedActive }; }; @@ -181,7 +183,7 @@ const style::icon *ChatTypeIcon(PeerData *peer, bool active, bool selected) { void paintUnreadBadge(Painter &p, const QRect &rect, const UnreadBadgeStyle &st) { t_assert(rect.height() == st.size); - int index = (st.active ? 0x01 : 0x00) | (st.muted ? 0x02 : 0x00); + int index = (st.muted ? 0x03 : 0x00) + (st.active ? 0x02 : (st.selected ? 0x01 : 0x00)); int size = st.size, sizehalf = size / 2; unreadBadgeStyle.createIfNull(); @@ -209,6 +211,7 @@ void paintUnreadBadge(Painter &p, const QRect &rect, const UnreadBadgeStyle &st) UnreadBadgeStyle::UnreadBadgeStyle() : align(style::al_right) , active(false) +, selected(false) , muted(false) , size(st::dialogsUnreadHeight) , sizeId(UnreadBadgeInDialogs) @@ -235,7 +238,7 @@ void paintUnreadCount(Painter &p, const QString &text, int x, int y, const Unrea paintUnreadBadge(p, QRect(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight), st); p.setFont(st.font); - p.setPen(st.active ? st::dialogsUnreadFgActive : st::dialogsUnreadFg); + p.setPen(st.active ? st::dialogsUnreadFgActive : (st.selected ? st::dialogsUnreadFgOver : st::dialogsUnreadFg)); p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + (unreadRectHeight - st.font->height) / 2 + st.font->ascent, text); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h index d5765bc60..4ec145ecd 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.h +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -50,6 +50,7 @@ struct UnreadBadgeStyle { style::align align; bool active; + bool selected; bool muted; int size; UnreadBadgeSize sizeId; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index ba87b6f03..93679c934 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1828,9 +1828,9 @@ void DialogsWidget::step_show(float64 ms, bool timer) { if (App::app()) App::app()->mtpUnpause(); } else { - a_coordUnder.update(dt, st::slideFunction); - a_coordOver.update(dt, st::slideFunction); - a_progress.update(dt, st::slideFunction); + a_coordUnder.update(dt, Window::SlideAnimation::transition()); + a_coordOver.update(dt, Window::SlideAnimation::transition()); + a_progress.update(dt, Window::SlideAnimation::transition()); } if (timer) update(); } diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index b17f4e898..6f21d0d5a 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -153,9 +153,6 @@ historyComposeField: flatTextarea { phPos: point(2px, 0px); phShift: 50px; phDuration: 100; - phLeftFunc: transition(linear); - phAlphaFunc: transition(linear); - phColorFunc: transition(linear); } historyComposeFieldMaxHeight: 224px; // historyMinHeight: 56px; diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp index 54a4e2861..71d835eab 100644 --- a/Telegram/SourceFiles/history/history_drag_area.cpp +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -38,7 +38,7 @@ DragArea::DragArea(QWidget *parent) : TWidget(parent) , _hiding(false) , _in(false) , a_opacity(0) -, a_color(st::dragColor->c) +, a_colorDrop(0) , _a_appearance(animation(this, &DragArea::step_appearance)) , _shadow(st::boxShadow) { setMouseTracking(true); @@ -52,7 +52,7 @@ void DragArea::mouseMoveEvent(QMouseEvent *e) { if (newIn != _in) { _in = newIn; a_opacity.start(1); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + a_colorDrop.start(_in ? 1. : 0.); _a_appearance.start(); } } @@ -63,7 +63,7 @@ void DragArea::dragMoveEvent(QDragMoveEvent *e) { if (newIn != _in) { _in = newIn; a_opacity.start(1); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + a_colorDrop.start(_in ? 1. : 0.); _a_appearance.start(); } e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction); @@ -90,7 +90,7 @@ void DragArea::paintEvent(QPaintEvent *e) { p.fillRect(r, st::dragBg); - p.setPen(a_color.current()); + p.setPen(anim::pen(st::dragColor, st::dragDropColor, a_colorDrop.current())); p.setFont(st::dragFont); p.drawText(QRect(0, (height() - st::dragHeight) / 2, width(), st::dragFont->height), _text, QTextOption(style::al_top)); @@ -109,7 +109,7 @@ void DragArea::dragLeaveEvent(QDragLeaveEvent *e) { static_cast(parentWidget())->dragLeaveEvent(e); _in = false; a_opacity.start(_hiding ? 0 : 1); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + a_colorDrop.start(_in ? 1. : 0.); _a_appearance.start(); } @@ -140,21 +140,21 @@ void DragArea::hideStart() { _hiding = true; _in = false; a_opacity.start(0); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + a_colorDrop.start(_in ? 1. : 0.); _a_appearance.start(); } void DragArea::hideFinish() { hide(); _in = false; - a_color = anim::cvalue(st::dragColor->c); + a_colorDrop = anim::fvalue(0.); } void DragArea::showStart() { _hiding = false; show(); a_opacity.start(1); - a_color.start((_in ? st::dragDropColor : st::dragColor)->c); + a_colorDrop.start(_in ? 1. : 0.); _a_appearance.start(); } @@ -162,14 +162,14 @@ void DragArea::step_appearance(float64 ms, bool timer) { float64 dt = ms / st::defaultDropdownDuration; if (dt >= 1) { a_opacity.finish(); - a_color.finish(); + a_colorDrop.finish(); if (_hiding) { hideFinish(); } _a_appearance.stop(); } else { a_opacity.update(dt, anim::linear); - a_color.update(dt, anim::linear); + a_colorDrop.update(dt, anim::linear); } if (timer) update(); } diff --git a/Telegram/SourceFiles/history/history_drag_area.h b/Telegram/SourceFiles/history/history_drag_area.h index 003bd9548..04bdb38ab 100644 --- a/Telegram/SourceFiles/history/history_drag_area.h +++ b/Telegram/SourceFiles/history/history_drag_area.h @@ -69,7 +69,7 @@ private: bool _hiding, _in; anim::fvalue a_opacity; - anim::cvalue a_color; + anim::fvalue a_colorDrop; Animation _a_appearance; Ui::RectShadow _shadow; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 759cb3f60..c4fcc31dc 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -417,7 +417,7 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uin p.setBrush(st::msgDateImgBgSelected); } else if (isThumbAnimation(ms)) { auto over = _animation->a_thumbOver.current(); - p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); + p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); @@ -755,7 +755,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, uin p.setBrush(st::msgDateImgBgSelected); } else if (isThumbAnimation(ms)) { auto over = _animation->a_thumbOver.current(); - p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); + p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); @@ -1109,7 +1109,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, p.setBrush(st::msgDateImgBgSelected); } else if (isThumbAnimation(ms)) { auto over = _animation->a_thumbOver.current(); - p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); + p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); @@ -1157,7 +1157,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); } else if (isThumbAnimation(ms)) { auto over = _animation->a_thumbOver.current(); - p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over)); + p.setBrush(anim::brush(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over)); } else { auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(outbg ? (over ? st::msgFileOutBgOver : st::msgFileOutBg) : (over ? st::msgFileInBgOver : st::msgFileInBg)); @@ -1731,7 +1731,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, uint6 p.setBrush(st::msgDateImgBgSelected); } else if (isThumbAnimation(ms)) { auto over = _animation->a_thumbOver.current(); - p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); + p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 20ff55111..8a1d11b03 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3010,7 +3010,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _field(this, st::historyComposeField, lang(lng_message_ph)) , _a_record(animation(this, &HistoryWidget::step_record)) , _a_recording(animation(this, &HistoryWidget::step_recording)) -, a_recordCancel(st::historyRecordCancel->c, st::historyRecordCancel->c) +, a_recordCancelActive(0, 0) , _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel))) , _kbScroll(this, st::botKbScroll) , _attachType(this, st::historyAttachDropdownMenu) @@ -3272,7 +3272,7 @@ void HistoryWidget::onTextChange() { _a_record.stop(); _inRecord = _inField = false; a_recordDown = anim::fvalue(0, 0); - a_recordCancel = anim::cvalue(st::historyRecordCancel->c, st::historyRecordCancel->c); + a_recordCancelActive = anim::fvalue(0, 0); } } if (updateCmdStartShown()) { @@ -5511,9 +5511,9 @@ void HistoryWidget::step_show(float64 ms, bool timer) { if (App::app()) App::app()->mtpUnpause(); } else { - a_coordUnder.update(dt, st::slideFunction); - a_coordOver.update(dt, st::slideFunction); - a_progress.update(dt, st::slideFunction); + a_coordUnder.update(dt, Window::SlideAnimation::transition()); + a_coordOver.update(dt, Window::SlideAnimation::transition()); + a_progress.update(dt, Window::SlideAnimation::transition()); } if (timer) { update(); @@ -5549,10 +5549,10 @@ void HistoryWidget::step_record(float64 ms, bool timer) { if (dt >= 1 || !_send->isHidden() || isBotStart() || isBlocked()) { _a_record.stop(); a_recordDown.finish(); - a_recordCancel.finish(); + a_recordCancelActive.finish(); } else { a_recordDown.update(dt, anim::linear); - a_recordCancel.update(dt, anim::linear); + a_recordCancelActive.update(dt, anim::linear); } if (timer) { if (_recording) { @@ -5672,7 +5672,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { if (inField != _inField && _recording) { _inField = inField; a_recordDown.start(_inField ? 1 : 0); - a_recordCancel.start(_inField ? st::historyRecordCancel->c : st::historyRecordCancelActive->c); + a_recordCancelActive.start(_inField ? 0. : 1.); _a_record.start(); } if (inReplyEdit != _inReplyEdit) { @@ -5721,7 +5721,7 @@ void HistoryWidget::stopRecording(bool send) { updateField(); a_recordDown.start(0); - a_recordCancel = anim::cvalue(st::historyRecordCancel->c, st::historyRecordCancel->c); + a_recordCancelActive = anim::fvalue(0., 0.); _a_record.start(); } @@ -7552,6 +7552,7 @@ HistoryWidget::PinnedBar::~PinnedBar() { } void HistoryWidget::updatePinnedBar(bool force) { + update(); if (!_pinnedBar) { return; } @@ -7560,6 +7561,7 @@ void HistoryWidget::updatePinnedBar(bool force) { return; } } + t_assert(_history != nullptr); if (!_pinnedBar->msg) { _pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId); @@ -7573,7 +7575,6 @@ void HistoryWidget::updatePinnedBar(bool force) { } destroyPinnedBar(); resizeEvent(0); - update(); } } @@ -7623,7 +7624,6 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { _pinnedBar->msg = 0; _pinnedBar->text.clear(); updatePinnedBar(); - update(); } if (!_pinnedBar->msg && App::api()) { App::api()->requestMessageData(_peer->asChannel(), _pinnedBar->msgId, replyEditMessageDataCallback()); @@ -8656,7 +8656,7 @@ void HistoryWidget::drawRecording(Painter &p) { int32 left = _attachPhoto->x() + _attachEmoji->width() + st::historyRecordFont->width(duration) + ((_send->width() - st::historyRecordVoice.width()) / 2); int32 right = width() - _send->width(); - p.setPen(a_recordCancel.current()); + p.setPen(anim::pen(st::historyRecordCancel, st::historyRecordCancelActive, a_recordCancelActive.current())); p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, lang(lng_record_cancel)); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index c479a6251..8af783d46 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -1124,7 +1124,7 @@ private: anim::ivalue a_recordingLevel = { 0, 0 }; int32 _recordingSamples = 0; anim::fvalue a_recordDown = { 0, 0 }; - anim::cvalue a_recordCancel; + anim::fvalue a_recordCancelActive; int32 _recordCancelWidth; bool kbWasHidden() const; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index e86ed36a6..d1bdca47e 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -172,7 +172,7 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons auto radialOpacity = (radial && loaded) ? _animation->radial.opacity() : 1.; if (_animation && _animation->_a_over.animating(context->ms)) { auto over = _animation->_a_over.current(); - p.fillRect(r, style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); + p.fillRect(r, anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { auto over = (_state & StateFlag::Over); p.fillRect(r, over ? st::msgDateImgBgOver : st::msgDateImgBg); @@ -708,11 +708,11 @@ void File::paint(Painter &p, const QRect &clip, const PaintContext *context) con auto inner = rtlrect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize, _width); p.setPen(Qt::NoPen); if (isThumbAnimation(context->ms)) { - float64 over = _animation->a_thumbOver.current(); - p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over)); + auto over = _animation->a_thumbOver.current(); + p.setBrush(anim::brush(st::msgFileInBg, st::msgFileInBgOver, over)); } else { bool over = ClickHandler::showAsActive(document->loading() ? _cancel : _open); - p.setBrush((over ? st::msgFileInBgOver : st::msgFileInBg)); + p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); } p.setRenderHint(QPainter::HighQualityAntialiasing); diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 3117d1930..22709b21b 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -65,10 +65,6 @@ introSize: size(400px, 460px); introSlideShift: 500px; // intro hiding animation introSlideDuration: 200; introSlideDelta: 0; // between hide start and show start -introHideFunc: transition(easeInCirc); -introShowFunc: transition(easeOutCirc); -introAlphaHideFunc: transition(easeOutCirc); -introAlphaShowFunc: transition(easeInCirc); introTextTop: 22px; introTextSize: size(400px, 93px); introCallSkip: 15px; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index d29791b21..f007cf49f 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -36,6 +36,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/effects/widget_fade_wrap.h" #include "styles/style_intro.h" #include "autoupdater.h" +#include "window/slide_animation.h" IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent) , _a_stage(animation(this, &IntroWidget::step_stage)) @@ -229,9 +230,9 @@ void IntroWidget::step_show(float64 ms, bool timer) { } if (App::app()) App::app()->mtpUnpause(); } else { - a_coordUnder.update(dt, st::slideFunction); - a_coordOver.update(dt, st::slideFunction); - a_shadow.update(dt, st::slideFunction); + a_coordUnder.update(dt, Window::SlideAnimation::transition()); + a_coordOver.update(dt, Window::SlideAnimation::transition()); + a_shadow.update(dt, Window::SlideAnimation::transition()); } if (timer) update(); } @@ -255,10 +256,10 @@ void IntroWidget::step_stage(float64 ms, bool timer) { step()->activate(); if (App::app()) App::app()->mtpUnpause(); } else { - a_coordShow.update(dt2, st::introShowFunc); - a_opacityShow.update(dt2, st::introAlphaShowFunc); - a_coordHide.update(dt1, st::introHideFunc); - a_opacityHide.update(dt1, st::introAlphaHideFunc); + a_coordShow.update(dt2, anim::easeOutCirc); + a_opacityShow.update(dt2, anim::easeInCirc); + a_coordHide.update(dt1, anim::easeInCirc); + a_opacityHide.update(dt1, anim::easeOutCirc); } if (timer) update(); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 3ae059064..72780f900 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2757,9 +2757,9 @@ void MainWidget::step_show(float64 ms, bool timer) { if (App::app()) App::app()->mtpUnpause(); } else { - a_coordUnder.update(dt, st::slideFunction); - a_coordOver.update(dt, st::slideFunction); - a_shadow.update(dt, st::slideFunction); + a_coordUnder.update(dt, Window::SlideAnimation::transition()); + a_coordOver.update(dt, Window::SlideAnimation::transition()); + a_shadow.update(dt, Window::SlideAnimation::transition()); } if (timer) update(); } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index d28788425..8b19be1c5 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -1820,16 +1820,8 @@ void MediaView::paintDocRadialLoading(Painter &p, bool radial, float64 radialOpa QRect inner(QPoint(_docIconRect.x() + ((_docIconRect.width() - st::radialSize.width()) / 2), _docIconRect.y() + ((_docIconRect.height() - st::radialSize.height()) / 2)), st::radialSize); p.setPen(Qt::NoPen); - if (o == 0.) { - p.setOpacity(_doc->loaded() ? radialOpacity : 1.); - p.setBrush(st::msgDateImgBg); - } else if (o == 1.) { - p.setOpacity(_doc->loaded() ? radialOpacity : 1.); - p.setBrush(st::msgDateImgBgOver); - } else { - p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - o)) + (st::msgDateImgBgOver->c.alphaF() * o)); - p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, o)); - } + p.setOpacity(_doc->loaded() ? radialOpacity : 1.); + p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, o)); p.setRenderHint(QPainter::HighQualityAntialiasing); p.drawEllipse(inner); diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index d1946aac7..6de183253 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -332,7 +332,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const } else if (_a_iconOver.animating()) { _a_iconOver.step(context->ms); auto over = a_iconOver.current(); - p.setBrush(style::interpolate(st::msgDateImgBg, st::msgDateImgBgOver, over)); + p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over)); } else { auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _savel)); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); @@ -454,10 +454,10 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const p.setBrush(st::msgFileInBgSelected); } else if (_a_iconOver.animating()) { _a_iconOver.step(context->ms); - float64 over = a_iconOver.current(); - p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over)); + auto over = a_iconOver.current(); + p.setBrush(anim::brush(st::msgFileInBg, st::msgFileInBgOver, over)); } else { - bool over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); + auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); } @@ -665,10 +665,10 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con p.setBrush(st::msgFileInBgSelected); } else if (_a_iconOver.animating()) { _a_iconOver.step(context->ms); - float64 over = a_iconOver.current(); - p.setBrush(style::interpolate(_st.songIconBg, _st.songOverBg, over)); + auto over = a_iconOver.current(); + p.setBrush(anim::brush(_st.songIconBg, _st.songOverBg, over)); } else { - bool over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); + auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); p.setBrush(over ? _st.songOverBg : _st.songIconBg); } @@ -741,7 +741,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con } else if (_a_iconOver.animating()) { _a_iconOver.step(context->ms); auto over = a_iconOver.current(); - p.setBrush(style::interpolate(wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex), wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex), over)); + p.setBrush(anim::brush(wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex), wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex), over)); } else { auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? (wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex)) : (wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex))); diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 7b67d7544..7172ff36b 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -2137,9 +2137,9 @@ void OverviewWidget::step_show(float64 ms, bool timer) { if (App::app()) App::app()->mtpUnpause(); } else { - a_coordUnder.update(dt, st::slideFunction); - a_coordOver.update(dt, st::slideFunction); - a_progress.update(dt, st::slideFunction); + a_coordUnder.update(dt, Window::SlideAnimation::transition()); + a_coordOver.update(dt, Window::SlideAnimation::transition()); + a_progress.update(dt, Window::SlideAnimation::transition()); } if (timer) { update(); diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 3e63025cc..3cc48564f 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/text/text.h" #include "ui/buttons/round_button.h" #include "styles/style_boxes.h" +#include "window/slide_animation.h" PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) , _a_show(animation(this, &PasscodeWidget::step_show)) @@ -140,9 +141,9 @@ void PasscodeWidget::step_show(float64 ms, bool timer) { Ui::showChatsList(); } else { - a_coordUnder.update(dt, st::slideFunction); - a_coordOver.update(dt, st::slideFunction); - a_shadow.update(dt, st::slideFunction); + a_coordUnder.update(dt, Window::SlideAnimation::transition()); + a_coordOver.update(dt, Window::SlideAnimation::transition()); + a_shadow.update(dt, Window::SlideAnimation::transition()); } if (timer) update(); } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index cef2dfd05..bf2863d2a 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -742,7 +742,7 @@ void MainWindow::psUpdateCounter() { if (trayIcon) { // Force Qt to use right icon size, not the larger one. QIcon forTrayIcon; - forTrayIcon.addPixmap(iconSizeSmall.width() >= 32 ? iconSmallPixmap32 : iconSmallPixmap16); + forTrayIcon.addPixmap(iconSizeSmall.width() >= 20 ? iconSmallPixmap32 : iconSmallPixmap16); trayIcon->setIcon(forTrayIcon); } diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index 624599163..42ef303d6 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -68,8 +68,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "styles/style_basic_types.h" #include "styles/style_basic.h" -#include "ui/twidget.h" #include "ui/animation.h" +#include "ui/twidget.h" #include "ui/flatinput.h" #include "ui/flattextarea.h" #include "ui/flatbutton.h" diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 41ab8dfa5..7b0873f00 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -3147,10 +3147,10 @@ void EmojiPan::step_slide(float64 ms, bool timer) { _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); + a_fromCoord.update(dt1, anim::easeInCirc); + a_fromAlpha.update(dt1, anim::easeOutCirc); + a_toCoord.update(dt2, anim::easeOutCirc); + a_toAlpha.update(dt2, anim::easeInCirc); } if (timer) update(); } diff --git a/Telegram/SourceFiles/ui/animation.cpp b/Telegram/SourceFiles/ui/animation.cpp index d404f7cf1..8a18e0595 100644 --- a/Telegram/SourceFiles/ui/animation.cpp +++ b/Telegram/SourceFiles/ui/animation.cpp @@ -46,52 +46,52 @@ AnimationManager *_manager = nullptr; namespace anim { -float64 linear(const float64 &delta, const float64 &dt) { +transition linear = [](const float64 &delta, const float64 &dt) { return delta * dt; -} +}; -float64 sineInOut(const float64 &delta, const float64 &dt) { +transition sineInOut = [](const float64 &delta, const float64 &dt) { return -(delta / 2) * (cos(M_PI * dt) - 1); -} +}; -float64 halfSine(const float64 &delta, const float64 &dt) { +transition halfSine = [](const float64 &delta, const float64 &dt) { return delta * sin(M_PI * dt / 2); -} +}; -float64 easeOutBack(const float64 &delta, const float64 &dt) { - static const float64 s = 1.70158; +transition easeOutBack = [](const float64 &delta, const float64 &dt) { + static constexpr auto s = 1.70158; const float64 t = dt - 1; return delta * (t * t * ((s + 1) * t + s) + 1); -} +}; -float64 easeInCirc(const float64 &delta, const float64 &dt) { +transition easeInCirc = [](const float64 &delta, const float64 &dt) { return -delta * (sqrt(1 - dt * dt) - 1); -} +}; -float64 easeOutCirc(const float64 &delta, const float64 &dt) { +transition easeOutCirc = [](const float64 &delta, const float64 &dt) { const float64 t = dt - 1; return delta * sqrt(1 - t * t); -} +}; -float64 easeInCubic(const float64 &delta, const float64 &dt) { +transition easeInCubic = [](const float64 &delta, const float64 &dt) { return delta * dt * dt * dt; -} +}; -float64 easeOutCubic(const float64 &delta, const float64 &dt) { +transition easeOutCubic = [](const float64 &delta, const float64 &dt) { const float64 t = dt - 1; return delta * (t * t * t + 1); -} +}; -float64 easeInQuint(const float64 &delta, const float64 &dt) { +transition easeInQuint = [](const float64 &delta, const float64 &dt) { const float64 t2 = dt * dt; return delta * t2 * t2 * dt; -} +}; -float64 easeOutQuint(const float64 &delta, const float64 &dt) { +transition easeOutQuint = [](const float64 &delta, const float64 &dt) { const float64 t = dt - 1, t2 = t * t; return delta * (t2 * t2 * t + 1); -} +}; void startManager() { stopManager(); diff --git a/Telegram/SourceFiles/ui/animation.h b/Telegram/SourceFiles/ui/animation.h index 52f17e015..f58e4b3b1 100644 --- a/Telegram/SourceFiles/ui/animation.h +++ b/Telegram/SourceFiles/ui/animation.h @@ -89,228 +89,188 @@ enum Notification { namespace anim { - typedef float64 (*transition)(const float64 &delta, const float64 &dt); +using transition = base::lambda_wrap; - float64 linear(const float64 &delta, const float64 &dt); - float64 sineInOut(const float64 &delta, const float64 &dt); - float64 halfSine(const float64 &delta, const float64 &dt); - float64 easeOutBack(const float64 &delta, const float64 &dt); - float64 easeInCirc(const float64 &delta, const float64 &dt); - float64 easeOutCirc(const float64 &delta, const float64 &dt); - float64 easeInCubic(const float64 &delta, const float64 &dt); - float64 easeOutCubic(const float64 &delta, const float64 &dt); - float64 easeInQuint(const float64 &delta, const float64 &dt); - float64 easeOutQuint(const float64 &delta, const float64 &dt); +extern transition linear; +extern transition sineInOut; +extern transition halfSine; +extern transition easeOutBack; +extern transition easeInCirc; +extern transition easeOutCirc; +extern transition easeInCubic; +extern transition easeOutCirc; +extern transition easeInQuint; +extern transition easeOutQuint; - template - float64 bumpy(const float64 &delta, const float64 &dt) { - struct Bumpy { - Bumpy() - : bump(BumpRatioNumerator / float64(BumpRatioDenominator)) - , dt0(bump - sqrt(bump * (bump - 1.))) - , k(1 / (2 * dt0 - 1)) { - } - float64 bump; - float64 dt0; - float64 k; - }; - static Bumpy data; - return delta * (data.bump - data.k * (dt - data.dt0) * (dt - data.dt0)); +inline transition bumpy(float64 bump) { + auto dt0 = (bump - sqrt(bump * (bump - 1.))); + auto k = (1 / (2 * dt0 - 1)); + return [bump, dt0, k](float64 delta, float64 dt) { + return delta * (bump - k * (dt - dt0) * (dt - dt0)); + }; +} + +// Basic animated value. +class value { +public: + using ValueType = float64; + + value() = default; + value(float64 from) : _cur(from), _from(from) { + } + value(float64 from, float64 to) : _cur(from), _from(from), _delta(to - from) { + } + void start(float64 to) { + _from = _cur; + _delta = to - _from; + } + void restart() { + _delta = _from + _delta - _cur; + _from = _cur; } - class fvalue { // float animated value - public: - using ValueType = float64; + float64 from() const { + return _from; + } + float64 current() const { + return _cur; + } + float64 to() const { + return _from + _delta; + } + void add(float64 delta) { + _from += delta; + _cur += delta; + } + value &update(float64 dt, const transition &func) { + _cur = _from + func(_delta, dt); + return *this; + } + void finish() { + _cur = _from + _delta; + _from = _cur; + _delta = 0; + } - fvalue() = default; - fvalue(float64 from) : _cur(from), _from(from) { - } - fvalue(float64 from, float64 to) : _cur(from), _from(from), _delta(to - from) { - } - void start(float64 to) { - _from = _cur; - _delta = to - _from; - } - void restart() { - _delta = _from + _delta - _cur; - _from = _cur; - } +private: + float64 _cur = 0.; + float64 _from = 0.; + float64 _delta = 0.; - float64 from() const { - return _from; - } - float64 current() const { - return _cur; - } - float64 to() const { - return _from + _delta; - } - void add(float64 delta) { - _from += delta; - _cur += delta; - } - fvalue &update(float64 dt, transition func) { - _cur = _from + (*func)(_delta, dt); - return *this; - } - void finish() { - _cur = _from + _delta; - _from = _cur; - _delta = 0; - } +}; - private: - float64 _cur = 0.; - float64 _from = 0.; - float64 _delta = 0.; +using fvalue = value; +class ivalue { // int animated value +public: + using ValueType = int; + + ivalue() = default; + ivalue(int from) : _cur(from), _from(float64(from)) { + } + ivalue(int from, int to) : _cur(from), _from(float64(from)), _delta(float64(to - from)) { + } + void start(int32 to) { + _from = float64(_cur); + _delta = float64(to) - _from; + } + void restart() { + _delta = _from + _delta - float64(_cur); + _from = float64(_cur); + } + + int from() const { + return _from; + } + int current() const { + return _cur; + } + int to() const { + return qRound(_from + _delta); + } + void add(int delta) { + _from += delta; + _cur += delta; + } + ivalue &update(float64 dt, const transition &func) { + _cur = qRound(_from + func(_delta, dt)); + return *this; + } + void finish() { + _cur = qRound(_from + _delta); + _from = _cur; + _delta = 0; + } + +private: + int _cur = 0; + float64 _from = 0.; + float64 _delta = 0.; + +}; + +void startManager(); +void stopManager(); +void registerClipManager(Media::Clip::Manager *manager); + +inline int interpolate(int a, int b, float64 b_ratio) { + return qRound(a + float64(b - a) * b_ratio); +} + +inline QColor color(QColor a, QColor b, float64 b_ratio) { + auto bOpacity = snap(interpolate(0, 255, b_ratio), 0, 255) + 1; + auto aOpacity = (256 - bOpacity); + return { + (a.red() * aOpacity + b.red() * bOpacity) >> 8, + (a.green() * aOpacity + b.green() * bOpacity) >> 8, + (a.blue() * aOpacity + b.blue() * bOpacity) >> 8, + (a.alpha() * aOpacity + b.alpha() * bOpacity) >> 8 }; +} - class ivalue { // int animated value - public: - using ValueType = int; +inline QColor color(const style::color &a, QColor b, float64 b_ratio) { + return color(a->c, b, b_ratio); +} - ivalue() = default; - ivalue(int from) : _cur(from), _from(float64(from)) { - } - ivalue(int from, int to) : _cur(from), _from(float64(from)), _delta(float64(to - from)) { - } - void start(int32 to) { - _from = float64(_cur); - _delta = float64(to) - _from; - } - void restart() { - _delta = _from + _delta - float64(_cur); - _from = float64(_cur); - } +inline QColor color(QColor a, const style::color &b, float64 b_ratio) { + return color(a, b->c, b_ratio); +} - int from() const { - return _from; - } - int current() const { - return _cur; - } - int to() const { - return qRound(_from + _delta); - } - void add(int delta) { - _from += delta; - _cur += delta; - } - ivalue &update(float64 dt, transition func) { - _cur = qRound(_from + (*func)(_delta, dt)); - return *this; - } - void finish() { - _cur = qRound(_from + _delta); - _from = _cur; - _delta = 0; - } +inline QColor color(const style::color &a, const style::color &b, float64 b_ratio) { + return color(a->c, b->c, b_ratio); +} - private: - int _cur = 0; - float64 _from = 0.; - float64 _delta = 0.; +inline QPen pen(QColor a, QColor b, float64 b_ratio) { + return color(a, b, b_ratio); +} - }; +inline QPen pen(const style::color &a, QColor b, float64 b_ratio) { + return (b_ratio > 0) ? pen(a->c, b, b_ratio) : a; +} - class cvalue { // QColor animated value - public: - using ValueType = QColor; +inline QPen pen(QColor a, const style::color &b, float64 b_ratio) { + return (b_ratio < 1) ? pen(a, b->c, b_ratio) : b; +} - cvalue() = default; - explicit cvalue(QColor from) - : _cur(from) - , _from_r(from.redF()) - , _from_g(from.greenF()) - , _from_b(from.blueF()) - , _from_a(from.alphaF()) { - } - cvalue(QColor from, QColor to) - : _cur(from) - , _from_r(from.redF()) - , _from_g(from.greenF()) - , _from_b(from.blueF()) - , _from_a(from.alphaF()) - , _delta_r(to.redF() - from.redF()) - , _delta_g(to.greenF() - from.greenF()) - , _delta_b(to.blueF() - from.blueF()) - , _delta_a(to.alphaF() - from.alphaF()) { - } - void start(QColor to) { - _from_r = _cur.redF(); - _from_g = _cur.greenF(); - _from_b = _cur.blueF(); - _from_a = _cur.alphaF(); - _delta_r = to.redF() - _from_r; - _delta_g = to.greenF() - _from_g; - _delta_b = to.blueF() - _from_b; - _delta_a = to.alphaF() - _from_a; - } - void restart() { - _delta_r = _from_r + _delta_r - _cur.redF(); - _delta_g = _from_g + _delta_g - _cur.greenF(); - _delta_b = _from_b + _delta_b - _cur.blueF(); - _delta_a = _from_a + _delta_a - _cur.alphaF(); - _from_r = _cur.redF(); - _from_g = _cur.greenF(); - _from_b = _cur.blueF(); - _from_a = _cur.alphaF(); - } - QColor from() const { - QColor result; - result.setRedF(_from_r); - result.setGreenF(_from_g); - result.setBlueF(_from_b); - result.setAlphaF(_from_a); - return result; - } - QColor current() const { - return _cur; - } - QColor to() const { - QColor result; - result.setRedF(_from_r + _delta_r); - result.setGreenF(_from_g + _delta_g); - result.setBlueF(_from_b + _delta_b); - result.setAlphaF(_from_a + _delta_a); - return result; - } - cvalue &update(const float64 &dt, transition func) { - _cur.setRedF(_from_r + (*func)(_delta_r, dt)); - _cur.setGreenF(_from_g + (*func)(_delta_g, dt)); - _cur.setBlueF(_from_b + (*func)(_delta_b, dt)); - _cur.setAlphaF(_from_a + (*func)(_delta_a, dt)); - return *this; - } - void finish() { - _cur.setRedF(_from_r + _delta_r); - _cur.setGreenF(_from_g + _delta_g); - _cur.setBlueF(_from_b + _delta_b); - _cur.setAlphaF(_from_a + _delta_a); - _from_r = _cur.redF(); - _from_g = _cur.greenF(); - _from_b = _cur.blueF(); - _from_a = _cur.alphaF(); - _delta_r = _delta_g = _delta_b = _delta_a = 0; - } +inline QPen pen(const style::color &a, const style::color &b, float64 b_ratio) { + return (b_ratio > 0) ? ((b_ratio < 1) ? pen(a->c, b->c, b_ratio) : b) : a; +} - private: - QColor _cur; - float64 _from_r = 0.; - float64 _from_g = 0.; - float64 _from_b = 0.; - float64 _from_a = 0.; - float64 _delta_r = 0.; - float64 _delta_g = 0.; - float64 _delta_b = 0.; - float64 _delta_a = 0.; +inline QBrush brush(QColor a, QColor b, float64 b_ratio) { + return color(a, b, b_ratio); +} - }; +inline QBrush brush(const style::color &a, QColor b, float64 b_ratio) { + return (b_ratio > 0) ? brush(a->c, b, b_ratio) : a; +} - void startManager(); - void stopManager(); - void registerClipManager(Media::Clip::Manager *manager); +inline QBrush brush(QColor a, const style::color &b, float64 b_ratio) { + return (b_ratio < 1) ? brush(a, b->c, b_ratio) : b; +} + +inline QBrush brush(const style::color &a, const style::color &b, float64 b_ratio) { + return (b_ratio > 0) ? ((b_ratio < 1) ? brush(a->c, b->c, b_ratio) : b) : a; +} }; @@ -573,7 +533,6 @@ private: using FloatAnimation = SimpleAnimation; using IntAnimation = SimpleAnimation; -using ColorAnimation = SimpleAnimation; class AnimationManager : public QObject { Q_OBJECT diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.cpp b/Telegram/SourceFiles/ui/buttons/icon_button.cpp index ee61a36c9..e5794fe68 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/icon_button.cpp @@ -93,9 +93,7 @@ MaskButton::MaskButton(QWidget *parent, const style::MaskButton &st) : Button(pa void MaskButton::onStateChanged(int oldState, ButtonStateChangeSource source) { auto over = (_state & StateOver); if (over != (oldState & StateOver)) { - auto from = over ? _st.iconBg->c : _st.iconBgOver->c; - auto to = over ? _st.iconBgOver->c : _st.iconBg->c; - _a_iconBg.start([this] { update(); }, from, to, _st.duration); + _a_iconOver.start([this] { update(); }, over ? 0. : 1., over ? 1. : 0., _st.duration); } } @@ -115,11 +113,7 @@ void MaskButton::paintEvent(QPaintEvent *e) { p.fillRect(clip, _st.bg); } if (icon.intersects(clip)) { - if (_a_iconBg.animating(getms())) { - p.fillRect(icon.intersected(clip), _a_iconBg.current()); - } else { - p.fillRect(icon.intersected(clip), (_state & StateOver) ? _st.iconBgOver : _st.iconBg); - } + p.fillRect(icon.intersected(clip), anim::brush(_st.iconBg, _st.iconBgOver, _a_iconOver.current(getms(), (_state & StateOver) ? 1. : 0.))); _st.icon.paint(p, position, width()); } } diff --git a/Telegram/SourceFiles/ui/buttons/icon_button.h b/Telegram/SourceFiles/ui/buttons/icon_button.h index a782681e1..7decf5144 100644 --- a/Telegram/SourceFiles/ui/buttons/icon_button.h +++ b/Telegram/SourceFiles/ui/buttons/icon_button.h @@ -58,7 +58,7 @@ protected: private: const style::MaskButton &_st; - ColorAnimation _a_iconBg; + FloatAnimation _a_iconOver; }; diff --git a/Telegram/SourceFiles/ui/effects/round_image_checkbox.cpp b/Telegram/SourceFiles/ui/effects/round_image_checkbox.cpp index f0fcb5456..ee57868d2 100644 --- a/Telegram/SourceFiles/ui/effects/round_image_checkbox.cpp +++ b/Telegram/SourceFiles/ui/effects/round_image_checkbox.cpp @@ -164,7 +164,7 @@ void RoundImageCheckbox::setChecked(bool checked, SetStyle speed) { } if (speed == SetStyle::Animated) { prepareWideCache(); - _selection.start(_updateCallback, _checked ? 0 : 1, _checked ? 1 : 0, _st.selectDuration, anim::bumpy<125, 100>); + _selection.start(_updateCallback, _checked ? 0 : 1, _checked ? 1 : 0, _st.selectDuration, anim::bumpy(1.25)); } else { _selection.finish(); } diff --git a/Telegram/SourceFiles/ui/flatbutton.cpp b/Telegram/SourceFiles/ui/flatbutton.cpp index 108b77a0b..5d44c1b74 100644 --- a/Telegram/SourceFiles/ui/flatbutton.cpp +++ b/Telegram/SourceFiles/ui/flatbutton.cpp @@ -26,8 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatButton &st) : Button(parent) , _text(text) , _st(st) -, a_bg(st.bgColor->c) -, a_text(st.color->c) +, a_over(0) , _a_appearance(animation(this, &FlatButton::step_appearance)) { if (_st.width < 0) { _width = textWidth() - _st.width; @@ -73,25 +72,18 @@ void FlatButton::step_appearance(float64 ms, bool timer) { float64 dt = ms / _st.duration; if (dt >= 1) { _a_appearance.stop(); - a_bg.finish(); - a_text.finish(); + a_over.finish(); } else { - a_bg.update(dt, anim::linear); - a_text.update(dt, anim::linear); + a_over.update(dt, anim::linear); } if (timer) update(); } void FlatButton::onStateChange(int oldState, ButtonStateChangeSource source) { - auto &bgColorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downBgColor : _st.overBgColor) : _st.bgColor; - auto &colorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downColor : _st.overColor) : _st.color; - - a_bg.start(bgColorTo->c); - a_text.start(colorTo->c); + a_over.start((_state & StateOver) ? 1. : 0.); if (source == ButtonByUser || source == ButtonByPress) { _a_appearance.stop(); - a_bg.finish(); - a_text.finish(); + a_over.finish(); update(); } else { _a_appearance.start(); @@ -103,23 +95,12 @@ void FlatButton::paintEvent(QPaintEvent *e) { QRect r(0, height() - _st.height, width(), _st.height); - auto animating = _a_appearance.animating(); - auto &bg = (_state & StateOver) ? ((_state & StateDown) ? _st.downBgColor : _st.overBgColor) : _st.bgColor; - auto &fg = (_state & StateOver) ? ((_state & StateDown) ? _st.downColor : _st.overColor) : _st.color; p.setOpacity(_opacity); - if (animating) { - p.fillRect(r, a_bg.current()); - } else { - p.fillRect(r, bg); - } + p.fillRect(r, anim::brush(_st.bgColor, _st.overBgColor, a_over.current())); p.setFont((_state & StateOver) ? _st.overFont : _st.font); p.setRenderHint(QPainter::TextAntialiasing); - if (animating) { - p.setPen(a_text.current()); - } else { - p.setPen(fg); - } + p.setPen(anim::pen(_st.color, _st.overColor, a_over.current())); int32 top = (_state & StateOver) ? ((_state & StateDown) ? _st.downTextTop : _st.overTextTop) : _st.textTop; r.setTop(top); diff --git a/Telegram/SourceFiles/ui/flatbutton.h b/Telegram/SourceFiles/ui/flatbutton.h index a4bcc3148..bb11410cc 100644 --- a/Telegram/SourceFiles/ui/flatbutton.h +++ b/Telegram/SourceFiles/ui/flatbutton.h @@ -51,7 +51,7 @@ private: const style::flatButton &_st; - anim::cvalue a_bg, a_text; + anim::fvalue a_over; Animation _a_appearance; float64 _opacity = 1.; diff --git a/Telegram/SourceFiles/ui/flatinput.cpp b/Telegram/SourceFiles/ui/flatinput.cpp index de9a5eae8..10b872d7b 100644 --- a/Telegram/SourceFiles/ui/flatinput.cpp +++ b/Telegram/SourceFiles/ui/flatinput.cpp @@ -79,9 +79,10 @@ FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString , _phVisible(!v.length()) , a_phLeft(_phVisible ? 0 : st.phShift) , a_phAlpha(_phVisible ? 1 : 0) -, a_phColor(st.phColor->c) -, a_borderColor(st.borderColor->c) -, a_bgColor(st.bgColor->c) +, a_phColorFocus(0) +, a_borderColorActive(0) +, a_borderColorError(0) +, a_bgColorActive(0) , _a_appearance(animation(this, &FlatInput::step_appearance)) , _notingBene(0) , _st(st) { @@ -173,10 +174,11 @@ void FlatInput::paintEvent(QPaintEvent *e) { Painter p(this); p.setRenderHint(QPainter::HighQualityAntialiasing); - auto pen = QPen(a_borderColor.current()); + auto borderColor = anim::color(_st.borderColor, _st.borderActive, a_borderColorActive.current()); + auto pen = anim::pen(borderColor, _st.borderError, a_borderColorError.current()); pen.setWidth(_st.borderWidth); p.setPen(pen); - p.setBrush(QBrush(a_bgColor.current())); + p.setBrush(anim::brush(_st.bgColor, _st.bgActive, a_bgColorActive.current())); p.drawRoundedRect(QRectF(0, 0, width(), height()).marginsRemoved(QMarginsF(_st.borderWidth / 2., _st.borderWidth / 2., _st.borderWidth / 2., _st.borderWidth / 2.)), st::buttonRadius - (_st.borderWidth / 2.), st::buttonRadius - (_st.borderWidth / 2.)); p.setRenderHint(QPainter::HighQualityAntialiasing, false); @@ -202,22 +204,20 @@ void FlatInput::paintEvent(QPaintEvent *e) { } void FlatInput::focusInEvent(QFocusEvent *e) { - a_phColor.start(_st.phFocusColor->c); - if (_notingBene <= 0) { - a_borderColor.start(_st.borderActive->c); - } - a_bgColor.start(_st.bgActive->c); + a_phColorFocus.start(1.); + a_borderColorActive.start(1.); + a_borderColorError.restart(); + a_bgColorActive.start(1); _a_appearance.start(); QLineEdit::focusInEvent(e); emit focused(); } void FlatInput::focusOutEvent(QFocusEvent *e) { - a_phColor.start(_st.phColor->c); - if (_notingBene <= 0) { - a_borderColor.start(_st.borderColor->c); - } - a_bgColor.start(_st.bgColor->c); + a_phColorFocus.start(0.); + a_borderColorActive.start(0.); + a_borderColorError.restart(); + a_bgColorActive.start(0); _a_appearance.start(); QLineEdit::focusOutEvent(e); emit blurred(); @@ -263,23 +263,25 @@ void FlatInput::step_appearance(float64 ms, bool timer) { _a_appearance.stop(); a_phLeft.finish(); a_phAlpha.finish(); - a_phColor.finish(); - a_bgColor.finish(); + a_phColorFocus.finish(); + a_bgColorActive.finish(); if (_notingBene > 0) { _notingBene = -1; - a_borderColor.start((hasFocus() ? _st.borderActive : _st.borderColor)->c); + a_borderColorActive.restart(); + a_borderColorError.start(0); _a_appearance.start(); return; } else if (_notingBene) { _notingBene = 0; } - a_borderColor.finish(); + a_borderColorActive.finish(); } else { - a_phLeft.update(dt, _st.phLeftFunc); - a_phAlpha.update(dt, _st.phAlphaFunc); - a_phColor.update(dt, _st.phColorFunc); - a_bgColor.update(dt, _st.phColorFunc); - a_borderColor.update(dt, _st.phColorFunc); + a_phLeft.update(dt, anim::linear); + a_phAlpha.update(dt, anim::linear); + a_phColorFocus.update(dt, anim::linear); + a_bgColorActive.update(dt, anim::linear); + a_borderColorActive.update(dt, anim::linear); + a_borderColorError.update(dt, anim::linear); } if (timer) update(); } @@ -326,8 +328,8 @@ void FlatInput::correctValue(const QString &was, QString &now) { } void FlatInput::phPrepare(Painter &p) { - p.setFont(_st.font->f); - p.setPen(a_phColor.current()); + p.setFont(_st.font); + p.setPen(anim::pen(_st.phColor, _st.phFocusColor, a_phColorFocus.current())); } void FlatInput::keyPressEvent(QKeyEvent *e) { @@ -381,7 +383,8 @@ void FlatInput::onTextChange(const QString &text) { void FlatInput::notaBene() { _notingBene = 1; setFocus(); - a_borderColor.start(_st.borderError->c); + a_borderColorError.start(1.); + a_borderColorActive.restart(); _a_appearance.start(); } @@ -594,12 +597,13 @@ InputArea::InputArea(QWidget *parent, const style::InputArea &st, const QString , _placeholderVisible(val.isEmpty()) , a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift) , a_placeholderOpacity(_placeholderVisible ? 1 : 0) -, a_placeholderFg(st.placeholderFg->c) +, a_placeholderFgActive(0) , _a_placeholderFg(animation(this, &InputArea::step_placeholderFg)) , _a_placeholderShift(animation(this, &InputArea::step_placeholderShift)) , a_borderOpacityActive(0) -, a_borderFg(st.borderFg->c) +, a_borderFgActive(0) +, a_borderFgError(0) , _a_border(animation(this, &InputArea::step_border)) , _focused(false) @@ -746,8 +750,10 @@ void InputArea::paintEvent(QPaintEvent *e) { p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg); } if (_st.borderActive && a_borderOpacityActive.current() > 0) { + auto borderFgActive = anim::color(_st.borderFg, _st.borderFgActive, a_borderFgActive.current()); + auto borderFg = anim::color(borderFgActive, _st.borderFgError, a_borderFgError.current()); p.setOpacity(a_borderOpacityActive.current()); - p.fillRect(0, height() - _st.borderActive, width(), _st.borderActive, a_borderFg.current()); + p.fillRect(0, height() - _st.borderActive, width(), _st.borderActive, borderFg); p.setOpacity(1); } @@ -765,7 +771,7 @@ void InputArea::paintEvent(QPaintEvent *e) { if (rtl()) r.moveLeft(width() - r.left() - r.width()); p.setFont(_st.font); - p.setPen(a_placeholderFg.current()); + p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, a_placeholderFgActive.current())); p.drawText(r, _placeholder, _st.placeholderAlign); p.restore(); @@ -774,7 +780,8 @@ void InputArea::paintEvent(QPaintEvent *e) { } void InputArea::startBorderAnimation() { - a_borderFg.start((_error ? _st.borderFgError : (_focused ? _st.borderFgActive : _st.borderFg))->c); + a_borderFgActive.start(_focused ? 1. : 0.); + a_borderFgError.start(_error ? 1. : 0.); a_borderOpacityActive.start((_error || _focused) ? 1 : 0); _a_border.start(); } @@ -801,7 +808,7 @@ void InputArea::focusInInner() { if (!_focused) { _focused = true; - a_placeholderFg.start(_st.placeholderFgActive->c); + a_placeholderFgActive.start(1.); _a_placeholderFg.start(); startBorderAnimation(); @@ -818,7 +825,7 @@ void InputArea::focusOutInner() { if (_focused) { _focused = false; - a_placeholderFg.start(_st.placeholderFg->c); + a_placeholderFgActive.start(0.); _a_placeholderFg.start(); startBorderAnimation(); @@ -1149,9 +1156,9 @@ void InputArea::step_placeholderFg(float64 ms, bool timer) { float64 dt = ms / _st.duration; if (dt >= 1) { _a_placeholderFg.stop(); - a_placeholderFg.finish(); + a_placeholderFgActive.finish(); } else { - a_placeholderFg.update(dt, anim::linear); + a_placeholderFgActive.update(dt, anim::linear); } if (timer) update(); } @@ -1174,10 +1181,12 @@ void InputArea::step_border(float64 ms, bool timer) { bool res = true; if (dt >= 1) { _a_border.stop(); - a_borderFg.finish(); + a_borderFgActive.finish(); + a_borderFgError.finish(); a_borderOpacityActive.finish(); } else { - a_borderFg.update(dt, anim::linear); + a_borderFgActive.update(dt, anim::linear); + a_borderFgError.update(dt, anim::linear); a_borderOpacityActive.update(dt, anim::linear); } if (timer) update(); @@ -1317,12 +1326,13 @@ InputField::InputField(QWidget *parent, const style::InputField &st, const QStri , _placeholderVisible(val.isEmpty()) , a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift) , a_placeholderOpacity(_placeholderVisible ? 1 : 0) -, a_placeholderFg(st.placeholderFg->c) +, a_placeholderFgActive(0) , _a_placeholderFg(animation(this, &InputField::step_placeholderFg)) , _a_placeholderShift(animation(this, &InputField::step_placeholderShift)) , a_borderOpacityActive(0) -, a_borderFg(st.borderFg->c) +, a_borderFgActive(0) +, a_borderFgError(0) , _a_border(animation(this, &InputField::step_border)) , _focused(false) @@ -1455,8 +1465,10 @@ void InputField::paintEvent(QPaintEvent *e) { p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b); } if (_st.borderActive && a_borderOpacityActive.current() > 0) { + auto borderFgActive = anim::color(_st.borderFg, _st.borderFgActive, a_borderFgActive.current()); + auto borderFg = anim::color(borderFgActive, _st.borderFgError, a_borderFgError.current()); p.setOpacity(a_borderOpacityActive.current()); - p.fillRect(0, height() - _st.borderActive, width(), _st.borderActive, a_borderFg.current()); + p.fillRect(0, height() - _st.borderActive, width(), _st.borderActive, borderFg); p.setOpacity(1); } @@ -1474,7 +1486,7 @@ void InputField::paintEvent(QPaintEvent *e) { if (rtl()) r.moveLeft(width() - r.left() - r.width()); p.setFont(_st.font); - p.setPen(a_placeholderFg.current()); + p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, a_placeholderFgActive.current())); p.drawText(r, _placeholder, _st.placeholderAlign); p.restore(); @@ -1483,7 +1495,8 @@ void InputField::paintEvent(QPaintEvent *e) { } void InputField::startBorderAnimation() { - a_borderFg.start((_error ? _st.borderFgError : (_focused ? _st.borderFgActive : _st.borderFg))->c); + a_borderFgActive.start(_focused ? 1. : 0.); + a_borderFgError.start(_error ? 1. : 0.); a_borderOpacityActive.start((_error || _focused) ? 1 : 0); _a_border.start(); } @@ -1510,7 +1523,7 @@ void InputField::focusInInner() { if (!_focused) { _focused = true; - a_placeholderFg.start(_st.placeholderFgActive->c); + a_placeholderFgActive.start(1.); _a_placeholderFg.start(); startBorderAnimation(); @@ -1527,7 +1540,7 @@ void InputField::focusOutInner() { if (_focused) { _focused = false; - a_placeholderFg.start(_st.placeholderFg->c); + a_placeholderFgActive.start(0.); _a_placeholderFg.start(); startBorderAnimation(); @@ -1889,9 +1902,9 @@ void InputField::step_placeholderFg(float64 ms, bool timer) { float64 dt = ms / _st.duration; if (dt >= 1) { _a_placeholderFg.stop(); - a_placeholderFg.finish(); + a_placeholderFgActive.finish(); } else { - a_placeholderFg.update(dt, anim::linear); + a_placeholderFgActive.update(dt, anim::linear); } if (timer) update(); } @@ -1918,10 +1931,12 @@ void InputField::step_border(float64 ms, bool timer) { float64 dt = ms / _st.duration; if (dt >= 1) { _a_border.stop(); - a_borderFg.finish(); + a_borderFgActive.finish(); + a_borderFgError.finish(); a_borderOpacityActive.finish(); } else { - a_borderFg.update(dt, anim::linear); + a_borderFgActive.update(dt, anim::linear); + a_borderFgError.update(dt, anim::linear); a_borderOpacityActive.update(dt, anim::linear); } if (timer) update(); @@ -2064,12 +2079,13 @@ MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st, , _placeholderFast(false) , a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift) , a_placeholderOpacity(_placeholderVisible ? 1 : 0) -, a_placeholderFg(st.placeholderFg->c) +, a_placeholderFgActive(0) , _a_placeholderFg(animation(this, &MaskedInputField::step_placeholderFg)) , _a_placeholderShift(animation(this, &MaskedInputField::step_placeholderShift)) , a_borderOpacityActive(0) -, a_borderFg(st.borderFg->c) +, a_borderFgActive(0) +, a_borderFgError(0) , _a_border(animation(this, &MaskedInputField::step_border)) , _focused(false) @@ -2183,8 +2199,10 @@ void MaskedInputField::paintEvent(QPaintEvent *e) { p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b); } if (_st.borderActive && a_borderOpacityActive.current() > 0) { + auto borderFgActive = anim::color(_st.borderFg, _st.borderFgActive, a_borderFgActive.current()); + auto borderFg = anim::color(borderFgActive, _st.borderFgError, a_borderFgError.current()); p.setOpacity(a_borderOpacityActive.current()); - p.fillRect(0, height() - _st.borderActive, width(), _st.borderActive, a_borderFg.current()); + p.fillRect(0, height() - _st.borderActive, width(), _st.borderActive, borderFg); p.setOpacity(1); } @@ -2195,7 +2213,8 @@ void MaskedInputField::paintEvent(QPaintEvent *e) { } void MaskedInputField::startBorderAnimation() { - a_borderFg.start((_error ? _st.borderFgError : (_focused ? _st.borderFgActive : _st.borderFg))->c); + a_borderFgActive.start(_focused ? 1. : 0.); + a_borderFgError.start(_error ? 1. : 0.); a_borderOpacityActive.start((_error || _focused) ? 1 : 0); _a_border.start(); } @@ -2204,7 +2223,7 @@ void MaskedInputField::focusInEvent(QFocusEvent *e) { if (!_focused) { _focused = true; - a_placeholderFg.start(_st.placeholderFgActive->c); + a_placeholderFgActive.start(1.); _a_placeholderFg.start(); startBorderAnimation(); @@ -2217,7 +2236,7 @@ void MaskedInputField::focusOutEvent(QFocusEvent *e) { if (_focused) { _focused = false; - a_placeholderFg.start(_st.placeholderFg->c); + a_placeholderFgActive.start(0.); _a_placeholderFg.start(); startBorderAnimation(); @@ -2263,9 +2282,9 @@ void MaskedInputField::step_placeholderFg(float64 ms, bool timer) { float64 dt = ms / _st.duration; if (dt >= 1) { _a_placeholderFg.stop(); - a_placeholderFg.finish(); + a_placeholderFgActive.finish(); } else { - a_placeholderFg.update(dt, anim::linear); + a_placeholderFgActive.update(dt, anim::linear); } if (timer) update(); } @@ -2287,10 +2306,12 @@ void MaskedInputField::step_border(float64 ms, bool timer) { float64 dt = ms / _st.duration; if (dt >= 1) { _a_border.stop(); - a_borderFg.finish(); + a_borderFgActive.finish(); + a_borderFgError.finish(); a_borderOpacityActive.finish(); } else { - a_borderFg.update(dt, anim::linear); + a_borderFgActive.update(dt, anim::linear); + a_borderFgError.update(dt, anim::linear); a_borderOpacityActive.update(dt, anim::linear); } if (timer) update(); @@ -2364,7 +2385,7 @@ void MaskedInputField::paintPlaceholder(Painter &p) { void MaskedInputField::placeholderPreparePaint(Painter &p) { p.setFont(_st.font); - p.setPen(a_placeholderFg.current()); + p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, a_placeholderFgActive.current())); } void MaskedInputField::keyPressEvent(QKeyEvent *e) { diff --git a/Telegram/SourceFiles/ui/flatinput.h b/Telegram/SourceFiles/ui/flatinput.h index 5f5dddd30..60d8a2e4f 100644 --- a/Telegram/SourceFiles/ui/flatinput.h +++ b/Telegram/SourceFiles/ui/flatinput.h @@ -98,7 +98,10 @@ private: bool _phVisible; anim::ivalue a_phLeft; anim::fvalue a_phAlpha; - anim::cvalue a_phColor, a_borderColor, a_bgColor; + anim::fvalue a_phColorFocus; + anim::fvalue a_borderColorActive; + anim::fvalue a_borderColorError; + anim::fvalue a_bgColorActive; Animation _a_appearance; int _notingBene; @@ -303,11 +306,12 @@ private: bool _placeholderVisible; anim::ivalue a_placeholderLeft; anim::fvalue a_placeholderOpacity; - anim::cvalue a_placeholderFg; + anim::fvalue a_placeholderFgActive; Animation _a_placeholderFg, _a_placeholderShift; anim::fvalue a_borderOpacityActive; - anim::cvalue a_borderFg; + anim::fvalue a_borderFgActive; + anim::fvalue a_borderFgError; Animation _a_border; bool _focused, _error; @@ -472,11 +476,12 @@ private: bool _placeholderVisible; anim::ivalue a_placeholderLeft; anim::fvalue a_placeholderOpacity; - anim::cvalue a_placeholderFg; + anim::fvalue a_placeholderFgActive; Animation _a_placeholderFg, _a_placeholderShift; anim::fvalue a_borderOpacityActive; - anim::cvalue a_borderFg; + anim::fvalue a_borderFgActive; + anim::fvalue a_borderFgError; Animation _a_border; bool _focused, _error; @@ -588,11 +593,12 @@ private: bool _placeholderVisible, _placeholderFast; anim::ivalue a_placeholderLeft; anim::fvalue a_placeholderOpacity; - anim::cvalue a_placeholderFg; + anim::fvalue a_placeholderFgActive; Animation _a_placeholderFg, _a_placeholderShift; anim::fvalue a_borderOpacityActive; - anim::cvalue a_borderFg; + anim::fvalue a_borderFgActive; + anim::fvalue a_borderFgError; Animation _a_border; bool _focused, _error; diff --git a/Telegram/SourceFiles/ui/flattextarea.cpp b/Telegram/SourceFiles/ui/flattextarea.cpp index 1e9635733..f8495a4a7 100644 --- a/Telegram/SourceFiles/ui/flattextarea.cpp +++ b/Telegram/SourceFiles/ui/flattextarea.cpp @@ -87,7 +87,7 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const , _phVisible(!v.length()) , a_phLeft(_phVisible ? 0 : st.phShift) , a_phAlpha(_phVisible ? 1 : 0) -, a_phColor(st.phColor->c) +, a_phColorFocused(0) , _a_appearance(animation(this, &FlatTextarea::step_appearance)) , _lastTextWithTags { v, tags } , _st(st) { @@ -285,7 +285,7 @@ void FlatTextarea::paintEvent(QPaintEvent *e) { p.save(); p.setClipRect(r); p.setFont(_st.font); - p.setPen(a_phColor.current()); + p.setPen(anim::pen(_st.phColor, _st.phFocusColor, a_phColorFocused.current())); if (_st.phAlign == style::al_topleft && _phAfter > 0) { int skipWidth = placeholderSkipWidth(); p.drawText(_st.textMrg.left() - _fakeMargin + a_phLeft.current() + skipWidth, _st.textMrg.top() - _fakeMargin - st::lineWidth + _st.font->ascent, _ph); @@ -312,13 +312,13 @@ int FlatTextarea::placeholderSkipWidth() const { } void FlatTextarea::focusInEvent(QFocusEvent *e) { - a_phColor.start(_st.phFocusColor->c); + a_phColorFocused.start(1.); _a_appearance.start(); QTextEdit::focusInEvent(e); } void FlatTextarea::focusOutEvent(QFocusEvent *e) { - a_phColor.start(_st.phColor->c); + a_phColorFocused.start(0.); _a_appearance.start(); QTextEdit::focusOutEvent(e); } @@ -1269,14 +1269,14 @@ void FlatTextarea::step_appearance(float64 ms, bool timer) { _a_appearance.stop(); a_phLeft.finish(); a_phAlpha.finish(); - a_phColor.finish(); + a_phColorFocused.finish(); a_phLeft = anim::ivalue(a_phLeft.current()); a_phAlpha = anim::fvalue(a_phAlpha.current()); - a_phColor = anim::cvalue(a_phColor.current()); + a_phColorFocused = anim::fvalue(a_phColorFocused.current()); } else { - a_phLeft.update(dt, _st.phLeftFunc); - a_phAlpha.update(dt, _st.phAlphaFunc); - a_phColor.update(dt, _st.phColorFunc); + a_phLeft.update(dt, anim::linear); + a_phAlpha.update(dt, anim::linear); + a_phColorFocused.update(dt, anim::linear); } if (timer) update(); } diff --git a/Telegram/SourceFiles/ui/flattextarea.h b/Telegram/SourceFiles/ui/flattextarea.h index 51929cc73..d78b6f904 100644 --- a/Telegram/SourceFiles/ui/flattextarea.h +++ b/Telegram/SourceFiles/ui/flattextarea.h @@ -196,7 +196,7 @@ private: bool _phVisible; anim::ivalue a_phLeft; anim::fvalue a_phAlpha; - anim::cvalue a_phColor; + anim::fvalue a_phColorFocused; Animation _a_appearance; TextWithTags _lastTextWithTags; diff --git a/Telegram/SourceFiles/ui/scrollarea.cpp b/Telegram/SourceFiles/ui/scrollarea.cpp index 5dd8e58a7..19c32037d 100644 --- a/Telegram/SourceFiles/ui/scrollarea.cpp +++ b/Telegram/SourceFiles/ui/scrollarea.cpp @@ -47,8 +47,9 @@ ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st) , _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar()) , _scrollMax(_connected->maximum()) , _hideIn(-1) -, a_bg(_st->hiding ? _st->bgColor->transparent() : _st->bgColor->c) -, a_bar(_st->hiding ? _st->barColor->transparent() : _st->barColor->c) +, a_bgOver(0.) +, a_barOver(0.) +, a_fullOpacity(_st->hiding ? 0. : 1.) , _a_appearance(animation(this, &ScrollBar::step_appearance)) { recountSize(); @@ -119,8 +120,9 @@ void ScrollBar::updateBar(bool force) { void ScrollBar::onHideTimer() { _hideIn = -1; - a_bg.start(QColor(a_bg.current().red(), a_bg.current().green(), a_bg.current().blue(), 0)); - a_bar.start(QColor(a_bar.current().red(), a_bar.current().green(), a_bar.current().blue(), 0)); + a_fullOpacity.start(0.); + a_bgOver.restart(); + a_barOver.restart(); _a_appearance.start(); } @@ -133,22 +135,27 @@ void ScrollBar::paintEvent(QPaintEvent *e) { hide(); return; } - if (!a_bg.current().alpha() && !a_bar.current().alpha()) return; + if (a_fullOpacity.current() == 0.) return; + QPainter p(this); - int32 deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0; - int32 deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax; + auto deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0; + auto deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax; p.setPen(Qt::NoPen); + auto bg = anim::color(_st->bgColor, _st->bgOverColor, a_bgOver.current()); + bg.setAlpha(anim::interpolate(0, bg.alpha(), a_fullOpacity.current())); + auto bar = anim::color(_st->barColor, _st->barOverColor, a_barOver.current()); + bar.setAlpha(anim::interpolate(0, bar.alpha(), a_fullOpacity.current())); if (_st->round) { p.setRenderHint(QPainter::HighQualityAntialiasing, true); - p.setBrush(a_bg.current()); + p.setBrush(bg); p.drawRoundedRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), _st->round, _st->round); - p.setBrush(a_bar.current()); + p.setBrush(bar); p.drawRoundedRect(_bar, _st->round, _st->round); p.setRenderHint(QPainter::HighQualityAntialiasing, false); } else { - p.fillRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), a_bg.current()); - p.fillRect(_bar, a_bar.current()); + p.fillRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), bg); + p.fillRect(_bar, bar); } } @@ -156,19 +163,22 @@ void ScrollBar::step_appearance(float64 ms, bool timer) { float64 dt = ms / _st->duration; if (dt >= 1) { _a_appearance.stop(); - a_bg.finish(); - a_bar.finish(); + a_bgOver.finish(); + a_barOver.finish(); + a_fullOpacity.finish(); } else { - a_bg.update(dt, anim::linear); - a_bar.update(dt, anim::linear); + a_bgOver.update(dt, anim::linear); + a_barOver.update(dt, anim::linear); + a_fullOpacity.update(dt, anim::linear); } if (timer) update(); } void ScrollBar::hideTimeout(int64 dt) { if (_hideIn < 0) { - a_bg.start((_over ? _st->bgOverColor : _st->bgColor)->c); - a_bar.start((_overbar ? _st->barOverColor : _st->barColor)->c); + a_bgOver.start(_over ? 1. : 0.); + a_barOver.start(_over ? 1. : 0.); + a_fullOpacity.start(1.); _a_appearance.start(); } _hideIn = dt; @@ -181,16 +191,18 @@ void ScrollBar::enterEvent(QEvent *e) { _hideTimer.stop(); setMouseTracking(true); _over = true; - a_bg.start(_st->bgOverColor->c); - a_bar.start(_st->barColor->c); + a_bgOver.start(1.); + a_barOver.start(1.); + a_fullOpacity.start(1.); _a_appearance.start(); } void ScrollBar::leaveEvent(QEvent *e) { if (!_moving) { setMouseTracking(false); - a_bg.start(_st->bgColor->c); - a_bar.start(_st->barColor->c); + a_bgOver.start(0.); + a_barOver.start(0.); + a_fullOpacity.start(1.); _a_appearance.start(); if (_hideIn >= 0) { _hideTimer.start(_hideIn); @@ -206,8 +218,9 @@ void ScrollBar::mouseMoveEvent(QMouseEvent *e) { if (_overbar != newOverBar) { _overbar = newOverBar; if (!_moving) { - a_bar.start((newOverBar ? _st->barOverColor : _st->barColor)->c); - a_bg.start(_st->bgOverColor->c); + a_barOver.start(newOverBar ? 1. : 0.); + a_bgOver.start(1.); + a_fullOpacity.start(1.); _a_appearance.start(); } } @@ -236,8 +249,9 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) { _connected->setValue(_startFrom); if (!_overbar) { _overbar = true; - a_bar.start(_st->barOverColor->c); - a_bg.start(_st->bgOverColor->c); + a_barOver.start(1.); + a_bgOver.start(1.); + a_fullOpacity.start(1.); _a_appearance.start(); } } @@ -252,13 +266,17 @@ void ScrollBar::mouseReleaseEvent(QMouseEvent *e) { bool a = false; if (!_overbar) { if (!_over || _hideIn) { - a_bar.start(_st->barColor->c); + a_bgOver.restart(); + a_barOver.start(0.); + a_fullOpacity.start(1.); a = true; } } if (!_over) { if (_hideIn) { - a_bg.start(_st->bgColor->c); + a_barOver.restart(); + a_bgOver.start(0.); + a_fullOpacity.start(1.); a = true; } if (_hideIn >= 0) { diff --git a/Telegram/SourceFiles/ui/scrollarea.h b/Telegram/SourceFiles/ui/scrollarea.h index 4f4a7e37f..306b36562 100644 --- a/Telegram/SourceFiles/ui/scrollarea.h +++ b/Telegram/SourceFiles/ui/scrollarea.h @@ -96,7 +96,7 @@ private: int64 _hideIn; QTimer _hideTimer; - anim::cvalue a_bg, a_bar; + anim::fvalue a_bgOver, a_barOver, a_fullOpacity; Animation _a_appearance; QRect _bar; diff --git a/Telegram/SourceFiles/ui/style/style_core_color.h b/Telegram/SourceFiles/ui/style/style_core_color.h index f02373b9e..0c8fed354 100644 --- a/Telegram/SourceFiles/ui/style/style_core_color.h +++ b/Telegram/SourceFiles/ui/style/style_core_color.h @@ -103,27 +103,4 @@ inline bool operator!=(const Color &a, const Color &b) { } } // namespace internal - -inline QColor interpolate(QColor a, QColor b, float64 opacity_b) { - auto bOpacity = static_cast(opacity_b * 255) + 1, aOpacity = (256 - bOpacity); - return { - (a.red() * aOpacity + b.red() * bOpacity) >> 8, - (a.green() * aOpacity + b.green() * bOpacity) >> 8, - (a.blue() * aOpacity + b.blue() * bOpacity) >> 8, - (a.alpha() * aOpacity + b.alpha() * bOpacity) >> 8 - }; -} - -inline QColor interpolate(const style::internal::Color &a, QColor b, float64 opacity_b) { - return interpolate(a->c, b, opacity_b); -} - -inline QColor interpolate(QColor a, const style::internal::Color &b, float64 opacity_b) { - return interpolate(a, b->c, opacity_b); -} - -inline QColor interpolate(const style::internal::Color &a, const style::internal::Color &b, float64 opacity_b) { - return interpolate(a->c, b->c, opacity_b); -} - } // namespace style diff --git a/Telegram/SourceFiles/ui/style/style_core_types.h b/Telegram/SourceFiles/ui/style/style_core_types.h index 89194a264..950f23ebc 100644 --- a/Telegram/SourceFiles/ui/style/style_core_types.h +++ b/Telegram/SourceFiles/ui/style/style_core_types.h @@ -27,7 +27,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include -#include "ui/animation.h" #include "ui/style/style_core_font.h" #include "ui/style/style_core_color.h" #include "ui/style/style_core_icon.h" @@ -38,7 +37,6 @@ using string = QString; using rect = QRect; using point = QPoint; using size = QSize; -using transition = anim::transition; using cursor = Qt::CursorShape; using align = Qt::Alignment; using margins = QMargins; diff --git a/Telegram/SourceFiles/ui/widgets/media_slider.cpp b/Telegram/SourceFiles/ui/widgets/media_slider.cpp index 743390f3c..c3a7000e6 100644 --- a/Telegram/SourceFiles/ui/widgets/media_slider.cpp +++ b/Telegram/SourceFiles/ui/widgets/media_slider.cpp @@ -62,21 +62,15 @@ void MediaSlider::paintEvent(QPaintEvent *e) { auto length = _alwaysDisplayMarker ? (horizontal ? width() : height()) : markerLength; auto mid = qRound(from + value * length); auto end = from + length; - auto activeFg = disabled ? &_st.activeFgDisabled : (over == 1. ? &_st.activeFgOver : (over == 0. ? &_st.activeFg : nullptr)); - auto inactiveFg = disabled ? &_st.inactiveFgDisabled : (over == 1. ? &_st.inactiveFgOver : (over == 0. ? &_st.inactiveFg : nullptr)); - auto activeFgOver = activeFg ? QColor() : style::interpolate(_st.activeFg, _st.activeFgOver, over); - auto inactiveFgOver = inactiveFg ? QColor() : style::interpolate(_st.inactiveFg, _st.inactiveFgOver, over); + auto activeFg = disabled ? _st.activeFgDisabled : anim::brush(_st.activeFg, _st.activeFgOver, over); + auto inactiveFg = disabled ? _st.inactiveFgDisabled : anim::brush(_st.inactiveFg, _st.inactiveFgOver, over); if (mid > from) { auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid); auto fromRect = horizontal ? QRect(from, (height() - _st.width) / 2, mid + radius - from, _st.width) : QRect((width() - _st.width) / 2, from, _st.width, mid + radius - from); p.setClipRect(fromClipRect); - if (auto brush = (horizontal ? activeFg : inactiveFg)) { - p.setBrush(*brush); - } else { - p.setBrush(horizontal ? activeFgOver : inactiveFgOver); - } + p.setBrush(horizontal ? activeFg : inactiveFg); p.drawRoundedRect(fromRect, radius, radius); } if (end > mid) { @@ -85,11 +79,7 @@ void MediaSlider::paintEvent(QPaintEvent *e) { ? QRect(mid - radius, (height() - _st.width) / 2, end - (mid - radius), _st.width) : QRect((width() - _st.width) / 2, mid - radius, _st.width, end - (mid - radius)); p.setClipRect(endClipRect); - if (auto brush = (horizontal ? inactiveFg : activeFg)) { - p.setBrush(*brush); - } else { - p.setBrush(horizontal ? inactiveFgOver : activeFgOver); - } + p.setBrush(horizontal ? inactiveFg : activeFg); p.drawRoundedRect(endRect, radius, radius); } auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over); @@ -102,11 +92,7 @@ void MediaSlider::paintEvent(QPaintEvent *e) { auto remove = static_cast(((1. - markerSizeRatio) * size) / 2.); if (remove * 2 < size) { p.setClipRect(rect()); - if (activeFg) { - p.setBrush(*activeFg); - } else { - p.setBrush(activeFgOver); - } + p.setBrush(activeFg); p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove))); } } diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp index a55cd9ea6..d4c7c0364 100644 --- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp +++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp @@ -349,7 +349,7 @@ void MultiSelect::Inner::Item::setVisibleAnimated(bool visible) { prepareCache(); auto from = visible ? 0. : 1.; auto to = visible ? 1. : 0.; - auto transition = visible ? anim::bumpy<1125, 1000> : anim::linear; + auto transition = visible ? anim::bumpy(1.125) : anim::linear; _visibility.start(_updateCallback, from, to, _st.duration, transition); } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index c12e34f7d..ca8a35195 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -400,7 +400,7 @@ void Widget::step_shift(float64 ms, bool timer) { } void Widget::hideSlow() { - hideAnimated(st::notifySlowHide, st::notifySlowHideFunc); + hideAnimated(st::notifySlowHide, anim::easeInCirc); } void Widget::hideFast() { diff --git a/Telegram/SourceFiles/window/slide_animation.cpp b/Telegram/SourceFiles/window/slide_animation.cpp index 5bab75b93..b6b2286ac 100644 --- a/Telegram/SourceFiles/window/slide_animation.cpp +++ b/Telegram/SourceFiles/window/slide_animation.cpp @@ -99,9 +99,9 @@ void SlideAnimation::step(float64 ms, bool timer) { } } - a_coordUnder.update(dt, st::slideFunction); - a_coordOver.update(dt, st::slideFunction); - a_progress.update(dt, st::slideFunction); + a_coordUnder.update(dt, transition()); + a_coordOver.update(dt, transition()); + a_progress.update(dt, transition()); if (timer && _repaintCallback) { _repaintCallback(); } diff --git a/Telegram/SourceFiles/window/slide_animation.h b/Telegram/SourceFiles/window/slide_animation.h index 53a24c3b6..ead4c85c2 100644 --- a/Telegram/SourceFiles/window/slide_animation.h +++ b/Telegram/SourceFiles/window/slide_animation.h @@ -45,6 +45,10 @@ public: void start(); + static const anim::transition &transition() { + return anim::easeOutCirc; + } + private: void step(float64 ms, bool timer); diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 4c130b571..0253aa14d 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -52,7 +52,6 @@ notifyClose: IconButton { notifyItemTop: 12px; notifyTextLeft: 12px; notifyTextTop: 7px; -notifySlowHideFunc: transition(easeInCirc); notifyWaitLongHide: 3000; notifyFastAnim: 150; notifyWidth: 320px; From dd9bd43331c4b853cee9708c985fcc1288ce535c Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Nov 2016 15:01:26 +0300 Subject: [PATCH 022/100] Closed beta 10019002: fixed build for Linux. --- Telegram/SourceFiles/platform/linux/main_window_linux.h | 2 +- Telegram/SourceFiles/platform/win/main_window_win.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index bff10d103..315924d79 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -71,7 +71,7 @@ protected: void psTrayMenuUpdated(); void psSetupTrayIcon(); - QTimer psUpdatedPositionTimer; + virtual void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) = 0; private: void psCreateTrayIcon(); diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index e7e232926..36ae6ef2a 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -105,6 +105,7 @@ protected: void psTrayMenuUpdated(); void psSetupTrayIcon(); + virtual void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) = 0; QTimer psUpdatedPositionTimer; From fac8e48381af9b82dcbb0d9dc12b7b6f40fb4cb8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Nov 2016 18:24:28 +0300 Subject: [PATCH 023/100] Default background changed to green, "video file" -> "video" in lang. --- Telegram/Resources/art/bg.jpg | Bin 266014 -> 107703 bytes .../art/{bg_old.png => bg_initial.png} | Bin Telegram/Resources/basic.style | 10 +- Telegram/Resources/colors.palette | 51 +++---- Telegram/Resources/icons/dialogs_lock@2x.png | Bin 408 -> 444 bytes .../Resources/icons/dialogs_unlock@2x.png | Bin 430 -> 450 bytes Telegram/Resources/langs/lang.strings | 16 +-- Telegram/Resources/sample.tdesktop-theme | 51 +++---- Telegram/Resources/telegram.qrc | 2 +- Telegram/SourceFiles/_other/packer.cpp | 124 +++++++++--------- Telegram/SourceFiles/_other/updater.cpp | 44 +++---- Telegram/SourceFiles/boxes/backgroundbox.cpp | 19 ++- Telegram/SourceFiles/boxes/boxes.style | 6 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 2 +- Telegram/SourceFiles/boxes/sharebox.cpp | 2 +- Telegram/SourceFiles/dialogs/dialogs.style | 9 +- Telegram/SourceFiles/dialogswidget.cpp | 12 +- Telegram/SourceFiles/history/history.style | 43 +++--- Telegram/SourceFiles/historywidget.cpp | 4 +- Telegram/SourceFiles/localstorage.cpp | 17 ++- Telegram/SourceFiles/mainwidget.cpp | 4 +- Telegram/SourceFiles/mainwindow.cpp | 5 +- Telegram/SourceFiles/mainwindow.h | 3 +- .../platform/win/window_title_win.cpp | 9 +- .../platform/win/window_title_win.h | 2 + Telegram/SourceFiles/profile/profile.style | 4 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 4 +- Telegram/SourceFiles/stickers/stickers.style | 2 +- Telegram/SourceFiles/window/main_window.cpp | 10 +- Telegram/SourceFiles/window/main_window.h | 2 +- Telegram/SourceFiles/window/window.style | 2 +- Telegram/SourceFiles/window/window_theme.cpp | 12 +- Telegram/SourceFiles/window/window_theme.h | 4 +- Telegram/SourceFiles/window/window_title.h | 3 + 34 files changed, 251 insertions(+), 227 deletions(-) rename Telegram/Resources/art/{bg_old.png => bg_initial.png} (100%) diff --git a/Telegram/Resources/art/bg.jpg b/Telegram/Resources/art/bg.jpg index f9ee1054a03da31d721791e37b5c6edd060db475..7fa05b3edd5798d508b69421baf45718cea8809f 100644 GIT binary patch literal 107703 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 z z&}zf57K3Bx#L-GgO=HZ8j$wtAZB@vZFns{-PTQ)JsgBFrUk$#igk^~=r;gG%CmE-> z(a3SjxEVvG^5^&b)L<)-IltyJHORvCnhS8njbhzCs#NOqG_zbeloT>~Djdm_c>zyY2bw02>z_qBoN?fXK3X`(@*ugx5#t;(nYzV-Ay``3drp=e9si zzX0j&?Ig6a(r4$#v%B4%TMglY@4Fiv*^%}7krKrWYgs>d$D&!Yv|m%FwxF# z%*H?UmyQuyi4H_?5nsqc^z`JSdLtqAkjTXKWk zMAa8W=*0AyZ?yFr*=Ig^Sfzu592x5&!u<%;!pA?u28snu+@{I)y?`*x)Mq342`C}X ztMx!TykacM;f?UG*KH*jY$0eTd;lDJ3oLa%dV%Lk9~?(C1dz?vuyF>uya7zBZXvBhO=s`{5ES}Pzj*ZUBB26kzl3F- zAVF1?z6vm;(R5#HybH3fVQq7KoG(D8`eK2lZ8fxr$jCFZ-hFc@3oA4-O&>h%Aa(TN z6(lM(K4@PQ1)Pk{K|il*9(176&dKraI?$g-WE!K1p7y?)=V;auGUFSD*0oav-yN|6#BTIq-s&A6=7JH=H6WOH}9TXVMgHWh+24u#0LiO9`6=pruH#JzqL9{*HNF?-+�ku=WqVF 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 literal 266014 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