From 8545a14763040d183eb934b371dabf2101bbb88c Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 29 Feb 2024 14:41:17 +0400 Subject: [PATCH] Fix shortcut messages sizing / emoji panel. --- Telegram/SourceFiles/api/api_editing.cpp | 5 +- .../SourceFiles/boxes/edit_caption_box.cpp | 1 + .../chat_helpers/compose/compose_features.h | 1 + .../data/business/data_shortcut_messages.cpp | 3 +- Telegram/SourceFiles/data/data_drafts.h | 13 +++ Telegram/SourceFiles/dialogs/dialogs_key.h | 1 + Telegram/SourceFiles/history/history_item.cpp | 2 +- .../history_view_compose_controls.cpp | 45 +++++--- .../controls/history_view_compose_controls.h | 8 ++ .../history/view/history_view_bottom_info.cpp | 2 +- .../view/history_view_context_menu.cpp | 4 +- .../history/view/history_view_list_widget.cpp | 27 ++++- .../history/view/history_view_list_widget.h | 9 ++ .../SourceFiles/info/info_content_widget.cpp | 12 ++- .../SourceFiles/info/info_layer_widget.cpp | 100 ++++++++++++------ Telegram/SourceFiles/info/info_layer_widget.h | 4 +- .../SourceFiles/info/info_section_widget.cpp | 2 +- .../info/settings/info_settings_widget.cpp | 8 +- .../business/settings_shortcut_messages.cpp | 86 +++++++++++---- .../SourceFiles/storage/localimageloader.cpp | 1 + Telegram/SourceFiles/ui/chat/chat.style | 1 + 21 files changed, 253 insertions(+), 82 deletions(-) diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp index a1e168adf..84f0cbfff 100644 --- a/Telegram/SourceFiles/api/api_editing.cpp +++ b/Telegram/SourceFiles/api/api_editing.cpp @@ -89,6 +89,9 @@ mtpRequestId EditMessage( : emptyFlag) | (options.scheduled ? MTPmessages_EditMessage::Flag::f_schedule_date + : emptyFlag) + | (item->isBusinessShortcut() + ? MTPmessages_EditMessage::Flag::f_quick_reply_shortcut_id : emptyFlag); const auto id = item->isScheduled() @@ -105,7 +108,7 @@ mtpRequestId EditMessage( MTPReplyMarkup(), sentEntities, MTP_int(options.scheduled), - MTPint() // quick_reply_shortcut_id + MTP_int(item->shortcutId()) )).done([=]( const MTPUpdates &result, [[maybe_unused]] mtpRequestId requestId) { diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 8e94beeb9..0c4b212c5 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -895,6 +895,7 @@ void EditCaptionBox::save() { auto options = Api::SendOptions(); options.scheduled = item->isScheduled() ? item->date() : 0; + options.shortcutId = item->shortcutId(); if (!_preparedList.files.empty()) { if ((_albumType != Ui::AlbumType::None) diff --git a/Telegram/SourceFiles/chat_helpers/compose/compose_features.h b/Telegram/SourceFiles/chat_helpers/compose/compose_features.h index ba6f43b4e..5466b34e9 100644 --- a/Telegram/SourceFiles/chat_helpers/compose/compose_features.h +++ b/Telegram/SourceFiles/chat_helpers/compose/compose_features.h @@ -23,6 +23,7 @@ struct ComposeFeatures { bool autocompleteHashtags = true; bool autocompleteMentions = true; bool autocompleteCommands = true; + bool commonTabbedPanel = true; }; } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp index d9d75ec52..e7b4a65a7 100644 --- a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp +++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp @@ -60,7 +60,8 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000); MTP_int(data.vttl_period().value_or_empty())); }, [&](const MTPDmessage &data) { return MTP_message( - MTP_flags(data.vflags().v | MTPDmessage::Flag::f_quick_reply_shortcut_id), + MTP_flags(data.vflags().v + | MTPDmessage::Flag::f_quick_reply_shortcut_id), data.vid(), data.vfrom_id() ? *data.vfrom_id() : MTPPeer(), MTPint(), // from_boosts_applied diff --git a/Telegram/SourceFiles/data/data_drafts.h b/Telegram/SourceFiles/data/data_drafts.h index 3f5e22a23..4fdd9159c 100644 --- a/Telegram/SourceFiles/data/data_drafts.h +++ b/Telegram/SourceFiles/data/data_drafts.h @@ -96,6 +96,18 @@ public: [[nodiscard]] static constexpr DraftKey ScheduledEdit() { return kScheduledDraftIndex + kEditDraftShift; } + [[nodiscard]] static constexpr DraftKey Shortcut( + BusinessShortcutId shortcutId) { + return (shortcutId < 0 || shortcutId >= ServerMaxMsgId) + ? None() + : (kShortcutDraftShift + shortcutId); + } + [[nodiscard]] static constexpr DraftKey ShortcutEdit( + BusinessShortcutId shortcutId) { + return (shortcutId < 0 || shortcutId >= ServerMaxMsgId) + ? None() + : (kShortcutDraftShift + kEditDraftShift + shortcutId); + } [[nodiscard]] static constexpr DraftKey FromSerialized(qint64 value) { return value; @@ -156,6 +168,7 @@ private: static constexpr auto kScheduledDraftIndex = -3; static constexpr auto kEditDraftShift = ServerMaxMsgId.bare; static constexpr auto kCloudDraftShift = 2 * ServerMaxMsgId.bare; + static constexpr auto kShortcutDraftShift = 3 * ServerMaxMsgId.bare; static constexpr auto kEditDraftShiftOld = 0x3FFF'FFFF; int64 _value = 0; diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.h b/Telegram/SourceFiles/dialogs/dialogs_key.h index 2396b216f..c43dc9f15 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.h +++ b/Telegram/SourceFiles/dialogs/dialogs_key.h @@ -108,6 +108,7 @@ struct EntryState { Replies, SavedSublist, ContextMenu, + ShortcutMessages, }; Key key; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 807563fc0..b55baa6d2 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -2108,7 +2108,7 @@ bool HistoryItem::allowsEdit(TimeId now) const { } bool HistoryItem::canBeEdited() const { - if ((!isRegular() && !isScheduled()) + if ((!isRegular() && !isScheduled() && !isBusinessShortcut()) || Has() || Has()) { return false; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 69f0f2de8..9cfec19f5 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -114,6 +114,9 @@ using ForwardPanel = Controls::ForwardPanel; } // namespace +const ChatHelpers::PauseReason kDefaultPanelsLevel + = ChatHelpers::PauseReason::TabbedPanel; + class FieldHeader final : public Ui::RpWidget { public: FieldHeader( @@ -760,7 +763,10 @@ MessageToEdit FieldHeader::queryToEdit() { } return { .fullId = item->fullId(), - .options = { .scheduled = item->isScheduled() ? item->date() : 0 }, + .options = { + .scheduled = item->isScheduled() ? item->date() : 0, + .shortcutId = item->shortcutId(), + }, }; } @@ -788,21 +794,24 @@ ComposeControls::ComposeControls( : st::defaultComposeControls) , _features(descriptor.features) , _parent(parent) +, _panelsParent(descriptor.panelsParent + ? descriptor.panelsParent + : _parent.get()) , _show(std::move(descriptor.show)) , _session(&_show->session()) , _regularWindow(descriptor.regularWindow) -, _ownedSelector(_regularWindow +, _ownedSelector((_regularWindow && _features.commonTabbedPanel) ? nullptr : std::make_unique( - _parent, + _panelsParent, ChatHelpers::TabbedSelectorDescriptor{ .show = _show, .st = _st.tabbed, - .level = Window::GifPauseReason::TabbedPanel, + .level = descriptor.panelsLevel, .mode = ChatHelpers::TabbedSelector::Mode::Full, .features = _features, })) -, _selector(_regularWindow +, _selector((_regularWindow && _features.commonTabbedPanel) ? _regularWindow->tabbedSelector() : not_null(_ownedSelector.get())) , _mode(descriptor.mode) @@ -876,6 +885,12 @@ void ComposeControls::updateTopicRootId(MsgId topicRootId) { _header->updateTopicRootId(_topicRootId); } +void ComposeControls::updateShortcutId(BusinessShortcutId shortcutId) { + unregisterDraftSources(); + _shortcutId = shortcutId; + registerDraftSource(); +} + void ComposeControls::setHistory(SetHistoryArgs &&args) { _showSlowmodeError = std::move(args.showSlowmodeError); _sendActionFactory = std::move(args.sendActionFactory); @@ -1592,7 +1607,7 @@ void ComposeControls::initField() { && Data::AllowEmojiWithoutPremium(_history->peer, emoji); }; const auto suggestions = Ui::Emoji::SuggestionsController::Init( - _parent, + _panelsParent, _field, _session, { @@ -1828,6 +1843,10 @@ Data::DraftKey ComposeControls::draftKey(DraftType type) const { return (type == DraftType::Edit) ? Key::ScheduledEdit() : Key::Scheduled(); + case Section::ShortcutMessages: + return (type == DraftType::Edit) + ? Key::ShortcutEdit(_shortcutId) + : Key::Shortcut(_shortcutId); } return Key::None(); } @@ -2053,7 +2072,9 @@ rpl::producer ComposeControls::sendActionUpdates() const { } void ComposeControls::initTabbedSelector() { - if (!_regularWindow || _regularWindow->hasTabbedSelectorOwnership()) { + if (!_regularWindow + || !_features.commonTabbedPanel + || _regularWindow->hasTabbedSelectorOwnership()) { createTabbedPanel(); } else { setTabbedPanel(nullptr); @@ -2686,7 +2707,7 @@ void ComposeControls::updateAttachBotsMenu() { return; } _attachBotsMenu = InlineBots::MakeAttachBotsMenu( - _parent, + _panelsParent, _regularWindow, _history->peer, _sendActionFactory, @@ -2729,7 +2750,7 @@ void ComposeControls::escape() { bool ComposeControls::pushTabbedSelectorToThirdSection( not_null thread, const Window::SectionShow ¶ms) { - if (!_tabbedPanel || !_regularWindow) { + if (!_tabbedPanel || !_regularWindow || !_features.commonTabbedPanel) { return true; //} else if (!_canSendMessages) { // Core::App().settings().setTabbedReplacedWithInfo(true); @@ -2764,7 +2785,7 @@ void ComposeControls::createTabbedPanel() { .nonOwnedSelector = _ownedSelector ? nullptr : _selector.get(), }; setTabbedPanel(std::make_unique( - _parent, + _panelsParent, std::move(descriptor))); _tabbedPanel->setDesiredHeightValues( st::emojiPanHeightRatio, @@ -2787,7 +2808,7 @@ void ComposeControls::setTabbedPanel( } void ComposeControls::toggleTabbedSelectorMode() { - if (!_history || !_regularWindow) { + if (!_history || !_regularWindow || !_features.commonTabbedPanel) { return; } if (_tabbedPanel) { @@ -3248,7 +3269,7 @@ void ComposeControls::applyInlineBotQuery( } if (!_inlineResults) { _inlineResults = std::make_unique( - _parent, + _panelsParent, _regularWindow); _inlineResults->setResultSelectedCallback([=]( InlineBots::ResultSelected result) { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index d5985d6cb..d21ea9884 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -37,6 +37,7 @@ class TabbedSelector; struct FileChosen; struct PhotoChosen; class Show; +enum class PauseReason; } // namespace ChatHelpers namespace Data { @@ -95,6 +96,8 @@ enum class ComposeControlsMode { Scheduled, }; +extern const ChatHelpers::PauseReason kDefaultPanelsLevel; + struct ComposeControlsDescriptor { const style::ComposeControls *stOverride = nullptr; std::shared_ptr show; @@ -104,6 +107,8 @@ struct ComposeControlsDescriptor { Window::SessionController *regularWindow = nullptr; rpl::producer stickerOrEmojiChosen; rpl::producer customPlaceholder; + QWidget *panelsParent = nullptr; + ChatHelpers::PauseReason panelsLevel = kDefaultPanelsLevel; QString voiceCustomCancelText; bool voiceLockFromBottom = false; ChatHelpers::ComposeFeatures features; @@ -137,6 +142,7 @@ public: [[nodiscard]] Main::Session &session() const; void setHistory(SetHistoryArgs &&args); void updateTopicRootId(MsgId topicRootId); + void updateShortcutId(BusinessShortcutId shortcutId); void setCurrentDialogsEntryState(Dialogs::EntryState state); [[nodiscard]] PeerData *sendAsPeer() const; @@ -343,6 +349,7 @@ private: const style::ComposeControls &_st; const ChatHelpers::ComposeFeatures _features; const not_null _parent; + const not_null _panelsParent; const std::shared_ptr _show; const not_null _session; @@ -353,6 +360,7 @@ private: History *_history = nullptr; MsgId _topicRootId = 0; + BusinessShortcutId _shortcutId = 0; Fn _showSlowmodeError; Fn _sendActionFactory; rpl::variable _slowmodeSecondsLeft; diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp index cfb31eabe..a13eb3fe5 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp @@ -551,7 +551,7 @@ void BottomInfo::layoutReactionsText() { QSize BottomInfo::countOptimalSize() { if (_data.flags & Data::Flag::Shortcut) { - return { st::historySendStateSpace / 2, st::msgDateFont->height }; + return { st::historyShortcutStateSpace, st::msgDateFont->height }; } auto width = 0; if (_data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) { diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 9267aeb5c..09c3b89f9 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -102,7 +102,9 @@ bool HasEditMessageAction( || item->hasFailed() || item->isEditingMedia() || !request.selectedItems.empty() - || (context != Context::History && context != Context::Replies)) { + || (context != Context::History + && context != Context::Replies + && context != Context::ShortcutMessages)) { return false; } const auto peer = item->history()->peer; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 31e6b4e9e..c37342fa2 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -2166,6 +2166,21 @@ void ListWidget::paintEvent(QPaintEvent *e) { context.translate(0, top); p.translate(0, -top); + paintUserpics(p, context, clip); + paintDates(p, context, clip); + + _reactionsManager->paint(p, context); + _emojiInteractions->paint(p); +} + +void ListWidget::paintUserpics( + Painter &p, + const Ui::ChatPaintContext &context, + QRect clip) { + if (_context == Context::ShortcutMessages) { + return; + } + const auto session = &controller()->session(); enumerateUserpics([&](not_null view, int userpicTop) { // stop the enumeration if the userpic is below the painted rect if (userpicTop >= clip.top() + clip.height()) { @@ -2210,6 +2225,15 @@ void ListWidget::paintEvent(QPaintEvent *e) { } return true; }); +} + +void ListWidget::paintDates( + Painter &p, + const Ui::ChatPaintContext &context, + QRect clip) { + if (_context == Context::ShortcutMessages) { + return; + } auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.); @@ -2256,9 +2280,6 @@ void ListWidget::paintEvent(QPaintEvent *e) { } return true; }); - - _reactionsManager->paint(p, context); - _emojiInteractions->paint(p); } void ListWidget::maybeMarkReactionsRead(not_null item) { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 2e2fdbae7..c15a041b8 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -600,6 +600,15 @@ private: void showPremiumStickerTooltip( not_null view); + void paintUserpics( + Painter &p, + const Ui::ChatPaintContext &context, + QRect clip); + void paintDates( + Painter &p, + const Ui::ChatPaintContext &context, + QRect clip); + // This function finds all history items that are displayed and calls template method // for each found message (in given direction) in the passed history with passed top offset. // diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index adf4f22d2..440c125b2 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -167,7 +167,12 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget( const auto bottom = top + height; _innerDesiredHeight = desired; _innerWrap->setVisibleTopBottom(top, bottom); + LOG(("TOP: %1, HEIGHT: %2, DESIRED: %3, TILL: %4").arg(top).arg(height).arg(desired).arg(std::max(desired - bottom, 0))); _scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0)); + //const auto bottom = _scroll->scrollTop() + _scroll->height(); + //_innerDesiredHeight = desired; + //_innerWrap->setVisibleTopBottom(_scroll->scrollTop(), bottom); + //_scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0)); }, _innerWrap->lifetime()); return _innerWrap->entity(); @@ -217,7 +222,12 @@ rpl::producer ContentWidget::desiredHeightValue() const { _innerWrap->entity()->desiredHeightValue(), _scrollTopSkip.value(), _scrollBottomSkip.value() - ) | rpl::map(_1 + _2 + _3); + //) | rpl::map(_1 + _2 + _3); + ) | rpl::map([=](int desired, int, int) { + return desired + + _scrollTopSkip.current() + + _scrollBottomSkip.current(); + }); } rpl::producer ContentWidget::desiredShadowVisibility() const { diff --git a/Telegram/SourceFiles/info/info_layer_widget.cpp b/Telegram/SourceFiles/info/info_layer_widget.cpp index b4fdd2414..50eca4456 100644 --- a/Telegram/SourceFiles/info/info_layer_widget.cpp +++ b/Telegram/SourceFiles/info/info_layer_widget.cpp @@ -30,7 +30,7 @@ LayerWidget::LayerWidget( not_null controller, not_null memento) : _controller(controller) -, _content(this, controller, Wrap::Layer, memento) { +, _contentWrap(this, controller, Wrap::Layer, memento) { setupHeightConsumers(); controller->window().replaceFloatPlayerDelegate(floatPlayerDelegate()); } @@ -39,7 +39,7 @@ LayerWidget::LayerWidget( not_null controller, not_null memento) : _controller(controller) -, _content(memento->takeContent(this, Wrap::Layer)) { +, _contentWrap(memento->takeContent(this, Wrap::Layer)) { setupHeightConsumers(); controller->window().replaceFloatPlayerDelegate(floatPlayerDelegate()); } @@ -64,17 +64,17 @@ void LayerWidget::floatPlayerToggleGifsPaused(bool paused) { auto LayerWidget::floatPlayerGetSection(Window::Column column) -> not_null<::Media::Player::FloatSectionDelegate*> { - Expects(_content != nullptr); + Expects(_contentWrap != nullptr); - return _content; + return _contentWrap; } void LayerWidget::floatPlayerEnumerateSections(Fn widget, Window::Column widgetColumn)> callback) { - Expects(_content != nullptr); + Expects(_contentWrap != nullptr); - callback(_content, Window::Column::Second); + callback(_contentWrap, Window::Column::Second); } bool LayerWidget::floatPlayerIsVisible(not_null item) { @@ -87,9 +87,9 @@ void LayerWidget::floatPlayerDoubleClickEvent( } void LayerWidget::setupHeightConsumers() { - Expects(_content != nullptr); + Expects(_contentWrap != nullptr); - _content->scrollTillBottomChanges( + _contentWrap->scrollTillBottomChanges( ) | rpl::filter([this] { if (!_inResize) { return true; @@ -100,10 +100,10 @@ void LayerWidget::setupHeightConsumers() { resizeToWidth(width()); }, lifetime()); - _content->grabbingForExpanding( + _contentWrap->grabbingForExpanding( ) | rpl::start_with_next([=](bool grabbing) { if (grabbing) { - _savedHeight = _contentHeight; + _savedHeight = _contentWrapHeight; _savedHeightAnimation = base::take(_heightAnimation); setContentHeight(_desiredHeight); } else { @@ -112,7 +112,7 @@ void LayerWidget::setupHeightConsumers() { } }, lifetime()); - _content->desiredHeightValue( + _contentWrap->desiredHeightValue( ) | rpl::start_with_next([this](int height) { if (!height) { // New content arrived. @@ -128,32 +128,32 @@ void LayerWidget::setupHeightConsumers() { _heightAnimated = true; _heightAnimation.start([=] { setContentHeight(_heightAnimation.value(_desiredHeight)); - }, _contentHeight, _desiredHeight, st::slideDuration); + }, _contentWrapHeight, _desiredHeight, st::slideDuration); resizeToWidth(width()); } }, lifetime()); } void LayerWidget::setContentHeight(int height) { - if (_contentHeight == height) { + if (_contentWrapHeight == height) { return; } - - _contentHeight = height; + LOG(("CONTENT WRAP HEIGHT: %1 -> %2").arg(_contentWrapHeight).arg(height)); + _contentWrapHeight = height; if (_inResize) { _pendingResize = true; - } else if (_content) { + } else if (_contentWrap) { resizeToWidth(width()); } } void LayerWidget::showFinished() { floatPlayerShowVisible(); - _content->showFast(); + _contentWrap->showFast(); } void LayerWidget::parentResized() { - if (!_content) { + if (!_contentWrap) { return; } @@ -163,7 +163,7 @@ void LayerWidget::parentResized() { Ui::FocusPersister persister(this); restoreFloatPlayerDelegate(); - auto memento = std::make_shared(std::move(_content)); + auto memento = std::make_shared(std::move(_contentWrap)); // We want to call hideSpecialLayer synchronously to avoid glitches, // but we can't destroy LayerStackWidget from its' resizeEvent, @@ -209,7 +209,7 @@ bool LayerWidget::takeToThirdSection() { // //Ui::FocusPersister persister(this); //auto localCopy = _controller; - //auto memento = MoveMemento(std::move(_content)); + //auto memento = MoveMemento(std::move(_contentWrap)); //localCopy->hideSpecialLayer(anim::type::instant); //// When creating third section in response to the window @@ -235,7 +235,7 @@ bool LayerWidget::takeToThirdSection() { bool LayerWidget::showSectionInternal( not_null memento, const Window::SectionShow ¶ms) { - if (_content && _content->showInternal(memento, params)) { + if (_contentWrap && _contentWrap->showInternal(memento, params)) { if (params.activation != anim::activation::background) { _controller->parentController()->hideLayer(); } @@ -245,7 +245,7 @@ bool LayerWidget::showSectionInternal( } bool LayerWidget::closeByOutsideClick() const { - return _content ? _content->closeByOutsideClick() : true; + return _contentWrap ? _contentWrap->closeByOutsideClick() : true; } int LayerWidget::MinimalSupportedWidth() { @@ -254,19 +254,48 @@ int LayerWidget::MinimalSupportedWidth() { } int LayerWidget::resizeGetHeight(int newWidth) { - if (!parentWidget() || !_content || !newWidth) { + if (!parentWidget() || !_contentWrap || !newWidth) { return 0; } constexpr auto kMaxAttempts = 16; auto attempts = 0; while (true) { _inResize = true; + { + const auto &parentSize = parentWidget()->size(); + const auto windowWidth = parentSize.width(); + const auto windowHeight = parentSize.height(); + const auto newLeft = (windowWidth - newWidth) / 2; + const auto newTop = std::clamp( + windowHeight / 24, + st::infoLayerTopMinimal, + st::infoLayerTopMaximal); + const auto newBottom = newTop; + + const auto bottomRadius = st::boxRadius; + const auto maxVisibleHeight = windowHeight - newTop; + // Top rounding is included in _contentWrapHeight. + auto desiredHeight = _contentWrapHeight + bottomRadius; + accumulate_min(desiredHeight, maxVisibleHeight - newBottom); + + // First resize content to new width and get the new desired height. + const auto contentLeft = 0; + const auto contentTop = 0; + const auto contentBottom = bottomRadius; + const auto contentWidth = newWidth; + auto contentHeight = desiredHeight - contentTop - contentBottom; + LOG(("ATTEMPT %1: WIDTH %2, WRAP HEIGHT %3, SCROLL TILL BOTTOM: %4" + ).arg(attempts + 1 + ).arg(newWidth + ).arg(_contentWrapHeight + ).arg(_contentWrap->scrollTillBottom(contentHeight))); + } const auto newGeometry = countGeometry(newWidth); _inResize = false; if (!_pendingResize) { const auto oldGeometry = geometry(); if (newGeometry != oldGeometry) { - _content->forceContentRepaint(); + _contentWrap->forceContentRepaint(); } if (newGeometry.topLeft() != oldGeometry.topLeft()) { move(newGeometry.topLeft()); @@ -292,8 +321,8 @@ QRect LayerWidget::countGeometry(int newWidth) { const auto bottomRadius = st::boxRadius; const auto maxVisibleHeight = windowHeight - newTop; - // Top rounding is included in _contentHeight. - auto desiredHeight = _contentHeight + bottomRadius; + // Top rounding is included in _contentWrapHeight. + auto desiredHeight = _contentWrapHeight + bottomRadius; accumulate_min(desiredHeight, maxVisibleHeight - newBottom); // First resize content to new width and get the new desired height. @@ -302,10 +331,11 @@ QRect LayerWidget::countGeometry(int newWidth) { const auto contentBottom = bottomRadius; const auto contentWidth = newWidth; auto contentHeight = desiredHeight - contentTop - contentBottom; - const auto scrollTillBottom = _content->scrollTillBottom(contentHeight); + const auto scrollTillBottom = _contentWrap->scrollTillBottom( + contentHeight); auto additionalScroll = std::min(scrollTillBottom, newBottom); - const auto expanding = (_desiredHeight > _contentHeight); + const auto expanding = (_desiredHeight > _contentWrapHeight); desiredHeight += additionalScroll; contentHeight += additionalScroll; @@ -313,11 +343,11 @@ QRect LayerWidget::countGeometry(int newWidth) { if (_tillBottom) { additionalScroll += contentBottom; } - _contentTillBottom = _tillBottom && !_content->scrollBottomSkip(); + _contentTillBottom = _tillBottom && !_contentWrap->scrollBottomSkip(); if (_contentTillBottom) { contentHeight += contentBottom; } - _content->updateGeometry({ + _contentWrap->updateGeometry({ contentLeft, contentTop, contentWidth, @@ -328,8 +358,8 @@ QRect LayerWidget::countGeometry(int newWidth) { } void LayerWidget::doSetInnerFocus() { - if (_content) { - _content->setInnerFocus(); + if (_contentWrap) { + _contentWrap->setInnerFocus(); } } @@ -342,7 +372,7 @@ void LayerWidget::paintEvent(QPaintEvent *e) { if (!_tillBottom) { const auto bottom = QRect{ 0, height() - radius, width(), radius }; if (clip.intersects(bottom)) { - if (const auto rounding = _content->bottomSkipRounding()) { + if (const auto rounding = _contentWrap->bottomSkipRounding()) { rounding->paint(p, rect(), RectPart::FullBottom); } else { Ui::FillRoundRect(p, bottom, st::boxBg, { @@ -351,11 +381,11 @@ void LayerWidget::paintEvent(QPaintEvent *e) { } } } else if (!_contentTillBottom) { - const auto rounding = _content->bottomSkipRounding(); + const auto rounding = _contentWrap->bottomSkipRounding(); const auto &color = rounding ? rounding->color() : st::boxBg; p.fillRect(0, height() - radius, width(), radius, color); } - if (_content->animatingShow()) { + if (_contentWrap->animatingShow()) { const auto top = QRect{ 0, 0, width(), radius }; if (clip.intersects(top)) { Ui::FillRoundRect(p, top, st::boxBg, { diff --git a/Telegram/SourceFiles/info/info_layer_widget.h b/Telegram/SourceFiles/info/info_layer_widget.h index c223b9d82..df8bc2726 100644 --- a/Telegram/SourceFiles/info/info_layer_widget.h +++ b/Telegram/SourceFiles/info/info_layer_widget.h @@ -73,10 +73,10 @@ private: [[nodiscard]] QRect countGeometry(int newWidth); not_null _controller; - object_ptr _content; + object_ptr _contentWrap; int _desiredHeight = 0; - int _contentHeight = 0; + int _contentWrapHeight = 0; int _savedHeight = 0; Ui::Animations::Simple _heightAnimation; Ui::Animations::Simple _savedHeightAnimation; diff --git a/Telegram/SourceFiles/info/info_section_widget.cpp b/Telegram/SourceFiles/info/info_section_widget.cpp index a70131428..0a7b7da67 100644 --- a/Telegram/SourceFiles/info/info_section_widget.cpp +++ b/Telegram/SourceFiles/info/info_section_widget.cpp @@ -50,8 +50,8 @@ void SectionWidget::init() { return (_content != nullptr); }) | rpl::start_with_next([=](QSize size, int) { const auto expanding = false; - const auto additionalScroll = st::boxRadius; const auto full = !_content->scrollBottomSkip(); + const auto additionalScroll = (full ? st::boxRadius : 0); const auto height = size.height() - (full ? 0 : st::boxRadius); const auto wrapGeometry = QRect{ 0, 0, size.width(), height }; _content->updateGeometry( diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp index 3524be661..8a53dc553 100644 --- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp +++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp @@ -117,17 +117,17 @@ Widget::Widget( } if (_pinnedToBottom) { - const auto processHeight = [=](int bottomHeight, int height) { - setScrollBottomSkip(bottomHeight); + const auto processHeight = [=] { + setScrollBottomSkip(_pinnedToBottom->height()); _pinnedToBottom->moveToLeft( _pinnedToBottom->x(), - height - bottomHeight); + height() - _pinnedToBottom->height()); }; _inner->sizeValue( ) | rpl::start_with_next([=](const QSize &s) { _pinnedToBottom->resizeToWidth(s.width()); - processHeight(_pinnedToBottom->height(), height()); + //processHeight(); }, _pinnedToBottom->lifetime()); rpl::combine( diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index 0681b078a..8df33ebf0 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -98,6 +98,7 @@ public: private: void outerResized(QSize outer); + void updateComposeControlsPosition(); // ListDelegate interface. Context listContext() override; @@ -505,7 +506,7 @@ void ShortcutMessages::outerResized(QSize outer) { ? base::make_optional(_scroll->scrollTop()) : 0; _skipScrollEvent = true; - _inner->resizeToWidth(contentWidth, _scroll->height()); + _inner->resizeToWidth(contentWidth, st::boxWidth); _skipScrollEvent = false; if (!_scroll->isHidden()) { @@ -514,25 +515,34 @@ void ShortcutMessages::outerResized(QSize outer) { } updateInnerVisibleArea(); } - _composeControls->setAutocompleteBoundingRect(_scroll->geometry()); + updateComposeControlsPosition(); _cornerButtons.updatePositions(); } +void ShortcutMessages::updateComposeControlsPosition() { + const auto bottom = _scroll->parentWidget()->height(); + const auto controlsHeight = _composeControls->heightCurrent(); + _composeControls->move(0, bottom - controlsHeight + st::boxRadius); + _composeControls->setAutocompleteBoundingRect(_scroll->geometry()); +} + void ShortcutMessages::setupComposeControls() { + _shortcutId.value() | rpl::start_with_next([=](BusinessShortcutId id) { + _composeControls->updateShortcutId(id); + }, lifetime()); + + const auto state = Dialogs::EntryState{ + .key = Dialogs::Key{ _history }, + .section = Dialogs::EntryState::Section::ShortcutMessages, + .currentReplyTo = replyTo(), + }; + _composeControls->setCurrentDialogsEntryState(state); + _composeControls->setHistory({ .history = _history.get(), .writeRestriction = rpl::single(Controls::WriteRestriction()), }); - _composeControls->height( - ) | rpl::start_with_next([=](int height) { - const auto wasMax = (_scroll->scrollTopMax() == _scroll->scrollTop()); - _controlsWrap->resize(width(), height); - if (wasMax) { - listScrollTo(_scroll->scrollTopMax()); - } - }, lifetime()); - _composeControls->cancelRequests( ) | rpl::start_with_next([=] { listCancelRequest(); @@ -637,20 +647,58 @@ void ShortcutMessages::setupComposeControls() { _controlsWrap->widthValue() | rpl::start_with_next([=](int width) { _composeControls->resizeToWidth(width); }, _controlsWrap->lifetime()); - _composeControls->height() | rpl::start_with_next([=](int height) { - _controlsWrap->resize(_controlsWrap->width(), height); - }, _controlsWrap->lifetime()); + + _composeControls->height( + ) | rpl::start_with_next([=](int height) { + const auto wasMax = (_scroll->scrollTopMax() == _scroll->scrollTop()); + _controlsWrap->resize(width(), height - st::boxRadius); + updateComposeControlsPosition(); + if (wasMax) { + listScrollTo(_scroll->scrollTopMax()); + } + }, lifetime()); } QPointer ShortcutMessages::createPinnedToBottom( not_null parent) { + auto placeholder = rpl::deferred([=] { + return _shortcutId.value(); + }) | rpl::map([=](BusinessShortcutId id) { + return _session->data().shortcutMessages().lookupShortcut(id).name; + }) | rpl::map([=](const QString &shortcut) { + return (shortcut == u"away"_q) + ? tr::lng_away_message_placeholder() + : (shortcut == u"hello"_q) + ? tr::lng_greeting_message_placeholder() + : tr::lng_replies_message_placeholder(); + }) | rpl::flatten_latest(); + _controlsWrap = std::make_unique(parent); _composeControls = std::make_unique( - _controlsWrap.get(), - _controller, - [=](not_null emoji) { listShowPremiumToast(emoji); }, - ComposeControls::Mode::Normal, - SendMenu::Type::Disabled); + dynamic_cast(_scroll->parentWidget()), + ComposeControlsDescriptor{ + .show = _controller->uiShow(), + .unavailableEmojiPasted = [=](not_null emoji) { + listShowPremiumToast(emoji); + }, + .mode = HistoryView::ComposeControlsMode::Normal, + .sendMenuType = SendMenu::Type::Disabled, + .regularWindow = _controller, + .stickerOrEmojiChosen = _controller->stickerOrEmojiChosen(), + .customPlaceholder = std::move(placeholder), + .panelsLevel = Window::GifPauseReason::Layer, + .voiceCustomCancelText = tr::lng_record_cancel_stories(tr::now), + .voiceLockFromBottom = true, + .features = { + .sendAs = false, + .ttlInfo = false, + .botCommandSend = false, + .silentBroadcastToggle = false, + .attachBotsMenu = false, + .megagroupSet = false, + .commonTabbedPanel = false, + }, + }); setupComposeControls(); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 532a1d35e..c26b68458 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -517,6 +517,7 @@ FileLoadTask::FileLoadTask( , _caption(caption) , _spoiler(spoiler) { Expects(to.options.scheduled + || to.options.shortcutId || !to.replaceMediaOf || IsServerMsgId(to.replaceMediaOf)); diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 2a2733f2a..4ec9954a5 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -301,6 +301,7 @@ historySentInvertedIcon: icon {{ "history_sent", historyIconFgInverted, point(2p historyReceivedIcon: icon {{ "history_received", historyOutIconFg, point(2px, 4px) }}; historyReceivedSelectedIcon: icon {{ "history_received", historyOutIconFgSelected, point(2px, 4px) }}; historyReceivedInvertedIcon: icon {{ "history_received", historyIconFgInverted, point(2px, 4px) }}; +historyShortcutStateSpace: 18px; historyViewsSpace: 8px; historyViewsWidth: 20px;