From 6e33f039b2c0e7c6f070c4199a83d833aae6ed29 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 26 Oct 2016 19:43:13 +0300 Subject: [PATCH] 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 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

^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..',