diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 6630cd8aa..828d7abdc 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -402,6 +402,21 @@ shareNameTop: 6px; shareColumnSkip: 6px; shareActivateDuration: 150; shareScrollDuration: 300; +shareComment: InputField(defaultInputField) { + font: normalFont; + textMargins: margins(8px, 8px, 8px, 6px); + heightMin: 36px; + heightMax: 72px; + placeholderFg: placeholderFg; + placeholderFgActive: placeholderFgActive; + placeholderFgError: placeholderFgActive; + placeholderMargins: margins(2px, 0px, 2px, 0px); + placeholderScale: 0.; + placeholderFont: normalFont; + border: 0px; + borderActive: 0px; +} +shareCommentPadding: margins(5px, 5px, 5px, 5px); notificationsBoxMonitor: icon {{ "monitor", notificationsBoxMonitorFg }}; notificationsBoxScreenTop: 10px; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 4c0a34fd0..c36a10d0f 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -215,8 +215,7 @@ EditCaptionBox::EditCaptionBox( _field->setInstantReplaces(Ui::InstantReplaces::Default()); _field->setInstantReplacesEnabled(Global::ReplaceEmojiValue()); _field->setMarkdownReplacesEnabled(rpl::single(true)); - _field->setEditLinkCallback( - DefaultEditLinkCallback(_controller, _field)); + _field->setEditLinkCallback(DefaultEditLinkCallback(_field)); } void EditCaptionBox::prepareGifPreview(not_null document) { diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 6d0ab556e..19a7b7e67 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -1582,8 +1582,7 @@ void SendFilesBox::setupCaption() { _caption->setInstantReplaces(Ui::InstantReplaces::Default()); _caption->setInstantReplacesEnabled(Global::ReplaceEmojiValue()); _caption->setMarkdownReplacesEnabled(rpl::single(true)); - _caption->setEditLinkCallback( - DefaultEditLinkCallback(_controller, _caption)); + _caption->setEditLinkCallback(DefaultEditLinkCallback(_caption)); } void SendFilesBox::captionResized() { diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 2b01973d3..916616f66 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -8,8 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/share_box.h" #include "dialogs/dialogs_indexed_list.h" -#include "styles/style_boxes.h" -#include "styles/style_history.h" #include "observer_peer.h" #include "lang/lang_keys.h" #include "mainwindow.h" @@ -22,7 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/multi_select.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" +#include "ui/widgets/input_fields.h" +#include "ui/wrap/slide_wrap.h" #include "ui/text_options.h" +#include "chat_helpers/message_field.h" #include "history/history.h" #include "history/history_media_types.h" #include "history/history_message.h" @@ -30,69 +31,248 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_box.h" #include "auth_session.h" #include "messenger.h" +#include "styles/style_boxes.h" +#include "styles/style_history.h" -ShareBox::ShareBox(QWidget*, CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback) + +class ShareBox::Inner + : public Ui::RpWidget + , public RPCSender + , private base::Subscriber { +public: + Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback); + + void setPeerSelectedChangedCallback( + Fn callback); + void peerUnselected(not_null peer); + + QVector selected() const; + bool hasSelected() const; + + void peopleReceived( + const QString &query, + const QVector &my, + const QVector &people); + + void activateSkipRow(int direction); + void activateSkipColumn(int direction); + void activateSkipPage(int pageHeight, int direction); + void updateFilter(QString filter = QString()); + void selectActive(); + + rpl::producer scrollToRequests() const; + rpl::producer<> searchRequests() const; + +protected: + void visibleTopBottomUpdated( + int visibleTop, + int visibleBottom) override; + + void paintEvent(QPaintEvent *e) override; + void enterEventHook(QEvent *e) override; + void leaveEventHook(QEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +private: + struct Chat { + Chat(PeerData *peer, Fn updateCallback); + + PeerData *peer; + Ui::RoundImageCheckbox checkbox; + Text name; + Animation nameActive; + }; + + void notifyPeerUpdated(const Notify::PeerUpdate &update); + void invalidateCache(); + + int displayedChatsCount() const; + + void paintChat(Painter &p, TimeMs ms, not_null chat, int index); + void updateChat(not_null peer); + void updateChatName(not_null chat, not_null peer); + void repaintChat(not_null peer); + int chatIndex(not_null peer) const; + void repaintChatAtIndex(int index); + Chat *getChatAtIndex(int index); + + void loadProfilePhotos(int yFrom); + void changeCheckState(Chat *chat); + enum class ChangeStateWay { + Default, + SkipCallback, + }; + void changePeerCheckState( + not_null chat, + bool checked, + ChangeStateWay useCallback = ChangeStateWay::Default); + + not_null getChat(not_null row); + void setActive(int active); + void updateUpon(const QPoint &pos); + + void refresh(); + + float64 _columnSkip = 0.; + float64 _rowWidthReal = 0.; + int _rowsLeft = 0; + int _rowsTop = 0; + int _rowWidth = 0; + int _rowHeight = 0; + int _columnCount = 4; + int _active = -1; + int _upon = -1; + + ShareBox::FilterCallback _filterCallback; + std::unique_ptr _chatsIndexed; + QString _filter; + std::vector _filtered; + + std::map, std::unique_ptr> _dataMap; + base::flat_set> _selected; + + Fn _peerSelectedChangedCallback; + + bool _searching = false; + QString _lastQuery; + std::vector _byUsernameFiltered; + std::vector> d_byUsernameFiltered; + + rpl::event_stream _scrollToRequests; + rpl::event_stream<> _searchRequests; + +}; + +ShareBox::ShareBox( + QWidget*, + CopyCallback &©Callback, + SubmitCallback &&submitCallback, + FilterCallback &&filterCallback) : _copyCallback(std::move(copyCallback)) , _submitCallback(std::move(submitCallback)) , _filterCallback(std::move(filterCallback)) -, _select(this, st::contactsMultiSelect, langFactory(lng_participant_filter)) -, _searchTimer(this) { +, _select( + this, + st::contactsMultiSelect, + langFactory(lng_participant_filter)) +, _comment( + this, + object_ptr( + this, + st::shareComment, + Ui::InputField::Mode::MultiLine, + langFactory(lng_photos_comment)), + st::shareCommentPadding) +, _searchTimer([=] { searchByUsername(); }) { +} + +void ShareBox::prepareCommentField() { + using namespace rpl::mappers; + + _comment->hide(anim::type::instant); + + rpl::combine( + heightValue(), + _comment->heightValue(), + _1 - _2 + ) | rpl::start_with_next([=](int top) { + _comment->moveToLeft(0, top); + }, _comment->lifetime()); + + const auto field = _comment->entity(); + + connect(field, &Ui::InputField::submitted, [=] { + submit(); + }); + + field->setInstantReplaces(Ui::InstantReplaces::Default()); + field->setInstantReplacesEnabled(Global::ReplaceEmojiValue()); + field->setMarkdownReplacesEnabled(rpl::single(true)); + field->setEditLinkCallback(DefaultEditLinkCallback(field)); + + Ui::SendPendingMoveResizeEvents(_comment); } void ShareBox::prepare() { + prepareCommentField(); + _select->resizeToWidth(st::boxWideWidth); Ui::SendPendingMoveResizeEvents(_select); setTitle(langFactory(lng_share_title)); - _inner = setInnerWidget(object_ptr(this, std::move(_filterCallback)), getTopScrollSkip()); - connect(_inner, SIGNAL(mustScrollTo(int,int)), this, SLOT(onMustScrollTo(int,int))); + _inner = setInnerWidget( + object_ptr( + this, + std::move(_filterCallback)), + getTopScrollSkip(), + getBottomScrollSkip()); createButtons(); setDimensions(st::boxWideWidth, st::boxMaxListHeight); - _select->setQueryChangedCallback([this](const QString &query) { onFilterUpdate(query); }); - _select->setItemRemovedCallback([this](uint64 itemId) { - if (auto peer = App::peerLoaded(itemId)) { + _select->setQueryChangedCallback([=](const QString &query) { + onFilterUpdate(query); + }); + _select->setItemRemovedCallback([=](uint64 itemId) { + if (const auto peer = App::peerLoaded(itemId)) { _inner->peerUnselected(peer); - onSelectedChanged(); + selectedChanged(); update(); } }); - _select->setResizedCallback([this] { updateScrollSkips(); }); - _select->setSubmittedCallback([this](Qt::KeyboardModifiers modifiers) { + _select->setResizedCallback([=] { updateScrollSkips(); }); + _select->setSubmittedCallback([=](Qt::KeyboardModifiers modifiers) { if (modifiers.testFlag(Qt::ControlModifier) || modifiers.testFlag(Qt::MetaModifier)) { - onSubmit(); + submit(); } else { - _inner->onSelectActive(); + _inner->selectActive(); } }); - connect(_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername())); - _inner->setPeerSelectedChangedCallback([this](PeerData *peer, bool checked) { - onPeerSelectedChanged(peer, checked); - }); + _comment->heightValue( + ) | rpl::start_with_next([=] { + updateScrollSkips(); + }, _comment->lifetime()); - _searchTimer->setSingleShot(true); - connect(_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername())); + _inner->searchRequests( + ) | rpl::start_with_next([=] { + needSearchByUsername(); + }, _inner->lifetime()); + + _inner->scrollToRequests( + ) | rpl::start_with_next([=](const Ui::ScrollToRequest &request) { + scrollTo(request); + }, _inner->lifetime()); + + _inner->setPeerSelectedChangedCallback([=](PeerData *peer, bool checked) { + innerSelectedChanged(peer, checked); + }); _select->raise(); } int ShareBox::getTopScrollSkip() const { - auto result = 0; - if (!_select->isHidden()) { - result += _select->height(); - } - return result; + return _select->isHidden() ? 0 : _select->height(); +} + +int ShareBox::getBottomScrollSkip() const { + return _comment->isHidden() ? 0 : _comment->height(); +} + +int ShareBox::contentHeight() const { + return height() - getTopScrollSkip() - getBottomScrollSkip(); } void ShareBox::updateScrollSkips() { setInnerTopSkip(getTopScrollSkip(), true); + setInnerBottomSkip(getBottomScrollSkip()); } -bool ShareBox::onSearchByUsername(bool searchCache) { +bool ShareBox::searchByUsername(bool searchCache) { auto query = _select->getQuery(); if (query.isEmpty()) { if (_peopleRequest) { @@ -124,9 +304,9 @@ bool ShareBox::onSearchByUsername(bool searchCache) { return false; } -void ShareBox::onNeedSearchByUsername() { - if (!onSearchByUsername(true)) { - _searchTimer->start(AutoSearchTimeout); +void ShareBox::needSearchByUsername() { + if (!searchByUsername(true)) { + _searchTimer.callOnce(AutoSearchTimeout); } } @@ -172,7 +352,11 @@ bool ShareBox::peopleFailed(const RPCError &error, mtpRequestId requestId) { } void ShareBox::setInnerFocus() { - _select->setInnerFocus(); + if (_comment->isHidden()) { + _select->setInnerFocus(); + } else { + _comment->entity()->setFocusFast(); + } } void ShareBox::resizeEvent(QResizeEvent *e) { @@ -194,9 +378,9 @@ void ShareBox::keyPressEvent(QKeyEvent *e) { } else if (e->key() == Qt::Key_Down) { _inner->activateSkipColumn(1); } else if (e->key() == Qt::Key_PageUp) { - _inner->activateSkipPage(height() - getTopScrollSkip(), -1); + _inner->activateSkipPage(contentHeight(), -1); } else if (e->key() == Qt::Key_PageDown) { - _inner->activateSkipPage(height() - getTopScrollSkip(), 1); + _inner->activateSkipPage(contentHeight(), 1); } else { BoxContent::keyPressEvent(e); } @@ -205,22 +389,14 @@ void ShareBox::keyPressEvent(QKeyEvent *e) { } } -void ShareBox::updateButtons() { - auto hasSelected = _inner->hasSelected(); - if (_hasSelected != hasSelected) { - _hasSelected = hasSelected; - createButtons(); - } -} - void ShareBox::createButtons() { clearButtons(); if (_hasSelected) { - addButton(langFactory(lng_share_confirm), [this] { onSubmit(); }); + addButton(langFactory(lng_share_confirm), [=] { submit(); }); } else if (_copyCallback) { - addButton(langFactory(lng_share_copy_link), [this] { onCopyLink(); }); + addButton(langFactory(lng_share_copy_link), [=] { copyLink(); }); } - addButton(langFactory(lng_cancel), [this] { closeBox(); }); + addButton(langFactory(lng_cancel), [=] { closeBox(); }); } void ShareBox::onFilterUpdate(const QString &query) { @@ -239,36 +415,44 @@ void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) { addItemWay); } -void ShareBox::onPeerSelectedChanged(PeerData *peer, bool checked) { +void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) { if (checked) { addPeerToMultiSelect(peer); _select->clearQuery(); } else { _select->removeItem(peer->id); } - onSelectedChanged(); + selectedChanged(); update(); } -void ShareBox::onSubmit() { +void ShareBox::submit() { if (_submitCallback) { - _submitCallback(_inner->selected()); + _submitCallback( + _inner->selected(), + _comment->entity()->getTextWithAppliedMarkdown()); } } -void ShareBox::onCopyLink() { +void ShareBox::copyLink() { if (_copyCallback) { _copyCallback(); } } -void ShareBox::onSelectedChanged() { - updateButtons(); +void ShareBox::selectedChanged() { + auto hasSelected = _inner->hasSelected(); + if (_hasSelected != hasSelected) { + _hasSelected = hasSelected; + createButtons(); + _comment->toggle(_hasSelected, anim::type::normal); + _comment->resizeToWidth(st::boxWideWidth); + } update(); } -void ShareBox::onMustScrollTo(int top, int bottom) { - onScrollToY(top, bottom); +void ShareBox::scrollTo(Ui::ScrollToRequest request) { + onScrollToY(request.ymin, request.ymax); //auto scrollTop = scrollArea()->scrollTop(), scrollBottom = scrollTop + scrollArea()->height(); //auto from = scrollTop, to = scrollTop; //if (scrollTop > top) { @@ -286,9 +470,14 @@ void ShareBox::scrollAnimationCallback() { //scrollArea()->scrollToY(scrollTop); } -ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : TWidget(parent) +ShareBox::Inner::Inner( + QWidget *parent, + ShareBox::FilterCallback &&filterCallback) +: RpWidget(parent) , _filterCallback(std::move(filterCallback)) -, _chatsIndexed(std::make_unique(Dialogs::SortMode::Add)) { +, _chatsIndexed( + std::make_unique( + Dialogs::SortMode::Add)) { _rowsTop = st::shareRowsTop; _rowHeight = st::shareRowHeight; setAttribute(Qt::WA_OpaquePaintEvent); @@ -325,7 +514,7 @@ ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallbac } void ShareBox::Inner::invalidateCache() { - for_const (auto data, _dataMap) { + for (const auto &[peer, data] : _dataMap) { data->checkbox.invalidateCache(); } } @@ -377,9 +566,8 @@ void ShareBox::Inner::notifyPeerUpdated(const Notify::PeerUpdate &update) { } void ShareBox::Inner::updateChat(not_null peer) { - auto i = _dataMap.find(peer); - if (i != _dataMap.cend()) { - updateChatName(i.value(), peer); + if (const auto i = _dataMap.find(peer); i != end(_dataMap)) { + updateChatName(i->second.get(), peer); repaintChat(peer); } } @@ -400,11 +588,17 @@ void ShareBox::Inner::repaintChatAtIndex(int index) { } ShareBox::Inner::Chat *ShareBox::Inner::getChatAtIndex(int index) { - if (index < 0) return nullptr; - auto row = ([this, index]() -> Dialogs::Row* { - if (_filter.isEmpty()) return _chatsIndexed->rowAtY(index, 1); - return (index < _filtered.size()) ? _filtered[index] : nullptr; - })(); + if (index < 0) { + return nullptr; + } + const auto row = [=] { + if (_filter.isEmpty()) { + return _chatsIndexed->rowAtY(index, 1); + } + return (index < _filtered.size()) + ? _filtered[index] + : nullptr; + }(); if (row) { return static_cast(row->attached); } @@ -412,7 +606,7 @@ ShareBox::Inner::Chat *ShareBox::Inner::getChatAtIndex(int index) { if (!_filter.isEmpty()) { index -= _filtered.size(); if (index >= 0 && index < d_byUsernameFiltered.size()) { - return d_byUsernameFiltered[index]; + return d_byUsernameFiltered[index].get(); } } return nullptr; @@ -442,7 +636,7 @@ int ShareBox::Inner::chatIndex(not_null peer) const { } ++index; } - for (const auto row : d_byUsernameFiltered) { + for (const auto &row : d_byUsernameFiltered) { if (row->peer == peer) { return index; } @@ -478,7 +672,7 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) { (*i)->entry()->loadUserpic(); } } - } else if (!_filtered.isEmpty()) { + } else if (!_filtered.empty()) { int from = yFrom / _rowHeight; if (from < 0) from = 0; if (from < _filtered.size()) { @@ -492,23 +686,24 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) { } } -ShareBox::Inner::Chat *ShareBox::Inner::getChat(Dialogs::Row *row) { +auto ShareBox::Inner::getChat(not_null row) +-> not_null { Expects(row->history() != nullptr); - auto data = static_cast(row->attached); - if (!data) { - auto peer = row->history()->peer; - auto i = _dataMap.constFind(peer); - if (i == _dataMap.cend()) { - data = new Chat(peer, [this, peer] { repaintChat(peer); }); - _dataMap.insert(peer, data); - updateChatName(data, peer); - } else { - data = i.value(); - } - row->attached = data; + if (const auto data = static_cast(row->attached)) { + return data; } - return data; + const auto peer = row->history()->peer; + if (const auto i = _dataMap.find(peer); i != end(_dataMap)) { + row->attached = i->second.get(); + return i->second.get(); + } + const auto [i, ok] = _dataMap.emplace( + peer, + std::make_unique(peer, [=] { repaintChat(peer); })); + updateChatName(i->second.get(), peer); + row->attached = i->second.get(); + return i->second.get(); } void ShareBox::Inner::setActive(int active) { @@ -525,7 +720,7 @@ void ShareBox::Inner::setActive(int active) { changeNameFg(_active, 0., 1.); } auto y = (_active < _columnCount) ? 0 : (_rowsTop + ((_active / _columnCount) * _rowHeight)); - emit mustScrollTo(y, y + _rowHeight); + _scrollToRequests.fire({ y, y + _rowHeight }); } void ShareBox::Inner::paintChat( @@ -587,7 +782,7 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) { style::al_center); } } else { - if (_filtered.isEmpty() + if (_filtered.empty() && _byUsernameFiltered.empty() && !_searching) { p.setFont(st::noContactsFont); @@ -616,7 +811,11 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) { if (indexFrom >= d_byUsernameFiltered.size()) { break; } - paintChat(p, ms, d_byUsernameFiltered[indexFrom], filteredSize + indexFrom); + paintChat( + p, + ms, + d_byUsernameFiltered[indexFrom].get(), + filteredSize + indexFrom); ++indexFrom; } } @@ -659,7 +858,7 @@ void ShareBox::Inner::mousePressEvent(QMouseEvent *e) { } } -void ShareBox::Inner::onSelectActive() { +void ShareBox::Inner::selectActive() { changeCheckState(getChatAtIndex(_active > 0 ? _active : 0)); } @@ -693,12 +892,16 @@ void ShareBox::Inner::changeCheckState(Chat *chat) { } void ShareBox::Inner::peerUnselected(not_null peer) { - if (auto chat = _dataMap.value(peer, nullptr)) { - changePeerCheckState(chat, false, ChangeStateWay::SkipCallback); + if (const auto i = _dataMap.find(peer); i != end(_dataMap)) { + changePeerCheckState( + i->second.get(), + false, + ChangeStateWay::SkipCallback); } } -void ShareBox::Inner::setPeerSelectedChangedCallback(Fn callback) { +void ShareBox::Inner::setPeerSelectedChangedCallback( + Fn callback) { _peerSelectedChangedCallback = std::move(callback); } @@ -732,9 +935,6 @@ void ShareBox::Inner::updateFilter(QString filter) { _filter = filter; _byUsernameFiltered.clear(); - for (int i = 0, l = d_byUsernameFiltered.size(); i < l; ++i) { - delete d_byUsernameFiltered[i]; - } d_byUsernameFiltered.clear(); if (_filter.isEmpty()) { @@ -782,7 +982,7 @@ void ShareBox::Inner::updateFilter(QString filter) { refresh(); _searching = true; - emit searchByUsername(); + _searchRequests.fire({}); } setActive(-1); update(); @@ -790,6 +990,14 @@ void ShareBox::Inner::updateFilter(QString filter) { } } +rpl::producer ShareBox::Inner::scrollToRequests() const { + return _scrollToRequests.events(); +} + +rpl::producer<> ShareBox::Inner::searchRequests() const { + return _searchRequests.events(); +} + void ShareBox::Inner::peopleReceived( const QString &query, const QVector &my, @@ -802,8 +1010,8 @@ void ShareBox::Inner::peopleReceived( _byUsernameFiltered.reserve(already + my.size() + people.size()); d_byUsernameFiltered.reserve(already + my.size() + people.size()); const auto feedList = [&](const QVector &list) { - for (const auto &mtpPeer : list) { - if (const auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) { + for (const auto &data : list) { + if (const auto peer = App::peerLoaded(peerFromMTP(data))) { const auto history = App::historyLoaded(peer); if (!_filterCallback(peer)) { continue; @@ -812,10 +1020,11 @@ void ShareBox::Inner::peopleReceived( } else if (base::contains(_byUsernameFiltered, peer)) { continue; } - auto chat = new Chat(peer, [=] { repaintChat(peer); }); - updateChatName(chat, peer); _byUsernameFiltered.push_back(peer); - d_byUsernameFiltered.push_back(chat); + d_byUsernameFiltered.push_back(std::make_unique( + peer, + [=] { repaintChat(peer); })); + updateChatName(d_byUsernameFiltered.back().get(), peer); } } }; @@ -837,18 +1046,12 @@ void ShareBox::Inner::refresh() { update(); } -ShareBox::Inner::~Inner() { - for_const (auto chat, _dataMap) { - delete chat; - } -} - QVector ShareBox::Inner::selected() const { - QVector result; + auto result = QVector(); result.reserve(_dataMap.size()); - for_const (auto chat, _dataMap) { + for (const auto &[peer, chat] : _dataMap) { if (chat->checkbox.checked()) { - result.push_back(chat->peer); + result.push_back(peer); } } return result; diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index ce3136a7c..551496da0 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" #include "base/observer.h" +#include "base/timer.h" #include "ui/effects/round_checkbox.h" namespace Dialogs { @@ -22,19 +23,25 @@ struct PeerUpdate; namespace Ui { class MultiSelect; +class InputField; +struct ScrollToRequest; +template +class SlideWrap; } // namespace Ui QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId); void ShareGameScoreByHash(const QString &hash); class ShareBox : public BoxContent, public RPCSender { - Q_OBJECT - public: using CopyCallback = Fn; - using SubmitCallback = Fn &)>; + using SubmitCallback = Fn&&, TextWithTags&&)>; using FilterCallback = Fn; - ShareBox(QWidget*, CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback); + ShareBox( + QWidget*, + CopyCallback &©Callback, + SubmitCallback &&submitCallback, + FilterCallback &&filterCallback); protected: void prepare() override; @@ -43,27 +50,26 @@ protected: void resizeEvent(QResizeEvent *e) override; void keyPressEvent(QKeyEvent *e) override; -private slots: - bool onSearchByUsername(bool searchCache = false); - void onNeedSearchByUsername(); - - void onSubmit(); - void onCopyLink(); - - void onMustScrollTo(int top, int bottom); - private: + void prepareCommentField(); void scrollAnimationCallback(); + void submit(); + void copyLink(); + bool searchByUsername(bool useCache = false); + + void scrollTo(Ui::ScrollToRequest request); + void needSearchByUsername(); void onFilterUpdate(const QString &query); - void onSelectedChanged(); - void updateButtons(); + void selectedChanged(); void createButtons(); int getTopScrollSkip() const; + int getBottomScrollSkip() const; + int contentHeight() const; void updateScrollSkips(); void addPeerToMultiSelect(PeerData *peer, bool skipAnimation = false); - void onPeerSelectedChanged(PeerData *peer, bool checked); + void innerSelectedChanged(PeerData *peer, bool checked); void peopleReceived( const MTPcontacts_Found &result, @@ -75,13 +81,14 @@ private: FilterCallback _filterCallback; object_ptr _select; + object_ptr> _comment; class Inner; QPointer _inner; bool _hasSelected = false; - object_ptr _searchTimer; + base::Timer _searchTimer; QString _peopleQuery; bool _peopleFull = false; mtpRequestId _peopleRequest = 0; @@ -95,119 +102,3 @@ private: Animation _scrollAnimation; }; - -// This class is hold in header because it requires Qt preprocessing. -class ShareBox::Inner : public TWidget, public RPCSender, private base::Subscriber { - Q_OBJECT - -public: - Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback); - - void setPeerSelectedChangedCallback(Fn callback); - void peerUnselected(not_null peer); - - QVector selected() const; - bool hasSelected() const; - - void peopleReceived( - const QString &query, - const QVector &my, - const QVector &people); - - void activateSkipRow(int direction); - void activateSkipColumn(int direction); - void activateSkipPage(int pageHeight, int direction); - void updateFilter(QString filter = QString()); - - ~Inner(); - -public slots: - void onSelectActive(); - -signals: - void mustScrollTo(int ymin, int ymax); - void searchByUsername(); - -protected: - void visibleTopBottomUpdated( - int visibleTop, - int visibleBottom) override; - - void paintEvent(QPaintEvent *e) override; - void enterEventHook(QEvent *e) override; - void leaveEventHook(QEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -private: - // Observed notifications. - void notifyPeerUpdated(const Notify::PeerUpdate &update); - void invalidateCache(); - - int displayedChatsCount() const; - - struct Chat { - Chat(PeerData *peer, Fn updateCallback); - - PeerData *peer; - Ui::RoundImageCheckbox checkbox; - Text name; - Animation nameActive; - }; - void paintChat(Painter &p, TimeMs ms, not_null chat, int index); - void updateChat(not_null peer); - void updateChatName(not_null chat, not_null peer); - void repaintChat(not_null peer); - int chatIndex(not_null peer) const; - void repaintChatAtIndex(int index); - Chat *getChatAtIndex(int index); - - void loadProfilePhotos(int yFrom); - void changeCheckState(Chat *chat); - enum class ChangeStateWay { - Default, - SkipCallback, - }; - void changePeerCheckState( - not_null chat, - bool checked, - ChangeStateWay useCallback = ChangeStateWay::Default); - - Chat *getChat(Dialogs::Row *row); - void setActive(int active); - void updateUpon(const QPoint &pos); - - void refresh(); - - float64 _columnSkip = 0.; - float64 _rowWidthReal = 0.; - int _rowsLeft = 0; - int _rowsTop = 0; - int _rowWidth = 0; - int _rowHeight = 0; - int _columnCount = 4; - int _active = -1; - int _upon = -1; - - ShareBox::FilterCallback _filterCallback; - std::unique_ptr _chatsIndexed; - QString _filter; - using FilteredDialogs = QVector; - FilteredDialogs _filtered; - - using DataMap = QMap; - DataMap _dataMap; - using SelectedChats = OrderedSet; - SelectedChats _selected; - - Fn _peerSelectedChangedCallback; - - ChatData *data(Dialogs::Row *row); - - bool _searching = false; - QString _lastQuery; - std::vector _byUsernameFiltered; - std::vector d_byUsernameFiltered; - -}; diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index ac3a45dae..33ef39e09 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -279,7 +279,6 @@ Fn DefaultEditLinkCallback( - not_null controller, not_null field) { const auto weak = make_weak(field); return [=]( @@ -318,8 +317,7 @@ void InitMessageField( field->setInstantReplaces(Ui::InstantReplaces::Default()); field->setInstantReplacesEnabled(Global::ReplaceEmojiValue()); field->setMarkdownReplacesEnabled(rpl::single(true)); - field->setEditLinkCallback( - DefaultEditLinkCallback(controller, field)); + field->setEditLinkCallback(DefaultEditLinkCallback(field)); } bool HasSendText(not_null field) { diff --git a/Telegram/SourceFiles/chat_helpers/message_field.h b/Telegram/SourceFiles/chat_helpers/message_field.h index bf9ea05f3..cc537a3e2 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.h +++ b/Telegram/SourceFiles/chat_helpers/message_field.h @@ -32,7 +32,6 @@ Fn DefaultEditLinkCallback( - not_null controller, not_null field); void InitMessageField( not_null controller, diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index a63efdbbc..36fe629df 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -123,7 +123,9 @@ void FastShareMessage(not_null item) { } } }; - auto submitCallback = [data, isGroup](const QVector &result) { + auto submitCallback = [=]( + QVector &&result, + TextWithTags &&comment) { if (!data->requests.empty()) { return; // Share clicked already. } @@ -187,6 +189,13 @@ void FastShareMessage(not_null item) { continue; } + const auto history = App::history(peer); + if (!comment.text.isEmpty()) { + auto message = ApiWrap::MessageToSend(history); + message.textWithTags = comment; + message.clearDraft = false; + Auth().api().sendMessage(std::move(message)); + } auto request = MTPmessages_ForwardMessages( MTP_flags(sendFlags), data->peer->input, @@ -194,8 +203,14 @@ void FastShareMessage(not_null item) { MTP_vector(generateRandom()), peer->input); auto callback = doneCallback; - auto requestId = MTP::send(request, rpcDone(std::move(callback))); - data->requests.insert(requestId); + history->sendRequestId = MTP::send( + request, + rpcDone(base::duplicate(doneCallback)), + nullptr, + 0, + 0, + history->sendRequestId); + data->requests.insert(history->sendRequestId); } } };