Search and save state in common groups.

This commit is contained in:
John Preston 2017-11-03 22:26:14 +04:00
parent a6361d6221
commit 628c8e10f7
22 changed files with 290 additions and 85 deletions

View File

@ -28,13 +28,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mtproto/sender.h" #include "mtproto/sender.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/search_field_controller.h"
#include "apiwrap.h" #include "apiwrap.h"
namespace Info { namespace Info {
namespace CommonGroups { namespace CommonGroups {
namespace { namespace {
constexpr int kCommonGroupsPerPage = 40; constexpr auto kCommonGroupsPerPage = 40;
constexpr auto kCommonGroupsSearchAfter = 20;
class ListController class ListController
: public PeerListController : public PeerListController
@ -49,7 +51,22 @@ public:
void rowClicked(not_null<PeerListRow*> row) override; void rowClicked(not_null<PeerListRow*> row) override;
void loadMoreRows() override; void loadMoreRows() override;
std::unique_ptr<PeerListRow> createRestoredRow(
not_null<PeerData*> peer) override {
return createRow(peer);
}
std::unique_ptr<PeerListState> saveState() override;
void restoreState(std::unique_ptr<PeerListState> state) override;
private: private:
std::unique_ptr<PeerListRow> createRow(not_null<PeerData*> peer);
struct SavedState : SavedStateBase {
int32 preloadGroupId = 0;
bool allLoaded = false;
bool wasLoading = false;
};
const not_null<Controller*> _controller; const not_null<Controller*> _controller;
not_null<UserData*> _user; not_null<UserData*> _user;
mtpRequestId _preloadRequestId = 0; mtpRequestId _preloadRequestId = 0;
@ -64,6 +81,14 @@ ListController::ListController(
: PeerListController() : PeerListController()
, _controller(controller) , _controller(controller)
, _user(user) { , _user(user) {
_controller->setSearchEnabledByContent(false);
}
std::unique_ptr<PeerListRow> ListController::createRow(
not_null<PeerData*> peer) {
auto result = std::make_unique<PeerListRow>(peer);
result->setCustomStatus(QString());
return result;
} }
void ListController::prepare() { void ListController::prepare() {
@ -90,9 +115,8 @@ void ListController::loadMoreRows() {
for_const (auto &chatData, list) { for_const (auto &chatData, list) {
if (auto chat = App::feedChat(chatData)) { if (auto chat = App::feedChat(chatData)) {
if (!chat->migrateTo()) { if (!chat->migrateTo()) {
auto row = std::make_unique<PeerListRow>(chat); delegate()->peerListAppendRow(
row->setCustomStatus(QString()); createRow(chat));
delegate()->peerListAppendRow(std::move(row));
} }
_preloadGroupId = chat->bareId(); _preloadGroupId = chat->bareId();
_allLoaded = false; _allLoaded = false;
@ -101,9 +125,48 @@ void ListController::loadMoreRows() {
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
} }
} }
auto fullCount = delegate()->peerListFullRowsCount();
if (fullCount > kCommonGroupsSearchAfter) {
_controller->setSearchEnabledByContent(true);
}
}).send(); }).send();
} }
std::unique_ptr<PeerListState> ListController::saveState() {
auto result = PeerListController::saveState();
auto my = std::make_unique<SavedState>();
my->preloadGroupId = _preloadGroupId;
my->allLoaded = _allLoaded;
if (auto requestId = base::take(_preloadRequestId)) {
request(requestId).cancel();
my->wasLoading = true;
}
result->controllerState = std::move(my);
return result;
}
void ListController::restoreState(
std::unique_ptr<PeerListState> state) {
auto typeErasedState = state
? state->controllerState.get()
: nullptr;
if (auto my = dynamic_cast<SavedState*>(typeErasedState)) {
if (auto requestId = base::take(_preloadRequestId)) {
request(requestId).cancel();
}
_allLoaded = my->allLoaded;
_preloadGroupId = my->preloadGroupId;
if (my->wasLoading) {
loadMoreRows();
}
PeerListController::restoreState(std::move(state));
auto fullCount = delegate()->peerListFullRowsCount();
if (fullCount > kCommonGroupsSearchAfter) {
_controller->setSearchEnabledByContent(true);
}
}
}
void ListController::rowClicked(not_null<PeerListRow*> row) { void ListController::rowClicked(not_null<PeerListRow*> row) {
_controller->window()->showPeerHistory( _controller->window()->showPeerHistory(
row->peer(), row->peer(),
@ -117,11 +180,17 @@ InnerWidget::InnerWidget(
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<UserData*> user) not_null<UserData*> user)
: RpWidget(parent) : RpWidget(parent)
, _controller(controller)
, _user(user) , _user(user)
, _listController(std::make_unique<ListController>(controller, _user)) , _listController(std::make_unique<ListController>(controller, _user))
, _list(setupList(this, _listController.get())) { , _list(setupList(this, _listController.get())) {
setContent(_list.data()); setContent(_list.data());
_listController->setDelegate(static_cast<PeerListDelegate*>(this)); _listController->setDelegate(static_cast<PeerListDelegate*>(this));
_controller->searchFieldController()->queryValue()
| rpl::start_with_next([this](QString &&query) {
peerListScrollToTop();
content()->searchQueryChanged(std::move(query));
}, lifetime());
} }
void InnerWidget::visibleTopBottomUpdated( void InnerWidget::visibleTopBottomUpdated(
@ -131,9 +200,11 @@ void InnerWidget::visibleTopBottomUpdated(
} }
void InnerWidget::saveState(not_null<Memento*> memento) { void InnerWidget::saveState(not_null<Memento*> memento) {
memento->setListState(_listController->saveState());
} }
void InnerWidget::restoreState(not_null<Memento*> memento) { void InnerWidget::restoreState(not_null<Memento*> memento) {
_listController->restoreState(std::move(memento->listState()));
} }
int InnerWidget::desiredHeight() const { int InnerWidget::desiredHeight() const {

View File

@ -80,6 +80,7 @@ private:
RpWidget *parent, RpWidget *parent,
not_null<PeerListController*> controller) const; not_null<PeerListController*> controller) const;
not_null<Controller*> _controller;
not_null<UserData*> _user; not_null<UserData*> _user;
std::unique_ptr<PeerListController> _listController; std::unique_ptr<PeerListController> _listController;
object_ptr<ListWidget> _list; object_ptr<ListWidget> _list;

View File

@ -22,7 +22,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/common_groups/info_common_groups_inner_widget.h" #include "info/common_groups/info_common_groups_inner_widget.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "ui/search_field_controller.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "styles/style_info.h"
namespace Info { namespace Info {
namespace CommonGroups { namespace CommonGroups {
@ -43,6 +45,16 @@ object_ptr<ContentWidget> Memento::createWidget(
return std::move(result); return std::move(result);
} }
void Memento::setListState(std::unique_ptr<PeerListState> state) {
_listState = std::move(state);
}
std::unique_ptr<PeerListState> Memento::listState() {
return std::move(_listState);
}
Memento::~Memento() = default;
Widget::Widget( Widget::Widget(
QWidget *parent, QWidget *parent,
not_null<Controller*> controller, not_null<Controller*> controller,
@ -77,7 +89,7 @@ void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento)
restoreState(memento); restoreState(memento);
} }
std::unique_ptr<ContentMemento> Widget::createMemento() { std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
auto result = std::make_unique<Memento>(user()->bareId()); auto result = std::make_unique<Memento>(user()->bareId());
saveState(result.get()); saveState(result.get());
return std::move(result); return std::move(result);
@ -92,7 +104,6 @@ void Widget::restoreState(not_null<Memento*> memento) {
_inner->restoreState(memento); _inner->restoreState(memento);
auto scrollTop = memento->scrollTop(); auto scrollTop = memento->scrollTop();
scrollTopRestore(memento->scrollTop()); scrollTopRestore(memento->scrollTop());
// TODO is setVisibleTopBottom called?
} }
} // namespace CommonGroups } // namespace CommonGroups

View File

@ -23,6 +23,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/producer.h> #include <rpl/producer.h>
#include "info/info_content_widget.h" #include "info/info_content_widget.h"
struct PeerListState;
namespace Ui {
class SearchFieldController;
} // namespace Ui
namespace Info { namespace Info {
namespace CommonGroups { namespace CommonGroups {
@ -45,7 +51,13 @@ public:
return peerToUser(peerId()); return peerToUser(peerId());
} }
void setListState(std::unique_ptr<PeerListState> state);
std::unique_ptr<PeerListState> listState();
~Memento();
private: private:
std::unique_ptr<PeerListState> _listState;
}; };
@ -60,7 +72,6 @@ public:
bool showInternal( bool showInternal(
not_null<ContentMemento*> memento) override; not_null<ContentMemento*> memento) override;
std::unique_ptr<ContentMemento> createMemento() override;
void setInternalState( void setInternalState(
const QRect &geometry, const QRect &geometry,
@ -70,6 +81,8 @@ private:
void saveState(not_null<Memento*> memento); void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento); void restoreState(not_null<Memento*> memento);
std::unique_ptr<ContentMemento> doCreateMemento() override;
InnerWidget *_inner = nullptr; InnerWidget *_inner = nullptr;
}; };

View File

@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/range.h> #include <rpl/range.h>
#include "window/window_controller.h" #include "window/window_controller.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/search_field_controller.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "info/profile/info_profile_widget.h" #include "info/profile/info_profile_widget.h"
#include "info/media/info_media_widget.h" #include "info/media/info_media_widget.h"
@ -43,6 +44,8 @@ ContentWidget::ContentWidget(
: RpWidget(parent) : RpWidget(parent)
, _controller(controller) , _controller(controller)
, _scroll(this, st::infoScroll) { , _scroll(this, st::infoScroll) {
using namespace rpl::mappers;
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
_controller->wrapValue() _controller->wrapValue()
| rpl::start_with_next([this](Wrap value) { | rpl::start_with_next([this](Wrap value) {
@ -51,6 +54,13 @@ ContentWidget::ContentWidget(
: st::profileBg; : st::profileBg;
update(); update();
}, lifetime()); }, lifetime());
rpl::combine(
_controller->wrapValue(),
_controller->searchEnabledByContent(),
($1 == Wrap::Layer) && $2)
| rpl::start_with_next([this](bool shown) {
refreshSearchField(shown);
}, lifetime());
_scrollTopSkip.changes() _scrollTopSkip.changes()
| rpl::start_with_next([this] { | rpl::start_with_next([this] {
updateControlsGeometry(); updateControlsGeometry();
@ -62,6 +72,9 @@ void ContentWidget::resizeEvent(QResizeEvent *e) {
} }
void ContentWidget::updateControlsGeometry() { void ContentWidget::updateControlsGeometry() {
if (!_inner) {
return;
}
auto newScrollTop = _scroll->scrollTop() + _topDelta; auto newScrollTop = _scroll->scrollTop() + _topDelta;
auto scrollGeometry = rect().marginsRemoved( auto scrollGeometry = rect().marginsRemoved(
QMargins(0, _scrollTopSkip.current(), 0, 0)); QMargins(0, _scrollTopSkip.current(), 0, 0));
@ -75,10 +88,18 @@ void ContentWidget::updateControlsGeometry() {
_scroll->scrollToY(newScrollTop); _scroll->scrollToY(newScrollTop);
} }
auto scrollTop = _scroll->scrollTop(); auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); _inner->setVisibleTopBottom(
scrollTop,
scrollTop + _scroll->height());
} }
} }
std::unique_ptr<ContentMemento> ContentWidget::createMemento() {
auto result = doCreateMemento();
_controller->saveSearchState(result.get());
return result;
}
void ContentWidget::paintEvent(QPaintEvent *e) { void ContentWidget::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
p.fillRect(e->rect(), _bg); p.fillRect(e->rect(), _bg);
@ -100,8 +121,7 @@ void ContentWidget::setGeometryWithTopMoved(
} }
Ui::RpWidget *ContentWidget::doSetInnerWidget( Ui::RpWidget *ContentWidget::doSetInnerWidget(
object_ptr<RpWidget> inner, object_ptr<RpWidget> inner) {
int scrollTopSkip) {
using namespace rpl::mappers; using namespace rpl::mappers;
_inner = _scroll->setOwnedWidget(std::move(inner)); _inner = _scroll->setOwnedWidget(std::move(inner));
@ -119,8 +139,6 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget(
inner->setVisibleTopBottom(top, bottom); inner->setVisibleTopBottom(top, bottom);
}, _inner->lifetime()); }, _inner->lifetime());
setScrollTopSkip(scrollTopSkip);
return _inner; return _inner;
} }
@ -176,4 +194,24 @@ rpl::producer<SelectedItems> ContentWidget::selectedListValue() const {
return rpl::single(SelectedItems(Storage::SharedMediaType::Photo)); return rpl::single(SelectedItems(Storage::SharedMediaType::Photo));
} }
void ContentWidget::refreshSearchField(bool shown) {
auto search = _controller->searchFieldController();
if (search && shown) {
_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 Info } // namespace Info

View File

@ -46,7 +46,7 @@ public:
virtual bool showInternal( virtual bool showInternal(
not_null<ContentMemento*> memento) = 0; not_null<ContentMemento*> memento) = 0;
virtual std::unique_ptr<ContentMemento> createMemento() = 0; std::unique_ptr<ContentMemento> createMemento();
virtual rpl::producer<Section> sectionRequest() const; virtual rpl::producer<Section> sectionRequest() const;
virtual void setIsStackBottom(bool isStackBottom) { virtual void setIsStackBottom(bool isStackBottom) {
@ -77,11 +77,9 @@ public:
protected: protected:
template <typename Widget> template <typename Widget>
Widget *setInnerWidget( Widget *setInnerWidget(object_ptr<Widget> inner) {
object_ptr<Widget> inner,
int scrollTopSkip = 0) {
return static_cast<Widget*>( return static_cast<Widget*>(
doSetInnerWidget(std::move(inner), scrollTopSkip)); doSetInnerWidget(std::move(inner)));
} }
not_null<Controller*> controller() const { not_null<Controller*> controller() const {
@ -97,10 +95,11 @@ protected:
void scrollTo(const Ui::ScrollToRequest &request); void scrollTo(const Ui::ScrollToRequest &request);
private: private:
RpWidget *doSetInnerWidget( RpWidget *doSetInnerWidget(object_ptr<RpWidget> inner);
object_ptr<RpWidget> inner,
int scrollTopSkip);
void updateControlsGeometry(); void updateControlsGeometry();
void refreshSearchField(bool shown);
virtual std::unique_ptr<ContentMemento> doCreateMemento() = 0;
const not_null<Controller*> _controller; const not_null<Controller*> _controller;
@ -108,6 +107,7 @@ private:
rpl::variable<int> _scrollTopSkip = -1; rpl::variable<int> _scrollTopSkip = -1;
object_ptr<Ui::ScrollArea> _scroll; object_ptr<Ui::ScrollArea> _scroll;
Ui::RpWidget *_inner = nullptr; Ui::RpWidget *_inner = nullptr;
base::unique_qptr<Ui::RpWidget> _searchField = nullptr;
// Saving here topDelta in setGeometryWithTopMoved() to get it passed to resizeEvent(). // Saving here topDelta in setGeometryWithTopMoved() to get it passed to resizeEvent().
int _topDelta = 0; int _topDelta = 0;
@ -149,12 +149,19 @@ public:
QString searchFieldQuery() const { QString searchFieldQuery() const {
return _searchFieldQuery; return _searchFieldQuery;
} }
void setSearchEnabledByContent(bool enabled) {
_searchEnabledByContent = enabled;
}
bool searchEnabledByContent() const {
return _searchEnabledByContent;
}
private: private:
const PeerId _peerId = 0; const PeerId _peerId = 0;
const PeerId _migratedPeerId = 0; const PeerId _migratedPeerId = 0;
int _scrollTop = 0; int _scrollTop = 0;
QString _searchFieldQuery; QString _searchFieldQuery;
bool _searchEnabledByContent = false;
}; };

View File

@ -85,8 +85,8 @@ void Controller::updateSearchControllers(
: Section::MediaType::kCount; : Section::MediaType::kCount;
auto hasMediaSearch = isMedia auto hasMediaSearch = isMedia
&& SharedMediaAllowSearch(mediaType); && SharedMediaAllowSearch(mediaType);
// auto hasCommonGroupsSearch auto hasCommonGroupsSearch
// = (_section.type() == Section::Type::CommonGroups); = (_section.type() == Section::Type::CommonGroups);
auto searchQuery = memento->searchFieldQuery(); auto searchQuery = memento->searchFieldQuery();
if (isMedia) { if (isMedia) {
_searchController _searchController
@ -98,15 +98,18 @@ void Controller::updateSearchControllers(
} else { } else {
_searchController = nullptr; _searchController = nullptr;
} }
if (hasMediaSearch) { if (hasMediaSearch || hasCommonGroupsSearch) {
_searchFieldController _searchFieldController
= std::make_unique<Ui::SearchFieldController>( = std::make_unique<Ui::SearchFieldController>(
searchQuery); searchQuery);
_searchFieldController->queryValue() if (_searchController) {
| rpl::start_with_next([=](QString &&query) { _searchFieldController->queryValue()
_searchController->setQuery( | rpl::start_with_next([=](QString &&query) {
produceSearchQuery(std::move(query))); _searchController->setQuery(
}, _searchFieldController->lifetime()); produceSearchQuery(std::move(query)));
}, _searchFieldController->lifetime());
}
_seachEnabledByContent = memento->searchEnabledByContent();
} else { } else {
_searchFieldController = nullptr; _searchFieldController = nullptr;
} }
@ -116,6 +119,8 @@ void Controller::saveSearchState(not_null<ContentMemento*> memento) {
if (_searchFieldController) { if (_searchFieldController) {
memento->setSearchFieldQuery( memento->setSearchFieldQuery(
_searchFieldController->query()); _searchFieldController->query());
memento->setSearchEnabledByContent(
_seachEnabledByContent.current());
} }
if (_searchController) { if (_searchController) {
auto mediaMemento = dynamic_cast<Media::Memento*>( auto mediaMemento = dynamic_cast<Media::Memento*>(

View File

@ -106,6 +106,12 @@ public:
Ui::SearchFieldController *searchFieldController() const { Ui::SearchFieldController *searchFieldController() const {
return _searchFieldController.get(); return _searchFieldController.get();
} }
void setSearchEnabledByContent(bool enabled) {
_seachEnabledByContent = enabled;
}
rpl::producer<bool> searchEnabledByContent() const {
return _seachEnabledByContent.value();
}
rpl::producer<SparseIdsMergedSlice> mediaSource( rpl::producer<SparseIdsMergedSlice> mediaSource(
SparseIdsMergedSlice::UniversalMsgId aroundId, SparseIdsMergedSlice::UniversalMsgId aroundId,
int limitBefore, int limitBefore,
@ -137,6 +143,7 @@ private:
std::unique_ptr<Ui::SearchFieldController> _searchFieldController; std::unique_ptr<Ui::SearchFieldController> _searchFieldController;
std::unique_ptr<Api::DelayedSearchController> _searchController; std::unique_ptr<Api::DelayedSearchController> _searchController;
rpl::variable<bool> _seachEnabledByContent = false;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;

View File

@ -34,11 +34,19 @@ Memento::Memento(PeerId peerId)
} }
Memento::Memento(PeerId peerId, Section section) Memento::Memento(PeerId peerId, Section section)
: Memento(Default(peerId, section)) { : Memento(DefaultStack(peerId, section)) {
} }
Memento::Memento(std::unique_ptr<ContentMemento> content) Memento::Memento(std::vector<std::unique_ptr<ContentMemento>> stack)
: _content(std::move(content)) { : _stack(std::move(stack)) {
}
std::vector<std::unique_ptr<ContentMemento>> Memento::DefaultStack(
PeerId peerId,
Section section) {
auto result = std::vector<std::unique_ptr<ContentMemento>>();
result.push_back(Default(peerId, section));
return result;
} }
std::unique_ptr<ContentMemento> Memento::Default( std::unique_ptr<ContentMemento> Memento::Default(
@ -98,8 +106,8 @@ object_ptr<Window::LayerWidget> Memento::createLayer(
return nullptr; return nullptr;
} }
void Memento::setInner(std::unique_ptr<ContentMemento> content) { std::vector<std::unique_ptr<ContentMemento>> Memento::takeStack() {
_content = std::move(content); return std::move(_stack);
} }
Memento::~Memento() = default; Memento::~Memento() = default;

View File

@ -42,7 +42,7 @@ class Memento final : public Window::SectionMemento {
public: public:
Memento(PeerId peerId); Memento(PeerId peerId);
Memento(PeerId peerId, Section section); Memento(PeerId peerId, Section section);
Memento(std::unique_ptr<ContentMemento> content); Memento(std::vector<std::unique_ptr<ContentMemento>> stack);
object_ptr<Window::SectionWidget> createWidget( object_ptr<Window::SectionWidget> createWidget(
QWidget *parent, QWidget *parent,
@ -54,19 +54,27 @@ public:
not_null<Window::Controller*> controller, not_null<Window::Controller*> controller,
const QRect &geometry) override; const QRect &geometry) override;
void setInner(std::unique_ptr<ContentMemento> content); int stackSize() const {
return int(_stack.size());
}
std::vector<std::unique_ptr<ContentMemento>> takeStack();
not_null<ContentMemento*> content() { not_null<ContentMemento*> content() {
return _content.get(); Expects(!_stack.empty());
return _stack.back().get();
} }
~Memento(); ~Memento();
private: private:
static std::vector<std::unique_ptr<ContentMemento>> DefaultStack(
PeerId peerId,
Section section);
static std::unique_ptr<ContentMemento> Default( static std::unique_ptr<ContentMemento> Default(
PeerId peerId, PeerId peerId,
Section section); Section section);
std::unique_ptr<ContentMemento> _content; std::vector<std::unique_ptr<ContentMemento>> _stack;
}; };

View File

@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/ */
#include "info/info_top_bar.h" #include "info/info_top_bar.h"
#include <rpl/never.h>
#include "styles/style_info.h" #include "styles/style_info.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "info/info_wrap_widget.h" #include "info/info_wrap_widget.h"
@ -64,10 +65,11 @@ void TopBar::enableBackButton(bool enable) {
} }
void TopBar::createSearchView( void TopBar::createSearchView(
not_null<Ui::SearchFieldController*> controller) { not_null<Ui::SearchFieldController*> controller,
setSearchField(controller->createField( rpl::producer<bool> &&shown) {
this, setSearchField(
_st.searchRow.field)); controller->createField(this, _st.searchRow.field),
std::move(shown));
} }
void TopBar::pushButton(base::unique_qptr<Ui::RpWidget> button) { void TopBar::pushButton(base::unique_qptr<Ui::RpWidget> button) {
@ -81,15 +83,18 @@ void TopBar::pushButton(base::unique_qptr<Ui::RpWidget> button) {
} }
void TopBar::setSearchField( void TopBar::setSearchField(
base::unique_qptr<Ui::InputField> field) { base::unique_qptr<Ui::InputField> field,
rpl::producer<bool> &&shown) {
if (auto value = field.release()) { if (auto value = field.release()) {
createSearchView(value); createSearchView(value, std::move(shown));
} else { } else {
_searchView = nullptr; _searchView = nullptr;
} }
} }
void TopBar::createSearchView(not_null<Ui::InputField*> field) { void TopBar::createSearchView(
not_null<Ui::InputField*> field,
rpl::producer<bool> &&shown) {
_searchView = base::make_unique_q<Ui::FixedHeightWidget>( _searchView = base::make_unique_q<Ui::FixedHeightWidget>(
this, this,
_st.searchRow.height); _st.searchRow.height);
@ -162,12 +167,22 @@ void TopBar::createSearchView(not_null<Ui::InputField*> field) {
| rpl::start_with_done([=] { | rpl::start_with_done([=] {
field->setParent(nullptr); field->setParent(nullptr);
removeButton(search); removeButton(search);
setSearchField(nullptr); setSearchField(nullptr, rpl::never<bool>());
}, _searchView->lifetime()); }, _searchView->lifetime());
toggleSearchMode( toggleSearchMode(
!field->getLastText().isEmpty(), !field->getLastText().isEmpty(),
anim::type::instant); anim::type::instant);
std::move(shown)
| rpl::start_with_next([=](bool visible) {
if (!field->getLastText().isEmpty()) {
return;
}
toggleSearchMode(false, anim::type::instant);
wrap->setVisible(visible);
search->toggle(visible, anim::type::instant);
}, wrap->lifetime());
} }
void TopBar::removeButton(not_null<Ui::RpWidget*> button) { void TopBar::removeButton(not_null<Ui::RpWidget*> button) {

View File

@ -60,7 +60,8 @@ public:
} }
void createSearchView( void createSearchView(
not_null<Ui::SearchFieldController*> controller); not_null<Ui::SearchFieldController*> controller,
rpl::producer<bool> &&shown);
protected: protected:
int resizeGetHeight(int newWidth) override; int resizeGetHeight(int newWidth) override;
@ -71,8 +72,12 @@ private:
void pushButton(base::unique_qptr<Ui::RpWidget> button); void pushButton(base::unique_qptr<Ui::RpWidget> button);
void removeButton(not_null<Ui::RpWidget*> button); void removeButton(not_null<Ui::RpWidget*> button);
void setSearchField(base::unique_qptr<Ui::InputField> field); void setSearchField(
void createSearchView(not_null<Ui::InputField*> field); base::unique_qptr<Ui::InputField> field,
rpl::producer<bool> &&shown);
void createSearchView(
not_null<Ui::InputField*> field,
rpl::producer<bool> &&shown);
const style::InfoTopBar &_st; const style::InfoTopBar &_st;
object_ptr<Ui::IconButton> _back = { nullptr }; object_ptr<Ui::IconButton> _back = { nullptr };

View File

@ -78,7 +78,24 @@ WrapWidget::WrapWidget(
refreshTopBarOverride(std::move(items)); refreshTopBarOverride(std::move(items));
}); });
}, lifetime()); }, lifetime());
showNewContent(memento->content()); restoreHistoryStack(memento->takeStack());
}
void WrapWidget::restoreHistoryStack(
std::vector<std::unique_ptr<ContentMemento>> stack) {
Expects(!stack.empty());
Expects(_historyStack.empty());
auto content = std::move(stack.back());
stack.pop_back();
if (!stack.empty()) {
_historyStack.reserve(stack.size());
for (auto &stackItem : stack) {
auto item = StackItem();
item.section = std::move(stackItem);
_historyStack.push_back(std::move(item));
}
}
showNewContent(content.get());
} }
std::unique_ptr<Controller> WrapWidget::createController( std::unique_ptr<Controller> WrapWidget::createController(
@ -232,7 +249,9 @@ void WrapWidget::createTopBar() {
} else if (requireTopBarSearch()) { } else if (requireTopBarSearch()) {
auto search = _controller->searchFieldController(); auto search = _controller->searchFieldController();
Assert(search != nullptr); Assert(search != nullptr);
_topBar->createSearchView(search); _topBar->createSearchView(
search,
_controller->searchEnabledByContent());
} }
_topBar->move(0, 0); _topBar->move(0, 0);
@ -469,6 +488,9 @@ bool WrapWidget::showInternal(
not_null<Window::SectionMemento*> memento, not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) { const Window::SectionShow &params) {
if (auto infoMemento = dynamic_cast<Memento*>(memento.get())) { if (auto infoMemento = dynamic_cast<Memento*>(memento.get())) {
if (infoMemento->stackSize() > 1) {
return false;
}
auto content = infoMemento->content(); auto content = infoMemento->content();
if (_controller->validateMementoPeer(content)) { if (_controller->validateMementoPeer(content)) {
if (_content->showInternal(content)) { if (_content->showInternal(content)) {
@ -484,7 +506,13 @@ bool WrapWidget::showInternal(
} }
std::unique_ptr<Window::SectionMemento> WrapWidget::createMemento() { std::unique_ptr<Window::SectionMemento> WrapWidget::createMemento() {
return std::make_unique<Memento>(_content->createMemento()); auto stack = std::vector<std::unique_ptr<ContentMemento>>();
stack.reserve(_historyStack.size() + 1);
for (auto &stackItem : _historyStack) {
stack.push_back(std::move(stackItem.section));
}
stack.push_back(_content->createMemento());
return std::make_unique<Memento>(std::move(stack));
} }
rpl::producer<int> WrapWidget::desiredHeightValue() const { rpl::producer<int> WrapWidget::desiredHeightValue() const {

View File

@ -142,6 +142,8 @@ private:
}; };
struct StackItem; struct StackItem;
void restoreHistoryStack(
std::vector<std::unique_ptr<ContentMemento>> stack);
void showBackFromStack(); void showBackFromStack();
void showNewContent(not_null<ContentMemento*> memento); void showNewContent(not_null<ContentMemento*> memento);
void showNewContent( void showNewContent(

View File

@ -58,6 +58,12 @@ void InnerWidget::setupOtherTypes() {
_otherTypes.destroy(); _otherTypes.destroy();
refreshHeight(); refreshHeight();
} }
}, lifetime());
rpl::combine(
_controller->wrapValue(),
_controller->searchEnabledByContent())
| rpl::start_with_next([this](Wrap wrap, bool enabled) {
_searchEnabled = enabled;
refreshSearchField(); refreshSearchField();
}, lifetime()); }, lifetime());
} }
@ -204,7 +210,7 @@ void InnerWidget::switchToTab(Memento &&memento) {
void InnerWidget::refreshSearchField() { void InnerWidget::refreshSearchField() {
auto search = _controller->searchFieldController(); auto search = _controller->searchFieldController();
if (search && _otherTabs) { if (search && _otherTabs && _searchEnabled) {
_searchField = search->createRowView( _searchField = search->createRowView(
this, this,
st::infoMediaSearch); st::infoMediaSearch);
@ -216,7 +222,6 @@ void InnerWidget::refreshSearchField() {
} }
object_ptr<ListWidget> InnerWidget::setupList() { object_ptr<ListWidget> InnerWidget::setupList() {
refreshSearchField();
auto result = object_ptr<ListWidget>( auto result = object_ptr<ListWidget>(
this, this,
_controller); _controller);

View File

@ -86,6 +86,7 @@ private:
object_ptr<Ui::PlainShadow> _otherTabsShadow = { nullptr }; object_ptr<Ui::PlainShadow> _otherTabsShadow = { nullptr };
base::unique_qptr<Ui::RpWidget> _searchField = nullptr; base::unique_qptr<Ui::RpWidget> _searchField = nullptr;
object_ptr<ListWidget> _list = { nullptr }; object_ptr<ListWidget> _list = { nullptr };
bool _searchEnabled = false;
bool _inResize = false; bool _inResize = false;

View File

@ -49,6 +49,7 @@ constexpr auto kPreloadedScreensCount = 4;
constexpr auto kPreloadIfLessThanScreens = 2; constexpr auto kPreloadIfLessThanScreens = 2;
constexpr auto kPreloadedScreensCountFull constexpr auto kPreloadedScreensCountFull
= kPreloadedScreensCount + 1 + kPreloadedScreensCount; = kPreloadedScreensCount + 1 + kPreloadedScreensCount;
constexpr auto kMediaCountForSearch = 10;
UniversalMsgId GetUniversalId(FullMsgId itemId) { UniversalMsgId GetUniversalId(FullMsgId itemId) {
return (itemId.channel != 0) return (itemId.channel != 0)
@ -552,6 +553,7 @@ ListWidget::ListWidget(
} }
void ListWidget::start() { void ListWidget::start() {
_controller->setSearchEnabledByContent(false);
ObservableViewer(*Window::Theme::Background()) ObservableViewer(*Window::Theme::Background())
| rpl::start_with_next([this](const auto &update) { | rpl::start_with_next([this](const auto &update) {
if (update.paletteChanged()) { if (update.paletteChanged()) {
@ -889,6 +891,10 @@ void ListWidget::refreshRows() {
_sections.push_back(std::move(section)); _sections.push_back(std::move(section));
} }
if (_layouts.size() > kMediaCountForSearch) {
_controller->setSearchEnabledByContent(true);
}
clearStaleLayouts(); clearStaleLayouts();
resizeToWidth(width()); resizeToWidth(width());

View File

@ -91,10 +91,6 @@ Widget::Widget(
| rpl::start_with_next([this](int skip) { | rpl::start_with_next([this](int skip) {
scrollTo({ skip, -1 }); scrollTo({ skip, -1 });
}, _inner->lifetime()); }, _inner->lifetime());
controller->wrapValue()
| rpl::start_with_next([this] {
refreshSearchField();
}, lifetime());
} }
rpl::producer<SelectedItems> Widget::selectedListValue() const { rpl::producer<SelectedItems> Widget::selectedListValue() const {
@ -123,14 +119,13 @@ void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento)
restoreState(memento); restoreState(memento);
} }
std::unique_ptr<ContentMemento> Widget::createMemento() { std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
auto result = std::make_unique<Memento>(controller()); auto result = std::make_unique<Memento>(controller());
saveState(result.get()); saveState(result.get());
return std::move(result); return std::move(result);
} }
void Widget::saveState(not_null<Memento*> memento) { void Widget::saveState(not_null<Memento*> memento) {
controller()->saveSearchState(memento);
_inner->saveState(memento); _inner->saveState(memento);
} }
@ -138,25 +133,5 @@ void Widget::restoreState(not_null<Memento*> memento) {
_inner->restoreState(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 Media
} // namespace Info } // namespace Info

View File

@ -106,7 +106,6 @@ public:
bool showInternal( bool showInternal(
not_null<ContentMemento*> memento) override; not_null<ContentMemento*> memento) override;
std::unique_ptr<ContentMemento> createMemento() override;
void setInternalState( void setInternalState(
const QRect &geometry, const QRect &geometry,
@ -119,10 +118,9 @@ private:
void saveState(not_null<Memento*> memento); void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento); void restoreState(not_null<Memento*> memento);
void refreshSearchField(); std::unique_ptr<ContentMemento> doCreateMemento() override;
InnerWidget *_inner = nullptr; InnerWidget *_inner = nullptr;
base::unique_qptr<Ui::RpWidget> _searchField = nullptr;
}; };

View File

@ -43,7 +43,7 @@ namespace Info {
namespace Profile { namespace Profile {
namespace { namespace {
constexpr auto kEnableSearchMembersAfterCount = 50; constexpr auto kEnableSearchMembersAfterCount = 20;
} // namespace } // namespace

View File

@ -92,7 +92,7 @@ void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento)
restoreState(memento); restoreState(memento);
} }
std::unique_ptr<ContentMemento> Widget::createMemento() { std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
auto result = std::make_unique<Memento>(controller()); auto result = std::make_unique<Memento>(controller());
saveState(result.get()); saveState(result.get());
return std::move(result); return std::move(result);

View File

@ -79,7 +79,6 @@ public:
bool showInternal( bool showInternal(
not_null<ContentMemento*> memento) override; not_null<ContentMemento*> memento) override;
std::unique_ptr<ContentMemento> createMemento() override;
void setInternalState( void setInternalState(
const QRect &geometry, const QRect &geometry,
@ -91,6 +90,8 @@ private:
void saveState(not_null<Memento*> memento); void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento); void restoreState(not_null<Memento*> memento);
std::unique_ptr<ContentMemento> doCreateMemento() override;
InnerWidget *_inner = nullptr; InnerWidget *_inner = nullptr;
}; };