diff --git a/Telegram/Resources/icons/chat/large_away.png b/Telegram/Resources/icons/chat/large_away.png new file mode 100644 index 0000000000..b0a943e0c0 Binary files /dev/null and b/Telegram/Resources/icons/chat/large_away.png differ diff --git a/Telegram/Resources/icons/chat/large_away@2x.png b/Telegram/Resources/icons/chat/large_away@2x.png new file mode 100644 index 0000000000..ca2b6adb61 Binary files /dev/null and b/Telegram/Resources/icons/chat/large_away@2x.png differ diff --git a/Telegram/Resources/icons/chat/large_away@3x.png b/Telegram/Resources/icons/chat/large_away@3x.png new file mode 100644 index 0000000000..064090f768 Binary files /dev/null and b/Telegram/Resources/icons/chat/large_away@3x.png differ diff --git a/Telegram/Resources/icons/chat/large_greeting.png b/Telegram/Resources/icons/chat/large_greeting.png new file mode 100644 index 0000000000..0b1cb033e5 Binary files /dev/null and b/Telegram/Resources/icons/chat/large_greeting.png differ diff --git a/Telegram/Resources/icons/chat/large_greeting@2x.png b/Telegram/Resources/icons/chat/large_greeting@2x.png new file mode 100644 index 0000000000..66fd705ad5 Binary files /dev/null and b/Telegram/Resources/icons/chat/large_greeting@2x.png differ diff --git a/Telegram/Resources/icons/chat/large_greeting@3x.png b/Telegram/Resources/icons/chat/large_greeting@3x.png new file mode 100644 index 0000000000..cd08060ff3 Binary files /dev/null and b/Telegram/Resources/icons/chat/large_greeting@3x.png differ diff --git a/Telegram/Resources/icons/chat/large_quickreply.png b/Telegram/Resources/icons/chat/large_quickreply.png new file mode 100644 index 0000000000..084b399ea3 Binary files /dev/null and b/Telegram/Resources/icons/chat/large_quickreply.png differ diff --git a/Telegram/Resources/icons/chat/large_quickreply@2x.png b/Telegram/Resources/icons/chat/large_quickreply@2x.png new file mode 100644 index 0000000000..5ec1ffe4fe Binary files /dev/null and b/Telegram/Resources/icons/chat/large_quickreply@2x.png differ diff --git a/Telegram/Resources/icons/chat/large_quickreply@3x.png b/Telegram/Resources/icons/chat/large_quickreply@3x.png new file mode 100644 index 0000000000..8bc18f9ade Binary files /dev/null and b/Telegram/Resources/icons/chat/large_quickreply@3x.png differ diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp index 8a53dc553e..5bc315aa11 100644 --- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp +++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp @@ -47,7 +47,11 @@ Widget::Widget( auto inner = _type->create( this, controller->parentController(), - scroll()); + scroll(), + controller->wrapValue( + ) | rpl::map([](Wrap wrap) { return (wrap == Wrap::Layer) + ? ::Settings::Container::Layer + : ::Settings::Container::Section; })); if (inner->hasFlexibleTopBar()) { auto filler = setInnerWidget(object_ptr(this)); filler->resize(1, 1); diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp index 431dcd4e10..aac5f918c2 100644 --- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp +++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp @@ -96,9 +96,8 @@ void QuickReplies::setupContent( Box(EditShortcutNameBox, QString(), crl::guard(this, submit))); }); - Ui::AddSkip(content); - Ui::AddDivider(content); - Ui::AddSkip(content); + const auto dividerWrap = content->add( + object_ptr(content)); const auto inner = content->add( object_ptr(content)); @@ -133,6 +132,18 @@ void QuickReplies::setupContent( while (old--) { delete inner->widgetAt(0); } + if (!inner->count()) { + while (dividerWrap->count()) { + delete dividerWrap->widgetAt(0); + } + } else if (!dividerWrap->count()) { + AddSkip(dividerWrap); + AddDivider(dividerWrap); + AddSkip(dividerWrap); + } + if (const auto width = content->width()) { + content->resizeToWidth(width); + } }, content->lifetime()); Ui::ResizeFitChild(this, content); @@ -170,6 +181,7 @@ void EditShortcutNameBox( box->setFocusCallback([=] { field->setFocusFast(); }); + field->selectAll(); const auto callback = [=] { const auto name = field->getLastText().trimmed(); diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index 8df33ebf03..b72acab4b4 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_corner_buttons.h" #include "history/view/history_view_empty_list_bubble.h" #include "history/view/history_view_list_widget.h" +#include "history/view/history_view_service_message.h" #include "history/view/history_view_sticker_toast.h" #include "history/history.h" #include "history/history_item.h" @@ -51,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/widgets/menu/menu_add_action_callback.h" #include "ui/widgets/scroll_area.h" +#include "ui/painter.h" #include "window/themes/window_theme.h" #include "window/section_widget.h" #include "window/window_session_controller.h" @@ -74,6 +76,7 @@ public: QWidget *parent, not_null controller, not_null scroll, + rpl::producer containerValue, BusinessShortcutId shortcutId); ~ShortcutMessages(); @@ -97,7 +100,7 @@ public: QRect clip) override; private: - void outerResized(QSize outer); + void outerResized(); void updateComposeControlsPosition(); // ListDelegate interface. @@ -247,6 +250,7 @@ private: const Window::SectionShow ¶ms); void showAtEnd(); void finishSending(); + void refreshEmptyText(); const not_null _controller; const not_null _session; @@ -254,6 +258,7 @@ private: const not_null _history; rpl::variable _shortcutId; rpl::variable _shortcut; + rpl::variable _container; std::shared_ptr _style; std::shared_ptr _theme; QPointer _inner; @@ -262,6 +267,14 @@ private: rpl::event_stream<> _showBackRequests; bool _skipScrollEvent = false; + QSize _inOuterResize; + QSize _pendingOuterResize; + + const style::icon *_emptyIcon = nullptr; + Ui::Text::String _emptyText; + int _emptyTextWidth = 0; + int _emptyTextHeight = 0; + rpl::variable _selectedItems = Info::SelectedItems(Storage::SharedMediaType::kCount); @@ -283,22 +296,33 @@ struct Factory final : AbstractSectionFactory { object_ptr create( not_null parent, not_null controller, - not_null scroll + not_null scroll, + rpl::producer containerValue ) const final override { return object_ptr( parent, controller, scroll, + std::move(containerValue), shortcutId); } const BusinessShortcutId shortcutId = {}; }; +[[nodiscard]] bool IsAway(const QString &shortcut) { + return (shortcut == u"away"_q); +} + +[[nodiscard]] bool IsGreeting(const QString &shortcut) { + return (shortcut == u"hello"_q); +} + ShortcutMessages::ShortcutMessages( QWidget *parent, not_null controller, not_null scroll, + rpl::producer containerValue, BusinessShortcutId shortcutId) : AbstractSection(parent) , _controller(controller) @@ -308,6 +332,7 @@ ShortcutMessages::ShortcutMessages( , _shortcutId(shortcutId) , _shortcut( _session->data().shortcutMessages().lookupShortcut(shortcutId).name) +, _container(std::move(containerValue)) , _cornerButtons( _scroll, controller->chatStyle(), @@ -345,8 +370,8 @@ ShortcutMessages::ShortcutMessages( _scroll->sizeValue() | rpl::filter([](QSize size) { return !size.isEmpty(); - }) | rpl::start_with_next([=](QSize size) { - outerResized(size); + }) | rpl::start_with_next([=] { + outerResized(); }, lifetime()); _scroll->scrolls( @@ -354,6 +379,11 @@ ShortcutMessages::ShortcutMessages( processScroll(); }, lifetime()); + _shortcut.value() | rpl::start_with_next([=] { + refreshEmptyText(); + _inner->update(); + }, lifetime()); + _inner->editMessageRequested( ) | rpl::start_with_next([=](auto fullId) { if (const auto item = _session->data().message(fullId)) { @@ -364,17 +394,6 @@ ShortcutMessages::ShortcutMessages( } }, _inner->lifetime()); - { - auto emptyInfo = base::make_unique_q( - _inner, - _style.get(), - st::msgServicePadding); - const auto emptyText = Ui::Text::Semibold( - u"give me your money.."_q); - emptyInfo->setText(emptyText); - _inner->setEmptyInfoWidget(std::move(emptyInfo)); - } - _inner->heightValue() | rpl::start_with_next([=](int height) { resize(width(), height); }, lifetime()); @@ -382,15 +401,62 @@ ShortcutMessages::ShortcutMessages( ShortcutMessages::~ShortcutMessages() = default; +void ShortcutMessages::refreshEmptyText() { + const auto &shortcut = _shortcut.current(); + const auto away = IsAway(shortcut); + const auto greeting = !away && IsGreeting(shortcut); + auto text = away + ? tr::lng_away_empty_title( + tr::now, + Ui::Text::Bold + ).append("\n\n").append(tr::lng_away_empty_about(tr::now)) + : greeting + ? tr::lng_greeting_empty_title( + tr::now, + Ui::Text::Bold + ).append("\n\n").append(tr::lng_greeting_empty_about(tr::now)) + : tr::lng_replies_empty_title( + tr::now, + Ui::Text::Bold + ).append("\n\n").append(tr::lng_replies_empty_about( + tr::now, + lt_shortcut, + Ui::Text::Bold('/' + shortcut), + Ui::Text::WithEntities)); + _emptyIcon = away + ? &st::awayEmptyIcon + : greeting + ? &st::greetingEmptyIcon + : &st::repliesEmptyIcon; + const auto padding = st::repliesEmptyPadding; + const auto minWidth = st::repliesEmptyWidth / 4; + const auto maxWidth = std::max( + minWidth + 1, + st::repliesEmptyWidth - padding.left() - padding.right()); + _emptyText = Ui::Text::String( + st::messageTextStyle, + text, + kMarkupTextOptions, + minWidth); + const auto countHeight = [&](int width) { + return _emptyText.countHeight(width); + }; + _emptyTextWidth = Ui::FindNiceTooltipWidth( + minWidth, + maxWidth, + countHeight); + _emptyTextHeight = countHeight(_emptyTextWidth); +} + Type ShortcutMessages::Id(BusinessShortcutId shortcutId) { return std::make_shared(shortcutId); } rpl::producer ShortcutMessages::title() { return _shortcut.value() | rpl::map([=](const QString &shortcut) { - return (shortcut == u"away"_q) + return IsAway(shortcut) ? tr::lng_away_title() - : (shortcut == u"hello"_q) + : IsGreeting(shortcut) ? tr::lng_greeting_title() : rpl::single('/' + shortcut); }) | rpl::flatten_latest(); @@ -497,22 +563,36 @@ bool ShortcutMessages::paintOuter( return true; } -void ShortcutMessages::outerResized(QSize outer) { - const auto contentWidth = outer.width(); +void ShortcutMessages::outerResized() { + const auto outer = _scroll->size(); + if (!_inOuterResize.isEmpty()) { + _pendingOuterResize = (_inOuterResize != outer) + ? outer + : QSize(); + return; + } + _inOuterResize = outer; - const auto newScrollTop = _scroll->isHidden() - ? std::nullopt - : _scroll->scrollTop() - ? base::make_optional(_scroll->scrollTop()) - : 0; - _skipScrollEvent = true; - _inner->resizeToWidth(contentWidth, st::boxWidth); - _skipScrollEvent = false; + do { + const auto newScrollTop = _scroll->isHidden() + ? std::nullopt + : _scroll->scrollTop() + ? base::make_optional(_scroll->scrollTop()) + : 0; + _skipScrollEvent = true; + const auto minHeight = (_container.current() == Container::Layer) + ? st::boxWidth + : _inOuterResize.height(); + _inner->resizeToWidth(_inOuterResize.width(), minHeight); + _skipScrollEvent = false; - if (!_scroll->isHidden()) { - if (newScrollTop) { + if (!_scroll->isHidden() && newScrollTop) { _scroll->scrollToY(*newScrollTop); } + _inOuterResize = base::take(_pendingOuterResize); + } while (!_inOuterResize.isEmpty()); + + if (!_scroll->isHidden()) { updateInnerVisibleArea(); } updateComposeControlsPosition(); @@ -896,8 +976,36 @@ void ShortcutMessages::listOpenDocument( } void ShortcutMessages::listPaintEmpty( - Painter &p, - const Ui::ChatPaintContext &context) { + Painter &p, + const Ui::ChatPaintContext &context) { + Expects(_emptyIcon != nullptr); + + const auto width = st::repliesEmptyWidth; + const auto padding = st::repliesEmptyPadding; + const auto height = padding.top() + + _emptyIcon->height() + + st::repliesEmptySkip + + _emptyTextHeight + + padding.bottom(); + const auto r = QRect( + (this->width() - width) / 2, + (this->height() - height) / 3, + width, + height); + HistoryView::ServiceMessagePainter::PaintBubble(p, context.st, r); + + _emptyIcon->paint( + p, + r.x() + (r.width() - _emptyIcon->width()) / 2, + r.y() + padding.top(), + this->width()); + p.setPen(st::msgServiceFg); + _emptyText.draw( + p, + r.x() + (r.width() - _emptyTextWidth) / 2, + r.y() + padding.top() + _emptyIcon->height() + st::repliesEmptySkip, + _emptyTextWidth, + style::al_top); } QString ShortcutMessages::listElementAuthorRank( diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp index 0881925b57..a8792783c5 100644 --- a/Telegram/SourceFiles/settings/settings_business.cpp +++ b/Telegram/SourceFiles/settings/settings_business.cpp @@ -562,7 +562,8 @@ struct SectionFactory : AbstractSectionFactory { object_ptr create( not_null parent, not_null controller, - not_null scroll + not_null scroll, + rpl::producer containerValue ) const final override { return object_ptr(parent, controller); } diff --git a/Telegram/SourceFiles/settings/settings_common_session.h b/Telegram/SourceFiles/settings/settings_common_session.h index 2ebd1319c5..2b439bacf3 100644 --- a/Telegram/SourceFiles/settings/settings_common_session.h +++ b/Telegram/SourceFiles/settings/settings_common_session.h @@ -26,13 +26,19 @@ class SessionController; namespace Settings { +enum class Container { + Section, + Layer, +}; + class AbstractSection; struct AbstractSectionFactory { [[nodiscard]] virtual object_ptr create( not_null parent, not_null controller, - not_null scroll) const = 0; + not_null scroll, + rpl::producer containerValue) const = 0; [[nodiscard]] virtual bool hasCustomTopBar() const { return false; } @@ -45,7 +51,8 @@ struct SectionFactory : AbstractSectionFactory { object_ptr create( not_null parent, not_null controller, - not_null scroll + not_null scroll, + rpl::producer containerValue ) const final override { return object_ptr(parent, controller); } diff --git a/Telegram/SourceFiles/settings/settings_notifications_type.cpp b/Telegram/SourceFiles/settings/settings_notifications_type.cpp index 717627573e..802a82ee6f 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_type.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications_type.cpp @@ -44,7 +44,8 @@ struct Factory : AbstractSectionFactory { object_ptr create( not_null parent, not_null controller, - not_null scroll + not_null scroll, + rpl::producer containerValue ) const final override { return object_ptr(parent, controller, type); } diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index ec17c8aaca..809b8ba8ee 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -1268,7 +1268,8 @@ struct SectionFactory : AbstractSectionFactory { object_ptr create( not_null parent, not_null controller, - not_null scroll + not_null scroll, + rpl::producer containerValue ) const final override { return object_ptr(parent, controller); } diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 4ec9954a5e..b4217d85ba 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1044,6 +1044,13 @@ premiumRequiredWidth: 186px; premiumRequiredIcon: icon{{ "chat/large_lockedchat", msgServiceFg }}; premiumRequiredCircle: 60px; +repliesEmptyIcon: icon{{ "chat/large_quickreply", msgServiceFg }}; +greetingEmptyIcon: icon{{ "chat/large_greeting", msgServiceFg }}; +awayEmptyIcon: icon{{ "chat/large_away", msgServiceFg }}; +repliesEmptyWidth: 264px; +repliesEmptySkip: 16px; +repliesEmptyPadding: margins(10px, 20px, 10px, 16px); + boostMessageIcon: icon {{ "stories/boost_mini", windowFg }}; boostMessageIconPadding: margins(0px, 2px, 0px, 0px); boostsMessageIcon: icon {{ "stories/boosts_mini", windowFg }};