From 3a622f111ac9c3dfb92ec1ee1d84b5b62b51cf8d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 29 Mar 2025 23:13:41 +0300 Subject: [PATCH] Added initial dummy draft for top bar suggestions in dialogs. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 3 + .../dialogs/dialogs_top_bar_suggestion.cpp | 44 ++++++ .../dialogs/dialogs_top_bar_suggestion.h | 30 ++++ .../SourceFiles/dialogs/dialogs_widget.cpp | 53 ++++++- Telegram/SourceFiles/dialogs/dialogs_widget.h | 7 +- .../ui/dialogs_top_bar_suggestion_content.cpp | 136 ++++++++++++++++++ .../ui/dialogs_top_bar_suggestion_content.h | 51 +++++++ Telegram/cmake/td_ui.cmake | 2 + 9 files changed, 320 insertions(+), 8 deletions(-) create mode 100644 Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp create mode 100644 Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.h create mode 100644 Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.cpp create mode 100644 Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index bb6ebbceac..5b918517c2 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -690,6 +690,8 @@ PRIVATE dialogs/dialogs_search_from_controllers.h dialogs/dialogs_search_tags.cpp dialogs/dialogs_search_tags.h + dialogs/dialogs_top_bar_suggestion.cpp + dialogs/dialogs_top_bar_suggestion.h dialogs/dialogs_widget.cpp dialogs/dialogs_widget.h editor/color_picker.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index bfadf60c39..bc008b1fba 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3863,6 +3863,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_dialogs_skip_archive_in_search" = "Skip results from archive"; "lng_dialogs_show_archive_in_search" = "With results from archive"; +"lng_dialogs_top_bar_suggestions_birthday_title" = "Add your birthday! 🎂"; +"lng_dialogs_top_bar_suggestions_birthday_about" = "Let your contacts know when you’re celebrating."; + "lng_about_random" = "Send a {emoji} emoji to any chat to try your luck."; "lng_about_random_send" = "Send"; diff --git a/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp new file mode 100644 index 0000000000..9f25d7c396 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp @@ -0,0 +1,44 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "dialogs/dialogs_top_bar_suggestion.h" + +#include "dialogs/ui/dialogs_top_bar_suggestion_content.h" +#include "lang/lang_keys.h" +#include "ui/text/text_utilities.h" +#include "ui/wrap/slide_wrap.h" + +namespace Dialogs { + +object_ptr> CreateTopBarSuggestion( + not_null parent, + not_null session) { + const auto content = Ui::CreateChild(parent); + auto result = object_ptr>( + parent, + object_ptr::fromRaw(content)); + const auto wrap = result.data(); + + content->setContent( + tr::lng_dialogs_top_bar_suggestions_birthday_title( + tr::now, + Ui::Text::Bold), + tr::lng_dialogs_top_bar_suggestions_birthday_about( + tr::now, + TextWithEntities::Simple)); + + rpl::combine( + parent->widthValue(), + content->desiredHeightValue() + ) | rpl::start_with_next([=](int width, int height) { + content->resize(width, height); + }, content->lifetime()); + + return result; +} + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.h b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.h new file mode 100644 index 0000000000..631ea2dcad --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.h @@ -0,0 +1,30 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +template +class object_ptr; + +namespace Main { +class Session; +} // namespace Main + +namespace Ui { +class RpWidget; +template +class SlideWrap; +} // namespace Ui + +namespace Dialogs { + +[[nodiscard]] object_ptr> CreateTopBarSuggestion( + not_null parent, + not_null); + +} // namespace Dialogs + diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 14cef4d46a..0049af0aca 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/ui/dialogs_suggestions.h" #include "dialogs/dialogs_inner_widget.h" #include "dialogs/dialogs_search_from_controllers.h" +#include "dialogs/dialogs_top_bar_suggestion.h" #include "dialogs/dialogs_quick_action.h" #include "dialogs/dialogs_key.h" #include "history/history.h" @@ -31,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/elastic_scroll.h" #include "ui/widgets/fields/input_field.h" #include "ui/wrap/fade_wrap.h" +#include "ui/wrap/vertical_layout.h" #include "ui/effects/radial_animation.h" #include "ui/chat/requests_bar.h" #include "ui/chat/group_call_bar.h" @@ -375,20 +377,42 @@ Widget::Widget( _scroll->setOverscrollTypes( _stories ? OverscrollType::Virtual : OverscrollType::Real, OverscrollType::Real); - _inner = _scroll->setOwnedWidget(object_ptr( - this, + const auto innerList = _scroll->setOwnedWidget( + object_ptr(this)); + if (_layout != Layout::Child) { + _topBarSuggestion = innerList->add(CreateTopBarSuggestion( + innerList, + &session())); + rpl::combine( + _topBarSuggestion->entity()->desiredHeightValue(), + _childListShown.value() + ) | rpl::start_with_next([=](int desiredHeight, float64 shown) { + const auto newHeight = desiredHeight * (1. - shown); + _topBarSuggestion->entity()->setMaximumHeight(newHeight); + _topBarSuggestion->entity()->setMinimumWidth(width()); + _topBarSuggestion->entity()->resize(width(), newHeight); + }, _topBarSuggestion->lifetime()); + } + _inner = innerList->add(object_ptr( + innerList, controller, rpl::combine( _childListPeerId.value(), _childListShown.value(), makeChildListShown))); _scroll->heightValue() | rpl::start_with_next([=](int height) { - _inner->setMinimumHeight(height); + innerList->setMinimumHeight(height); + _inner->setMinimumHeight(height + - (_topBarSuggestion ? _topBarSuggestion->height() : 0)); _inner->refresh(); - }, _inner->lifetime()); + }, innerList->lifetime()); + _scroll->widthValue() | rpl::start_with_next([=](int width) { + innerList->resizeToWidth(width); + }, innerList->lifetime()); _scrollToTop->raise(); _lockUnlock->toggle(false, anim::type::instant); + _inner->updated( ) | rpl::start_with_next([=] { listScrollUpdated(); @@ -1022,6 +1046,18 @@ void Widget::updateFrozenAccountBar() { } } +void Widget::updateTopBarSuggestions() { + if (_topBarSuggestion) { + if ((_layout == Layout::Child) + || _openedForum + || _openedFolder) { + _topBarSuggestion->toggle(false, anim::type::instant); + } else { + _topBarSuggestion->toggle(true, anim::type::instant); + } + } +} + void Widget::setupMoreChatsBar() { if (_layout == Layout::Child) { return; @@ -1454,7 +1490,7 @@ void Widget::updateControlsVisibility(bool fast) { _frozenAccountBar->show(); } if (_chatFilters) { - _chatFilters->show(); + _chatFilters->setVisible(!_openedForum); } if (_openedFolder || _openedForum) { _subsectionTopBar->show(); @@ -1772,6 +1808,7 @@ void Widget::changeOpenedFolder(Data::Folder *folder, anim::type animated) { storiesExplicitCollapse(); } updateFrozenAccountBar(); + updateTopBarSuggestions(); }, (folder != nullptr), animated); } @@ -1829,6 +1866,7 @@ void Widget::changeOpenedForum(Data::Forum *forum, anim::type animated) { _inner->changeOpenedForum(forum); storiesToggleExplicitExpand(false); updateFrozenAccountBar(); + updateTopBarSuggestions(); updateStoriesVisibility(); }, (forum != nullptr), animated); } @@ -3402,7 +3440,8 @@ bool Widget::applySearchState(SearchState state) { : nullptr; _searchState = state; if (_chatFilters && queryEmptyChanged) { - _chatFilters->setVisible(_searchState.query.isEmpty()); + _chatFilters->setVisible(_searchState.query.isEmpty() + && !_openedForum); updateControlsGeometry(); } _searchWithPostsPreview = computeSearchWithPostsPreview(); @@ -3829,7 +3868,7 @@ void Widget::updateControlsGeometry() { _chatFilters->move(0, chatFiltersTop); } const auto scrollTop = chatFiltersTop - + ((_chatFilters && _searchState.query.isEmpty()) + + ((_chatFilters && _searchState.query.isEmpty() && !_openedForum) ? (_chatFilters->height() * (1. - narrowRatio)) : 0); const auto scrollHeight = height() - scrollTop - bottomSkip; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index 7d231d3c77..5f2699cb47 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -53,6 +53,8 @@ class JumpDownButton; class ElasticScroll; template class FadeWrapScaled; +template +class SlideWrap; } // namespace Ui namespace Window { @@ -223,6 +225,7 @@ private: void showMainMenu(); void clearSearchCache(bool clearPosts); void setSearchQuery(const QString &query, int cursorPosition = -1); + void updateTopBarSuggestions(); void updateFrozenAccountBar(); void updateControlsVisibility(bool fast = false); void updateLockUnlockVisibility( @@ -313,7 +316,7 @@ private: object_ptr> _chooseFromUser; object_ptr> _jumpToDate; object_ptr _cancelSearch; - object_ptr< Ui::FadeWrapScaled> _lockUnlock; + object_ptr> _lockUnlock; std::unique_ptr _moreChatsBar; @@ -324,6 +327,8 @@ private: base::unique_qptr _chatFilters; + Ui::SlideWrap *_topBarSuggestion = nullptr; + object_ptr _scroll; QPointer _inner; std::unique_ptr _suggestions; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.cpp new file mode 100644 index 0000000000..94e0b2d40f --- /dev/null +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.cpp @@ -0,0 +1,136 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "dialogs/ui/dialogs_top_bar_suggestion_content.h" +#include "styles/style_chat.h" +#include "styles/style_chat_helpers.h" +#include "styles/style_dialogs.h" + +namespace Dialogs { +namespace { +} // namespace + +TopBarSuggestionContent::TopBarSuggestionContent(not_null p) +: Ui::RippleButton(p, st::defaultRippleAnimationBgOver) +, _titleSt(st::semiboldTextStyle) +, _contentTitleSt(st::semiboldTextStyle) +, _contentTextSt(st::defaultTextStyle) { +} + +void TopBarSuggestionContent::draw(QPainter &p) { + const auto kLinesForPhoto = 3; + const auto rightPhotoSize = _titleSt.font->ascent * kLinesForPhoto; + const auto rightPhotoPlaceholder = _titleSt.font->height * kLinesForPhoto; + + const auto r = Ui::RpWidget::rect(); + p.fillRect(r, st::historyPinnedBg); + Ui::RippleButton::paintRipple(p, 0, 0); + const auto leftPadding = st::msgReplyBarSkip + st::msgReplyBarSkip; + const auto rightPadding = st::msgReplyBarSkip; + const auto topPadding = st::msgReplyPadding.top(); + const auto availableWidthNoPhoto = r.width() + - leftPadding + - rightPadding; + const auto availableWidth = availableWidthNoPhoto + - (_rightHide ? _rightHide->width() : 0); + const auto titleRight = leftPadding + + _titleSt.font->spacew * 2; + const auto hasSecondLineTitle = (titleRight + > (availableWidth - _contentTitle.maxWidth())); + p.setPen(st::windowActiveTextFg); + p.setPen(st::windowFg); + { + const auto left = hasSecondLineTitle ? leftPadding : titleRight; + const auto top = hasSecondLineTitle + ? (topPadding + _titleSt.font->height) + : topPadding; + _contentTitle.draw(p, { + .position = QPoint(left, top), + .outerWidth = hasSecondLineTitle + ? availableWidth + : (availableWidth - titleRight), + .availableWidth = availableWidth, + .elisionLines = 1, + }); + } + { + const auto left = leftPadding; + const auto top = hasSecondLineTitle + ? (topPadding + + _titleSt.font->height + + _contentTitleSt.font->height) + : topPadding + _titleSt.font->height; + auto lastContentLineAmount = 0; + const auto lineHeight = _contentTextSt.font->height; + const auto lineLayout = [&](int line) -> Ui::Text::LineGeometry { + line++; + lastContentLineAmount = line; + const auto diff = (st::sponsoredMessageBarMaxHeight) + - line * lineHeight; + if (diff < 3 * lineHeight) { + return { + .width = availableWidthNoPhoto, + .elided = true, + }; + } else if (diff < 2 * lineHeight) { + return {}; + } + line += (hasSecondLineTitle ? 2 : 1) + 1; + return { + .width = (line > kLinesForPhoto) + ? availableWidthNoPhoto + : availableWidth, + }; + }; + _contentText.draw(p, { + .position = QPoint(left, top), + .outerWidth = availableWidth, + .availableWidth = availableWidth, + .geometry = Ui::Text::GeometryDescriptor{ + .layout = std::move(lineLayout), + }, + }); + _lastPaintedContentTop = top; + _lastPaintedContentLineAmount = lastContentLineAmount; + } +} + +void TopBarSuggestionContent::setContent( + TextWithEntities title, + TextWithEntities description) { + _contentTitle.setMarkedText(_contentTitleSt, std::move(title)); + _contentText.setMarkedText(_contentTextSt, std::move(description)); +} + +void TopBarSuggestionContent::paintEvent(QPaintEvent *) { + auto p = QPainter(this); + draw(p); +} + +rpl::producer TopBarSuggestionContent::desiredHeightValue() const { + const auto kLinesForPhoto = 3; + const auto rightPhotoSize = _titleSt.font->ascent * kLinesForPhoto; + const auto rightPhotoPlaceholder = _titleSt.font->height * kLinesForPhoto; + return rpl::combine( + _lastPaintedContentTop.value(), + _lastPaintedContentLineAmount.value() + ) | rpl::distinct_until_changed() | rpl::map([=]( + int lastTop, + int lastLines) { + const auto bottomPadding = st::msgReplyPadding.top(); + const auto desiredHeight = lastTop + + (lastLines * _contentTextSt.font->height) + + bottomPadding; + const auto minHeight = desiredHeight; + return std::clamp( + desiredHeight, + minHeight, + st::sponsoredMessageBarMaxHeight); + }); +} + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.h b/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.h new file mode 100644 index 0000000000..f29bbde95d --- /dev/null +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_top_bar_suggestion_content.h @@ -0,0 +1,51 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/widgets/buttons.h" + +namespace Ui { +class DynamicImage; +class IconButton; +} // namespace Ui + +namespace Dialogs { + +class TopBarSuggestionContent : public Ui::RippleButton { +public: + TopBarSuggestionContent(not_null); + + void setContent( + TextWithEntities title, + TextWithEntities description); + + [[nodiscard]] rpl::producer desiredHeightValue() const override; + +protected: + void paintEvent(QPaintEvent *) override; + +private: + void draw(QPainter &p); + + const style::TextStyle &_titleSt; + const style::TextStyle &_contentTitleSt; + const style::TextStyle &_contentTextSt; + + Ui::Text::String _contentTitle; + Ui::Text::String _contentText; + rpl::variable _lastPaintedContentLineAmount = 0; + rpl::variable _lastPaintedContentTop = 0; + + base::unique_qptr _rightHide; + + std::shared_ptr _rightPhoto; + QImage _rightPhotoImage; + +}; + +} // namespace Dialogs diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index ad9c8519a5..6e04f99630 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -105,6 +105,8 @@ PRIVATE dialogs/ui/dialogs_quick_action.h dialogs/ui/dialogs_stories_list.cpp dialogs/ui/dialogs_stories_list.h + dialogs/ui/dialogs_top_bar_suggestion_content.cpp + dialogs/ui/dialogs_top_bar_suggestion_content.h dialogs/ui/top_peers_strip.cpp dialogs/ui/top_peers_strip.h