From bef87c6dff18cb2028539396a663157fbdb40779 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 17 Nov 2017 17:23:36 +0400 Subject: [PATCH] Allow showing chat members in a special section. --- .../info_common_groups_inner_widget.cpp | 1 + .../info_common_groups_widget.cpp | 4 +- Telegram/SourceFiles/info/info.style | 15 +- .../SourceFiles/info/info_content_widget.cpp | 20 +- Telegram/SourceFiles/info/info_controller.cpp | 10 +- Telegram/SourceFiles/info/info_controller.h | 1 + Telegram/SourceFiles/info/info_memento.cpp | 7 + Telegram/SourceFiles/info/info_top_bar.cpp | 14 +- .../SourceFiles/info/info_wrap_widget.cpp | 6 +- .../info/media/info_media_widget.cpp | 4 +- .../info/members/info_members_widget.cpp | 111 +++++ .../info/members/info_members_widget.h | 91 ++++ .../profile/info_profile_inner_widget.cpp | 4 +- .../info/profile/info_profile_members.cpp | 427 ++++++++++-------- .../info/profile/info_profile_members.h | 71 ++- .../info/profile/info_profile_widget.cpp | 17 +- .../info/profile/info_profile_widget.h | 20 +- Telegram/gyp/telegram_sources.txt | 2 + 18 files changed, 562 insertions(+), 263 deletions(-) create mode 100644 Telegram/SourceFiles/info/members/info_members_widget.cpp create mode 100644 Telegram/SourceFiles/info/members/info_members_widget.h diff --git a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp index 11b3e2c21..a1bd5d87a 100644 --- a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp +++ b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp @@ -186,6 +186,7 @@ InnerWidget::InnerWidget( , _list(setupList(this, _listController.get())) { setContent(_list.data()); _listController->setDelegate(static_cast(this)); + _controller->searchFieldController()->queryValue() | rpl::start_with_next([this](QString &&query) { peerListScrollToTop(); diff --git a/Telegram/SourceFiles/info/common_groups/info_common_groups_widget.cpp b/Telegram/SourceFiles/info/common_groups/info_common_groups_widget.cpp index cd68e075a..de06ef5f7 100644 --- a/Telegram/SourceFiles/info/common_groups/info_common_groups_widget.cpp +++ b/Telegram/SourceFiles/info/common_groups/info_common_groups_widget.cpp @@ -83,7 +83,9 @@ bool Widget::showInternal(not_null memento) { return false; } -void Widget::setInternalState(const QRect &geometry, not_null memento) { +void Widget::setInternalState( + const QRect &geometry, + not_null memento) { setGeometry(geometry); myEnsureResized(this); restoreState(memento); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 6d610f4fc..8a01c4b85 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -450,20 +450,7 @@ infoMembersSearchActiveLayer: icon { infoMembersButtonIconPosition } }; -infoMembersSearchField: FlatInput(defaultFlatInput) { - 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); +infoMembersSearchField: InputField(contactsSearchField) { } infoMembersCancelSearch: CrossButton { width: 44px; diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index 32af22711..2e7c3ffe2 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -28,11 +28,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/search_field_controller.h" #include "lang/lang_keys.h" #include "info/profile/info_profile_widget.h" +#include "info/profile/info_profile_members.h" #include "info/media/info_media_widget.h" #include "info/common_groups/info_common_groups_widget.h" #include "info/info_layer_widget.h" #include "info/info_section_widget.h" #include "info/info_controller.h" +#include "boxes/peer_list_box.h" #include "styles/style_info.h" #include "styles/style_profile.h" @@ -54,13 +56,15 @@ ContentWidget::ContentWidget( : st::profileBg; update(); }, lifetime()); - rpl::combine( - _controller->wrapValue(), - _controller->searchEnabledByContent(), - ($1 == Wrap::Layer) && $2) - | rpl::start_with_next([this](bool shown) { - refreshSearchField(shown); - }, lifetime()); + if (_controller->section().type() != Section::Type::Profile) { + rpl::combine( + _controller->wrapValue(), + _controller->searchEnabledByContent(), + ($1 == Wrap::Layer) && $2) + | rpl::start_with_next([this](bool shown) { + refreshSearchField(shown); + }, lifetime()); + } _scrollTopSkip.changes() | rpl::start_with_next([this] { updateControlsGeometry(); @@ -211,8 +215,10 @@ void ContentWidget::refreshSearchField(bool shown) { field->moveToLeft(0, 0); }, field->lifetime()); field->show(); + field->setFocus(); setScrollTopSkip(field->heightNoMargins() - st::lineWidth); } else { + setFocus(); _searchField = nullptr; setScrollTopSkip(0); } diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp index 9e49f04b6..6699ae788 100644 --- a/Telegram/SourceFiles/info/info_controller.cpp +++ b/Telegram/SourceFiles/info/info_controller.cpp @@ -102,14 +102,18 @@ void Controller::setSection(not_null memento) { void Controller::updateSearchControllers( not_null memento) { - auto isMedia = (_section.type() == Section::Type::Media); + using Type = Section::Type; + auto type = _section.type(); + auto isMedia = (type == Type::Media); auto mediaType = isMedia ? _section.mediaType() : Section::MediaType::kCount; auto hasMediaSearch = isMedia && SharedMediaAllowSearch(mediaType); auto hasCommonGroupsSearch - = (_section.type() == Section::Type::CommonGroups); + = (type == Type::CommonGroups); + auto hasMembersSearch + = (type == Type::Members || type == Type::Profile); auto searchQuery = memento->searchFieldQuery(); if (isMedia) { _searchController @@ -121,7 +125,7 @@ void Controller::updateSearchControllers( } else { _searchController = nullptr; } - if (hasMediaSearch || hasCommonGroupsSearch) { + if (hasMediaSearch || hasCommonGroupsSearch || hasMembersSearch) { _searchFieldController = std::make_unique( searchQuery); diff --git a/Telegram/SourceFiles/info/info_controller.h b/Telegram/SourceFiles/info/info_controller.h index 7fdcab38a..a16e34379 100644 --- a/Telegram/SourceFiles/info/info_controller.h +++ b/Telegram/SourceFiles/info/info_controller.h @@ -44,6 +44,7 @@ public: Profile, Media, CommonGroups, + Members, }; using MediaType = Storage::SharedMediaType; diff --git a/Telegram/SourceFiles/info/info_memento.cpp b/Telegram/SourceFiles/info/info_memento.cpp index ab45ee921..8f6fc7e2f 100644 --- a/Telegram/SourceFiles/info/info_memento.cpp +++ b/Telegram/SourceFiles/info/info_memento.cpp @@ -21,11 +21,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "info/info_memento.h" #include "info/profile/info_profile_widget.h" +#include "info/profile/info_profile_members.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/info_section_widget.h" #include "info/info_layer_widget.h" #include "info/info_controller.h" +#include "boxes/peer_list_box.h" namespace Info { @@ -76,6 +79,10 @@ std::unique_ptr Memento::Default( Assert(peerIsUser(peerId)); return std::make_unique( peerToUser(peerId)); + case Section::Type::Members: + return std::make_unique( + peerId, + migratedPeerId); } Unexpected("Wrong section type in Info::Memento::Default()"); } diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index 4576e3bd5..fe1ee0260 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "lang/lang_keys.h" #include "info/info_wrap_widget.h" #include "info/info_controller.h" +#include "info/profile/info_profile_values.h" #include "storage/storage_shared_media.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -111,6 +112,9 @@ void TopBar::createSearchView( _st.searchRow.fieldCancel); auto toggleSearchMode = [=](bool enabled, anim::type animated) { + if (!enabled) { + setFocus(); + } if (_title) { _title->setVisible(!enabled); } @@ -120,6 +124,9 @@ void TopBar::createSearchView( cancel->finishAnimations(); } search->toggle(!enabled, animated); + if (enabled) { + field->setFocus(); + } }; auto cancelSearch = [=] { @@ -160,7 +167,6 @@ void TopBar::createSearchView( search->entity()->addClickHandler([=] { toggleSearchMode(true, anim::type::normal); - field->setFocus(); }); field->alive() @@ -248,6 +254,12 @@ void TopBar::startHighlightAnimation() { rpl::producer TitleValue( const Section §ion, not_null 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([&] { switch (section.type()) { case Section::Type::Profile: diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index 5466c3e34..301c491ba 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include #include "info/profile/info_profile_widget.h" +#include "info/profile/info_profile_members.h" #include "info/profile/info_profile_values.h" #include "info/media/info_media_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_slide_animation.h" #include "window/window_peer_menu.h" +#include "boxes/peer_list_box.h" #include "auth_session.h" #include "mainwidget.h" #include "lang/lang_keys.h" @@ -261,6 +263,7 @@ void WrapWidget::forceContentRepaint() { // } // break; // case Section::Type::CommonGroups: +// case Section::Type::Members: // setupTabs(Tab::None); // break; // } @@ -482,7 +485,8 @@ void WrapWidget::createTopBarOverride(SelectedItems &&items) { bool WrapWidget::requireTopBarSearch() const { if (!_controller->searchFieldController()) { return false; - } else if (_controller->wrap() == Wrap::Layer) { + } else if (_controller->wrap() == Wrap::Layer + || _controller->section().type() == Section::Type::Profile) { return false; } else if (hasStackHistory()) { return true; diff --git a/Telegram/SourceFiles/info/media/info_media_widget.cpp b/Telegram/SourceFiles/info/media/info_media_widget.cpp index 8a28fcd1f..393fd5ee6 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_widget.cpp @@ -114,7 +114,9 @@ bool Widget::showInternal(not_null memento) { return false; } -void Widget::setInternalState(const QRect &geometry, not_null memento) { +void Widget::setInternalState( + const QRect &geometry, + not_null memento) { setGeometry(geometry); myEnsureResized(this); restoreState(memento); diff --git a/Telegram/SourceFiles/info/members/info_members_widget.cpp b/Telegram/SourceFiles/info/members/info_members_widget.cpp new file mode 100644 index 000000000..8d763b218 --- /dev/null +++ b/Telegram/SourceFiles/info/members/info_members_widget.cpp @@ -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) +: Memento( + controller->peerId(), + controller->migratedPeerId()) { +} + +Section Memento::section() const { + return Section(Section::Type::Members); +} + +object_ptr Memento::createWidget( + QWidget *parent, + not_null controller, + const QRect &geometry) { + auto result = object_ptr( + parent, + controller); + result->setInternalState(geometry, this); + return std::move(result); +} + +void Memento::setState(std::unique_ptr state) { + _state = std::move(state); +} + +std::unique_ptr Memento::state() { + return std::move(_state); +} + +Memento::~Memento() = default; + +Widget::Widget( + QWidget *parent, + not_null controller) +: ContentWidget(parent, controller) { + _inner = setInnerWidget(object_ptr( + this, + controller, + controller->peer())); +} + +bool Widget::showInternal(not_null memento) { + if (!controller()->validateMementoPeer(memento)) { + return false; + } + if (auto membersMemento = dynamic_cast(memento.get())) { + restoreState(membersMemento); + return true; + } + return false; +} + +void Widget::setInternalState( + const QRect &geometry, + not_null memento) { + setGeometry(geometry); + myEnsureResized(this); + restoreState(memento); +} + +std::unique_ptr Widget::doCreateMemento() { + auto result = std::make_unique(controller()); + saveState(result.get()); + return std::move(result); +} + +void Widget::saveState(not_null memento) { + memento->setScrollTop(scrollTopSave()); + memento->setState(_inner->saveState()); +} + +void Widget::restoreState(not_null memento) { + _inner->restoreState(memento->state()); + auto scrollTop = memento->scrollTop(); + scrollTopRestore(memento->scrollTop()); +} + +} // namespace Members +} // namespace Info + diff --git a/Telegram/SourceFiles/info/members/info_members_widget.h b/Telegram/SourceFiles/info/members/info_members_widget.h new file mode 100644 index 000000000..8be617029 --- /dev/null +++ b/Telegram/SourceFiles/info/members/info_members_widget.h @@ -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 +#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); + Memento(PeerId peerId, PeerId migratedPeerId) + : ContentMemento(peerId, migratedPeerId) { + } + + object_ptr createWidget( + QWidget *parent, + not_null controller, + const QRect &geometry) override; + + Section section() const override; + + void setState(std::unique_ptr state); + std::unique_ptr state(); + + ~Memento(); + +private: + std::unique_ptr _state; + +}; + +class Widget final : public ContentWidget { +public: + Widget( + QWidget *parent, + not_null controller); + + bool showInternal( + not_null memento) override; + + void setInternalState( + const QRect &geometry, + not_null memento); + +private: + void saveState(not_null memento); + void restoreState(not_null memento); + + std::unique_ptr doCreateMemento() override; + + Profile::Members *_inner = nullptr; + +}; + +} // namespace Members +} // namespace Info + diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index b152e8d42..0f0648529 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -249,14 +249,14 @@ void InnerWidget::visibleTopBottomUpdated( void InnerWidget::saveState(not_null memento) { memento->setInfoExpanded(_cover->toggled()); if (_members) { - _members->saveState(memento); + memento->setMembersState(_members->saveState()); } } void InnerWidget::restoreState(not_null memento) { _cover->toggle(memento->infoExpanded(), anim::type::instant); if (_members) { - _members->restoreState(memento); + _members->restoreState(memento->membersState()); } if (_infoWrap) { _infoWrap->finishAnimating(); diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.cpp b/Telegram/SourceFiles/info/profile/info_profile_members.cpp index c2adb5826..64121177d 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_members.cpp @@ -28,15 +28,19 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "info/profile/info_profile_members_controllers.h" #include "info/info_content_widget.h" #include "info/info_controller.h" +#include "info/info_memento.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.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_info.h" #include "lang/lang_keys.h" #include "boxes/confirm_box.h" #include "boxes/peer_list_controllers.h" +#include "window/window_controller.h" namespace Info { namespace Profile { @@ -51,30 +55,23 @@ Members::Members( not_null controller, not_null peer) : RpWidget(parent) +, _controller(controller) , _peer(peer) -, _listController(CreateMembersController(controller->window(), _peer)) -, _labelWrap(this) -, _label(setupHeader()) -, _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()); +, _listController(CreateMembersController(controller->window(), _peer)) { + setupHeader(); + setupList(); setContent(_list.data()); _listController->setDelegate(static_cast(this)); + + _controller->searchFieldController()->queryValue() + | rpl::start_with_next([this](QString &&query) { + peerListScrollToTop(); + content()->searchQueryChanged(std::move(query)); + }, lifetime()); } int Members::desiredHeight() const { - auto desired = st::infoMembersHeader; + auto desired = _header ? _header->height() : 0; auto count = [this] { if (auto chat = _peer->asChat()) { return chat->count; @@ -92,30 +89,76 @@ rpl::producer Members::onlineCountValue() const { return _listController->onlineCountValue(); } -void Members::saveState(not_null memento) { - if (_searchShown) { - memento->setMembersSearch(_searchField->getLastText()); - } - memento->setMembersState(_listController->saveState()); +std::unique_ptr Members::saveState() { + auto result = std::make_unique(); + result->list = _listController->saveState(); + //if (_searchShown) { + // result->search = _searchField->getLastText(); + //} + return result; } -void Members::restoreState(not_null memento) { - _listController->restoreState(memento->membersState()); - if (auto text = memento->membersSearch()) { - if (!_searchShown) { - toggleSearch(anim::type::instant); - } - _searchField->setText(*text); - _searchField->updatePlaceholder(); - applySearch(); - } else if (_searchShown) { - toggleSearch(anim::type::instant); +void Members::restoreState(std::unique_ptr state) { + if (!state) { + return; } + _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 Members::setupHeader() { +void Members::setupHeader() { + if (_controller->section().type() == Section::Type::Members) { + return; + } + _header = object_ptr( + this, + st::infoMembersHeader); + auto parent = _header.data(); + + object_ptr( + parent, + st::infoIconMembers, + st::infoIconPosition); + + _openMembers = Ui::CreateChild(parent); + _titleWrap = Ui::CreateChild(parent); + _title = setupTitle(); + _addMember = Ui::CreateChild( + parent, + st::infoMembersAddMember); + //_searchField = _controller->searchFieldController()->createField( + // parent, + // st::infoMembersSearchField); + //_search = Ui::CreateChild( + // parent, + // st::infoMembersSearch); + //_cancelSearch = Ui::CreateChild( + // 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 Members::setupTitle() { auto result = object_ptr( - _labelWrap, + _titleWrap, MembersCountValue(_peer) | rpl::map([](int count) { return lng_chat_status_members(lt_count, count); @@ -129,8 +172,14 @@ object_ptr Members::setupHeader() { void Members::setupButtons() { using namespace rpl::mappers; - _searchField->hide(); - _cancelSearch->hideFast(); + _openMembers->addClickHandler([this] { + _controller->window()->showSection(Info::Memento( + _controller->peerId(), + Section::Type::Members)); + }); + + //_searchField->hide(); + //_cancelSearch->hideFast(); auto addMemberShown = CanAddMemberValue(_peer) | rpl::start_spawning(lifetime()); @@ -139,128 +188,129 @@ void Members::setupButtons() { this->addMember(); }); - auto searchShown = MembersCountValue(_peer) - | rpl::map($1 >= kEnableSearchMembersAfterCount) - | rpl::distinct_until_changed() - | rpl::start_spawning(lifetime()); - _search->showOn(rpl::duplicate(searchShown)); - _search->addClickHandler([this] { - this->showSearch(); - }); - _cancelSearch->addClickHandler([this] { - this->cancelSearch(); - }); + //auto searchShown = MembersCountValue(_peer) + // | rpl::map($1 >= kEnableSearchMembersAfterCount) + // | rpl::distinct_until_changed() + // | rpl::start_spawning(lifetime()); + //_search->showOn(rpl::duplicate(searchShown)); + //_search->addClickHandler([this] { + // this->showSearch(); + //}); + //_cancelSearch->addClickHandler([this] { + // this->cancelSearch(); + //}); - rpl::combine( - std::move(addMemberShown), - std::move(searchShown)) + //rpl::combine( + // std::move(addMemberShown), + // std::move(searchShown)) + std::move(addMemberShown) | rpl::start_with_next([this] { - this->resizeToWidth(width()); + updateHeaderControlsGeometry(width()); }, lifetime()); - - object_ptr( - 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::setupList( - RpWidget *parent, - not_null controller) const { - auto result = object_ptr( - parent, - controller, +void Members::setupList() { + auto topSkip = _header ? _header->height() : 0; + _list = object_ptr( + this, + _listController.get(), st::infoMembersList); - result->scrollToRequests() + _list->scrollToRequests() | rpl::start_with_next([this](Ui::ScrollToRequest request) { - auto addmin = (request.ymin < 0) + auto addmin = (request.ymin < 0 || !_header) ? 0 - : st::infoMembersHeader; - auto addmax = (request.ymax < 0) + : _header->height(); + auto addmax = (request.ymax < 0 || !_header) ? 0 - : st::infoMembersHeader; + : _header->height(); _scrollToRequests.fire({ request.ymin + addmin, request.ymax + addmax }); - }, result->lifetime()); - result->moveToLeft(0, st::infoMembersHeader); - parent->widthValue() - | rpl::start_with_next([list = result.data()](int newWidth) { - list->resizeToWidth(newWidth); - }, result->lifetime()); - result->heightValue() - | rpl::start_with_next([parent](int listHeight) { + }, _list->lifetime()); + widthValue() + | rpl::start_with_next([this](int newWidth) { + _list->resizeToWidth(newWidth); + }, _list->lifetime()); + _list->heightValue() + | rpl::start_with_next([=](int listHeight) { auto newHeight = (listHeight > st::membersMarginBottom) - ? (st::infoMembersHeader + ? (topSkip + listHeight + st::membersMarginBottom) : 0; - parent->resize(parent->width(), newHeight); - }, result->lifetime()); - return result; + resize(width(), newHeight); + }, _list->lifetime()); + _list->moveToLeft(0, topSkip); } 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 - st::infoMembersButtonPosition.x(); - auto cancelLeft = availableWidth - _cancelSearch->width(); - _cancelSearch->moveToLeft( - cancelLeft, - st::infoMembersButtonPosition.y()); + //auto cancelLeft = availableWidth - _cancelSearch->width(); + //_cancelSearch->moveToLeft( + // cancelLeft, + // st::infoMembersButtonPosition.y()); - auto searchShownLeft = st::infoIconPosition.x() - - st::infoMembersSearch.iconPosition.x(); - auto searchHiddenLeft = availableWidth - _search->width(); - auto searchShown = _searchShownAnimation.current(_searchShown ? 1. : 0.); - auto searchCurrentLeft = anim::interpolate( - searchHiddenLeft, - searchShownLeft, - searchShown); - _search->moveToLeft( - searchCurrentLeft, - st::infoMembersButtonPosition.y()); + //auto searchShownLeft = st::infoIconPosition.x() + // - st::infoMembersSearch.iconPosition.x(); + //auto searchHiddenLeft = availableWidth - _search->width(); + //auto searchShown = _searchShownAnimation.current(_searchShown ? 1. : 0.); + //auto searchCurrentLeft = anim::interpolate( + // searchHiddenLeft, + // searchShownLeft, + // searchShown); + //_search->moveToLeft( + // searchCurrentLeft, + // st::infoMembersButtonPosition.y()); - if (!_search->isHidden()) { - availableWidth -= st::infoMembersSearch.width; - } + //if (!_search->isHidden()) { + // availableWidth -= st::infoMembersSearch.width; + //} _addMember->moveToLeft( availableWidth - _addMember->width(), st::infoMembersButtonPosition.y(), newWidth); - auto fieldLeft = anim::interpolate( - cancelLeft, - st::infoBlockHeaderPosition.x(), - searchShown); - _searchField->setGeometryToLeft( - fieldLeft, - st::infoMembersSearchTop, - cancelLeft - fieldLeft, - _searchField->height()); + //auto fieldLeft = anim::interpolate( + // cancelLeft, + // st::infoBlockHeaderPosition.x(), + // searchShown); + //_searchField->setGeometryToLeft( + // fieldLeft, + // st::infoMembersSearchTop, + // cancelLeft - fieldLeft, + // _searchField->height()); - _labelWrap->resize( - searchCurrentLeft - st::infoBlockHeaderPosition.x(), - _label->height()); - _labelWrap->moveToLeft( + //_titleWrap->resize( + // searchCurrentLeft - st::infoBlockHeaderPosition.x(), + // _title->height()); + _titleWrap->resize( + availableWidth - _addMember->width() - st::infoBlockHeaderPosition.x(), + _title->height()); + _titleWrap->moveToLeft( st::infoBlockHeaderPosition.x(), st::infoBlockHeaderPosition.y(), newWidth); + _titleWrap->setAttribute(Qt::WA_TransparentForMouseEvents); - _label->resizeToWidth(searchHiddenLeft); - _label->moveToLeft(0, 0); - - return heightNoMargins(); + //_title->resizeToWidth(searchHiddenLeft); + _title->resizeToWidth(_titleWrap->width()); + _title->moveToLeft(0, 0); } void Members::addMember() { @@ -278,74 +328,63 @@ void Members::addMember() { } } -void Members::showSearch() { - if (!_searchShown) { - toggleSearch(); - } -} - -void Members::toggleSearch(anim::type animated) { - _searchShown = !_searchShown; - if (animated == anim::type::normal) { - _cancelSearch->toggleAnimated(_searchShown); - _searchShownAnimation.start( - [this] { searchAnimationCallback(); }, - _searchShown ? 0. : 1., - _searchShown ? 1. : 0., - st::slideWrapDuration); - } else { - _cancelSearch->toggleFast(_searchShown); - _searchShownAnimation.finish(); - searchAnimationCallback(); - } - _search->setDisabled(_searchShown); - if (_searchShown) { - _searchField->show(); - _searchField->setFocus(); - } else { - setFocus(); - } -} - -void Members::searchAnimationCallback() { - if (!_searchShownAnimation.animating()) { - _searchField->setVisible(_searchShown); - updateSearchOverrides(); - _search->setPointerCursor(!_searchShown); - } - resizeToWidth(width()); -} - -void Members::updateSearchOverrides() { - auto iconOverride = !_searchShown - ? nullptr - : (_wrap == Wrap::Layer) - ? &st::infoMembersSearchActiveLayer - : &st::infoMembersSearchActive; - _search->setIconOverride(iconOverride, iconOverride); -} - -void Members::cancelSearch() { - if (_searchShown) { - if (!_searchField->getLastText().isEmpty()) { - _searchField->setText(QString()); - _searchField->updatePlaceholder(); - _searchField->setFocus(); - applySearch(); - } else { - toggleSearch(); - } - } -} - -void Members::applySearch() { - peerListScrollToTop(); - content()->searchQueryChanged(_searchField->getLastText()); -} - -void Members::forceSearchSubmit() { - content()->submitted(); -} +//void Members::showSearch() { +// if (!_searchShown) { +// toggleSearch(); +// } +//} +// +//void Members::toggleSearch(anim::type animated) { +// _searchShown = !_searchShown; +// if (animated == anim::type::normal) { +// _cancelSearch->toggleAnimated(_searchShown); +// _searchShownAnimation.start( +// [this] { searchAnimationCallback(); }, +// _searchShown ? 0. : 1., +// _searchShown ? 1. : 0., +// st::slideWrapDuration); +// } else { +// _cancelSearch->toggleFast(_searchShown); +// _searchShownAnimation.finish(); +// searchAnimationCallback(); +// } +// _search->setDisabled(_searchShown); +// if (_searchShown) { +// _searchField->show(); +// _searchField->setFocus(); +// } else { +// setFocus(); +// } +//} +// +//void Members::searchAnimationCallback() { +// if (!_searchShownAnimation.animating()) { +// _searchField->setVisible(_searchShown); +// updateSearchOverrides(); +// _search->setPointerCursor(!_searchShown); +// } +// updateHeaderControlsGeometry(width()); +//} +// +//void Members::updateSearchOverrides() { +// auto iconOverride = !_searchShown +// ? nullptr +// : (_wrap == Wrap::Layer) +// ? &st::infoMembersSearchActiveLayer +// : &st::infoMembersSearchActive; +// _search->setIconOverride(iconOverride, iconOverride); +//} +// +//void Members::cancelSearch() { +// if (_searchShown) { +// if (!_searchField->getLastText().isEmpty()) { +// _searchField->setText(QString()); +// _searchField->setFocus(); +// } else { +// toggleSearch(); +// } +// } +//} void Members::visibleTopBottomUpdated( int visibleTop, diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.h b/Telegram/SourceFiles/info/profile/info_profile_members.h index d12b5453b..1047fc5d5 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members.h +++ b/Telegram/SourceFiles/info/profile/info_profile_members.h @@ -24,11 +24,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "boxes/peer_list_box.h" namespace Ui { -class FlatInput; +class InputField; class CrossButton; class IconButton; class FlatLabel; struct ScrollToRequest; +class AbstractButton; } // namespace Ui namespace Profile { @@ -44,6 +45,10 @@ enum class Wrap; namespace Profile { class Memento; +struct MembersState { + std::unique_ptr list; + base::optional search; +}; class Members : public Ui::RpWidget @@ -58,8 +63,8 @@ public: return _scrollToRequests.events(); } - void saveState(not_null memento); - void restoreState(not_null memento); + std::unique_ptr saveState(); + void restoreState(std::unique_ptr state); int desiredHeight() const; rpl::producer onlineCountValue() const; @@ -87,36 +92,54 @@ private: void peerListSetDescription( object_ptr description) override; - object_ptr setupHeader(); - object_ptr setupList( - RpWidget *parent, - not_null controller) const; + void peerListAppendRow( + std::unique_ptr row) override { + PeerListContentDelegate::peerListAppendRow(std::move(row)); + updateSearchEnabledByContent(); + } + void peerListPrependRow( + std::unique_ptr row) override { + PeerListContentDelegate::peerListPrependRow(std::move(row)); + updateSearchEnabledByContent(); + } + void peerListRemoveRow(not_null row) override { + PeerListContentDelegate::peerListRemoveRow(row); + updateSearchEnabledByContent(); + } + + void setupHeader(); + object_ptr setupTitle(); + void setupList(); void setupButtons(); - void updateSearchOverrides(); + //void updateSearchOverrides(); void addMember(); - void showSearch(); - void toggleSearch(anim::type animated = anim::type::normal); - void cancelSearch(); - void applySearch(); - void forceSearchSubmit(); - void searchAnimationCallback(); + //void showSearch(); + //void toggleSearch(anim::type animated = anim::type::normal); + //void cancelSearch(); + //void searchAnimationCallback(); + void updateHeaderControlsGeometry(int newWidth); + void updateSearchEnabledByContent(); Wrap _wrap; + not_null _controller; not_null _peer; std::unique_ptr _listController; - object_ptr _labelWrap; - object_ptr _label; - object_ptr _addMember; - object_ptr _searchField; - object_ptr _search; - object_ptr _cancelSearch; - object_ptr _list; + object_ptr _header = { nullptr }; + object_ptr _list = { nullptr }; - Animation _searchShownAnimation; - bool _searchShown = false; - base::Timer _searchTimer; + Ui::AbstractButton *_openMembers = nullptr; + Ui::RpWidget *_titleWrap = nullptr; + Ui::FlatLabel *_title = nullptr; + Ui::IconButton *_addMember = nullptr; + //base::unique_qptr _searchField; + //Ui::IconButton *_search = nullptr; + //Ui::CrossButton *_cancelSearch = nullptr; + + //Animation _searchShownAnimation; + //bool _searchShown = false; + //base::Timer _searchTimer; rpl::event_stream _scrollToRequests; diff --git a/Telegram/SourceFiles/info/profile/info_profile_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_widget.cpp index 853118482..8fb553ac2 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_widget.cpp @@ -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_inner_widget.h" +#include "info/profile/info_profile_members.h" #include "ui/widgets/scroll_area.h" #include "info/info_controller.h" @@ -48,10 +49,22 @@ object_ptr Memento::createWidget( return std::move(result); } +void Memento::setMembersState(std::unique_ptr state) { + _membersState = std::move(state); +} + +std::unique_ptr Memento::membersState() { + return std::move(_membersState); +} + +Memento::~Memento() = default; + Widget::Widget( QWidget *parent, not_null controller) : ContentWidget(parent, controller) { + controller->setSearchEnabledByContent(false); + _inner = setInnerWidget(object_ptr( this, controller)); @@ -86,7 +99,9 @@ bool Widget::showInternal(not_null memento) { return false; } -void Widget::setInternalState(const QRect &geometry, not_null memento) { +void Widget::setInternalState( + const QRect &geometry, + not_null memento) { setGeometry(geometry); myEnsureResized(this); restoreState(memento); diff --git a/Telegram/SourceFiles/info/profile/info_profile_widget.h b/Telegram/SourceFiles/info/profile/info_profile_widget.h index 4cb56d4a1..b092880cb 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_widget.h +++ b/Telegram/SourceFiles/info/profile/info_profile_widget.h @@ -22,12 +22,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include "info/info_content_widget.h" -#include "boxes/peer_list_box.h" namespace Info { namespace Profile { class InnerWidget; +struct MembersState; class Memento final : public ContentMemento { public: @@ -49,23 +49,15 @@ public: bool infoExpanded() const { return _infoExpanded; } - void setMembersSearch(QString query) { - _membersSearch = query; - } - base::optional membersSearch() const { - return _membersSearch; - } - void setMembersState(std::unique_ptr state) { - _membersState = std::move(state); - } - std::unique_ptr membersState() { - return std::move(_membersState); - } + void setMembersState(std::unique_ptr state); + std::unique_ptr membersState(); + + ~Memento(); private: bool _infoExpanded = true; base::optional _membersSearch; - std::unique_ptr _membersState; + std::unique_ptr _membersState; }; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 6940dc6b7..476bb0ff3 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -248,6 +248,8 @@ <(src_loc)/info/media/info_media_list_widget.h <(src_loc)/info/media/info_media_widget.cpp <(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.h <(src_loc)/info/profile/info_profile_button.cpp