mirror of https://github.com/procxx/kepka.git
Ui::MultiSelect control started: now it's just search field + cancel.
This commit is contained in:
parent
e5a5273b3a
commit
3455344c62
|
@ -201,7 +201,7 @@ ScrollableBox::ScrollableBox(const style::flatScroll &scroll, int32 w) : Abstrac
|
|||
}
|
||||
|
||||
void ScrollableBox::resizeEvent(QResizeEvent *e) {
|
||||
_scroll->setGeometry(0, _topSkip, width(), height() - _topSkip - _bottomSkip);
|
||||
updateScrollGeometry();
|
||||
AbstractBox::resizeEvent(e);
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,19 @@ void ScrollableBox::init(ScrolledWidget *inner, int bottomSkip, int topSkip) {
|
|||
_topSkip = topSkip;
|
||||
_scroll->setOwnedWidget(inner);
|
||||
_scroll->setFocusPolicy(Qt::NoFocus);
|
||||
ScrollableBox::resizeEvent(nullptr);
|
||||
updateScrollGeometry();
|
||||
}
|
||||
|
||||
void ScrollableBox::setScrollSkips(int bottomSkip, int topSkip) {
|
||||
if (_topSkip != topSkip || _bottomSkip != bottomSkip) {
|
||||
_topSkip = topSkip;
|
||||
_bottomSkip = bottomSkip;
|
||||
updateScrollGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollableBox::updateScrollGeometry() {
|
||||
_scroll->setGeometry(0, _topSkip, width(), height() - _topSkip - _bottomSkip);
|
||||
}
|
||||
|
||||
ItemListBox::ItemListBox(const style::flatScroll &scroll, int32 w) : ScrollableBox(scroll, w) {
|
||||
|
|
|
@ -106,6 +106,7 @@ public:
|
|||
|
||||
protected:
|
||||
void init(ScrolledWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
|
||||
void setScrollSkips(int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
|
@ -114,8 +115,10 @@ protected:
|
|||
}
|
||||
|
||||
private:
|
||||
void updateScrollGeometry();
|
||||
|
||||
ChildWidget<ScrollArea> _scroll;
|
||||
int32 _topSkip, _bottomSkip;
|
||||
int _topSkip, _bottomSkip;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -100,6 +100,11 @@ boxSearchCancel: IconButton {
|
|||
duration: 150;
|
||||
}
|
||||
|
||||
contactsMultiSelect: MultiSelect {
|
||||
field: boxSearchField;
|
||||
cancel: boxSearchCancel;
|
||||
maxHeight: 104px;
|
||||
}
|
||||
contactsPhotoCheckbox: RoundImageCheckbox {
|
||||
imageRadius: 21px;
|
||||
imageSmallRadius: 18px;
|
||||
|
|
|
@ -30,7 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "mainwindow.h"
|
||||
#include "application.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "ui/buttons/icon_button.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "boxes/photocropbox.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "observer_peer.h"
|
||||
|
@ -42,8 +42,7 @@ QString cantInviteError() {
|
|||
|
||||
ContactsBox::ContactsBox() : ItemListBox(st::contactsScroll)
|
||||
, _inner(this, CreatingGroupNone)
|
||||
, _filter(this, st::boxSearchField, lang(lng_participant_filter))
|
||||
, _filterCancel(this, st::boxSearchCancel)
|
||||
, _select(this, st::contactsMultiSelect, lang(lng_participant_filter))
|
||||
, _next(this, lang(lng_create_group_next), st::defaultBoxButton)
|
||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
||||
, _topShadow(this) {
|
||||
|
@ -52,8 +51,7 @@ ContactsBox::ContactsBox() : ItemListBox(st::contactsScroll)
|
|||
|
||||
ContactsBox::ContactsBox(const QString &name, const QImage &photo) : ItemListBox(st::boxScroll)
|
||||
, _inner(this, CreatingGroupGroup)
|
||||
, _filter(this, st::boxSearchField, lang(lng_participant_filter))
|
||||
, _filterCancel(this, st::boxSearchCancel)
|
||||
, _select(this, st::contactsMultiSelect, lang(lng_participant_filter))
|
||||
, _next(this, lang(lng_create_group_create), st::defaultBoxButton)
|
||||
, _cancel(this, lang(lng_create_group_back), st::cancelBoxButton)
|
||||
, _topShadow(this)
|
||||
|
@ -64,8 +62,7 @@ ContactsBox::ContactsBox(const QString &name, const QImage &photo) : ItemListBox
|
|||
|
||||
ContactsBox::ContactsBox(ChannelData *channel) : ItemListBox(st::boxScroll)
|
||||
, _inner(this, channel, MembersFilter::Recent, MembersAlreadyIn())
|
||||
, _filter(this, st::boxSearchField, lang(lng_participant_filter))
|
||||
, _filterCancel(this, st::boxSearchCancel)
|
||||
, _select(this, st::contactsMultiSelect, lang(lng_participant_filter))
|
||||
, _next(this, lang(lng_participant_invite), st::defaultBoxButton)
|
||||
, _cancel(this, lang(lng_create_group_skip), st::cancelBoxButton)
|
||||
, _topShadow(this) {
|
||||
|
@ -74,8 +71,7 @@ ContactsBox::ContactsBox(ChannelData *channel) : ItemListBox(st::boxScroll)
|
|||
|
||||
ContactsBox::ContactsBox(ChannelData *channel, MembersFilter filter, const MembersAlreadyIn &already) : ItemListBox((filter == MembersFilter::Admins) ? st::contactsScroll : st::boxScroll)
|
||||
, _inner(this, channel, filter, already)
|
||||
, _filter(this, st::boxSearchField, lang(lng_participant_filter))
|
||||
, _filterCancel(this, st::boxSearchCancel)
|
||||
, _select(this, st::contactsMultiSelect, lang(lng_participant_filter))
|
||||
, _next(this, lang(lng_participant_invite), st::defaultBoxButton)
|
||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
||||
, _topShadow(this) {
|
||||
|
@ -84,8 +80,7 @@ ContactsBox::ContactsBox(ChannelData *channel, MembersFilter filter, const Membe
|
|||
|
||||
ContactsBox::ContactsBox(ChatData *chat, MembersFilter filter) : ItemListBox(st::boxScroll)
|
||||
, _inner(this, chat, filter)
|
||||
, _filter(this, st::boxSearchField, lang(lng_participant_filter))
|
||||
, _filterCancel(this, st::boxSearchCancel)
|
||||
, _select(this, st::contactsMultiSelect, lang(lng_participant_filter))
|
||||
, _next(this, lang((filter == MembersFilter::Admins) ? lng_settings_save : lng_participant_invite), st::defaultBoxButton)
|
||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
||||
, _topShadow(this) {
|
||||
|
@ -94,8 +89,7 @@ ContactsBox::ContactsBox(ChatData *chat, MembersFilter filter) : ItemListBox(st:
|
|||
|
||||
ContactsBox::ContactsBox(UserData *bot) : ItemListBox(st::contactsScroll)
|
||||
, _inner(this, bot)
|
||||
, _filter(this, st::boxSearchField, lang(lng_participant_filter))
|
||||
, _filterCancel(this, st::boxSearchCancel)
|
||||
, _select(this, st::contactsMultiSelect, lang(lng_participant_filter))
|
||||
, _next(this, lang(lng_create_group_next), st::defaultBoxButton)
|
||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
||||
, _topShadow(this) {
|
||||
|
@ -103,10 +97,7 @@ ContactsBox::ContactsBox(UserData *bot) : ItemListBox(st::contactsScroll)
|
|||
}
|
||||
|
||||
void ContactsBox::init() {
|
||||
bool inviting = (_inner->creating() == CreatingGroupGroup) || (_inner->channel() && _inner->membersFilter() == MembersFilter::Recent) || _inner->chat();
|
||||
int32 topSkip = st::boxTitleHeight + _filter->height();
|
||||
int32 bottomSkip = inviting ? (st::boxButtonPadding.top() + _next.height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip;
|
||||
ItemListBox::init(_inner, bottomSkip, topSkip);
|
||||
ItemListBox::init(_inner);
|
||||
|
||||
connect(_inner, SIGNAL(chosenChanged()), this, SLOT(onChosenChanged()));
|
||||
connect(_inner, SIGNAL(addRequested()), App::wnd(), SLOT(onShowAddContact()));
|
||||
|
@ -128,16 +119,12 @@ void ContactsBox::init() {
|
|||
}
|
||||
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll()));
|
||||
connect(_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
|
||||
connect(_filter, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_filterCancel, SIGNAL(clicked()), this, SLOT(onFilterCancel()));
|
||||
_select->setQueryChangedCallback([this](const QString &query) { onFilterUpdate(query); });
|
||||
_select->setSubmittedCallback([this](bool) { onSubmit(); });
|
||||
connect(_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int)));
|
||||
connect(_inner, SIGNAL(selectAllQuery()), _filter, SLOT(selectAll()));
|
||||
connect(_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername()));
|
||||
connect(_inner, SIGNAL(adminAdded()), this, SIGNAL(adminAdded()));
|
||||
|
||||
_filterCancel->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
_searchTimer.setSingleShot(true);
|
||||
connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername()));
|
||||
|
||||
|
@ -145,7 +132,7 @@ void ContactsBox::init() {
|
|||
}
|
||||
|
||||
bool ContactsBox::onSearchByUsername(bool searchCache) {
|
||||
QString q = _filter->getLastText().trimmed();
|
||||
auto q = _select->getQuery();
|
||||
if (q.isEmpty()) {
|
||||
if (_peopleRequest) {
|
||||
_peopleRequest = 0;
|
||||
|
@ -213,12 +200,7 @@ bool ContactsBox::peopleFailed(const RPCError &error, mtpRequestId req) {
|
|||
}
|
||||
|
||||
void ContactsBox::showAll() {
|
||||
_filter->show();
|
||||
if (_filter->getLastText().isEmpty()) {
|
||||
_filterCancel->hide();
|
||||
} else {
|
||||
_filterCancel->show();
|
||||
}
|
||||
_select->show();
|
||||
if (_inner->channel() && _inner->membersFilter() == MembersFilter::Admins) {
|
||||
_next.hide();
|
||||
_cancel.hide();
|
||||
|
@ -238,7 +220,7 @@ void ContactsBox::showAll() {
|
|||
}
|
||||
|
||||
void ContactsBox::doSetInnerFocus() {
|
||||
_filter->setFocus();
|
||||
_select->setInnerFocus();
|
||||
}
|
||||
|
||||
void ContactsBox::onSubmit() {
|
||||
|
@ -246,7 +228,8 @@ void ContactsBox::onSubmit() {
|
|||
}
|
||||
|
||||
void ContactsBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (_filter->hasFocus()) {
|
||||
auto focused = focusWidget();
|
||||
if (_select == focused || _select->isAncestorOf(focusWidget())) {
|
||||
if (e->key() == Qt::Key_Down) {
|
||||
_inner->selectSkip(1);
|
||||
} else if (e->key() == Qt::Key_Up) {
|
||||
|
@ -285,13 +268,19 @@ void ContactsBox::paintEvent(QPaintEvent *e) {
|
|||
|
||||
void ContactsBox::resizeEvent(QResizeEvent *e) {
|
||||
ItemListBox::resizeEvent(e);
|
||||
_filter->resize(width(), _filter->height());
|
||||
_filter->moveToLeft(0, st::boxTitleHeight);
|
||||
_filterCancel->moveToRight(0, st::boxTitleHeight);
|
||||
|
||||
_select->resizeToWidth(width());
|
||||
_select->moveToLeft(0, st::boxTitleHeight);
|
||||
|
||||
auto inviting = (_inner->creating() == CreatingGroupGroup) || (_inner->channel() && _inner->membersFilter() == MembersFilter::Recent) || _inner->chat();
|
||||
auto topSkip = st::boxTitleHeight + _select->height();
|
||||
auto bottomSkip = inviting ? (st::boxButtonPadding.top() + _next.height() + st::boxButtonPadding.bottom()) : st::boxScrollSkip;
|
||||
setScrollSkips(bottomSkip, topSkip);
|
||||
|
||||
_inner->resize(width(), _inner->height());
|
||||
_next.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _next.height());
|
||||
_cancel.moveToRight(st::boxButtonPadding.right() + _next.width() + st::boxButtonPadding.left(), _next.y());
|
||||
_topShadow.setGeometry(0, st::boxTitleHeight + _filter->height(), width(), st::lineWidth);
|
||||
_topShadow.setGeometry(0, st::boxTitleHeight + _select->height(), width(), st::lineWidth);
|
||||
if (_bottomShadow) _bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _next.height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth);
|
||||
}
|
||||
|
||||
|
@ -301,18 +290,9 @@ void ContactsBox::closePressed() {
|
|||
}
|
||||
}
|
||||
|
||||
void ContactsBox::onFilterCancel() {
|
||||
_filter->setText(QString());
|
||||
}
|
||||
|
||||
void ContactsBox::onFilterUpdate() {
|
||||
void ContactsBox::onFilterUpdate(const QString &filter) {
|
||||
scrollArea()->scrollToY(0);
|
||||
if (_filter->getLastText().isEmpty()) {
|
||||
_filterCancel->hide();
|
||||
} else {
|
||||
_filterCancel->show();
|
||||
}
|
||||
_inner->updateFilter(_filter->getLastText());
|
||||
_inner->updateFilter(filter);
|
||||
}
|
||||
|
||||
void ContactsBox::onChosenChanged() {
|
||||
|
@ -322,8 +302,7 @@ void ContactsBox::onChosenChanged() {
|
|||
void ContactsBox::onInvite() {
|
||||
QVector<UserData*> users(_inner->selected());
|
||||
if (users.isEmpty()) {
|
||||
_filter->setFocus();
|
||||
_filter->showError();
|
||||
_select->setInnerFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -339,14 +318,12 @@ void ContactsBox::onInvite() {
|
|||
void ContactsBox::onCreate() {
|
||||
if (_saveRequestId) return;
|
||||
|
||||
MTPVector<MTPInputUser> users(MTP_vector<MTPInputUser>(_inner->selectedInputs()));
|
||||
const auto &v(users.c_vector().v);
|
||||
if (v.isEmpty() || (v.size() == 1 && v.at(0).type() == mtpc_inputUserSelf)) {
|
||||
_filter->setFocus();
|
||||
_filter->showError();
|
||||
auto users = _inner->selectedInputs();
|
||||
if (users.isEmpty() || (users.size() == 1 && users.at(0).type() == mtpc_inputUserSelf)) {
|
||||
_select->setInnerFocus();
|
||||
return;
|
||||
}
|
||||
_saveRequestId = MTP::send(MTPmessages_CreateChat(MTP_vector<MTPInputUser>(v), MTP_string(_creationName)), rpcDone(&ContactsBox::creationDone), rpcFail(&ContactsBox::creationFail));
|
||||
_saveRequestId = MTP::send(MTPmessages_CreateChat(MTP_vector<MTPInputUser>(users), MTP_string(_creationName)), rpcDone(&ContactsBox::creationDone), rpcFail(&ContactsBox::creationFail));
|
||||
}
|
||||
|
||||
void ContactsBox::onSaveAdmins() {
|
||||
|
@ -493,8 +470,7 @@ bool ContactsBox::creationFail(const RPCError &error) {
|
|||
onClose();
|
||||
return true;
|
||||
} else if (error.type() == "USERS_TOO_FEW") {
|
||||
_filter->setFocus();
|
||||
_filter->showError();
|
||||
_select->setInnerFocus();
|
||||
return true;
|
||||
} else if (error.type() == "PEER_FLOOD") {
|
||||
Ui::showLayer(new InformBox(cantInviteError()), KeepOtherLayers);
|
||||
|
@ -1263,7 +1239,6 @@ void ContactsBox::Inner::chooseParticipant() {
|
|||
if (_filteredSel < 0 || _filteredSel >= _filtered.size() || contactData(_filtered[_filteredSel])->disabledChecked) return;
|
||||
changeCheckState(_filtered[_filteredSel]);
|
||||
}
|
||||
emit selectAllQuery();
|
||||
}
|
||||
} else {
|
||||
PeerData *peer = 0;
|
||||
|
|
|
@ -31,7 +31,7 @@ class IndexedList;
|
|||
} // namespace Dialogs
|
||||
|
||||
namespace Ui {
|
||||
class IconButton;
|
||||
class MultiSelect;
|
||||
} // namespace Ui
|
||||
|
||||
QString cantInviteError();
|
||||
|
@ -57,8 +57,6 @@ signals:
|
|||
void adminAdded();
|
||||
|
||||
public slots:
|
||||
void onFilterUpdate();
|
||||
void onFilterCancel();
|
||||
void onChosenChanged();
|
||||
void onScroll();
|
||||
|
||||
|
@ -82,11 +80,11 @@ protected:
|
|||
|
||||
private:
|
||||
void init();
|
||||
void onFilterUpdate(const QString &filter);
|
||||
|
||||
class Inner;
|
||||
ChildWidget<Inner> _inner;
|
||||
ChildWidget<InputField> _filter;
|
||||
ChildWidget<Ui::IconButton> _filterCancel;
|
||||
ChildWidget<Ui::MultiSelect> _select;
|
||||
|
||||
BoxButton _next, _cancel;
|
||||
MembersFilter _membersFilter;
|
||||
|
@ -178,7 +176,6 @@ public:
|
|||
|
||||
signals:
|
||||
void mustScrollTo(int ymin, int ymax);
|
||||
void selectAllQuery();
|
||||
void searchByUsername();
|
||||
void chosenChanged();
|
||||
void adminAdded();
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
|
||||
#include "styles/style_widgets.h"
|
||||
#include "ui/buttons/icon_button.h"
|
||||
#include "lang.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
MultiSelect::MultiSelect(QWidget *parent, const style::MultiSelect &st, const QString &placeholder) : TWidget(parent)
|
||||
, _st(st)
|
||||
, _scroll(this, st::boxScroll)
|
||||
, _inner(this, st, placeholder) {
|
||||
_scroll->setOwnedWidget(_inner);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
}
|
||||
|
||||
void MultiSelect::setQueryChangedCallback(base::lambda_unique<void(const QString &query)> callback) {
|
||||
_inner->setQueryChangedCallback(std_::move(callback));
|
||||
}
|
||||
|
||||
void MultiSelect::setSubmittedCallback(base::lambda_unique<void(bool ctrlShiftEnter)> callback) {
|
||||
_inner->setSubmittedCallback(std_::move(callback));
|
||||
}
|
||||
|
||||
void MultiSelect::setInnerFocus() {
|
||||
if (_inner->setInnerFocus()) {
|
||||
_scroll->scrollToY(_scroll->scrollTopMax());
|
||||
}
|
||||
}
|
||||
|
||||
QString MultiSelect::getQuery() const {
|
||||
return _inner->getQuery();
|
||||
}
|
||||
|
||||
void MultiSelect::addItem(std_::unique_ptr<Item> item) {
|
||||
_inner->addItem(std_::move(item));
|
||||
}
|
||||
|
||||
int MultiSelect::resizeGetHeight(int newWidth) {
|
||||
_inner->resizeToWidth(newWidth);
|
||||
auto newHeight = qMin(_inner->height(), _st.maxHeight);
|
||||
_scroll->resize(newWidth, newHeight);
|
||||
return newHeight;
|
||||
}
|
||||
|
||||
void MultiSelect::resizeEvent(QResizeEvent *e) {
|
||||
_scroll->moveToLeft(0, 0);
|
||||
}
|
||||
|
||||
MultiSelect::Item::Item(uint64 id, const QString &text, const style::color &color)
|
||||
: _id(id) {
|
||||
}
|
||||
|
||||
void MultiSelect::Item::setText(const QString &text) {
|
||||
|
||||
}
|
||||
|
||||
void MultiSelect::Item::paint(Painter &p, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, const QString &placeholder) : ScrolledWidget(parent)
|
||||
, _st(st)
|
||||
, _filter(this, _st.field, placeholder)
|
||||
, _cancel(this, _st.cancel) {
|
||||
connect(_filter, SIGNAL(changed()), this, SLOT(onQueryChanged()));
|
||||
connect(_filter, SIGNAL(submitted(bool)), this, SLOT(onSubmitted(bool)));
|
||||
_cancel->hide();
|
||||
_cancel->setClickedCallback([this] {
|
||||
_filter->setText(QString());
|
||||
_filter->setFocus();
|
||||
});
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::onQueryChanged() {
|
||||
auto query = getQuery();
|
||||
_cancel->setVisible(!query.isEmpty());
|
||||
if (_queryChangedCallback) {
|
||||
_queryChangedCallback(query);
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiSelect::Inner::setInnerFocus() {
|
||||
if (!_filter->hasFocus()) {
|
||||
_filter->setFocus();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString MultiSelect::Inner::getQuery() const {
|
||||
return _filter->getLastText().trimmed();
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::setQueryChangedCallback(base::lambda_unique<void(const QString &query)> callback) {
|
||||
_queryChangedCallback = std_::move(callback);
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::setSubmittedCallback(base::lambda_unique<void(bool ctrlShiftEnter)> callback) {
|
||||
_submittedCallback = std_::move(callback);
|
||||
}
|
||||
|
||||
int MultiSelect::Inner::resizeGetHeight(int newWidth) {
|
||||
_filter->resizeToWidth(newWidth);
|
||||
return _filter->height();
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::resizeEvent(QResizeEvent *e) {
|
||||
_filter->moveToLeft(0, 0);
|
||||
_cancel->moveToRight(0, 0);
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::addItem(std_::unique_ptr<Item> item) {
|
||||
_items.push_back(item.release());
|
||||
refreshItemsGeometry(nullptr);
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::refreshItemsGeometry(Item *startingFromRowWithItem) {
|
||||
int startingFromRow = 0;
|
||||
int startingFromIndex = 0;
|
||||
for (int row = 1, rowsCount = qMin(_rows.size(), 1); row != rowsCount; ++row) {
|
||||
if (startingFromRowWithItem) {
|
||||
if (_rows[row - 1].contains(startingFromRowWithItem)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
startingFromIndex += _rows[row - 1].size();
|
||||
++startingFromRow;
|
||||
}
|
||||
while (_rows.size() > startingFromRow) {
|
||||
_rows.pop_back();
|
||||
}
|
||||
for (int i = startingFromIndex, count = _items.size(); i != count; ++i) {
|
||||
Row row;
|
||||
row.append(_items[i]);
|
||||
_rows.append(row);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::setItemText(uint64 itemId, const QString &text) {
|
||||
for (int i = 0, count = _items.size(); i != count; ++i) {
|
||||
auto item = _items[i];
|
||||
if (item->id() == itemId) {
|
||||
item->setText(text);
|
||||
refreshItemsGeometry(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::setItemRemovedCallback(base::lambda_unique<void(uint64 itemId)> callback) {
|
||||
_itemRemovedCallback = std_::move(callback);
|
||||
}
|
||||
|
||||
void MultiSelect::Inner::removeItem(uint64 itemId) {
|
||||
for (int i = 0, count = _items.size(); i != count; ++i) {
|
||||
auto item = _items[i];
|
||||
if (item->id() == itemId) {
|
||||
_items.removeAt(i);
|
||||
refreshItemsGeometry(item);
|
||||
delete item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_itemRemovedCallback) {
|
||||
_itemRemovedCallback(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
MultiSelect::Inner::~Inner() {
|
||||
base::take(_rows);
|
||||
for (auto item : base::take(_items)) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ui
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
class InputField;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class IconButton;
|
||||
|
||||
class MultiSelect : public TWidget {
|
||||
public:
|
||||
MultiSelect(QWidget *parent, const style::MultiSelect &st, const QString &placeholder = QString());
|
||||
|
||||
QString getQuery() const;
|
||||
void setInnerFocus();
|
||||
|
||||
void setQueryChangedCallback(base::lambda_unique<void(const QString &query)> callback);
|
||||
void setSubmittedCallback(base::lambda_unique<void(bool ctrlShiftEnter)> callback);
|
||||
|
||||
class Item;
|
||||
void addItem(std_::unique_ptr<Item> item);
|
||||
void setItemText(uint64 itemId, const QString &text);
|
||||
|
||||
void setItemRemovedCallback(base::lambda_unique<void(uint64 itemId)> callback);
|
||||
void removeItem(uint64 itemId); // Always calls the itemRemovedCallback().
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
ChildWidget<ScrollArea> _scroll;
|
||||
|
||||
class Inner;
|
||||
ChildWidget<Inner> _inner;
|
||||
|
||||
const style::MultiSelect &_st;
|
||||
|
||||
};
|
||||
|
||||
class MultiSelect::Item {
|
||||
public:
|
||||
Item(uint64 id, const QString &text, const style::color &color);
|
||||
|
||||
uint64 id() const {
|
||||
return _id;
|
||||
}
|
||||
void setText(const QString &text);
|
||||
void paint(Painter &p, int x, int y);
|
||||
|
||||
virtual ~Item() = default;
|
||||
|
||||
protected:
|
||||
virtual void paintImage(Painter &p, int x, int y, int outerWidth, int size) = 0;
|
||||
|
||||
private:
|
||||
uint64 _id;
|
||||
|
||||
};
|
||||
|
||||
// This class is hold in header because it requires Qt preprocessing.
|
||||
class MultiSelect::Inner : public ScrolledWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Inner(QWidget *parent, const style::MultiSelect &st, const QString &placeholder);
|
||||
|
||||
QString getQuery() const;
|
||||
bool setInnerFocus();
|
||||
|
||||
void setQueryChangedCallback(base::lambda_unique<void(const QString &query)> callback);
|
||||
void setSubmittedCallback(base::lambda_unique<void(bool ctrlShiftEnter)> callback);
|
||||
|
||||
void addItem(std_::unique_ptr<Item> item);
|
||||
void setItemText(uint64 itemId, const QString &text);
|
||||
|
||||
void setItemRemovedCallback(base::lambda_unique<void(uint64 itemId)> callback);
|
||||
void removeItem(uint64 itemId); // Always calls the itemRemovedCallback().
|
||||
|
||||
~Inner();
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void onQueryChanged();
|
||||
void onSubmitted(bool ctrlShiftEnter) {
|
||||
if (_submittedCallback) {
|
||||
_submittedCallback(ctrlShiftEnter);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void refreshItemsGeometry(Item *startingFromItem);
|
||||
|
||||
const style::MultiSelect &_st;
|
||||
|
||||
using Row = QList<Item*>;
|
||||
using Rows = QList<Row>;
|
||||
Rows _rows;
|
||||
|
||||
using Items = QList<Item*>;
|
||||
Items _items;
|
||||
|
||||
ChildWidget<InputField> _filter;
|
||||
ChildWidget<Ui::IconButton> _cancel;
|
||||
|
||||
base::lambda_unique<void(const QString &query)> _queryChangedCallback;
|
||||
base::lambda_unique<void(bool ctrlShiftEnter)> _submittedCallback;
|
||||
base::lambda_unique<void(uint64 itemId)> _itemRemovedCallback;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
|
@ -67,6 +67,12 @@ RoundImageCheckbox {
|
|||
checkIcon: icon;
|
||||
}
|
||||
|
||||
MultiSelect {
|
||||
field: InputField;
|
||||
cancel: IconButton;
|
||||
maxHeight: pixels;
|
||||
}
|
||||
|
||||
widgetSlideDuration: 200;
|
||||
|
||||
discreteSliderHeight: 39px;
|
||||
|
|
|
@ -493,6 +493,8 @@
|
|||
'<(src_loc)/ui/widgets/label_simple.h',
|
||||
'<(src_loc)/ui/widgets/media_slider.cpp',
|
||||
'<(src_loc)/ui/widgets/media_slider.h',
|
||||
'<(src_loc)/ui/widgets/multi_select.cpp',
|
||||
'<(src_loc)/ui/widgets/multi_select.h',
|
||||
'<(src_loc)/ui/widgets/shadow.cpp',
|
||||
'<(src_loc)/ui/widgets/shadow.h',
|
||||
'<(src_loc)/ui/widgets/widget_slide_wrap.h',
|
||||
|
|
Loading…
Reference in New Issue