Allow showing chat members in a special section.

This commit is contained in:
John Preston 2017-11-17 17:23:36 +04:00
parent 747ebd2136
commit bef87c6dff
18 changed files with 562 additions and 263 deletions

View File

@ -186,6 +186,7 @@ InnerWidget::InnerWidget(
, _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() _controller->searchFieldController()->queryValue()
| rpl::start_with_next([this](QString &&query) { | rpl::start_with_next([this](QString &&query) {
peerListScrollToTop(); peerListScrollToTop();

View File

@ -83,7 +83,9 @@ bool Widget::showInternal(not_null<ContentMemento*> memento) {
return false; return false;
} }
void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento) { void Widget::setInternalState(
const QRect &geometry,
not_null<Memento*> memento) {
setGeometry(geometry); setGeometry(geometry);
myEnsureResized(this); myEnsureResized(this);
restoreState(memento); restoreState(memento);

View File

@ -450,20 +450,7 @@ infoMembersSearchActiveLayer: icon {
infoMembersButtonIconPosition infoMembersButtonIconPosition
} }
}; };
infoMembersSearchField: FlatInput(defaultFlatInput) { infoMembersSearchField: InputField(contactsSearchField) {
textColor: windowFg;
bgColor: topBarBg;
bgActive: topBarBg;
font: font(fsize);
borderWidth: 0px;
borderColor: topBarBg;
borderActive: topBarBg;
width: 100px;
height: 32px;
textMrg: margins(0px, 0px, 0px, 0px);
} }
infoMembersCancelSearch: CrossButton { infoMembersCancelSearch: CrossButton {
width: 44px; width: 44px;

View File

@ -28,11 +28,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/search_field_controller.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/profile/info_profile_members.h"
#include "info/media/info_media_widget.h" #include "info/media/info_media_widget.h"
#include "info/common_groups/info_common_groups_widget.h" #include "info/common_groups/info_common_groups_widget.h"
#include "info/info_layer_widget.h" #include "info/info_layer_widget.h"
#include "info/info_section_widget.h" #include "info/info_section_widget.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "boxes/peer_list_box.h"
#include "styles/style_info.h" #include "styles/style_info.h"
#include "styles/style_profile.h" #include "styles/style_profile.h"
@ -54,13 +56,15 @@ ContentWidget::ContentWidget(
: st::profileBg; : st::profileBg;
update(); update();
}, lifetime()); }, lifetime());
rpl::combine( if (_controller->section().type() != Section::Type::Profile) {
_controller->wrapValue(), rpl::combine(
_controller->searchEnabledByContent(), _controller->wrapValue(),
($1 == Wrap::Layer) && $2) _controller->searchEnabledByContent(),
| rpl::start_with_next([this](bool shown) { ($1 == Wrap::Layer) && $2)
refreshSearchField(shown); | rpl::start_with_next([this](bool shown) {
}, lifetime()); refreshSearchField(shown);
}, lifetime());
}
_scrollTopSkip.changes() _scrollTopSkip.changes()
| rpl::start_with_next([this] { | rpl::start_with_next([this] {
updateControlsGeometry(); updateControlsGeometry();
@ -211,8 +215,10 @@ void ContentWidget::refreshSearchField(bool shown) {
field->moveToLeft(0, 0); field->moveToLeft(0, 0);
}, field->lifetime()); }, field->lifetime());
field->show(); field->show();
field->setFocus();
setScrollTopSkip(field->heightNoMargins() - st::lineWidth); setScrollTopSkip(field->heightNoMargins() - st::lineWidth);
} else { } else {
setFocus();
_searchField = nullptr; _searchField = nullptr;
setScrollTopSkip(0); setScrollTopSkip(0);
} }

View File

@ -102,14 +102,18 @@ void Controller::setSection(not_null<ContentMemento*> memento) {
void Controller::updateSearchControllers( void Controller::updateSearchControllers(
not_null<ContentMemento*> memento) { not_null<ContentMemento*> memento) {
auto isMedia = (_section.type() == Section::Type::Media); using Type = Section::Type;
auto type = _section.type();
auto isMedia = (type == Type::Media);
auto mediaType = isMedia auto mediaType = isMedia
? _section.mediaType() ? _section.mediaType()
: 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); = (type == Type::CommonGroups);
auto hasMembersSearch
= (type == Type::Members || type == Type::Profile);
auto searchQuery = memento->searchFieldQuery(); auto searchQuery = memento->searchFieldQuery();
if (isMedia) { if (isMedia) {
_searchController _searchController
@ -121,7 +125,7 @@ void Controller::updateSearchControllers(
} else { } else {
_searchController = nullptr; _searchController = nullptr;
} }
if (hasMediaSearch || hasCommonGroupsSearch) { if (hasMediaSearch || hasCommonGroupsSearch || hasMembersSearch) {
_searchFieldController _searchFieldController
= std::make_unique<Ui::SearchFieldController>( = std::make_unique<Ui::SearchFieldController>(
searchQuery); searchQuery);

View File

@ -44,6 +44,7 @@ public:
Profile, Profile,
Media, Media,
CommonGroups, CommonGroups,
Members,
}; };
using MediaType = Storage::SharedMediaType; using MediaType = Storage::SharedMediaType;

View File

@ -21,11 +21,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/info_memento.h" #include "info/info_memento.h"
#include "info/profile/info_profile_widget.h" #include "info/profile/info_profile_widget.h"
#include "info/profile/info_profile_members.h"
#include "info/media/info_media_widget.h" #include "info/media/info_media_widget.h"
#include "info/members/info_members_widget.h"
#include "info/common_groups/info_common_groups_widget.h" #include "info/common_groups/info_common_groups_widget.h"
#include "info/info_section_widget.h" #include "info/info_section_widget.h"
#include "info/info_layer_widget.h" #include "info/info_layer_widget.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "boxes/peer_list_box.h"
namespace Info { namespace Info {
@ -76,6 +79,10 @@ std::unique_ptr<ContentMemento> Memento::Default(
Assert(peerIsUser(peerId)); Assert(peerIsUser(peerId));
return std::make_unique<CommonGroups::Memento>( return std::make_unique<CommonGroups::Memento>(
peerToUser(peerId)); peerToUser(peerId));
case Section::Type::Members:
return std::make_unique<Members::Memento>(
peerId,
migratedPeerId);
} }
Unexpected("Wrong section type in Info::Memento::Default()"); Unexpected("Wrong section type in Info::Memento::Default()");
} }

View File

@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "info/info_wrap_widget.h" #include "info/info_wrap_widget.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "info/profile/info_profile_values.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
@ -111,6 +112,9 @@ void TopBar::createSearchView(
_st.searchRow.fieldCancel); _st.searchRow.fieldCancel);
auto toggleSearchMode = [=](bool enabled, anim::type animated) { auto toggleSearchMode = [=](bool enabled, anim::type animated) {
if (!enabled) {
setFocus();
}
if (_title) { if (_title) {
_title->setVisible(!enabled); _title->setVisible(!enabled);
} }
@ -120,6 +124,9 @@ void TopBar::createSearchView(
cancel->finishAnimations(); cancel->finishAnimations();
} }
search->toggle(!enabled, animated); search->toggle(!enabled, animated);
if (enabled) {
field->setFocus();
}
}; };
auto cancelSearch = [=] { auto cancelSearch = [=] {
@ -160,7 +167,6 @@ void TopBar::createSearchView(
search->entity()->addClickHandler([=] { search->entity()->addClickHandler([=] {
toggleSearchMode(true, anim::type::normal); toggleSearchMode(true, anim::type::normal);
field->setFocus();
}); });
field->alive() field->alive()
@ -248,6 +254,12 @@ void TopBar::startHighlightAnimation() {
rpl::producer<QString> TitleValue( rpl::producer<QString> TitleValue(
const Section &section, const Section &section,
not_null<PeerData*> peer) { not_null<PeerData*> peer) {
if (section.type() == Section::Type::Members) {
return Profile::MembersCountValue(peer)
| rpl::map([](int count) {
return lng_chat_status_members(lt_count, count);
});
}
return Lang::Viewer([&] { return Lang::Viewer([&] {
switch (section.type()) { switch (section.type()) {
case Section::Type::Profile: case Section::Type::Profile:

View File

@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/flatten_latest.h> #include <rpl/flatten_latest.h>
#include <rpl/combine.h> #include <rpl/combine.h>
#include "info/profile/info_profile_widget.h" #include "info/profile/info_profile_widget.h"
#include "info/profile/info_profile_members.h"
#include "info/profile/info_profile_values.h" #include "info/profile/info_profile_values.h"
#include "info/media/info_media_widget.h" #include "info/media/info_media_widget.h"
#include "info/info_content_widget.h" #include "info/info_content_widget.h"
@ -39,6 +40,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "window/window_controller.h" #include "window/window_controller.h"
#include "window/window_slide_animation.h" #include "window/window_slide_animation.h"
#include "window/window_peer_menu.h" #include "window/window_peer_menu.h"
#include "boxes/peer_list_box.h"
#include "auth_session.h" #include "auth_session.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -261,6 +263,7 @@ void WrapWidget::forceContentRepaint() {
// } // }
// break; // break;
// case Section::Type::CommonGroups: // case Section::Type::CommonGroups:
// case Section::Type::Members:
// setupTabs(Tab::None); // setupTabs(Tab::None);
// break; // break;
// } // }
@ -482,7 +485,8 @@ void WrapWidget::createTopBarOverride(SelectedItems &&items) {
bool WrapWidget::requireTopBarSearch() const { bool WrapWidget::requireTopBarSearch() const {
if (!_controller->searchFieldController()) { if (!_controller->searchFieldController()) {
return false; return false;
} else if (_controller->wrap() == Wrap::Layer) { } else if (_controller->wrap() == Wrap::Layer
|| _controller->section().type() == Section::Type::Profile) {
return false; return false;
} else if (hasStackHistory()) { } else if (hasStackHistory()) {
return true; return true;

View File

@ -114,7 +114,9 @@ bool Widget::showInternal(not_null<ContentMemento*> memento) {
return false; return false;
} }
void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento) { void Widget::setInternalState(
const QRect &geometry,
not_null<Memento*> memento) {
setGeometry(geometry); setGeometry(geometry);
myEnsureResized(this); myEnsureResized(this);
restoreState(memento); restoreState(memento);

View File

@ -0,0 +1,111 @@
/*
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-2017 John Preston, https://desktop.telegram.org
*/
#include "info/members/info_members_widget.h"
#include "info/profile/info_profile_members.h"
#include "info/info_controller.h"
#include "ui/search_field_controller.h"
#include "ui/widgets/scroll_area.h"
#include "styles/style_info.h"
namespace Info {
namespace Members {
Memento::Memento(not_null<Controller*> controller)
: Memento(
controller->peerId(),
controller->migratedPeerId()) {
}
Section Memento::section() const {
return Section(Section::Type::Members);
}
object_ptr<ContentWidget> Memento::createWidget(
QWidget *parent,
not_null<Controller*> controller,
const QRect &geometry) {
auto result = object_ptr<Widget>(
parent,
controller);
result->setInternalState(geometry, this);
return std::move(result);
}
void Memento::setState(std::unique_ptr<SavedState> state) {
_state = std::move(state);
}
std::unique_ptr<SavedState> Memento::state() {
return std::move(_state);
}
Memento::~Memento() = default;
Widget::Widget(
QWidget *parent,
not_null<Controller*> controller)
: ContentWidget(parent, controller) {
_inner = setInnerWidget(object_ptr<Profile::Members>(
this,
controller,
controller->peer()));
}
bool Widget::showInternal(not_null<ContentMemento*> memento) {
if (!controller()->validateMementoPeer(memento)) {
return false;
}
if (auto membersMemento = dynamic_cast<Memento*>(memento.get())) {
restoreState(membersMemento);
return true;
}
return false;
}
void Widget::setInternalState(
const QRect &geometry,
not_null<Memento*> memento) {
setGeometry(geometry);
myEnsureResized(this);
restoreState(memento);
}
std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
auto result = std::make_unique<Memento>(controller());
saveState(result.get());
return std::move(result);
}
void Widget::saveState(not_null<Memento*> memento) {
memento->setScrollTop(scrollTopSave());
memento->setState(_inner->saveState());
}
void Widget::restoreState(not_null<Memento*> memento) {
_inner->restoreState(memento->state());
auto scrollTop = memento->scrollTop();
scrollTopRestore(memento->scrollTop());
}
} // namespace Members
} // namespace Info

View File

@ -0,0 +1,91 @@
/*
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-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <rpl/producer.h>
#include "info/info_content_widget.h"
struct PeerListState;
namespace Ui {
class SearchFieldController;
} // namespace Ui
namespace Info {
namespace Profile {
class Members;
struct MembersState;
} // namespace Profile
namespace Members {
using SavedState = Profile::MembersState;
class Memento final : public ContentMemento {
public:
Memento(not_null<Controller*> controller);
Memento(PeerId peerId, PeerId migratedPeerId)
: ContentMemento(peerId, migratedPeerId) {
}
object_ptr<ContentWidget> createWidget(
QWidget *parent,
not_null<Controller*> controller,
const QRect &geometry) override;
Section section() const override;
void setState(std::unique_ptr<SavedState> state);
std::unique_ptr<SavedState> state();
~Memento();
private:
std::unique_ptr<SavedState> _state;
};
class Widget final : public ContentWidget {
public:
Widget(
QWidget *parent,
not_null<Controller*> controller);
bool showInternal(
not_null<ContentMemento*> memento) override;
void setInternalState(
const QRect &geometry,
not_null<Memento*> memento);
private:
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
std::unique_ptr<ContentMemento> doCreateMemento() override;
Profile::Members *_inner = nullptr;
};
} // namespace Members
} // namespace Info

View File

@ -249,14 +249,14 @@ void InnerWidget::visibleTopBottomUpdated(
void InnerWidget::saveState(not_null<Memento*> memento) { void InnerWidget::saveState(not_null<Memento*> memento) {
memento->setInfoExpanded(_cover->toggled()); memento->setInfoExpanded(_cover->toggled());
if (_members) { if (_members) {
_members->saveState(memento); memento->setMembersState(_members->saveState());
} }
} }
void InnerWidget::restoreState(not_null<Memento*> memento) { void InnerWidget::restoreState(not_null<Memento*> memento) {
_cover->toggle(memento->infoExpanded(), anim::type::instant); _cover->toggle(memento->infoExpanded(), anim::type::instant);
if (_members) { if (_members) {
_members->restoreState(memento); _members->restoreState(memento->membersState());
} }
if (_infoWrap) { if (_infoWrap) {
_infoWrap->finishAnimating(); _infoWrap->finishAnimating();

View File

@ -28,15 +28,19 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/profile/info_profile_members_controllers.h" #include "info/profile/info_profile_members_controllers.h"
#include "info/info_content_widget.h" #include "info/info_content_widget.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "info/info_memento.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/search_field_controller.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_info.h" #include "styles/style_info.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "window/window_controller.h"
namespace Info { namespace Info {
namespace Profile { namespace Profile {
@ -51,30 +55,23 @@ Members::Members(
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<PeerData*> peer) not_null<PeerData*> peer)
: RpWidget(parent) : RpWidget(parent)
, _controller(controller)
, _peer(peer) , _peer(peer)
, _listController(CreateMembersController(controller->window(), _peer)) , _listController(CreateMembersController(controller->window(), _peer)) {
, _labelWrap(this) setupHeader();
, _label(setupHeader()) setupList();
, _addMember(this, st::infoMembersAddMember)
, _searchField(
this,
st::infoMembersSearchField,
langFactory(lng_participant_filter))
, _search(this, st::infoMembersSearch)
, _cancelSearch(this, st::infoMembersCancelSearch)
, _list(setupList(this, _listController.get())) {
setupButtons();
controller->wrapValue()
| rpl::start_with_next([this](Wrap wrap) {
_wrap = wrap;
updateSearchOverrides();
}, lifetime());
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());
} }
int Members::desiredHeight() const { int Members::desiredHeight() const {
auto desired = st::infoMembersHeader; auto desired = _header ? _header->height() : 0;
auto count = [this] { auto count = [this] {
if (auto chat = _peer->asChat()) { if (auto chat = _peer->asChat()) {
return chat->count; return chat->count;
@ -92,30 +89,76 @@ rpl::producer<int> Members::onlineCountValue() const {
return _listController->onlineCountValue(); return _listController->onlineCountValue();
} }
void Members::saveState(not_null<Memento*> memento) { std::unique_ptr<MembersState> Members::saveState() {
if (_searchShown) { auto result = std::make_unique<MembersState>();
memento->setMembersSearch(_searchField->getLastText()); result->list = _listController->saveState();
} //if (_searchShown) {
memento->setMembersState(_listController->saveState()); // result->search = _searchField->getLastText();
//}
return result;
} }
void Members::restoreState(not_null<Memento*> memento) { void Members::restoreState(std::unique_ptr<MembersState> state) {
_listController->restoreState(memento->membersState()); if (!state) {
if (auto text = memento->membersSearch()) { return;
if (!_searchShown) {
toggleSearch(anim::type::instant);
}
_searchField->setText(*text);
_searchField->updatePlaceholder();
applySearch();
} else if (_searchShown) {
toggleSearch(anim::type::instant);
} }
_listController->restoreState(std::move(state->list));
updateSearchEnabledByContent();
//if (!_controller->searchFieldController()->query().isEmpty()) {
// if (!_searchShown) {
// toggleSearch(anim::type::instant);
// }
//} else if (_searchShown) {
// toggleSearch(anim::type::instant);
//}
} }
object_ptr<Ui::FlatLabel> Members::setupHeader() { void Members::setupHeader() {
if (_controller->section().type() == Section::Type::Members) {
return;
}
_header = object_ptr<Ui::FixedHeightWidget>(
this,
st::infoMembersHeader);
auto parent = _header.data();
object_ptr<FloatingIcon>(
parent,
st::infoIconMembers,
st::infoIconPosition);
_openMembers = Ui::CreateChild<Ui::AbstractButton>(parent);
_titleWrap = Ui::CreateChild<Ui::RpWidget>(parent);
_title = setupTitle();
_addMember = Ui::CreateChild<Ui::IconButton>(
parent,
st::infoMembersAddMember);
//_searchField = _controller->searchFieldController()->createField(
// parent,
// st::infoMembersSearchField);
//_search = Ui::CreateChild<Ui::IconButton>(
// parent,
// st::infoMembersSearch);
//_cancelSearch = Ui::CreateChild<Ui::CrossButton>(
// parent,
// st::infoMembersCancelSearch);
setupButtons();
//_controller->wrapValue()
// | rpl::start_with_next([this](Wrap wrap) {
// _wrap = wrap;
// updateSearchOverrides();
// }, lifetime());
widthValue()
| rpl::start_with_next([this](int width) {
_header->resizeToWidth(width);
}, _header->lifetime());
}
object_ptr<Ui::FlatLabel> Members::setupTitle() {
auto result = object_ptr<Ui::FlatLabel>( auto result = object_ptr<Ui::FlatLabel>(
_labelWrap, _titleWrap,
MembersCountValue(_peer) MembersCountValue(_peer)
| rpl::map([](int count) { | rpl::map([](int count) {
return lng_chat_status_members(lt_count, count); return lng_chat_status_members(lt_count, count);
@ -129,8 +172,14 @@ object_ptr<Ui::FlatLabel> Members::setupHeader() {
void Members::setupButtons() { void Members::setupButtons() {
using namespace rpl::mappers; using namespace rpl::mappers;
_searchField->hide(); _openMembers->addClickHandler([this] {
_cancelSearch->hideFast(); _controller->window()->showSection(Info::Memento(
_controller->peerId(),
Section::Type::Members));
});
//_searchField->hide();
//_cancelSearch->hideFast();
auto addMemberShown = CanAddMemberValue(_peer) auto addMemberShown = CanAddMemberValue(_peer)
| rpl::start_spawning(lifetime()); | rpl::start_spawning(lifetime());
@ -139,128 +188,129 @@ void Members::setupButtons() {
this->addMember(); this->addMember();
}); });
auto searchShown = MembersCountValue(_peer) //auto searchShown = MembersCountValue(_peer)
| rpl::map($1 >= kEnableSearchMembersAfterCount) // | rpl::map($1 >= kEnableSearchMembersAfterCount)
| rpl::distinct_until_changed() // | rpl::distinct_until_changed()
| rpl::start_spawning(lifetime()); // | rpl::start_spawning(lifetime());
_search->showOn(rpl::duplicate(searchShown)); //_search->showOn(rpl::duplicate(searchShown));
_search->addClickHandler([this] { //_search->addClickHandler([this] {
this->showSearch(); // this->showSearch();
}); //});
_cancelSearch->addClickHandler([this] { //_cancelSearch->addClickHandler([this] {
this->cancelSearch(); // this->cancelSearch();
}); //});
rpl::combine( //rpl::combine(
std::move(addMemberShown), // std::move(addMemberShown),
std::move(searchShown)) // std::move(searchShown))
std::move(addMemberShown)
| rpl::start_with_next([this] { | rpl::start_with_next([this] {
this->resizeToWidth(width()); updateHeaderControlsGeometry(width());
}, lifetime()); }, lifetime());
object_ptr<FloatingIcon>(
this,
st::infoIconMembers,
st::infoIconPosition)->lower();
connect(_searchField, &Ui::FlatInput::cancelled, this, [this] {
cancelSearch();
});
connect(_searchField, &Ui::FlatInput::changed, this, [this] {
applySearch();
});
connect(_searchField, &Ui::FlatInput::submitted, this, [this] {
forceSearchSubmit();
});
} }
object_ptr<Members::ListWidget> Members::setupList( void Members::setupList() {
RpWidget *parent, auto topSkip = _header ? _header->height() : 0;
not_null<PeerListController*> controller) const { _list = object_ptr<ListWidget>(
auto result = object_ptr<ListWidget>( this,
parent, _listController.get(),
controller,
st::infoMembersList); st::infoMembersList);
result->scrollToRequests() _list->scrollToRequests()
| rpl::start_with_next([this](Ui::ScrollToRequest request) { | rpl::start_with_next([this](Ui::ScrollToRequest request) {
auto addmin = (request.ymin < 0) auto addmin = (request.ymin < 0 || !_header)
? 0 ? 0
: st::infoMembersHeader; : _header->height();
auto addmax = (request.ymax < 0) auto addmax = (request.ymax < 0 || !_header)
? 0 ? 0
: st::infoMembersHeader; : _header->height();
_scrollToRequests.fire({ _scrollToRequests.fire({
request.ymin + addmin, request.ymin + addmin,
request.ymax + addmax }); request.ymax + addmax });
}, result->lifetime()); }, _list->lifetime());
result->moveToLeft(0, st::infoMembersHeader); widthValue()
parent->widthValue() | rpl::start_with_next([this](int newWidth) {
| rpl::start_with_next([list = result.data()](int newWidth) { _list->resizeToWidth(newWidth);
list->resizeToWidth(newWidth); }, _list->lifetime());
}, result->lifetime()); _list->heightValue()
result->heightValue() | rpl::start_with_next([=](int listHeight) {
| rpl::start_with_next([parent](int listHeight) {
auto newHeight = (listHeight > st::membersMarginBottom) auto newHeight = (listHeight > st::membersMarginBottom)
? (st::infoMembersHeader ? (topSkip
+ listHeight + listHeight
+ st::membersMarginBottom) + st::membersMarginBottom)
: 0; : 0;
parent->resize(parent->width(), newHeight); resize(width(), newHeight);
}, result->lifetime()); }, _list->lifetime());
return result; _list->moveToLeft(0, topSkip);
} }
int Members::resizeGetHeight(int newWidth) { int Members::resizeGetHeight(int newWidth) {
if (_header) {
updateHeaderControlsGeometry(newWidth);
}
return heightNoMargins();
}
void Members::updateSearchEnabledByContent() {
_controller->setSearchEnabledByContent(
peerListFullRowsCount() >= kEnableSearchMembersAfterCount);
}
void Members::updateHeaderControlsGeometry(int newWidth) {
_openMembers->setGeometry(0, 0, newWidth, st::infoMembersHeader);
auto availableWidth = newWidth auto availableWidth = newWidth
- st::infoMembersButtonPosition.x(); - st::infoMembersButtonPosition.x();
auto cancelLeft = availableWidth - _cancelSearch->width(); //auto cancelLeft = availableWidth - _cancelSearch->width();
_cancelSearch->moveToLeft( //_cancelSearch->moveToLeft(
cancelLeft, // cancelLeft,
st::infoMembersButtonPosition.y()); // st::infoMembersButtonPosition.y());
auto searchShownLeft = st::infoIconPosition.x() //auto searchShownLeft = st::infoIconPosition.x()
- st::infoMembersSearch.iconPosition.x(); // - st::infoMembersSearch.iconPosition.x();
auto searchHiddenLeft = availableWidth - _search->width(); //auto searchHiddenLeft = availableWidth - _search->width();
auto searchShown = _searchShownAnimation.current(_searchShown ? 1. : 0.); //auto searchShown = _searchShownAnimation.current(_searchShown ? 1. : 0.);
auto searchCurrentLeft = anim::interpolate( //auto searchCurrentLeft = anim::interpolate(
searchHiddenLeft, // searchHiddenLeft,
searchShownLeft, // searchShownLeft,
searchShown); // searchShown);
_search->moveToLeft( //_search->moveToLeft(
searchCurrentLeft, // searchCurrentLeft,
st::infoMembersButtonPosition.y()); // st::infoMembersButtonPosition.y());
if (!_search->isHidden()) { //if (!_search->isHidden()) {
availableWidth -= st::infoMembersSearch.width; // availableWidth -= st::infoMembersSearch.width;
} //}
_addMember->moveToLeft( _addMember->moveToLeft(
availableWidth - _addMember->width(), availableWidth - _addMember->width(),
st::infoMembersButtonPosition.y(), st::infoMembersButtonPosition.y(),
newWidth); newWidth);
auto fieldLeft = anim::interpolate( //auto fieldLeft = anim::interpolate(
cancelLeft, // cancelLeft,
st::infoBlockHeaderPosition.x(), // st::infoBlockHeaderPosition.x(),
searchShown); // searchShown);
_searchField->setGeometryToLeft( //_searchField->setGeometryToLeft(
fieldLeft, // fieldLeft,
st::infoMembersSearchTop, // st::infoMembersSearchTop,
cancelLeft - fieldLeft, // cancelLeft - fieldLeft,
_searchField->height()); // _searchField->height());
_labelWrap->resize( //_titleWrap->resize(
searchCurrentLeft - st::infoBlockHeaderPosition.x(), // searchCurrentLeft - st::infoBlockHeaderPosition.x(),
_label->height()); // _title->height());
_labelWrap->moveToLeft( _titleWrap->resize(
availableWidth - _addMember->width() - st::infoBlockHeaderPosition.x(),
_title->height());
_titleWrap->moveToLeft(
st::infoBlockHeaderPosition.x(), st::infoBlockHeaderPosition.x(),
st::infoBlockHeaderPosition.y(), st::infoBlockHeaderPosition.y(),
newWidth); newWidth);
_titleWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
_label->resizeToWidth(searchHiddenLeft); //_title->resizeToWidth(searchHiddenLeft);
_label->moveToLeft(0, 0); _title->resizeToWidth(_titleWrap->width());
_title->moveToLeft(0, 0);
return heightNoMargins();
} }
void Members::addMember() { void Members::addMember() {
@ -278,74 +328,63 @@ void Members::addMember() {
} }
} }
void Members::showSearch() { //void Members::showSearch() {
if (!_searchShown) { // if (!_searchShown) {
toggleSearch(); // toggleSearch();
} // }
} //}
//
void Members::toggleSearch(anim::type animated) { //void Members::toggleSearch(anim::type animated) {
_searchShown = !_searchShown; // _searchShown = !_searchShown;
if (animated == anim::type::normal) { // if (animated == anim::type::normal) {
_cancelSearch->toggleAnimated(_searchShown); // _cancelSearch->toggleAnimated(_searchShown);
_searchShownAnimation.start( // _searchShownAnimation.start(
[this] { searchAnimationCallback(); }, // [this] { searchAnimationCallback(); },
_searchShown ? 0. : 1., // _searchShown ? 0. : 1.,
_searchShown ? 1. : 0., // _searchShown ? 1. : 0.,
st::slideWrapDuration); // st::slideWrapDuration);
} else { // } else {
_cancelSearch->toggleFast(_searchShown); // _cancelSearch->toggleFast(_searchShown);
_searchShownAnimation.finish(); // _searchShownAnimation.finish();
searchAnimationCallback(); // searchAnimationCallback();
} // }
_search->setDisabled(_searchShown); // _search->setDisabled(_searchShown);
if (_searchShown) { // if (_searchShown) {
_searchField->show(); // _searchField->show();
_searchField->setFocus(); // _searchField->setFocus();
} else { // } else {
setFocus(); // setFocus();
} // }
} //}
//
void Members::searchAnimationCallback() { //void Members::searchAnimationCallback() {
if (!_searchShownAnimation.animating()) { // if (!_searchShownAnimation.animating()) {
_searchField->setVisible(_searchShown); // _searchField->setVisible(_searchShown);
updateSearchOverrides(); // updateSearchOverrides();
_search->setPointerCursor(!_searchShown); // _search->setPointerCursor(!_searchShown);
} // }
resizeToWidth(width()); // updateHeaderControlsGeometry(width());
} //}
//
void Members::updateSearchOverrides() { //void Members::updateSearchOverrides() {
auto iconOverride = !_searchShown // auto iconOverride = !_searchShown
? nullptr // ? nullptr
: (_wrap == Wrap::Layer) // : (_wrap == Wrap::Layer)
? &st::infoMembersSearchActiveLayer // ? &st::infoMembersSearchActiveLayer
: &st::infoMembersSearchActive; // : &st::infoMembersSearchActive;
_search->setIconOverride(iconOverride, iconOverride); // _search->setIconOverride(iconOverride, iconOverride);
} //}
//
void Members::cancelSearch() { //void Members::cancelSearch() {
if (_searchShown) { // if (_searchShown) {
if (!_searchField->getLastText().isEmpty()) { // if (!_searchField->getLastText().isEmpty()) {
_searchField->setText(QString()); // _searchField->setText(QString());
_searchField->updatePlaceholder(); // _searchField->setFocus();
_searchField->setFocus(); // } else {
applySearch(); // toggleSearch();
} else { // }
toggleSearch(); // }
} //}
}
}
void Members::applySearch() {
peerListScrollToTop();
content()->searchQueryChanged(_searchField->getLastText());
}
void Members::forceSearchSubmit() {
content()->submitted();
}
void Members::visibleTopBottomUpdated( void Members::visibleTopBottomUpdated(
int visibleTop, int visibleTop,

View File

@ -24,11 +24,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "boxes/peer_list_box.h" #include "boxes/peer_list_box.h"
namespace Ui { namespace Ui {
class FlatInput; class InputField;
class CrossButton; class CrossButton;
class IconButton; class IconButton;
class FlatLabel; class FlatLabel;
struct ScrollToRequest; struct ScrollToRequest;
class AbstractButton;
} // namespace Ui } // namespace Ui
namespace Profile { namespace Profile {
@ -44,6 +45,10 @@ enum class Wrap;
namespace Profile { namespace Profile {
class Memento; class Memento;
struct MembersState {
std::unique_ptr<PeerListState> list;
base::optional<QString> search;
};
class Members class Members
: public Ui::RpWidget : public Ui::RpWidget
@ -58,8 +63,8 @@ public:
return _scrollToRequests.events(); return _scrollToRequests.events();
} }
void saveState(not_null<Memento*> memento); std::unique_ptr<MembersState> saveState();
void restoreState(not_null<Memento*> memento); void restoreState(std::unique_ptr<MembersState> state);
int desiredHeight() const; int desiredHeight() const;
rpl::producer<int> onlineCountValue() const; rpl::producer<int> onlineCountValue() const;
@ -87,36 +92,54 @@ private:
void peerListSetDescription( void peerListSetDescription(
object_ptr<Ui::FlatLabel> description) override; object_ptr<Ui::FlatLabel> description) override;
object_ptr<Ui::FlatLabel> setupHeader(); void peerListAppendRow(
object_ptr<ListWidget> setupList( std::unique_ptr<PeerListRow> row) override {
RpWidget *parent, PeerListContentDelegate::peerListAppendRow(std::move(row));
not_null<PeerListController*> controller) const; updateSearchEnabledByContent();
}
void peerListPrependRow(
std::unique_ptr<PeerListRow> row) override {
PeerListContentDelegate::peerListPrependRow(std::move(row));
updateSearchEnabledByContent();
}
void peerListRemoveRow(not_null<PeerListRow*> row) override {
PeerListContentDelegate::peerListRemoveRow(row);
updateSearchEnabledByContent();
}
void setupHeader();
object_ptr<Ui::FlatLabel> setupTitle();
void setupList();
void setupButtons(); void setupButtons();
void updateSearchOverrides(); //void updateSearchOverrides();
void addMember(); void addMember();
void showSearch(); //void showSearch();
void toggleSearch(anim::type animated = anim::type::normal); //void toggleSearch(anim::type animated = anim::type::normal);
void cancelSearch(); //void cancelSearch();
void applySearch(); //void searchAnimationCallback();
void forceSearchSubmit(); void updateHeaderControlsGeometry(int newWidth);
void searchAnimationCallback(); void updateSearchEnabledByContent();
Wrap _wrap; Wrap _wrap;
not_null<Controller*> _controller;
not_null<PeerData*> _peer; not_null<PeerData*> _peer;
std::unique_ptr<PeerListController> _listController; std::unique_ptr<PeerListController> _listController;
object_ptr<Ui::RpWidget> _labelWrap; object_ptr<Ui::RpWidget> _header = { nullptr };
object_ptr<Ui::FlatLabel> _label; object_ptr<ListWidget> _list = { nullptr };
object_ptr<Ui::IconButton> _addMember;
object_ptr<Ui::FlatInput> _searchField;
object_ptr<Ui::IconButton> _search;
object_ptr<Ui::CrossButton> _cancelSearch;
object_ptr<ListWidget> _list;
Animation _searchShownAnimation; Ui::AbstractButton *_openMembers = nullptr;
bool _searchShown = false; Ui::RpWidget *_titleWrap = nullptr;
base::Timer _searchTimer; Ui::FlatLabel *_title = nullptr;
Ui::IconButton *_addMember = nullptr;
//base::unique_qptr<Ui::InputField> _searchField;
//Ui::IconButton *_search = nullptr;
//Ui::CrossButton *_cancelSearch = nullptr;
//Animation _searchShownAnimation;
//bool _searchShown = false;
//base::Timer _searchTimer;
rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests; rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "info/profile/info_profile_widget.h" #include "info/profile/info_profile_widget.h"
#include "info/profile/info_profile_inner_widget.h" #include "info/profile/info_profile_inner_widget.h"
#include "info/profile/info_profile_members.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "info/info_controller.h" #include "info/info_controller.h"
@ -48,10 +49,22 @@ object_ptr<ContentWidget> Memento::createWidget(
return std::move(result); return std::move(result);
} }
void Memento::setMembersState(std::unique_ptr<MembersState> state) {
_membersState = std::move(state);
}
std::unique_ptr<MembersState> Memento::membersState() {
return std::move(_membersState);
}
Memento::~Memento() = default;
Widget::Widget( Widget::Widget(
QWidget *parent, QWidget *parent,
not_null<Controller*> controller) not_null<Controller*> controller)
: ContentWidget(parent, controller) { : ContentWidget(parent, controller) {
controller->setSearchEnabledByContent(false);
_inner = setInnerWidget(object_ptr<InnerWidget>( _inner = setInnerWidget(object_ptr<InnerWidget>(
this, this,
controller)); controller));
@ -86,7 +99,9 @@ bool Widget::showInternal(not_null<ContentMemento*> memento) {
return false; return false;
} }
void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento) { void Widget::setInternalState(
const QRect &geometry,
not_null<Memento*> memento) {
setGeometry(geometry); setGeometry(geometry);
myEnsureResized(this); myEnsureResized(this);
restoreState(memento); restoreState(memento);

View File

@ -22,12 +22,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"
#include "boxes/peer_list_box.h"
namespace Info { namespace Info {
namespace Profile { namespace Profile {
class InnerWidget; class InnerWidget;
struct MembersState;
class Memento final : public ContentMemento { class Memento final : public ContentMemento {
public: public:
@ -49,23 +49,15 @@ public:
bool infoExpanded() const { bool infoExpanded() const {
return _infoExpanded; return _infoExpanded;
} }
void setMembersSearch(QString query) { void setMembersState(std::unique_ptr<MembersState> state);
_membersSearch = query; std::unique_ptr<MembersState> membersState();
}
base::optional<QString> membersSearch() const { ~Memento();
return _membersSearch;
}
void setMembersState(std::unique_ptr<PeerListState> state) {
_membersState = std::move(state);
}
std::unique_ptr<PeerListState> membersState() {
return std::move(_membersState);
}
private: private:
bool _infoExpanded = true; bool _infoExpanded = true;
base::optional<QString> _membersSearch; base::optional<QString> _membersSearch;
std::unique_ptr<PeerListState> _membersState; std::unique_ptr<MembersState> _membersState;
}; };

View File

@ -248,6 +248,8 @@
<(src_loc)/info/media/info_media_list_widget.h <(src_loc)/info/media/info_media_list_widget.h
<(src_loc)/info/media/info_media_widget.cpp <(src_loc)/info/media/info_media_widget.cpp
<(src_loc)/info/media/info_media_widget.h <(src_loc)/info/media/info_media_widget.h
<(src_loc)/info/members/info_members_widget.cpp
<(src_loc)/info/members/info_members_widget.h
<(src_loc)/info/profile/info_profile_actions.cpp <(src_loc)/info/profile/info_profile_actions.cpp
<(src_loc)/info/profile/info_profile_actions.h <(src_loc)/info/profile/info_profile_actions.h
<(src_loc)/info/profile/info_profile_button.cpp <(src_loc)/info/profile/info_profile_button.cpp