Ui::MultiSelect control started: now it's just search field + cancel.

This commit is contained in:
John Preston 2016-10-20 22:48:35 +03:00
parent e5a5273b3a
commit 3455344c62
9 changed files with 403 additions and 67 deletions

View File

@ -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) {

View File

@ -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;
};

View File

@ -100,6 +100,11 @@ boxSearchCancel: IconButton {
duration: 150;
}
contactsMultiSelect: MultiSelect {
field: boxSearchField;
cancel: boxSearchCancel;
maxHeight: 104px;
}
contactsPhotoCheckbox: RoundImageCheckbox {
imageRadius: 21px;
imageSmallRadius: 18px;

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -67,6 +67,12 @@ RoundImageCheckbox {
checkIcon: icon;
}
MultiSelect {
field: InputField;
cancel: IconButton;
maxHeight: pixels;
}
widgetSlideDuration: 200;
discreteSliderHeight: 39px;

View File

@ -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',