diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index 26417374cf..550e049a8b 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -225,6 +225,10 @@ int ContentWidget::scrollTopSave() const { return _scroll->scrollTop(); } +rpl::producer ContentWidget::scrollTopValue() const { + return _scroll->scrollTopValue(); +} + void ContentWidget::scrollTopRestore(int scrollTop) { _scroll->scrollToY(scrollTop); } diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index cddaef8413..900885d1ea 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -101,6 +101,7 @@ protected: int scrollTopSave() const; void scrollTopRestore(int scrollTop); void scrollTo(const Ui::ScrollToRequest &request); + [[nodiscard]] rpl::producer scrollTopValue() const; private: RpWidget *doSetInnerWidget(object_ptr inner); diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp index 0797a96c2c..727459fb48 100644 --- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp +++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp @@ -44,9 +44,34 @@ Widget::Widget( : ContentWidget(parent, controller) , _self(controller->key().settingsSelf()) , _type(controller->section().settingsType()) -, _inner( - setInnerWidget( - _type()->create(this, controller->parentController()))) +, _inner([&] { + auto inner = _type()->create(this, controller->parentController()); + if (inner->hasFlexibleTopBar()) { + auto filler = setInnerWidget(object_ptr(this)); + filler->resize(1, 1); + + _flexibleScroll.contentHeightValue.events( + ) | rpl::start_with_next([=](int h) { + filler->resize(filler->width(), h); + }, filler->lifetime()); + + filler->widthValue( + ) | rpl::start_to_stream( + _flexibleScroll.fillerWidthValue, + lifetime()); + + // ScrollArea -> PaddingWrap -> RpWidget. + inner->setParent(filler->parentWidget()->parentWidget()); + inner->raise(); + + using InnerPtr = base::unique_qptr<::Settings::AbstractSection>; + auto owner = filler->lifetime().make_state( + std::move(inner.release())); + return owner->get(); + } else { + return setInnerWidget(std::move(inner)); + } +}()) , _pinnedToTop(_inner->createPinnedToTop(this)) , _pinnedToBottom(_inner->createPinnedToBottom(this)) { _inner->sectionShowOther( @@ -103,6 +128,39 @@ Widget::Widget( heightValue() ) | rpl::start_with_next(processHeight, _pinnedToBottom->lifetime()); } + + if (_pinnedToTop + && _pinnedToTop->minimumHeight() + && _inner->hasFlexibleTopBar()) { + const auto heightDiff = [=] { + return _pinnedToTop->maximumHeight() + - _pinnedToTop->minimumHeight(); + }; + + _inner->heightValue( + ) | rpl::start_with_next([=](int h) { + _flexibleScroll.contentHeightValue.fire(h + heightDiff()); + }, _pinnedToTop->lifetime()); + + scrollTopValue( + ) | rpl::start_with_next([=](int top) { + if (!_pinnedToTop) { + return; + } + const auto current = heightDiff() - top; + _inner->moveToLeft(0, std::min(0, current)); + _pinnedToTop->resize( + _pinnedToTop->width(), + std::max(current + _pinnedToTop->minimumHeight(), 0)); + }, _inner->lifetime()); + + _flexibleScroll.fillerWidthValue.events( + ) | rpl::start_with_next([=](int w) { + _inner->resizeToWidth(w); + }, _inner->lifetime()); + + setPaintPadding({ 0, _pinnedToTop->minimumHeight(), 0, 0 }); + } } Widget::~Widget() = default; diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.h b/Telegram/SourceFiles/info/settings/info_settings_widget.h index 5b6290ba86..fd77b4f0bb 100644 --- a/Telegram/SourceFiles/info/settings/info_settings_widget.h +++ b/Telegram/SourceFiles/info/settings/info_settings_widget.h @@ -81,6 +81,10 @@ private: not_null _self; Type _type = Type(); + struct { + rpl::event_stream contentHeightValue; + rpl::event_stream fillerWidthValue; + } _flexibleScroll; not_null<::Settings::AbstractSection*> _inner; QPointer _pinnedToTop; QPointer _pinnedToBottom; diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h index cf50deaa42..48589fafb3 100644 --- a/Telegram/SourceFiles/settings/settings_common.h +++ b/Telegram/SourceFiles/settings/settings_common.h @@ -101,6 +101,9 @@ public: not_null parent) { return nullptr; } + [[nodiscard]] virtual bool hasFlexibleTopBar() const { + return false; + } virtual void setStepDataReference(std::any &data) { } }; diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 4051f15e56..000e7c5ec9 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -44,6 +44,8 @@ public: [[nodiscard]] QPointer createPinnedToBottom( not_null parent) override; + [[nodiscard]] bool hasFlexibleTopBar() const override; + private: void setupContent(); @@ -63,6 +65,10 @@ rpl::producer Premium::title() { return tr::lng_premium_summary_title(); } +bool Premium::hasFlexibleTopBar() const { + return true; +} + void Premium::setupContent() { const auto content = Ui::CreateChild(this);