Add media search to all info modes.

This commit is contained in:
John Preston 2017-11-03 16:03:00 +04:00
parent 86ad15612a
commit 09d1e3629a
20 changed files with 408 additions and 133 deletions

View File

@ -54,6 +54,54 @@ infoScroll: ScrollArea(defaultScrollArea) {
topsh: 0px;
}
infoMediaSearch: SearchFieldRow {
height: 44px;
padding: margins(8px, 6px, 8px, 6px);
field: contactsSearchField;
fieldIcon: icon {{
"box_search-flip_horizontal",
menuIconFg,
point(6px, 8px)
}};
fieldIconSkip: 36px;
fieldCancel: contactsSearchCancel;
fieldCancelSkip: 40px;
}
infoLayerMediaSearch: SearchFieldRow(infoMediaSearch) {
height: 46px;
fieldIcon: icon {{
"box_search-flip_horizontal",
menuIconFg,
point(9px, 9px)
}};
fieldIconSkip: 34px;
fieldCancel: CrossButton(contactsSearchCancel) {
width: 50px;
cross: CrossAnimation {
size: 38px;
skip: 12px;
stroke: 2px;
minScale: 0.3;
}
crossPosition: point(3px, 4px);
}
fieldCancelSkip: 46px;
}
infoTopBarSearchRow: SearchFieldRow(infoLayerMediaSearch) {
padding: margins(0px, 12px, 8px, 10px);
fieldCancel: CrossButton(contactsSearchCancel) {
width: 50px;
height: 52px;
cross: CrossAnimation {
size: 38px;
skip: 12px;
stroke: 2px;
minScale: 0.3;
}
crossPosition: point(3px, 8px);
}
}
infoTopBarBackIcon: icon {{ "info_back", boxTitleCloseFg }};
infoTopBarBackIconOver: icon {{ "info_back", boxTitleCloseFgOver }};
infoTopBarHeight: 54px;
@ -84,6 +132,11 @@ infoTopBarClose: IconButton(infoTopBarBack) {
icon: icon {{ "info_close", boxTitleCloseFg }};
iconOver: icon {{ "info_close", boxTitleCloseFgOver }};
}
infoTopBarSearch: IconButton(infoTopBarBack) {
width: 56px;
icon: icon {{ "top_bar_search", boxTitleCloseFg }};
iconOver: icon {{ "top_bar_search", boxTitleCloseFgOver }};
}
infoTopBarForward: IconButton(infoTopBarBack) {
width: 46px;
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
@ -105,6 +158,8 @@ infoTopBar: InfoTopBar {
mediaActionsSkip: 4px;
mediaForward: infoTopBarForward;
mediaDelete: infoTopBarDelete;
search: infoTopBarSearch;
searchRow: infoTopBarSearchRow;
}
infoLayerTopBarHeight: boxLayerTitleHeight;
@ -147,6 +202,8 @@ infoLayerTopBar: InfoTopBar {
mediaActionsSkip: 6px;
mediaForward: infoLayerTopBarForward;
mediaDelete: infoLayerTopBarDelete;
search: infoTopBarSearch;
searchRow: infoTopBarSearchRow;
}
infoMinimalWidth: 324px;
@ -404,13 +461,3 @@ infoCommonGroupsList: PeerList(infoMembersList) {
statusPosition: point(79px, 31px);
}
}
infoMediaSearch: SearchFieldRow {
height: 44px;
padding: margins(8px, 6px, 8px, 6px);
field: contactsSearchField;
fieldIcon: boxFieldSearchIcon;
fieldIconSkip: 36px;
fieldCancel: contactsSearchCancel;
fieldCancelSkip: 40px;
}

View File

@ -51,6 +51,10 @@ ContentWidget::ContentWidget(
: st::profileBg;
update();
}, lifetime());
_scrollTopSkip.changes()
| rpl::start_with_next([this] {
updateControlsGeometry();
}, lifetime());
}
void ContentWidget::resizeEvent(QResizeEvent *e) {
@ -60,7 +64,7 @@ void ContentWidget::resizeEvent(QResizeEvent *e) {
void ContentWidget::updateControlsGeometry() {
auto newScrollTop = _scroll->scrollTop() + _topDelta;
auto scrollGeometry = rect().marginsRemoved(
QMargins(0, _scrollTopSkip, 0, 0));
QMargins(0, _scrollTopSkip.current(), 0, 0));
if (_scroll->geometry() != scrollGeometry) {
_scroll->setGeometry(scrollGeometry);
_inner->resizeToWidth(_scroll->width());
@ -115,27 +119,33 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget(
inner->setVisibleTopBottom(top, bottom);
}, _inner->lifetime());
_scrollTopSkip = scrollTopSkip;
updateControlsGeometry();
setScrollTopSkip(scrollTopSkip);
return _inner;
}
void ContentWidget::setScrollTopSkip(int scrollTopSkip) {
_scrollTopSkip = scrollTopSkip;
}
rpl::producer<Section> ContentWidget::sectionRequest() const {
return rpl::never<Section>();
}
rpl::producer<int> ContentWidget::desiredHeightValue() const {
return _inner->desiredHeightValue()
| rpl::map([this](int value) {
return value + _scrollTopSkip;
});
using namespace rpl::mappers;
return rpl::combine(
_inner->desiredHeightValue(),
_scrollTopSkip.value())
| rpl::map($1 + $2);
}
rpl::producer<bool> ContentWidget::desiredShadowVisibility() const {
using namespace rpl::mappers;
return _scroll->scrollTopValue()
| rpl::map($1 > 0);
return rpl::combine(
_scroll->scrollTopValue(),
_scrollTopSkip.value())
| rpl::map(($1 > 0) || ($2 > 0));
}
bool ContentWidget::hasTopBarShadow() const {

View File

@ -91,9 +91,9 @@ protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void setScrollTopSkip(int scrollTopSkip);
int scrollTopSave() const;
void scrollTopRestore(int scrollTop);
void scrollTo(const Ui::ScrollToRequest &request);
private:
@ -105,7 +105,7 @@ private:
const not_null<Controller*> _controller;
style::color _bg;
int _scrollTopSkip = 0;
rpl::variable<int> _scrollTopSkip = -1;
object_ptr<Ui::ScrollArea> _scroll;
Ui::RpWidget *_inner = nullptr;

View File

@ -61,6 +61,10 @@ rpl::producer<Wrap> Controller::wrapValue() const {
return _widget->wrapValue();
}
bool Controller::hasStackHistory() const {
return _widget->hasStackHistory();
}
bool Controller::validateMementoPeer(
not_null<ContentMemento*> memento) const {
return memento->peerId() == peerId()

View File

@ -98,6 +98,7 @@ public:
Wrap wrap() const;
rpl::producer<Wrap> wrapValue() const;
void setSection(const Section &section);
bool hasStackHistory() const;
not_null<Window::Controller*> window() const {
return _window;

View File

@ -27,8 +27,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/storage_shared_media.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/search_field_controller.h"
namespace Info {
@ -60,15 +63,119 @@ void TopBar::enableBackButton(bool enable) {
updateControlsGeometry(width());
}
void TopBar::pushButton(object_ptr<Ui::RpWidget> button) {
auto weak = Ui::AttachParentChild(this, button);
void TopBar::createSearchView(
not_null<Ui::SearchFieldController*> controller) {
setSearchField(controller->createField(
this,
_st.searchRow.field));
}
void TopBar::pushButton(base::unique_qptr<Ui::RpWidget> button) {
auto weak = button.get();
_buttons.push_back(std::move(button));
weak->setParent(this);
weak->widthValue()
| rpl::start_with_next([this] {
this->updateControlsGeometry(this->width());
updateControlsGeometry(width());
}, lifetime());
}
void TopBar::setSearchField(
base::unique_qptr<Ui::InputField> field) {
if (auto value = field.release()) {
createSearchView(value);
} else {
_searchView = nullptr;
}
}
void TopBar::createSearchView(not_null<Ui::InputField*> field) {
_searchView = base::make_unique_q<Ui::FixedHeightWidget>(
this,
_st.searchRow.height);
auto wrap = _searchView.get();
field->setParent(wrap);
auto search = addButton(
base::make_unique_q<Ui::FadeWrapScaled<Ui::IconButton>>(
this,
object_ptr<Ui::IconButton>(this, _st.search)));
auto cancel = Ui::CreateChild<Ui::CrossButton>(
wrap,
_st.searchRow.fieldCancel);
auto toggleSearchMode = [=](bool enabled, anim::type animated) {
if (_title) {
_title->setVisible(!enabled);
}
field->setVisible(enabled);
cancel->toggleAnimated(enabled);
if (animated == anim::type::instant) {
cancel->finishAnimations();
}
search->toggle(!enabled, animated);
};
auto cancelSearch = [=] {
if (!field->getLastText().isEmpty()) {
field->setText(QString());
} else {
toggleSearchMode(false, anim::type::normal);
}
};
cancel->addClickHandler(cancelSearch);
field->connect(field, &Ui::InputField::cancelled, cancelSearch);
wrap->widthValue()
| rpl::start_with_next([=](int newWidth) {
auto availableWidth = newWidth
- _st.searchRow.fieldCancelSkip;
field->setGeometryToLeft(
_st.searchRow.padding.left(),
_st.searchRow.padding.top(),
availableWidth,
field->height());
cancel->moveToRight(0, 0);
}, wrap->lifetime());
widthValue()
| rpl::start_with_next([=](int newWidth) {
auto left = _back
? _st.back.width
: _st.titlePosition.x();
wrap->setGeometryToLeft(
left,
0,
newWidth - left,
wrap->height(),
newWidth);
}, wrap->lifetime());
search->entity()->addClickHandler([=] {
toggleSearchMode(true, anim::type::normal);
field->setFocus();
});
field->alive()
| rpl::start_with_done([=] {
field->setParent(nullptr);
removeButton(search);
setSearchField(nullptr);
}, _searchView->lifetime());
toggleSearchMode(
!field->getLastText().isEmpty(),
anim::type::instant);
}
void TopBar::removeButton(not_null<Ui::RpWidget*> button) {
_buttons.erase(
std::remove(_buttons.begin(), _buttons.end(), button),
_buttons.end());
}
int TopBar::resizeGetHeight(int newWidth) {
updateControlsGeometry(newWidth);
return _st.height;
@ -77,6 +184,7 @@ int TopBar::resizeGetHeight(int newWidth) {
void TopBar::updateControlsGeometry(int newWidth) {
auto right = 0;
for (auto &button : _buttons) {
if (!button) continue;
button->moveToRight(right, 0, newWidth);
right += button->width();
}

View File

@ -29,6 +29,8 @@ struct InfoTopBar;
namespace Ui {
class IconButton;
class FlatLabel;
class InputField;
class SearchFieldController;
} // namespace Ui
namespace Info {
@ -51,25 +53,34 @@ public:
void enableBackButton(bool enable);
template <typename ButtonWidget>
ButtonWidget *addButton(object_ptr<ButtonWidget> button) {
auto result = button.data();
ButtonWidget *addButton(base::unique_qptr<ButtonWidget> button) {
auto result = button.get();
pushButton(std::move(button));
return result;
}
void createSearchView(
not_null<Ui::SearchFieldController*> controller);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
void updateControlsGeometry(int newWidth);
void pushButton(object_ptr<Ui::RpWidget> button);
void pushButton(base::unique_qptr<Ui::RpWidget> button);
void removeButton(not_null<Ui::RpWidget*> button);
void setSearchField(base::unique_qptr<Ui::InputField> field);
void createSearchView(not_null<Ui::InputField*> field);
const style::InfoTopBar &_st;
object_ptr<Ui::IconButton> _back = { nullptr };
std::vector<object_ptr<Ui::RpWidget>> _buttons;
std::vector<base::unique_qptr<Ui::RpWidget>> _buttons;
object_ptr<Ui::FlatLabel> _title = { nullptr };
base::unique_qptr<Ui::RpWidget> _searchView;
rpl::event_stream<> _backClicks;
};

View File

@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/search_field_controller.h"
#include "window/window_controller.h"
#include "window/window_slide_animation.h"
#include "auth_session.h"
@ -221,12 +222,17 @@ void WrapWidget::createTopBar() {
}, _topBar->lifetime());
}
if (wrap() == Wrap::Layer) {
auto close = _topBar->addButton(object_ptr<Ui::IconButton>(
_topBar,
st::infoLayerTopBarClose));
auto close = _topBar->addButton(
base::make_unique_q<Ui::IconButton>(
_topBar,
st::infoLayerTopBarClose));
close->addClickHandler([this] {
_controller->window()->hideSpecialLayer();
});
} else if (requireTopBarSearch()) {
auto search = _controller->searchFieldController();
Assert(search != nullptr);
_topBar->createSearchView(search);
}
_topBar->move(0, 0);
@ -288,6 +294,19 @@ void WrapWidget::createTopBarOverride(SelectedItems &&items) {
_topBarOverride->show();
}
bool WrapWidget::requireTopBarSearch() const {
if (!_controller->searchFieldController()) {
return false;
} else if (_controller->wrap() == Wrap::Layer) {
return false;
} else if (hasStackHistory()) {
return true;
}
auto section = _controller->section();
return (section.type() != Section::Type::Media)
|| !Media::TypeToTabIndex(section.mediaType()).has_value();
}
void WrapWidget::showBackFromStack() {
auto params = Window::SectionShow(
Window::SectionShow::Way::Backward);
@ -359,7 +378,7 @@ std::unique_ptr<ContentMemento> WrapWidget::createTabMemento(
case Tab::Media: return std::make_unique<Media::Memento>(
_controller->peerId(),
_controller->migratedPeerId(),
Media::Memento::Type::Photo);
Media::Type::Photo);
}
Unexpected("Tab value in Info::WrapWidget::createInner()");
}
@ -373,6 +392,38 @@ object_ptr<ContentWidget> WrapWidget::createContent(
contentGeometry());
}
void WrapWidget::convertProfileFromStackToTab() {
if (_historyStack.empty()) {
return;
}
auto &entry = _historyStack[0];
if (entry.section->section().type() != Section::Type::Profile) {
return;
}
auto convertInsideStack = (_historyStack.size() > 1);
auto checkSection = convertInsideStack
? _historyStack[1].section->section()
: _controller->section();
auto &anotherMemento = convertInsideStack
? _historyStack[1].anotherTab
: _anotherTabMemento;
if (checkSection.type() != Section::Type::Media) {
return;
}
if (!Info::Media::TypeToTabIndex(checkSection.mediaType())) {
return;
}
anotherMemento = std::move(entry.section);
_historyStack.erase(_historyStack.begin());
}
void WrapWidget::setWrap(Wrap wrap) {
if (_wrap.current() != Wrap::Side && wrap == Wrap::Side) {
convertProfileFromStackToTab();
}
_wrap = wrap;
}
bool WrapWidget::hasTopBarShadow() const {
return _topShadow->toggled();
}

View File

@ -95,8 +95,9 @@ public:
rpl::producer<Wrap> wrapValue() const {
return _wrap.value();
}
void setWrap(Wrap wrap) {
_wrap = wrap;
void setWrap(Wrap wrap);
bool hasStackHistory() const {
return !_historyStack.empty();
}
not_null<Controller*> controller() {
@ -169,12 +170,14 @@ private:
std::unique_ptr<Controller> createController(
not_null<Window::Controller*> window,
not_null<ContentMemento*> memento);
void convertProfileFromStackToTab();
rpl::producer<SelectedItems> selectedListValue() const;
void refreshTopBarOverride();
void refreshTopBarOverride(SelectedItems &&items);
void createTopBarOverride(SelectedItems &&items);
void destroyTopBarOverride();
bool requireTopBarSearch() const;
rpl::variable<Wrap> _wrap;
std::unique_ptr<Controller> _controller;

View File

@ -36,29 +36,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Info {
namespace Media {
namespace {
using Type = InnerWidget::Type;
base::optional<int> TypeToTabIndex(Type type) {
switch (type) {
case Type::Photo: return 0;
case Type::Video: return 1;
case Type::File: return 2;
}
return base::none;
}
Type TabIndexToType(int index) {
switch (index) {
case 0: return Type::Photo;
case 1: return Type::Video;
case 2: return Type::File;
}
Unexpected("Index in Info::Media::TabIndexToType()");
}
} // namespace
InnerWidget::InnerWidget(
QWidget *parent,
@ -73,6 +50,7 @@ void InnerWidget::setupOtherTypes() {
_controller->wrapValue()
| rpl::start_with_next([this](Wrap value) {
if (value == Wrap::Side
&& !_controller->hasStackHistory()
&& TypeToTabIndex(type())) {
createOtherTypes();
} else {
@ -227,7 +205,7 @@ void InnerWidget::switchToTab(Memento &&memento) {
void InnerWidget::refreshSearchField() {
auto search = _controller->searchFieldController();
if (search && _otherTabs) {
_searchField = search->createView(
_searchField = search->createRowView(
this,
st::infoMediaSearch);
_searchField->resizeToWidth(width());

View File

@ -24,7 +24,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "base/unique_qptr.h"
#include "info/media/info_media_widget.h"
#include "info/media/info_media_list_widget.h"
#include "history/history_search_controller.h"
namespace Ui {
class SettingsSlider;
@ -43,7 +42,6 @@ class ListWidget;
class InnerWidget final : public Ui::RpWidget {
public:
using Type = Widget::Type;
InnerWidget(
QWidget *parent,
not_null<Controller*> controller);

View File

@ -49,7 +49,6 @@ using UniversalMsgId = int32;
class ListWidget : public Ui::RpWidget {
public:
using Type = Widget::Type;
ListWidget(
QWidget *parent,
not_null<Controller*> controller);
@ -88,7 +87,6 @@ protected:
void leaveEventHook(QEvent *e) override;
private:
using Type = Widget::Type;
enum class MouseAction {
None,
PrepareDrag,

View File

@ -23,10 +23,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/media/info_media_inner_widget.h"
#include "info/info_controller.h"
#include "ui/widgets/scroll_area.h"
#include "ui/search_field_controller.h"
#include "styles/style_info.h"
namespace Info {
namespace Media {
base::optional<int> TypeToTabIndex(Type type) {
switch (type) {
case Type::Photo: return 0;
case Type::Video: return 1;
case Type::File: return 2;
}
return base::none;
}
Type TabIndexToType(int index) {
switch (index) {
case 0: return Type::Photo;
case 1: return Type::Video;
case 2: return Type::File;
}
Unexpected("Index in Info::Media::TabIndexToType()");
}
Memento::Memento(not_null<Controller*> controller)
: Memento(
controller->peerId(),
@ -60,6 +80,10 @@ Widget::Widget(
| rpl::start_with_next([this](int skip) {
scrollTo({ skip, -1 });
}, _inner->lifetime());
controller->wrapValue()
| rpl::start_with_next([this] {
refreshSearchField();
}, lifetime());
}
rpl::producer<SelectedItems> Widget::selectedListValue() const {
@ -103,5 +127,25 @@ void Widget::restoreState(not_null<Memento*> memento) {
_inner->restoreState(memento);
}
void Widget::refreshSearchField() {
auto search = controller()->searchFieldController();
if (search && controller()->wrap() == Wrap::Layer) {
_searchField = search->createRowView(
this,
st::infoLayerMediaSearch);
auto field = _searchField.get();
widthValue()
| rpl::start_with_next([field](int newWidth) {
field->resizeToWidth(newWidth);
field->moveToLeft(0, 0);
}, field->lifetime());
field->show();
setScrollTopSkip(field->heightNoMargins() - st::lineWidth);
} else {
_searchField = nullptr;
setScrollTopSkip(0);
}
}
} // namespace Media
} // namespace Info

View File

@ -24,15 +24,22 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/info_content_widget.h"
#include "storage/storage_shared_media.h"
namespace Ui {
class SearchFieldController;
} // namespace Ui
namespace Info {
namespace Media {
using Type = Storage::SharedMediaType;
base::optional<int> TypeToTabIndex(Type type);
Type TabIndexToType(int index);
class InnerWidget;
class Memento final : public ContentMemento {
public:
using Type = Storage::SharedMediaType;
Memento(not_null<Controller*> controller);
Memento(PeerId peerId, PeerId migratedPeerId, Type type)
@ -87,8 +94,6 @@ private:
class Widget final : public ContentWidget {
public:
using Type = Memento::Type;
Widget(
QWidget *parent,
not_null<Controller*> controller);
@ -108,7 +113,10 @@ private:
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
void refreshSearchField();
InnerWidget *_inner = nullptr;
base::unique_qptr<Ui::RpWidget> _searchField = nullptr;
};

View File

@ -29,35 +29,39 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Ui {
base::unique_qptr<Ui::RpWidget> SearchFieldController::createView(
base::unique_qptr<Ui::RpWidget> SearchFieldController::createRowView(
QWidget *parent,
const style::SearchFieldRow &st) {
auto result = base::make_unique_q<Ui::FixedHeightWidget>(
parent,
st.height);
auto wrap = result.get();
auto field = createField(wrap, st.field).release();
field->show();
field->connect(field, &Ui::InputField::cancelled, [=] {
field->setText(QString());
});
auto cancel = CreateChild<Ui::CrossButton>(
result.get(),
wrap,
st.fieldCancel);
cancel->addClickHandler([=] { clearQuery(); });
auto field = CreateChild<Ui::InputField>(
result.get(),
st.field,
langFactory(lng_dlg_filter),
_query.current());
field->show();
field->connect(field, &Ui::InputField::changed, [=] {
setQueryFromField(field->getLastText());
});
field->connect(field, &Ui::InputField::cancelled, [=] {
clearQuery();
cancel->addClickHandler([=] {
field->setText(QString());
});
queryValue()
| rpl::map([](const QString &value) {
return !value.isEmpty();
})
| rpl::start_with_next([cancel](bool shown) {
cancel->toggleAnimated(shown);
}, cancel->lifetime());
cancel->finishAnimations();
auto shadow = CreateChild<Ui::PlainShadow>(result.get());
auto shadow = CreateChild<Ui::PlainShadow>(wrap);
shadow->show();
result->widthValue()
wrap->widthValue()
| rpl::start_with_next([=, &st](int newWidth) {
auto availableWidth = newWidth
- st.fieldIconSkip
@ -73,36 +77,36 @@ base::unique_qptr<Ui::RpWidget> SearchFieldController::createView(
st.height - st::lineWidth,
newWidth,
st::lineWidth);
}, result->lifetime());
result->paintRequest()
}, wrap->lifetime());
wrap->paintRequest()
| rpl::start_with_next([=, &st] {
Painter p(_view.wrap);
Painter p(wrap);
st.fieldIcon.paint(
p,
st.padding.left(),
st.padding.top(),
_view.wrap->width());
}, result->lifetime());
wrap->width());
}, wrap->lifetime());
_view.wrap.reset(result.get());
_view.cancel = cancel;
_view.field = field;
_view.release();
_view.reset(wrap);
return std::move(result);
}
void SearchFieldController::setQueryFromField(const QString &query) {
_query = query;
if (_view.cancel) {
_view.cancel->toggleAnimated(!query.isEmpty());
}
}
void SearchFieldController::clearQuery() {
if (_view.field) {
_view.field->setText(QString());
} else {
setQueryFromField(QString());
}
base::unique_qptr<Ui::InputField> SearchFieldController::createField(
QWidget *parent,
const style::InputField &st) {
auto result = base::make_unique_q<Ui::InputField>(
parent,
st,
langFactory(lng_dlg_filter),
_query.current());
auto field = result.get();
field->connect(field, &Ui::InputField::changed, [=] {
_query = field->getLastText();
});
_view.reset(field);
return result;
}
} // namespace Ui

View File

@ -26,6 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace style {
struct SearchFieldRow;
struct InputField;
} // namespace style
namespace Ui {
@ -35,7 +36,10 @@ class InputField;
class SearchFieldController {
public:
base::unique_qptr<Ui::RpWidget> createView(
base::unique_qptr<Ui::InputField> createField(
QWidget *parent,
const style::InputField &st);
base::unique_qptr<Ui::RpWidget> createRowView(
QWidget *parent,
const style::SearchFieldRow &st);
@ -48,15 +52,7 @@ public:
}
private:
void setQueryFromField(const QString &query);
void clearQuery();
struct View {
base::unique_qptr<Ui::RpWidget> wrap;
Ui::InputField *field = nullptr;
Ui::CrossButton *cancel = nullptr;
};
View _view;
base::unique_qptr<QWidget> _view;
rpl::variable<QString> _query;
rpl::lifetime _lifetime;

View File

@ -227,6 +227,9 @@ public:
}
void toggleFast(bool visible) {
toggleAnimated(visible);
finishAnimations();
}
void finishAnimations() {
_a_show.finish();
}

View File

@ -2499,9 +2499,14 @@ void InputArea::setErrorShown(bool error) {
}
}
InputField::InputField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val) : TWidget(parent)
InputField::InputField(
QWidget *parent,
const style::InputField &st,
base::lambda<QString()> placeholderFactory,
const QString &val)
: RpWidget(parent)
, _st(st)
, _inner(this)
, _inner(std::make_unique<Inner>(this))
, _oldtext(val)
, _placeholderFactory(std::move(placeholderFactory)) {
_inner->setAcceptRichText(false);
@ -2542,9 +2547,9 @@ InputField::InputField(QWidget *parent, const style::InputField &st, base::lambd
connect(_inner->document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onDocumentContentsChange(int,int,int)));
connect(_inner->document(), SIGNAL(contentsChanged()), this, SLOT(onDocumentContentsChanged()));
connect(_inner, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool)));
connect(_inner, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
if (App::wnd()) connect(_inner, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
connect(_inner.get(), SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool)));
connect(_inner.get(), SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
if (App::wnd()) connect(_inner.get(), SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
setCursor(style::cur_text);
if (!val.isEmpty()) {
@ -2744,7 +2749,9 @@ void InputField::setFocused(bool focused) {
}
void InputField::startPlaceholderAnimation() {
auto placeholderShifted = _forcePlaceholderHidden || (_focused && _st.placeholderScale > 0.) || !getLastText().isEmpty();
auto placeholderShifted = _forcePlaceholderHidden
|| (_focused && _st.placeholderScale > 0.)
|| !getLastText().isEmpty();
if (_placeholderShifted != placeholderShifted) {
_placeholderShifted = placeholderShifted;
_a_placeholderShifted.start([this] { update(); }, _placeholderShifted ? 0. : 1., _placeholderShifted ? 1. : 0., _st.duration);

View File

@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/rp_widget.h"
#include "styles/style_widgets.h"
class UserData;
@ -499,7 +500,7 @@ private:
};
class InputField : public TWidget, private base::Subscriber {
class InputField : public RpWidget, private base::Subscriber {
Q_OBJECT
public:
@ -645,11 +646,11 @@ private:
const style::InputField &_st;
std::unique_ptr<Inner> _inner;
int _maxLength = -1;
bool _forcePlaceholderHidden = false;
object_ptr<Inner> _inner;
QString _oldtext;
bool _undoAvailable = false;

View File

@ -1111,6 +1111,16 @@ defaultPeerList: PeerList {
item: defaultPeerListItem;
}
SearchFieldRow {
height: pixels;
padding: margins;
field: InputField;
fieldIcon: icon;
fieldIconSkip: pixels;
fieldCancel: CrossButton;
fieldCancelSkip: pixels;
}
InfoTopBar {
height: pixels;
back: IconButton;
@ -1121,13 +1131,6 @@ InfoTopBar {
mediaActionsSkip: pixels;
mediaForward: IconButton;
mediaDelete: IconButton;
}
SearchFieldRow {
height: pixels;
padding: margins;
field: InputField;
fieldIcon: icon;
fieldIconSkip: pixels;
fieldCancel: CrossButton;
fieldCancelSkip: pixels;
search: IconButton;
searchRow: SearchFieldRow;
}