Add a comment field to ShareBox.

This commit is contained in:
John Preston 2018-11-04 11:32:26 +04:00
parent 65b2db2160
commit 5f665b8ecb
8 changed files with 369 additions and 250 deletions

View File

@ -402,6 +402,21 @@ shareNameTop: 6px;
shareColumnSkip: 6px; shareColumnSkip: 6px;
shareActivateDuration: 150; shareActivateDuration: 150;
shareScrollDuration: 300; 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 }}; notificationsBoxMonitor: icon {{ "monitor", notificationsBoxMonitorFg }};
notificationsBoxScreenTop: 10px; notificationsBoxScreenTop: 10px;

View File

@ -215,8 +215,7 @@ EditCaptionBox::EditCaptionBox(
_field->setInstantReplaces(Ui::InstantReplaces::Default()); _field->setInstantReplaces(Ui::InstantReplaces::Default());
_field->setInstantReplacesEnabled(Global::ReplaceEmojiValue()); _field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
_field->setMarkdownReplacesEnabled(rpl::single(true)); _field->setMarkdownReplacesEnabled(rpl::single(true));
_field->setEditLinkCallback( _field->setEditLinkCallback(DefaultEditLinkCallback(_field));
DefaultEditLinkCallback(_controller, _field));
} }
void EditCaptionBox::prepareGifPreview(not_null<DocumentData*> document) { void EditCaptionBox::prepareGifPreview(not_null<DocumentData*> document) {

View File

@ -1582,8 +1582,7 @@ void SendFilesBox::setupCaption() {
_caption->setInstantReplaces(Ui::InstantReplaces::Default()); _caption->setInstantReplaces(Ui::InstantReplaces::Default());
_caption->setInstantReplacesEnabled(Global::ReplaceEmojiValue()); _caption->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
_caption->setMarkdownReplacesEnabled(rpl::single(true)); _caption->setMarkdownReplacesEnabled(rpl::single(true));
_caption->setEditLinkCallback( _caption->setEditLinkCallback(DefaultEditLinkCallback(_caption));
DefaultEditLinkCallback(_controller, _caption));
} }
void SendFilesBox::captionResized() { void SendFilesBox::captionResized() {

View File

@ -8,8 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/share_box.h" #include "boxes/share_box.h"
#include "dialogs/dialogs_indexed_list.h" #include "dialogs/dialogs_indexed_list.h"
#include "styles/style_boxes.h"
#include "styles/style_history.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "mainwindow.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/multi_select.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.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 "ui/text_options.h"
#include "chat_helpers/message_field.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_media_types.h" #include "history/history_media_types.h"
#include "history/history_message.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 "boxes/peer_list_box.h"
#include "auth_session.h" #include "auth_session.h"
#include "messenger.h" #include "messenger.h"
#include "styles/style_boxes.h"
#include "styles/style_history.h"
ShareBox::ShareBox(QWidget*, CopyCallback &&copyCallback, 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<void(PeerData *peer, bool selected)> callback);
void peerUnselected(not_null<PeerData*> peer);
QVector<PeerData*> selected() const;
bool hasSelected() const;
void peopleReceived(
const QString &query,
const QVector<MTPPeer> &my,
const QVector<MTPPeer> &people);
void activateSkipRow(int direction);
void activateSkipColumn(int direction);
void activateSkipPage(int pageHeight, int direction);
void updateFilter(QString filter = QString());
void selectActive();
rpl::producer<Ui::ScrollToRequest> 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<void()> 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*> chat, int index);
void updateChat(not_null<PeerData*> peer);
void updateChatName(not_null<Chat*> chat, not_null<PeerData*> peer);
void repaintChat(not_null<PeerData*> peer);
int chatIndex(not_null<PeerData*> 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*> chat,
bool checked,
ChangeStateWay useCallback = ChangeStateWay::Default);
not_null<Chat*> getChat(not_null<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<Dialogs::IndexedList> _chatsIndexed;
QString _filter;
std::vector<Dialogs::Row*> _filtered;
std::map<not_null<PeerData*>, std::unique_ptr<Chat>> _dataMap;
base::flat_set<not_null<PeerData*>> _selected;
Fn<void(PeerData *peer, bool selected)> _peerSelectedChangedCallback;
bool _searching = false;
QString _lastQuery;
std::vector<PeerData*> _byUsernameFiltered;
std::vector<std::unique_ptr<Chat>> d_byUsernameFiltered;
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
rpl::event_stream<> _searchRequests;
};
ShareBox::ShareBox(
QWidget*,
CopyCallback &&copyCallback,
SubmitCallback &&submitCallback,
FilterCallback &&filterCallback)
: _copyCallback(std::move(copyCallback)) : _copyCallback(std::move(copyCallback))
, _submitCallback(std::move(submitCallback)) , _submitCallback(std::move(submitCallback))
, _filterCallback(std::move(filterCallback)) , _filterCallback(std::move(filterCallback))
, _select(this, st::contactsMultiSelect, langFactory(lng_participant_filter)) , _select(
, _searchTimer(this) { this,
st::contactsMultiSelect,
langFactory(lng_participant_filter))
, _comment(
this,
object_ptr<Ui::InputField>(
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() { void ShareBox::prepare() {
prepareCommentField();
_select->resizeToWidth(st::boxWideWidth); _select->resizeToWidth(st::boxWideWidth);
Ui::SendPendingMoveResizeEvents(_select); Ui::SendPendingMoveResizeEvents(_select);
setTitle(langFactory(lng_share_title)); setTitle(langFactory(lng_share_title));
_inner = setInnerWidget(object_ptr<Inner>(this, std::move(_filterCallback)), getTopScrollSkip()); _inner = setInnerWidget(
connect(_inner, SIGNAL(mustScrollTo(int,int)), this, SLOT(onMustScrollTo(int,int))); object_ptr<Inner>(
this,
std::move(_filterCallback)),
getTopScrollSkip(),
getBottomScrollSkip());
createButtons(); createButtons();
setDimensions(st::boxWideWidth, st::boxMaxListHeight); setDimensions(st::boxWideWidth, st::boxMaxListHeight);
_select->setQueryChangedCallback([this](const QString &query) { onFilterUpdate(query); }); _select->setQueryChangedCallback([=](const QString &query) {
_select->setItemRemovedCallback([this](uint64 itemId) { onFilterUpdate(query);
if (auto peer = App::peerLoaded(itemId)) { });
_select->setItemRemovedCallback([=](uint64 itemId) {
if (const auto peer = App::peerLoaded(itemId)) {
_inner->peerUnselected(peer); _inner->peerUnselected(peer);
onSelectedChanged(); selectedChanged();
update(); update();
} }
}); });
_select->setResizedCallback([this] { updateScrollSkips(); }); _select->setResizedCallback([=] { updateScrollSkips(); });
_select->setSubmittedCallback([this](Qt::KeyboardModifiers modifiers) { _select->setSubmittedCallback([=](Qt::KeyboardModifiers modifiers) {
if (modifiers.testFlag(Qt::ControlModifier) if (modifiers.testFlag(Qt::ControlModifier)
|| modifiers.testFlag(Qt::MetaModifier)) { || modifiers.testFlag(Qt::MetaModifier)) {
onSubmit(); submit();
} else { } else {
_inner->onSelectActive(); _inner->selectActive();
} }
}); });
connect(_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername())); _comment->heightValue(
_inner->setPeerSelectedChangedCallback([this](PeerData *peer, bool checked) { ) | rpl::start_with_next([=] {
onPeerSelectedChanged(peer, checked); updateScrollSkips();
}); }, _comment->lifetime());
_searchTimer->setSingleShot(true); _inner->searchRequests(
connect(_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername())); ) | 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(); _select->raise();
} }
int ShareBox::getTopScrollSkip() const { int ShareBox::getTopScrollSkip() const {
auto result = 0; return _select->isHidden() ? 0 : _select->height();
if (!_select->isHidden()) { }
result += _select->height();
} int ShareBox::getBottomScrollSkip() const {
return result; return _comment->isHidden() ? 0 : _comment->height();
}
int ShareBox::contentHeight() const {
return height() - getTopScrollSkip() - getBottomScrollSkip();
} }
void ShareBox::updateScrollSkips() { void ShareBox::updateScrollSkips() {
setInnerTopSkip(getTopScrollSkip(), true); setInnerTopSkip(getTopScrollSkip(), true);
setInnerBottomSkip(getBottomScrollSkip());
} }
bool ShareBox::onSearchByUsername(bool searchCache) { bool ShareBox::searchByUsername(bool searchCache) {
auto query = _select->getQuery(); auto query = _select->getQuery();
if (query.isEmpty()) { if (query.isEmpty()) {
if (_peopleRequest) { if (_peopleRequest) {
@ -124,9 +304,9 @@ bool ShareBox::onSearchByUsername(bool searchCache) {
return false; return false;
} }
void ShareBox::onNeedSearchByUsername() { void ShareBox::needSearchByUsername() {
if (!onSearchByUsername(true)) { if (!searchByUsername(true)) {
_searchTimer->start(AutoSearchTimeout); _searchTimer.callOnce(AutoSearchTimeout);
} }
} }
@ -172,7 +352,11 @@ bool ShareBox::peopleFailed(const RPCError &error, mtpRequestId requestId) {
} }
void ShareBox::setInnerFocus() { void ShareBox::setInnerFocus() {
_select->setInnerFocus(); if (_comment->isHidden()) {
_select->setInnerFocus();
} else {
_comment->entity()->setFocusFast();
}
} }
void ShareBox::resizeEvent(QResizeEvent *e) { void ShareBox::resizeEvent(QResizeEvent *e) {
@ -194,9 +378,9 @@ void ShareBox::keyPressEvent(QKeyEvent *e) {
} else if (e->key() == Qt::Key_Down) { } else if (e->key() == Qt::Key_Down) {
_inner->activateSkipColumn(1); _inner->activateSkipColumn(1);
} else if (e->key() == Qt::Key_PageUp) { } else if (e->key() == Qt::Key_PageUp) {
_inner->activateSkipPage(height() - getTopScrollSkip(), -1); _inner->activateSkipPage(contentHeight(), -1);
} else if (e->key() == Qt::Key_PageDown) { } else if (e->key() == Qt::Key_PageDown) {
_inner->activateSkipPage(height() - getTopScrollSkip(), 1); _inner->activateSkipPage(contentHeight(), 1);
} else { } else {
BoxContent::keyPressEvent(e); 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() { void ShareBox::createButtons() {
clearButtons(); clearButtons();
if (_hasSelected) { if (_hasSelected) {
addButton(langFactory(lng_share_confirm), [this] { onSubmit(); }); addButton(langFactory(lng_share_confirm), [=] { submit(); });
} else if (_copyCallback) { } 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) { void ShareBox::onFilterUpdate(const QString &query) {
@ -239,36 +415,44 @@ void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) {
addItemWay); addItemWay);
} }
void ShareBox::onPeerSelectedChanged(PeerData *peer, bool checked) { void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) {
if (checked) { if (checked) {
addPeerToMultiSelect(peer); addPeerToMultiSelect(peer);
_select->clearQuery(); _select->clearQuery();
} else { } else {
_select->removeItem(peer->id); _select->removeItem(peer->id);
} }
onSelectedChanged(); selectedChanged();
update(); update();
} }
void ShareBox::onSubmit() { void ShareBox::submit() {
if (_submitCallback) { if (_submitCallback) {
_submitCallback(_inner->selected()); _submitCallback(
_inner->selected(),
_comment->entity()->getTextWithAppliedMarkdown());
} }
} }
void ShareBox::onCopyLink() { void ShareBox::copyLink() {
if (_copyCallback) { if (_copyCallback) {
_copyCallback(); _copyCallback();
} }
} }
void ShareBox::onSelectedChanged() { void ShareBox::selectedChanged() {
updateButtons(); auto hasSelected = _inner->hasSelected();
if (_hasSelected != hasSelected) {
_hasSelected = hasSelected;
createButtons();
_comment->toggle(_hasSelected, anim::type::normal);
_comment->resizeToWidth(st::boxWideWidth);
}
update(); update();
} }
void ShareBox::onMustScrollTo(int top, int bottom) { void ShareBox::scrollTo(Ui::ScrollToRequest request) {
onScrollToY(top, bottom); onScrollToY(request.ymin, request.ymax);
//auto scrollTop = scrollArea()->scrollTop(), scrollBottom = scrollTop + scrollArea()->height(); //auto scrollTop = scrollArea()->scrollTop(), scrollBottom = scrollTop + scrollArea()->height();
//auto from = scrollTop, to = scrollTop; //auto from = scrollTop, to = scrollTop;
//if (scrollTop > top) { //if (scrollTop > top) {
@ -286,9 +470,14 @@ void ShareBox::scrollAnimationCallback() {
//scrollArea()->scrollToY(scrollTop); //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)) , _filterCallback(std::move(filterCallback))
, _chatsIndexed(std::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) { , _chatsIndexed(
std::make_unique<Dialogs::IndexedList>(
Dialogs::SortMode::Add)) {
_rowsTop = st::shareRowsTop; _rowsTop = st::shareRowsTop;
_rowHeight = st::shareRowHeight; _rowHeight = st::shareRowHeight;
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
@ -325,7 +514,7 @@ ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallbac
} }
void ShareBox::Inner::invalidateCache() { void ShareBox::Inner::invalidateCache() {
for_const (auto data, _dataMap) { for (const auto &[peer, data] : _dataMap) {
data->checkbox.invalidateCache(); data->checkbox.invalidateCache();
} }
} }
@ -377,9 +566,8 @@ void ShareBox::Inner::notifyPeerUpdated(const Notify::PeerUpdate &update) {
} }
void ShareBox::Inner::updateChat(not_null<PeerData*> peer) { void ShareBox::Inner::updateChat(not_null<PeerData*> peer) {
auto i = _dataMap.find(peer); if (const auto i = _dataMap.find(peer); i != end(_dataMap)) {
if (i != _dataMap.cend()) { updateChatName(i->second.get(), peer);
updateChatName(i.value(), peer);
repaintChat(peer); repaintChat(peer);
} }
} }
@ -400,11 +588,17 @@ void ShareBox::Inner::repaintChatAtIndex(int index) {
} }
ShareBox::Inner::Chat *ShareBox::Inner::getChatAtIndex(int index) { ShareBox::Inner::Chat *ShareBox::Inner::getChatAtIndex(int index) {
if (index < 0) return nullptr; if (index < 0) {
auto row = ([this, index]() -> Dialogs::Row* { return nullptr;
if (_filter.isEmpty()) return _chatsIndexed->rowAtY(index, 1); }
return (index < _filtered.size()) ? _filtered[index] : nullptr; const auto row = [=] {
})(); if (_filter.isEmpty()) {
return _chatsIndexed->rowAtY(index, 1);
}
return (index < _filtered.size())
? _filtered[index]
: nullptr;
}();
if (row) { if (row) {
return static_cast<Chat*>(row->attached); return static_cast<Chat*>(row->attached);
} }
@ -412,7 +606,7 @@ ShareBox::Inner::Chat *ShareBox::Inner::getChatAtIndex(int index) {
if (!_filter.isEmpty()) { if (!_filter.isEmpty()) {
index -= _filtered.size(); index -= _filtered.size();
if (index >= 0 && index < d_byUsernameFiltered.size()) { if (index >= 0 && index < d_byUsernameFiltered.size()) {
return d_byUsernameFiltered[index]; return d_byUsernameFiltered[index].get();
} }
} }
return nullptr; return nullptr;
@ -442,7 +636,7 @@ int ShareBox::Inner::chatIndex(not_null<PeerData*> peer) const {
} }
++index; ++index;
} }
for (const auto row : d_byUsernameFiltered) { for (const auto &row : d_byUsernameFiltered) {
if (row->peer == peer) { if (row->peer == peer) {
return index; return index;
} }
@ -478,7 +672,7 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) {
(*i)->entry()->loadUserpic(); (*i)->entry()->loadUserpic();
} }
} }
} else if (!_filtered.isEmpty()) { } else if (!_filtered.empty()) {
int from = yFrom / _rowHeight; int from = yFrom / _rowHeight;
if (from < 0) from = 0; if (from < 0) from = 0;
if (from < _filtered.size()) { 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<Dialogs::Row*> row)
-> not_null<Chat*> {
Expects(row->history() != nullptr); Expects(row->history() != nullptr);
auto data = static_cast<Chat*>(row->attached); if (const auto data = static_cast<Chat*>(row->attached)) {
if (!data) { return 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;
} }
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<Chat>(peer, [=] { repaintChat(peer); }));
updateChatName(i->second.get(), peer);
row->attached = i->second.get();
return i->second.get();
} }
void ShareBox::Inner::setActive(int active) { void ShareBox::Inner::setActive(int active) {
@ -525,7 +720,7 @@ void ShareBox::Inner::setActive(int active) {
changeNameFg(_active, 0., 1.); changeNameFg(_active, 0., 1.);
} }
auto y = (_active < _columnCount) ? 0 : (_rowsTop + ((_active / _columnCount) * _rowHeight)); auto y = (_active < _columnCount) ? 0 : (_rowsTop + ((_active / _columnCount) * _rowHeight));
emit mustScrollTo(y, y + _rowHeight); _scrollToRequests.fire({ y, y + _rowHeight });
} }
void ShareBox::Inner::paintChat( void ShareBox::Inner::paintChat(
@ -587,7 +782,7 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) {
style::al_center); style::al_center);
} }
} else { } else {
if (_filtered.isEmpty() if (_filtered.empty()
&& _byUsernameFiltered.empty() && _byUsernameFiltered.empty()
&& !_searching) { && !_searching) {
p.setFont(st::noContactsFont); p.setFont(st::noContactsFont);
@ -616,7 +811,11 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) {
if (indexFrom >= d_byUsernameFiltered.size()) { if (indexFrom >= d_byUsernameFiltered.size()) {
break; break;
} }
paintChat(p, ms, d_byUsernameFiltered[indexFrom], filteredSize + indexFrom); paintChat(
p,
ms,
d_byUsernameFiltered[indexFrom].get(),
filteredSize + indexFrom);
++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)); changeCheckState(getChatAtIndex(_active > 0 ? _active : 0));
} }
@ -693,12 +892,16 @@ void ShareBox::Inner::changeCheckState(Chat *chat) {
} }
void ShareBox::Inner::peerUnselected(not_null<PeerData*> peer) { void ShareBox::Inner::peerUnselected(not_null<PeerData*> peer) {
if (auto chat = _dataMap.value(peer, nullptr)) { if (const auto i = _dataMap.find(peer); i != end(_dataMap)) {
changePeerCheckState(chat, false, ChangeStateWay::SkipCallback); changePeerCheckState(
i->second.get(),
false,
ChangeStateWay::SkipCallback);
} }
} }
void ShareBox::Inner::setPeerSelectedChangedCallback(Fn<void(PeerData *peer, bool selected)> callback) { void ShareBox::Inner::setPeerSelectedChangedCallback(
Fn<void(PeerData *peer, bool selected)> callback) {
_peerSelectedChangedCallback = std::move(callback); _peerSelectedChangedCallback = std::move(callback);
} }
@ -732,9 +935,6 @@ void ShareBox::Inner::updateFilter(QString filter) {
_filter = filter; _filter = filter;
_byUsernameFiltered.clear(); _byUsernameFiltered.clear();
for (int i = 0, l = d_byUsernameFiltered.size(); i < l; ++i) {
delete d_byUsernameFiltered[i];
}
d_byUsernameFiltered.clear(); d_byUsernameFiltered.clear();
if (_filter.isEmpty()) { if (_filter.isEmpty()) {
@ -782,7 +982,7 @@ void ShareBox::Inner::updateFilter(QString filter) {
refresh(); refresh();
_searching = true; _searching = true;
emit searchByUsername(); _searchRequests.fire({});
} }
setActive(-1); setActive(-1);
update(); update();
@ -790,6 +990,14 @@ void ShareBox::Inner::updateFilter(QString filter) {
} }
} }
rpl::producer<Ui::ScrollToRequest> ShareBox::Inner::scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<> ShareBox::Inner::searchRequests() const {
return _searchRequests.events();
}
void ShareBox::Inner::peopleReceived( void ShareBox::Inner::peopleReceived(
const QString &query, const QString &query,
const QVector<MTPPeer> &my, const QVector<MTPPeer> &my,
@ -802,8 +1010,8 @@ void ShareBox::Inner::peopleReceived(
_byUsernameFiltered.reserve(already + my.size() + people.size()); _byUsernameFiltered.reserve(already + my.size() + people.size());
d_byUsernameFiltered.reserve(already + my.size() + people.size()); d_byUsernameFiltered.reserve(already + my.size() + people.size());
const auto feedList = [&](const QVector<MTPPeer> &list) { const auto feedList = [&](const QVector<MTPPeer> &list) {
for (const auto &mtpPeer : list) { for (const auto &data : list) {
if (const auto peer = App::peerLoaded(peerFromMTP(mtpPeer))) { if (const auto peer = App::peerLoaded(peerFromMTP(data))) {
const auto history = App::historyLoaded(peer); const auto history = App::historyLoaded(peer);
if (!_filterCallback(peer)) { if (!_filterCallback(peer)) {
continue; continue;
@ -812,10 +1020,11 @@ void ShareBox::Inner::peopleReceived(
} else if (base::contains(_byUsernameFiltered, peer)) { } else if (base::contains(_byUsernameFiltered, peer)) {
continue; continue;
} }
auto chat = new Chat(peer, [=] { repaintChat(peer); });
updateChatName(chat, peer);
_byUsernameFiltered.push_back(peer); _byUsernameFiltered.push_back(peer);
d_byUsernameFiltered.push_back(chat); d_byUsernameFiltered.push_back(std::make_unique<Chat>(
peer,
[=] { repaintChat(peer); }));
updateChatName(d_byUsernameFiltered.back().get(), peer);
} }
} }
}; };
@ -837,18 +1046,12 @@ void ShareBox::Inner::refresh() {
update(); update();
} }
ShareBox::Inner::~Inner() {
for_const (auto chat, _dataMap) {
delete chat;
}
}
QVector<PeerData*> ShareBox::Inner::selected() const { QVector<PeerData*> ShareBox::Inner::selected() const {
QVector<PeerData*> result; auto result = QVector<PeerData*>();
result.reserve(_dataMap.size()); result.reserve(_dataMap.size());
for_const (auto chat, _dataMap) { for (const auto &[peer, chat] : _dataMap) {
if (chat->checkbox.checked()) { if (chat->checkbox.checked()) {
result.push_back(chat->peer); result.push_back(peer);
} }
} }
return result; return result;

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
#include "base/observer.h" #include "base/observer.h"
#include "base/timer.h"
#include "ui/effects/round_checkbox.h" #include "ui/effects/round_checkbox.h"
namespace Dialogs { namespace Dialogs {
@ -22,19 +23,25 @@ struct PeerUpdate;
namespace Ui { namespace Ui {
class MultiSelect; class MultiSelect;
class InputField;
struct ScrollToRequest;
template <typename Widget>
class SlideWrap;
} // namespace Ui } // namespace Ui
QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId); QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId);
void ShareGameScoreByHash(const QString &hash); void ShareGameScoreByHash(const QString &hash);
class ShareBox : public BoxContent, public RPCSender { class ShareBox : public BoxContent, public RPCSender {
Q_OBJECT
public: public:
using CopyCallback = Fn<void()>; using CopyCallback = Fn<void()>;
using SubmitCallback = Fn<void(const QVector<PeerData*> &)>; using SubmitCallback = Fn<void(QVector<PeerData*>&&, TextWithTags&&)>;
using FilterCallback = Fn<bool(PeerData*)>; using FilterCallback = Fn<bool(PeerData*)>;
ShareBox(QWidget*, CopyCallback &&copyCallback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback); ShareBox(
QWidget*,
CopyCallback &&copyCallback,
SubmitCallback &&submitCallback,
FilterCallback &&filterCallback);
protected: protected:
void prepare() override; void prepare() override;
@ -43,27 +50,26 @@ protected:
void resizeEvent(QResizeEvent *e) override; void resizeEvent(QResizeEvent *e) override;
void keyPressEvent(QKeyEvent *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: private:
void prepareCommentField();
void scrollAnimationCallback(); void scrollAnimationCallback();
void submit();
void copyLink();
bool searchByUsername(bool useCache = false);
void scrollTo(Ui::ScrollToRequest request);
void needSearchByUsername();
void onFilterUpdate(const QString &query); void onFilterUpdate(const QString &query);
void onSelectedChanged(); void selectedChanged();
void updateButtons();
void createButtons(); void createButtons();
int getTopScrollSkip() const; int getTopScrollSkip() const;
int getBottomScrollSkip() const;
int contentHeight() const;
void updateScrollSkips(); void updateScrollSkips();
void addPeerToMultiSelect(PeerData *peer, bool skipAnimation = false); void addPeerToMultiSelect(PeerData *peer, bool skipAnimation = false);
void onPeerSelectedChanged(PeerData *peer, bool checked); void innerSelectedChanged(PeerData *peer, bool checked);
void peopleReceived( void peopleReceived(
const MTPcontacts_Found &result, const MTPcontacts_Found &result,
@ -75,13 +81,14 @@ private:
FilterCallback _filterCallback; FilterCallback _filterCallback;
object_ptr<Ui::MultiSelect> _select; object_ptr<Ui::MultiSelect> _select;
object_ptr<Ui::SlideWrap<Ui::InputField>> _comment;
class Inner; class Inner;
QPointer<Inner> _inner; QPointer<Inner> _inner;
bool _hasSelected = false; bool _hasSelected = false;
object_ptr<QTimer> _searchTimer; base::Timer _searchTimer;
QString _peopleQuery; QString _peopleQuery;
bool _peopleFull = false; bool _peopleFull = false;
mtpRequestId _peopleRequest = 0; mtpRequestId _peopleRequest = 0;
@ -95,119 +102,3 @@ private:
Animation _scrollAnimation; 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<void(PeerData *peer, bool selected)> callback);
void peerUnselected(not_null<PeerData*> peer);
QVector<PeerData*> selected() const;
bool hasSelected() const;
void peopleReceived(
const QString &query,
const QVector<MTPPeer> &my,
const QVector<MTPPeer> &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<void()> updateCallback);
PeerData *peer;
Ui::RoundImageCheckbox checkbox;
Text name;
Animation nameActive;
};
void paintChat(Painter &p, TimeMs ms, not_null<Chat*> chat, int index);
void updateChat(not_null<PeerData*> peer);
void updateChatName(not_null<Chat*> chat, not_null<PeerData*> peer);
void repaintChat(not_null<PeerData*> peer);
int chatIndex(not_null<PeerData*> 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*> 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<Dialogs::IndexedList> _chatsIndexed;
QString _filter;
using FilteredDialogs = QVector<Dialogs::Row*>;
FilteredDialogs _filtered;
using DataMap = QMap<PeerData*, Chat*>;
DataMap _dataMap;
using SelectedChats = OrderedSet<PeerData*>;
SelectedChats _selected;
Fn<void(PeerData *peer, bool selected)> _peerSelectedChangedCallback;
ChatData *data(Dialogs::Row *row);
bool _searching = false;
QString _lastQuery;
std::vector<PeerData*> _byUsernameFiltered;
std::vector<Chat*> d_byUsernameFiltered;
};

View File

@ -279,7 +279,6 @@ Fn<bool(
QString text, QString text,
QString link, QString link,
EditLinkAction action)> DefaultEditLinkCallback( EditLinkAction action)> DefaultEditLinkCallback(
not_null<Window::Controller*> controller,
not_null<Ui::InputField*> field) { not_null<Ui::InputField*> field) {
const auto weak = make_weak(field); const auto weak = make_weak(field);
return [=]( return [=](
@ -318,8 +317,7 @@ void InitMessageField(
field->setInstantReplaces(Ui::InstantReplaces::Default()); field->setInstantReplaces(Ui::InstantReplaces::Default());
field->setInstantReplacesEnabled(Global::ReplaceEmojiValue()); field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
field->setMarkdownReplacesEnabled(rpl::single(true)); field->setMarkdownReplacesEnabled(rpl::single(true));
field->setEditLinkCallback( field->setEditLinkCallback(DefaultEditLinkCallback(field));
DefaultEditLinkCallback(controller, field));
} }
bool HasSendText(not_null<const Ui::InputField*> field) { bool HasSendText(not_null<const Ui::InputField*> field) {

View File

@ -32,7 +32,6 @@ Fn<bool(
QString text, QString text,
QString link, QString link,
Ui::InputField::EditLinkAction action)> DefaultEditLinkCallback( Ui::InputField::EditLinkAction action)> DefaultEditLinkCallback(
not_null<Window::Controller*> controller,
not_null<Ui::InputField*> field); not_null<Ui::InputField*> field);
void InitMessageField( void InitMessageField(
not_null<Window::Controller*> controller, not_null<Window::Controller*> controller,

View File

@ -123,7 +123,9 @@ void FastShareMessage(not_null<HistoryItem*> item) {
} }
} }
}; };
auto submitCallback = [data, isGroup](const QVector<PeerData*> &result) { auto submitCallback = [=](
QVector<PeerData*> &&result,
TextWithTags &&comment) {
if (!data->requests.empty()) { if (!data->requests.empty()) {
return; // Share clicked already. return; // Share clicked already.
} }
@ -187,6 +189,13 @@ void FastShareMessage(not_null<HistoryItem*> item) {
continue; 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( auto request = MTPmessages_ForwardMessages(
MTP_flags(sendFlags), MTP_flags(sendFlags),
data->peer->input, data->peer->input,
@ -194,8 +203,14 @@ void FastShareMessage(not_null<HistoryItem*> item) {
MTP_vector<MTPlong>(generateRandom()), MTP_vector<MTPlong>(generateRandom()),
peer->input); peer->input);
auto callback = doneCallback; auto callback = doneCallback;
auto requestId = MTP::send(request, rpcDone(std::move(callback))); history->sendRequestId = MTP::send(
data->requests.insert(requestId); request,
rpcDone(base::duplicate(doneCallback)),
nullptr,
0,
0,
history->sendRequestId);
data->requests.insert(history->sendRequestId);
} }
} }
}; };