From be5f4c9a7177e7bfd138fa428d722e51d4ebb86f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 20 Oct 2017 19:19:42 +0300 Subject: [PATCH] Allow delete / forward selected in shared media. Also use PeerListBox with a chats list with global search controller instead of HistoryHider for forward / share contact. --- .../Resources/icons/info_media_delete.png | Bin 0 -> 224 bytes .../Resources/icons/info_media_delete@2x.png | Bin 0 -> 398 bytes .../Resources/icons/info_media_forward.png | Bin 0 -> 204 bytes .../Resources/icons/info_media_forward@2x.png | Bin 0 -> 377 bytes Telegram/SourceFiles/boxes/peer_list_box.cpp | 1 + Telegram/SourceFiles/info/info.style | 34 ++++ .../SourceFiles/info/info_content_widget.cpp | 5 + .../SourceFiles/info/info_content_widget.h | 4 + .../info/info_top_bar_override.cpp | 176 ++++++++++++++++++ .../SourceFiles/info/info_top_bar_override.h | 91 +++++++++ .../SourceFiles/info/info_wrap_widget.cpp | 76 +++++++- Telegram/SourceFiles/info/info_wrap_widget.h | 28 +++ .../info/media/info_media_buttons.h | 2 +- .../info/media/info_media_inner_widget.cpp | 11 ++ .../info/media/info_media_inner_widget.h | 3 + .../info/media/info_media_list_widget.cpp | 46 ++--- .../info/media/info_media_list_widget.h | 23 +-- .../info/media/info_media_widget.cpp | 8 + .../info/media/info_media_widget.h | 5 +- .../profile/info_profile_inner_widget.cpp | 33 +++- .../info/profile/info_profile_inner_widget.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 2 +- Telegram/SourceFiles/ui/widgets/widgets.style | 4 + Telegram/gyp/telegram_sources.txt | 2 + 24 files changed, 508 insertions(+), 47 deletions(-) create mode 100644 Telegram/Resources/icons/info_media_delete.png create mode 100644 Telegram/Resources/icons/info_media_delete@2x.png create mode 100644 Telegram/Resources/icons/info_media_forward.png create mode 100644 Telegram/Resources/icons/info_media_forward@2x.png create mode 100644 Telegram/SourceFiles/info/info_top_bar_override.cpp create mode 100644 Telegram/SourceFiles/info/info_top_bar_override.h diff --git a/Telegram/Resources/icons/info_media_delete.png b/Telegram/Resources/icons/info_media_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..6535df215a2dd40cbbb7cf5382df314a30af29c9 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ<(@8%ArY-_rv`E{EAqG;zjXQO zt&6icK8jsFUzS-eyj^G0gvLLapL!N+GIX?7tDjuJ={@F`&Qt`G| zrxlJ}e;Cx&^Eg9nr_5*b=<|jau4dncT|7>7FT$8eFMmn9q$>x z#6HxyyPl!AA!>E|an-)Z2ImdfXFR{@`b6jVzSAbY&n%y*AC&(W_xs*?=JK=swou3T zvMgZDP-4(^5N5>Y-j{X#J}=_lkURVOis@@!zq0bT?VB=5 z@ARUzrhB`u%>ptsRmIZ<1Gm3tn(y_!yb-JS!8Yz=w6*$CUceRk2pG-`p00i_>zopr E0J_Ym2><{9 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info_media_forward.png b/Telegram/Resources/icons/info_media_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..9ab287d184930cfbebd1eb5e46b143438abf687d GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ$(}BbArY-_uioW4puoZU;Bwqa z@waoU49Z21h)p?Hcr4(9){*|YH4Fg)j}y%N9hm+x2ZXNBayb(I-{iP}V0Nv}fyNHz zD^a!`77PshN&7BD?aqCxRkc=dWBEF<2EmQ@!t(Q!?lH#*ZZ7?$!m_W9WsA2*ecLn{ z#>D0m%nSOH+&@<^e6=n!xqeA>%>w^JEGi9r7L2oJgdbs6x~K$n9)qW=pUXO@geCwy CQ%hF> literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info_media_forward@2x.png b/Telegram/Resources/icons/info_media_forward@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..36c7d299f9b037bff011b8968e431f44d874c8a9 GIT binary patch literal 377 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV07?waSW-L^Y&IEU$X*FTjHIm z7OipTqF)*AijNi(4p4u~ow>hde#^Jc9AUGiQ&cR^b?Y_wFfinishAnimating(); + myEnsureResized(_select); _scrollBottomFixed = true; onScrollToY(0); } diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 9694cbd251..16fbc1e931 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -80,12 +80,31 @@ infoTopBarTitle: FlatLabel(defaultFlatLabel) { linkFontOver: font(14px semibold); } } +infoTopBarClose: IconButton(infoTopBarBack) { + icon: icon {{ "info_close", boxTitleCloseFg }}; + iconOver: icon {{ "info_close", boxTitleCloseFgOver }}; +} +infoTopBarForward: IconButton(infoTopBarBack) { + width: 46px; + icon: icon {{ "info_media_forward", boxTitleCloseFg }}; + iconOver: icon {{ "info_media_forward", boxTitleCloseFgOver }}; + iconPosition: point(6px, -1px); + rippleAreaPosition: point(1px, 6px); +} +infoTopBarDelete: IconButton(infoTopBarForward) { + icon: icon {{ "info_media_delete", boxTitleCloseFg }}; + iconOver: icon {{ "info_media_delete", boxTitleCloseFgOver }}; +} infoTopBar: InfoTopBar { height: infoTopBarHeight; back: infoTopBarBack; title: infoTopBarTitle; titlePosition: point(23px, 18px); bg: windowBg; + mediaCancel: infoTopBarClose; + mediaActionsSkip: 4px; + mediaForward: infoTopBarForward; + mediaDelete: infoTopBarDelete; } infoLayerTopBarHeight: boxLayerTitleHeight; @@ -107,12 +126,27 @@ infoLayerTopBarClose: IconButton(infoLayerTopBarBack) { icon: infoLayerTopBarCloseIcon; iconOver: infoLayerTopBarCloseIconOver; } +infoLayerTopBarForward: IconButton(infoLayerTopBarBack) { + width: 45px; + icon: icon {{ "info_media_forward", boxTitleCloseFg }}; + iconOver: icon {{ "info_media_forward", boxTitleCloseFgOver }}; + iconPosition: point(6px, -1px); + rippleAreaPosition: point(1px, 6px); +} +infoLayerTopBarDelete: IconButton(infoLayerTopBarForward) { + icon: icon {{ "info_media_delete", boxTitleCloseFg }}; + iconOver: icon {{ "info_media_delete", boxTitleCloseFgOver }}; +} infoLayerTopBar: InfoTopBar { height: infoLayerTopBarHeight; back: infoLayerTopBarBack; title: boxTitle; titlePosition: boxLayerTitlePosition; bg: boxBg; + mediaCancel: infoLayerTopBarClose; + mediaActionsSkip: 6px; + mediaForward: infoLayerTopBarForward; + mediaDelete: infoLayerTopBarDelete; } infoMinimalWidth: 324px; diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index 99be3b683a..0f4cd1a71a 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include +#include #include "window/window_controller.h" #include "ui/widgets/scroll_area.h" #include "lang/lang_keys.h" @@ -156,4 +157,8 @@ QRect ContentWidget::rectForFloatPlayer() const { return mapToGlobal(_scroll->geometry()); } +rpl::producer ContentWidget::selectedListValue() const { + return rpl::single(SelectedItems(Storage::SharedMediaType::Photo)); +} + } // namespace Info diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index ebd4995307..e84ccfe05e 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -79,6 +79,10 @@ public: bool wheelEventFromFloatPlayer(QEvent *e); QRect rectForFloatPlayer() const; + virtual rpl::producer selectedListValue() const; + virtual void cancelSelection() { + } + protected: template Widget *setInnerWidget( diff --git a/Telegram/SourceFiles/info/info_top_bar_override.cpp b/Telegram/SourceFiles/info/info_top_bar_override.cpp new file mode 100644 index 0000000000..148586e187 --- /dev/null +++ b/Telegram/SourceFiles/info/info_top_bar_override.cpp @@ -0,0 +1,176 @@ +/* +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-2017 John Preston, https://desktop.telegram.org +*/ +#include "info/info_top_bar_override.h" + +#include +#include "styles/style_info.h" +#include "lang/lang_keys.h" +#include "info/info_wrap_widget.h" +#include "storage/storage_shared_media.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/fade_wrap.h" +#include "ui/widgets/shadow.h" +#include "mainwidget.h" +#include "boxes/confirm_box.h" +#include "boxes/peer_list_controllers.h" + +namespace Info { + +ChooseRecipientBoxController::ChooseRecipientBoxController( + base::lambda)> callback) +: _callback(std::move(callback)) { +} + +void ChooseRecipientBoxController::prepareViewHook() { + delegate()->peerListSetTitle(langFactory(lng_forward_choose)); +} + +void ChooseRecipientBoxController::rowClicked(not_null row) { + _callback(row->peer()); +} + +auto ChooseRecipientBoxController::createRow( + not_null history) -> std::unique_ptr { + return std::make_unique(history); +} + +TopBarOverride::TopBarOverride( + QWidget *parent, + const style::InfoTopBar &st, + SelectedItems &&items) +: RpWidget(parent) +, _st(st) +, _items(std::move(items)) +, _canDelete(computeCanDelete()) +, _cancel(this, _st.mediaCancel) +, _text(this, generateText(), Ui::FlatLabel::InitType::Simple, _st.title) +, _forward(this, _st.mediaForward) +, _delete(this, _st.mediaDelete) { + setAttribute(Qt::WA_OpaquePaintEvent); + + updateControlsVisibility(); + + _forward->addClickHandler([this] { performForward(); }); + _delete->addClickHandler([this] { performDelete(); }); +} + +QString TopBarOverride::generateText() const { + using Type = Storage::SharedMediaType; + auto phrase = [&] { + switch (_items.type) { + case Type::Photo: return lng_profile_photos; + case Type::Video: return lng_profile_videos; + case Type::File: return lng_profile_files; + case Type::MusicFile: return lng_profile_songs; + case Type::Link: return lng_profile_shared_links; + case Type::VoiceFile: return lng_profile_audios; + case Type::RoundFile: return lng_profile_rounds; + } + Unexpected("Type in TopBarOverride::generateText()"); + }(); + return phrase(lt_count, _items.list.size()); +} + +bool TopBarOverride::computeCanDelete() const { + return base::find_if(_items.list, [](const SelectedItem &item) { + return !item.canDelete; + }) == _items.list.end(); +} + +void TopBarOverride::setItems(SelectedItems &&items) { + _items = std::move(items); + _canDelete = computeCanDelete(); + + _text->setText(generateText()); + updateControlsVisibility(); + updateControlsGeometry(width()); +} + +rpl::producer<> TopBarOverride::cancelRequests() const { + return rpl::merge( + _cancel->clicks(), + _correctionCancelRequests.events()); +} + +int TopBarOverride::resizeGetHeight(int newWidth) { + updateControlsGeometry(newWidth); + return _st.height; +} + +void TopBarOverride::updateControlsGeometry(int newWidth) { + auto right = _st.mediaActionsSkip; + if (_canDelete) { + _delete->moveToRight(right, 0, newWidth); + right += _delete->width(); + } + _forward->moveToRight(right, 0, newWidth); + _cancel->moveToLeft(0, 0); + _text->moveToLeft(_cancel->width(), _st.titlePosition.y()); +} + +void TopBarOverride::updateControlsVisibility() { + _delete->setVisible(_canDelete); +} + +void TopBarOverride::paintEvent(QPaintEvent *e) { + Painter p(this); + p.fillRect(e->rect(), _st.bg); +} + +SelectedItemSet TopBarOverride::collectItems() const { + auto result = SelectedItemSet(); + for (auto value : _items.list) { + if (auto item = App::histItemById(value.msgId)) { + result.insert(result.size(), item); + } + } + return result; +} + +void TopBarOverride::performForward() { + auto items = collectItems(); + if (items.empty()) { + _correctionCancelRequests.fire({}); + return; + } + auto callback = [items = std::move(items)](not_null peer) { + App::main()->setForwardDraft(peer->id, items); + }; + Ui::show(Box( + std::make_unique(std::move(callback)), + [](not_null box) { + box->addButton(langFactory(lng_cancel), [box] { + box->closeBox(); + }); + })); +} + +void TopBarOverride::performDelete() { + auto items = collectItems(); + if (items.empty()) { + _correctionCancelRequests.fire({}); + } else { + Ui::show(Box(items)); + } +} + +} // namespace Info diff --git a/Telegram/SourceFiles/info/info_top_bar_override.h b/Telegram/SourceFiles/info/info_top_bar_override.h new file mode 100644 index 0000000000..9a8ec6d02d --- /dev/null +++ b/Telegram/SourceFiles/info/info_top_bar_override.h @@ -0,0 +1,91 @@ +/* +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-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "ui/rp_widget.h" +#include "info/info_wrap_widget.h" +#include "boxes/peer_list_controllers.h" + +namespace style { +struct InfoTopBar; +} // namespace style + +namespace Ui { +class IconButton; +class FlatLabel; +} // namespace Ui + +namespace Info { + +class ChooseRecipientBoxController : public ChatsListBoxController { +public: + ChooseRecipientBoxController( + base::lambda)> callback); + + void rowClicked(not_null row) override; + +protected: + void prepareViewHook() override; + std::unique_ptr createRow( + not_null history) override; + + base::lambda)> _callback; + +}; + +class TopBarOverride : public Ui::RpWidget { +public: + TopBarOverride( + QWidget *parent, + const style::InfoTopBar &st, + SelectedItems &&items); + + void setItems(SelectedItems &&items); + + rpl::producer<> cancelRequests() const; + +protected: + int resizeGetHeight(int newWidth) override; + void paintEvent(QPaintEvent *e) override; + +private: + void updateControlsVisibility(); + void updateControlsGeometry(int newWidth); + QString generateText() const; + [[nodiscard]] bool computeCanDelete() const; + [[nodiscard]] SelectedItemSet collectItems() const; + + void performForward(); + void performDelete(); + + const style::InfoTopBar &_st; + SelectedItems _items; + bool _canDelete = false; + object_ptr _cancel; + object_ptr _text; + object_ptr _forward; + object_ptr _delete; + rpl::event_stream<> _correctionCancelRequests; + +}; + + +} // namespace Info diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index bcfd7d6061..2800328ccb 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "info/info_content_widget.h" #include "info/info_memento.h" #include "info/info_top_bar.h" +#include "info/info_top_bar_override.h" #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" @@ -39,6 +40,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_profile.h" namespace Info { +namespace { + +const style::InfoTopBar &TopBarStyle(Wrap wrap) { + return (wrap == Wrap::Layer) + ? st::infoLayerTopBar + : st::infoTopBar; +} + +} // namespace struct WrapWidget::StackItem { std::unique_ptr section; @@ -54,6 +64,12 @@ WrapWidget::WrapWidget( , _wrap(wrap) , _topShadow(this) { _topShadow->toggleOn(topShadowToggledValue()); + selectedListValue() + | rpl::start_with_next([this](SelectedItems &&items) { + InvokeQueued(this, [this, items = std::move(items)]() mutable { + refreshTopBarOverride(std::move(items)); + }); + }, lifetime()); showNewContent(memento->content()); } @@ -182,11 +198,7 @@ void WrapWidget::setupTop( void WrapWidget::createTopBar( const Section §ion, PeerId peerId) { - _topBar.create( - this, - (wrap() == Wrap::Layer) - ? st::infoLayerTopBar - : st::infoTopBar); + _topBar.create(this, TopBarStyle(wrap())); _topBar->setTitle(TitleValue( section, @@ -212,6 +224,52 @@ void WrapWidget::createTopBar( _topBar->show(); } +void WrapWidget::refreshTopBarOverride(SelectedItems &&items) { + if (items.list.empty()) { + destroyTopBarOverride(); + } else if (_topBarOverride) { + _topBarOverride->setItems(std::move(items)); + } else { + createTopBarOverride(std::move(items)); + } +} + +void WrapWidget::destroyTopBarOverride() { + if (!_topBarOverride) { + return; + } + auto widget = std::exchange(_topBarOverride, nullptr); + auto handle = weak(widget.data()); + _topBarOverrideAnimation.start([this, handle] { + }, 1., 0., st::slideWrapDuration); + widget.destroy(); + if (_topTabs) { + _topTabs->show(); + } else if (_topBar) { + _topBar->show(); + } +} + +void WrapWidget::createTopBarOverride(SelectedItems &&items) { + Expects(_topBarOverride == nullptr); + _topBarOverride.create( + this, + TopBarStyle(wrap()), + std::move(items)); + if (_topTabs) { + _topTabs->hide(); + } else if (_topBar) { + _topBar->hide(); + } + _topBarOverride->cancelRequests() + | rpl::start_with_next([this](auto) { + _content->cancelSelection(); + }, _topBarOverride->lifetime()); + _topBarOverride->moveToLeft(0, 0); + _topBarOverride->resizeToWidth(width()); + _topBarOverride->show(); +} + void WrapWidget::showBackFromStack() { auto params = Window::SectionShow( Window::SectionShow::Way::Backward); @@ -245,6 +303,7 @@ void WrapWidget::finishShowContent() { updateContentGeometry(); _desiredHeights.fire(desiredHeightForContent()); _desiredShadowVisibilities.fire(_content->desiredShadowVisibility()); + _selectedLists.fire(_content->selectedListValue()); _topShadow->raise(); _topShadow->finishAnimating(); if (_topTabs) { @@ -268,6 +327,10 @@ rpl::producer WrapWidget::desiredHeightForContent() const { $1 + $2); } +rpl::producer WrapWidget::selectedListValue() const { + return _selectedLists.events() | rpl::flatten_latest(); +} + object_ptr WrapWidget::createContent(Tab tab) { switch (tab) { case Tab::Profile: return createProfileWidget(); @@ -450,6 +513,9 @@ void WrapWidget::resizeEvent(QResizeEvent *e) { } else if (_topBar) { _topBar->resizeToWidth(width()); } + if (_topBarOverride) { + _topBarOverride->resizeToWidth(width()); + } updateContentGeometry(); } diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h index 0926088bad..98247e3cd3 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.h +++ b/Telegram/SourceFiles/info/info_wrap_widget.h @@ -49,6 +49,7 @@ class MoveMemento; class ContentMemento; class ContentWidget; class TopBar; +class TopBarOverride; enum class Wrap { Layer, @@ -87,6 +88,25 @@ private: }; +struct SelectedItem { + explicit SelectedItem(FullMsgId msgId) : msgId(msgId) { + } + + FullMsgId msgId; + bool canDelete = false; + bool canForward = false; +}; + +struct SelectedItems { + explicit SelectedItems(Storage::SharedMediaType type) + : type(type) { + } + + Storage::SharedMediaType type; + std::vector list; + +}; + class WrapWidget final : public Window::SectionWidget { public: WrapWidget( @@ -171,11 +191,18 @@ private: object_ptr createContent( not_null memento); + rpl::producer selectedListValue() const; + void refreshTopBarOverride(SelectedItems &&items); + void createTopBarOverride(SelectedItems &&items); + void destroyTopBarOverride(); + rpl::variable _wrap; object_ptr _content = { nullptr }; object_ptr _topTabsBackground = { nullptr }; object_ptr _topTabs = { nullptr }; object_ptr _topBar = { nullptr }; + object_ptr _topBarOverride = { nullptr }; + Animation _topBarOverrideAnimation; object_ptr _topShadow; Tab _tab = Tab::Profile; std::unique_ptr _anotherTabMemento; @@ -183,6 +210,7 @@ private: rpl::event_stream> _desiredHeights; rpl::event_stream> _desiredShadowVisibilities; + rpl::event_stream> _selectedLists; }; diff --git a/Telegram/SourceFiles/info/media/info_media_buttons.h b/Telegram/SourceFiles/info/media/info_media_buttons.h index b03488512d..bb907255a1 100644 --- a/Telegram/SourceFiles/info/media/info_media_buttons.h +++ b/Telegram/SourceFiles/info/media/info_media_buttons.h @@ -47,7 +47,7 @@ inline auto MediaTextPhrase(Type type) { case Type::VoiceFile: return lng_profile_audios; case Type::RoundFile: return lng_profile_rounds; } - Unexpected("Type in setupSharedMedia()"); + Unexpected("Type in MediaTextPhrase()"); }; inline auto MediaText(Type type) { diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp index 91908f0c7b..f1de1e47cd 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp @@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "info/media/info_media_inner_widget.h" +#include #include "boxes/abstract_box.h" #include "info/media/info_media_list_widget.h" #include "info/media/info_media_buttons.h" @@ -246,6 +247,7 @@ object_ptr InnerWidget::setupList( | rpl::start_to_stream( _scrollToRequests, result->lifetime()); + _selectedLists.fire(result->selectedListValue()); return result; } @@ -255,6 +257,15 @@ void InnerWidget::saveState(not_null memento) { void InnerWidget::restoreState(not_null memento) { } +rpl::producer InnerWidget::selectedListValue() const { + return _selectedLists.events_starting_with(_list->selectedListValue()) + | rpl::flatten_latest(); +} + +void InnerWidget::cancelSelection() { + _list->cancelSelection(); +} + int InnerWidget::resizeGetHeight(int newWidth) { _inResize = true; auto guard = gsl::finally([this] { _inResize = false; }); diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.h b/Telegram/SourceFiles/info/media/info_media_inner_widget.h index 3a483dce9d..7a6387d951 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.h @@ -55,6 +55,8 @@ public: rpl::producer scrollToRequests() const { return _scrollToRequests.events(); } + rpl::producer selectedListValue() const; + void cancelSelection(); protected: int resizeGetHeight(int newWidth) override; @@ -86,6 +88,7 @@ private: object_ptr _list = { nullptr }; rpl::event_stream _scrollToRequests; + rpl::event_stream> _selectedLists; }; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 958c133824..baa17c2a90 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -159,21 +159,14 @@ bool ListWidget::IsAfter( bool ListWidget::SkipSelectFromItem(const CursorState &state) { if (state.cursor.y() >= state.size.height() - && state.cursor.x() >= 0) { - return true; - } else if (state.cursor.x() >= state.size.width() - && state.cursor.y() >= 0) { + || state.cursor.x() >= state.size.width()) { return true; } return false; } bool ListWidget::SkipSelectTillItem(const CursorState &state) { - if (state.cursor.y() < state.size.height() - && state.cursor.x() < 0) { - return true; - } else if (state.cursor.x() < state.size.width() - && state.cursor.y() < 0) { + if (state.cursor.x() < 0 || state.cursor.y() < 0) { return true; } return false; @@ -600,7 +593,6 @@ void ListWidget::itemRemoved(not_null item) { auto i = _selected.find(universalId); if (i != _selected.cend()) { removeItemSelection(i); - pushSelectedItems(); } mouseActionUpdate(_mousePosition); @@ -630,18 +622,20 @@ auto ListWidget::collectSelectedItems() const -> SelectedItems { auto transformation = [&](const auto &item) { return convert(item.first, item.second); }; - auto items = SelectedItems(); - items.reserve(_selected.size()); - std::transform( - _selected.begin(), - _selected.end(), - std::back_inserter(items), - transformation); + auto items = SelectedItems(_type); + if (hasSelectedItems()) { + items.list.reserve(_selected.size()); + std::transform( + _selected.begin(), + _selected.end(), + std::back_inserter(items.list), + transformation); + } return items; } void ListWidget::pushSelectedItems() { - _selectedItemsStream.fire(collectSelectedItems()); + _selectedListStream.fire(collectSelectedItems()); } bool ListWidget::hasSelected() const { @@ -661,6 +655,7 @@ void ListWidget::removeItemSelection( if (_selected.empty()) { update(); } + pushSelectedItems(); } bool ListWidget::hasSelectedText() const { @@ -854,8 +849,8 @@ void ListWidget::refreshRows() { clearStaleLayouts(); resizeToWidth(width()); - restoreScrollState(); + mouseActionUpdate(); } void ListWidget::markLayoutsStale() { @@ -1025,7 +1020,7 @@ void ListWidget::paintEvent(QPaintEvent *e) { fromSectionIt, clip.y() + clip.height()); auto context = Context { - Layout::PaintContext(ms, !_selected.empty()), + Layout::PaintContext(ms, hasSelectedItems()), &_selected, &_dragSelected, _dragSelectAction @@ -1113,6 +1108,7 @@ void ListWidget::applyItemSelection( universalId, selection)) { repaintItem(universalId); + pushSelectedItems(); } } @@ -1203,10 +1199,12 @@ void ListWidget::clearSelected() { } if (hasSelectedText()) { repaintItem(_selected.begin()->first); + _selected.clear(); } else { + _selected.clear(); + pushSelectedItems(); update(); } - _selected.clear(); } void ListWidget::validateTrippleClickStartTime() { @@ -1246,7 +1244,7 @@ QPoint ListWidget::clampMousePosition(QPoint position) const { } void ListWidget::mouseActionUpdate(const QPoint &screenPos) { - if (_sections.empty()) { + if (_sections.empty() || _visibleBottom <= _visibleTop) { return; } @@ -1657,6 +1655,7 @@ void ListWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton butt void ListWidget::applyDragSelection() { applyDragSelection(_selected); clearDragSelection(); + pushSelectedItems(); } void ListWidget::applyDragSelection(SelectedMap &applyTo) const { @@ -1693,6 +1692,9 @@ void ListWidget::mouseActionUpdate() { void ListWidget::clearStaleLayouts() { for (auto i = _layouts.begin(); i != _layouts.end();) { if (i->second.stale) { + if (i->second.item.get() == _overLayout) { + _overLayout = nullptr; + } i = _layouts.erase(i); } else { ++i; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.h b/Telegram/SourceFiles/info/media/info_media_list_widget.h index 9343e37a81..cfa0ccd744 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.h @@ -66,17 +66,12 @@ public: rpl::producer scrollToRequests() const { return _scrollToRequests.events(); } - struct SelectedItem { - explicit SelectedItem(FullMsgId msgId) : msgId(msgId) { - } - - FullMsgId msgId; - bool canDelete = false; - bool canForward = false; - }; - using SelectedItems = std::vector; - rpl::producer selectedItemsValue() const { - return _selectedItemsStream.events(); + rpl::producer selectedListValue() const { + return _selectedListStream.events_starting_with( + collectSelectedItems()); + } + void cancelSelection() { + clearSelected(); } ~ListWidget(); @@ -246,10 +241,6 @@ private: void updateDragSelection(); void clearDragSelection(); - void setDragSelection( - BaseLayout *dragSelectFrom, - BaseLayout *dragSelectTill, - DragSelectAction action); void trySwitchToWordSelection(); void switchToWordSelection(); @@ -285,7 +276,7 @@ private: bool _pressWasInactive = false; SelectedMap _selected; SelectedMap _dragSelected; - rpl::event_stream _selectedItemsStream; + rpl::event_stream _selectedListStream; style::cursor _cursor = style::cur_default; DragSelectAction _dragSelectAction = DragSelectAction::None; bool _wasSelectedText = false; // was some text selected in current drag action diff --git a/Telegram/SourceFiles/info/media/info_media_widget.cpp b/Telegram/SourceFiles/info/media/info_media_widget.cpp index f4871d0003..e0f85097e3 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_widget.cpp @@ -60,6 +60,14 @@ Widget::Widget( }, _inner->lifetime()); } +rpl::producer Widget::selectedListValue() const { + return _inner->selectedListValue(); +} + +void Widget::cancelSelection() { + _inner->cancelSelection(); +} + Section Widget::section() const { return Section(type()); } diff --git a/Telegram/SourceFiles/info/media/info_media_widget.h b/Telegram/SourceFiles/info/media/info_media_widget.h index 1aee5fcf8a..918ba6fd26 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_widget.h @@ -79,10 +79,13 @@ public: const QRect &geometry, not_null memento); + rpl::producer selectedListValue() const override; + void cancelSelection() override; + private: void saveState(not_null memento); void restoreState(not_null memento); - + InnerWidget *_inner = nullptr; }; diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index 28b7d1d326..b9010a5abe 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "info/profile/info_profile_icon.h" #include "info/profile/info_profile_members.h" #include "info/media/info_media_buttons.h" +#include "info/info_top_bar_override.h" #include "boxes/abstract_box.h" #include "boxes/add_contact_box.h" #include "boxes/confirm_box.h" @@ -351,7 +352,7 @@ object_ptr InnerWidget::setupUserActions( addButton( Lang::Viewer(lng_profile_share_contact), CanShareContactValue(user), - [user] { App::main()->shareContactLayer(user); }); + [this, user] { shareContact(user); }); addButton( Lang::Viewer(lng_info_edit_contact), IsContactValue(user), @@ -422,6 +423,36 @@ object_ptr InnerWidget::setupUserActions( return std::move(result); } +void InnerWidget::shareContact(not_null user) const { + auto callback = [user](not_null peer) { + if (!peer->canWrite()) { + Ui::show(Box( + lang(lng_forward_share_cant)), + LayerOption::KeepOther); + return; + } + auto recipient = peer->isUser() + ? peer->name + : '\xAB' + peer->name + '\xBB'; + Ui::show(Box( + lng_forward_share_contact(lt_recipient, recipient), + lang(lng_forward_send), + [peer, user] { + App::main()->onShareContact( + peer->id, + user); + Ui::hideLayer(); + }), LayerOption::KeepOther); + }; + Ui::show(Box( + std::make_unique(std::move(callback)), + [](not_null box) { + box->addButton(langFactory(lng_cancel), [box] { + box->closeBox(); + }); + })); +} + object_ptr InnerWidget::createSkipWidget( RpWidget *parent) const { return Ui::CreateSkipWidget(parent, st::infoProfileSkip); diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h index 941a90e68a..b7b600d6c6 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h @@ -88,6 +88,7 @@ private: object_ptr setupUserActions( RpWidget *parent, not_null user) const; + void shareContact(not_null user) const; object_ptr createSkipWidget(RpWidget *parent) const; object_ptr> createSlideSkipWidget( diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 3ead774950..668e9fc234 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -677,7 +677,7 @@ bool MainWidget::setForwardDraft(PeerId peerId, const SelectedItemSet &items) { auto peer = App::peer(peerId); auto error = GetErrorTextForForward(peer, items); if (!error.isEmpty()) { - Ui::show(Box(error)); + Ui::show(Box(error), LayerOption::KeepOther); return false; } diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 44ca33e19f..7cf688296e 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -1117,4 +1117,8 @@ InfoTopBar { title: FlatLabel; titlePosition: point; bg: color; + mediaCancel: IconButton; + mediaActionsSkip: pixels; + mediaForward: IconButton; + mediaDelete: IconButton; } diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 47b2b3685c..c43dc002b2 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -219,6 +219,8 @@ <(src_loc)/info/info_section_widget.h <(src_loc)/info/info_top_bar.cpp <(src_loc)/info/info_top_bar.h +<(src_loc)/info/info_top_bar_override.cpp +<(src_loc)/info/info_top_bar_override.h <(src_loc)/info/info_wrap_widget.cpp <(src_loc)/info/info_wrap_widget.h <(src_loc)/info/media/info_media_buttons.h