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 6c0f635e8..a4edc44cd 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1108,6 +1108,11 @@ bool ComposeControls::focus() { return true; } +rpl::producer ComposeControls::focusedValue() const { + return rpl::single(Ui::InFocusChain(_wrap.get())) + | rpl::then(_focusChanges.events()); +} + rpl::producer<> ComposeControls::cancelRequests() const { return _cancelRequests.events(); } @@ -1596,6 +1601,8 @@ void ComposeControls::initKeyHandler() { }); return Result::Cancel; } + } else if (k->key() == Qt::Key_Escape) { + return Result::Cancel; } return Result::Continue; }); @@ -1608,7 +1615,12 @@ void ComposeControls::initField() { Ui::Connect(_field, &Ui::InputField::cancelled, [=] { escape(); }); Ui::Connect(_field, &Ui::InputField::tabbed, [=] { fieldTabbed(); }); Ui::Connect(_field, &Ui::InputField::resized, [=] { updateHeight(); }); - //Ui::Connect(_field, &Ui::InputField::focused, [=] { fieldFocused(); }); + Ui::Connect(_field, &Ui::InputField::focused, [=] { + _focusChanges.fire(true); + }); + Ui::Connect(_field, &Ui::InputField::blurred, [=] { + _focusChanges.fire(false); + }); Ui::Connect(_field, &Ui::InputField::changed, [=] { fieldChanged(); }); InitMessageField(_show, _field, [=](not_null emoji) { if (_history && Data::AllowEmojiWithoutPremium(_history->peer)) { 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 2f5b12491..16a114ec8 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -136,6 +136,7 @@ public: [[nodiscard]] int heightCurrent() const; bool focus(); + [[nodiscard]] rpl::producer focusedValue() const; [[nodiscard]] rpl::producer<> cancelRequests() const; [[nodiscard]] rpl::producer sendRequests() const; [[nodiscard]] rpl::producer sendVoiceRequests() const; @@ -390,6 +391,7 @@ private: std::unique_ptr _preview; Fn _raiseEmojiSuggestions; + rpl::event_stream _focusChanges; rpl::lifetime _historyLifetime; rpl::lifetime _uploaderSubscriptions; diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index 29d4f07c0..344b41738 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -29,6 +29,7 @@ namespace { constexpr auto kPhotoProgressInterval = crl::time(100); constexpr auto kPhotoDuration = 5 * crl::time(1000); constexpr auto kSiblingMultiplier = 0.448; +constexpr auto kFullContentFade = 0.2; } // namespace @@ -108,6 +109,19 @@ Controller::Controller(not_null delegate) , _slider(std::make_unique(this)) , _replyArea(std::make_unique(this)) { initLayout(); + + _replyArea->focusedValue( + ) | rpl::start_with_next([=](bool focused) { + _contentFaded = focused; + _contentFadeAnimation.start( + [=] { _delegate->storiesRepaint(); }, + focused ? 0. : 1., + focused ? 1. : 0., + st::fadeWrapDuration); + togglePaused(focused); + }, _lifetime); + + _contentFadeAnimation.stop(); } Controller::~Controller() = default; @@ -223,6 +237,11 @@ rpl::producer Controller::layoutValue() const { return _layout.value() | rpl::filter_optional(); } +float64 Controller::contentFade() const { + return _contentFadeAnimation.value(_contentFaded ? 1. : 0.) + * kFullContentFade; +} + std::shared_ptr Controller::uiShow() const { return _delegate->storiesShow(); } @@ -406,6 +425,10 @@ SiblingView Controller::siblingRight() const { return {}; } +void Controller::unfocusReply() { + _wrap->setFocus(); +} + rpl::lifetime &Controller::lifetime() { return _lifetime; } diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h index 762554a43..9e3791442 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "data/data_stories.h" +#include "ui/effects/animations.h" namespace base { class PowerSaveBlocker; @@ -66,6 +67,7 @@ public: [[nodiscard]] not_null wrap() const; [[nodiscard]] Layout layout() const; [[nodiscard]] rpl::producer layoutValue() const; + [[nodiscard]] float64 contentFade() const; [[nodiscard]] std::shared_ptr uiShow() const; [[nodiscard]] auto stickerOrEmojiChosen() const @@ -91,6 +93,8 @@ public: [[nodiscard]] SiblingView siblingLeft() const; [[nodiscard]] SiblingView siblingRight() const; + void unfocusReply(); + [[nodiscard]] rpl::lifetime &lifetime(); private: @@ -118,6 +122,9 @@ private: const std::unique_ptr _replyArea; std::unique_ptr _photoPlayback; + Ui::Animations::Simple _contentFadeAnimation; + bool _contentFaded = false; + Data::FullStoryId _shown; std::optional _list; int _index = 0; diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp index e4a24807c..000635db6 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp @@ -68,7 +68,7 @@ void ReplyArea::chooseAttach(std::optional overrideCompress) { void ReplyArea::initActions() { _controls->cancelRequests( ) | rpl::start_with_next([=] { - // #TODO stories + _controller->unfocusReply(); }, _lifetime); _controls->sendRequests( @@ -157,6 +157,10 @@ void ReplyArea::show(ReplyAreaData data) { _controls->clear(); } +rpl::producer ReplyArea::focusedValue() const { + return _controls->focusedValue(); +} + void ReplyArea::showPremiumToast(not_null emoji) { // #TODO stories } diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.h b/Telegram/SourceFiles/media/stories/media_stories_reply.h index 571c9c88c..9587e2e07 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.h +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.h @@ -40,6 +40,8 @@ public: void show(ReplyAreaData data); + [[nodiscard]] rpl::producer focusedValue() const; + private: using VoiceToSend = HistoryView::Controls::VoiceToSend; diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.cpp b/Telegram/SourceFiles/media/stories/media_stories_view.cpp index 37454aa75..dbcf9cdf4 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_view.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_view.cpp @@ -47,6 +47,10 @@ rpl::producer View::contentGeometryValue() const { }) | rpl::distinct_until_changed(); } +float64 View::contentFade() const { + return _controller->contentFade(); +} + void View::updatePlayback(const Player::TrackState &state) { _controller->updateVideoPlayback(state); } diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.h b/Telegram/SourceFiles/media/stories/media_stories_view.h index e2399c483..a11f49436 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_view.h +++ b/Telegram/SourceFiles/media/stories/media_stories_view.h @@ -46,6 +46,7 @@ public: [[nodiscard]] bool canDownload() const; [[nodiscard]] QRect contentGeometry() const; [[nodiscard]] rpl::producer contentGeometryValue() const; + [[nodiscard]] float64 contentFade() const; [[nodiscard]] SiblingView siblingLeft() const; [[nodiscard]] SiblingView siblingRight() const; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index 2e037a46c..4756c6531 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -35,12 +35,13 @@ constexpr auto kControlValues = 4 * 4 + 4 * 4; // over + icon .header = R"( uniform sampler2D f_texture; uniform vec4 shadowTopRect; -uniform vec2 shadowBottomAndOpacity; +uniform vec3 shadowBottomOpacityFullFade; )", .body = R"( float topHeight = shadowTopRect.w; - float bottomHeight = shadowBottomAndOpacity.x; - float opacity = shadowBottomAndOpacity.y; + float bottomHeight = shadowBottomOpacityFullFade.x; + float opacity = shadowBottomOpacityFullFade.y; + float fullFade = shadowBottomOpacityFullFade.z; float viewportHeight = shadowTopRect.y + topHeight; float fullHeight = topHeight + bottomHeight; float topY = min( @@ -50,7 +51,8 @@ uniform vec2 shadowBottomAndOpacity; vec4 fadeTop = texture2D(f_texture, vec2(topX, topY)) * opacity; float bottomY = max(fullHeight - gl_FragCoord.y, topHeight) / fullHeight; vec4 fadeBottom = texture2D(f_texture, vec2(0.5, bottomY)) * opacity; - result.rgb = result.rgb * (1. - fadeTop.a) * (1. - fadeBottom.a); + float fade = min((1. - fadeTop.a) * (1. - fadeBottom.a), fullFade); + result.rgb = result.rgb * fade; )", }; } @@ -113,10 +115,12 @@ OverlayWidget::RendererGL::RendererGL(not_null owner) invalidateControls(); }, _lifetime); - _owner->_storiesChanged.events( - ) | rpl::start_with_next([=] { - invalidateControls(); - }, _lifetime); + crl::on_main(this, [=] { + _owner->_storiesChanged.events( + ) | rpl::start_with_next([=] { + invalidateControls(); + }, _lifetime); + }); } void OverlayWidget::RendererGL::init( @@ -478,14 +482,17 @@ void OverlayWidget::RendererGL::paintTransformedContent( program->setUniformValue("viewport", _uniformViewport); const auto &top = st::mediaviewShadowTop.size(); - const auto point = QPoint(_shadowTopFlip ? 0 : (_viewport.width() - top.width()), 0); + const auto point = QPoint( + _shadowTopFlip ? 0 : (_viewport.width() - top.width()), + 0); program->setUniformValue( "shadowTopRect", Uniform(transformRect(QRect(point, top)))); const auto &bottom = st::mediaviewShadowBottom; - program->setUniformValue( - "shadowBottomAndOpacity", - QVector2D(bottom.height() * _factor, geometry.controlsOpacity)); + program->setUniformValue("shadowBottomOpacityFullFade", QVector3D( + bottom.height() * _factor, + geometry.controlsOpacity, + 1.f - float(geometry.fade))); FillTexturedRectangle(*_f, &*program); } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h index 88d66e2d4..a171752de 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h @@ -15,7 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media::View { -class OverlayWidget::RendererGL final : public OverlayWidget::Renderer { +class OverlayWidget::RendererGL final + : public OverlayWidget::Renderer + , public base::has_weak_ptr { public: explicit RendererGL(not_null owner); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index ab14487a5..8c404e65d 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -1492,6 +1492,7 @@ QRect OverlayWidget::finalContentRect() const { } OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const { + const auto fade = _stories ? _stories->contentFade() : 0.; const auto controlsOpacity = _controlsOpacity.current(); const auto toRotation = qreal(finalContentRotation()); const auto toRectRotated = QRectF(finalContentRect()); @@ -1504,7 +1505,7 @@ OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const { toRectRotated.width()) : toRectRotated; if (!_geometryAnimation.animating()) { - return { toRect, toRotation, controlsOpacity }; + return { toRect, toRotation, controlsOpacity, fade }; } const auto fromRect = _oldGeometry.rect; const auto fromRotation = _oldGeometry.rotation; @@ -1527,7 +1528,7 @@ OverlayWidget::ContentGeometry OverlayWidget::contentGeometry() const { fromRect.width() + (toRect.width() - fromRect.width()) * progress, fromRect.height() + (toRect.height() - fromRect.height()) * progress ); - return { useRect, useRotation, controlsOpacity }; + return { useRect, useRotation, controlsOpacity, fade }; } void OverlayWidget::updateContentRect() { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index e0bed4d14..61b240659 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -165,6 +165,7 @@ private: QRectF rect; qreal rotation = 0.; qreal controlsOpacity = 0.; + qreal fade = 0.; }; struct StartStreaming { StartStreaming() : continueStreaming(false), startTime(0) {