diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index a2a971e59b..3b73b3d8b0 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -523,8 +523,6 @@ TabbedSelector::TabbedSelector( if (hasEmojiTab()) { emoji()->refreshEmoji(); } - setupSwipe(); - //setAttribute(Qt::WA_AcceptTouchEvents); setAttribute(Qt::WA_OpaquePaintEvent, false); showAll(); hide(); @@ -532,8 +530,10 @@ TabbedSelector::TabbedSelector( TabbedSelector::~TabbedSelector() = default; -void TabbedSelector::setupSwipe() { - Ui::Controls::SetupSwipeHandler(this, _scroll.data(), [=]( +void TabbedSelector::reinstallSwipe(not_null widget) { + _swipeLifetime.destroy(); + + Ui::Controls::SetupSwipeHandler(widget, _scroll.data(), [=]( Ui::Controls::SwipeContextData data) { if (data.translation != 0) { if (!_swipeBackData.callback) { @@ -571,7 +571,7 @@ void TabbedSelector::setupSwipe() { }); } return Ui::Controls::SwipeHandlerFinishData(); - }, nullptr); + }, nullptr, &_swipeLifetime); } const style::EmojiPan &TabbedSelector::st() const { @@ -1345,6 +1345,10 @@ void TabbedSelector::setWidgetToScrollArea() { inner->moveToLeft(0, 0); inner->show(); + if (_tabs.size() > 1) { + reinstallSwipe(inner); + } + _scroll->disableScroll(false); scrollToY(currentTab()->getScrollTop()); handleScroll(); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index 272cb7f067..47e8c4b649 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -288,7 +288,7 @@ private: not_null gifs() const; not_null masks() const; - void setupSwipe(); + void reinstallSwipe(not_null widget); const style::EmojiPan &_st; const ComposeFeatures _features; @@ -334,6 +334,8 @@ private: rpl::event_stream<> _showRequests; rpl::event_stream<> _slideFinished; + rpl::lifetime _swipeLifetime; + }; class TabbedSelector::Inner : public Ui::RpWidget { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 39e047897d..5836207a1f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -2972,7 +2972,7 @@ bool InnerWidget::processTouchEvent(not_null e) { } if (_chatPreviewTouchGlobal) { const auto delta = (*_chatPreviewTouchGlobal - *point); - if (delta.manhattanLength() > _st->photoSize) { + if (delta.manhattanLength() >= QApplication::startDragDistance()) { cancelChatPreview(); } } @@ -2981,7 +2981,7 @@ bool InnerWidget::processTouchEvent(not_null e) { return _dragging != nullptr; } else if (_touchDragStartGlobal) { const auto delta = (*_touchDragStartGlobal - *point); - if (delta.manhattanLength() > QApplication::startDragDistance()) { + if (delta.manhattanLength() >= QApplication::startDragDistance()) { if (_touchDragPinnedTimer.isActive()) { _touchDragPinnedTimer.cancel(); _touchDragStartGlobal = {}; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 76d8930205..83ab9665e9 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -685,7 +685,7 @@ void Widget::setupSwipeBack() { } return !current; }; - Ui::Controls::SetupSwipeHandler(_scroll.data(), _scroll.data(), [=]( + Ui::Controls::SetupSwipeHandler(_inner, _scroll.data(), [=]( Ui::Controls::SwipeContextData data) { if (data.translation != 0) { if (!_swipeBackData.callback) { @@ -765,7 +765,7 @@ void Widget::setupSwipeBack() { } } return Ui::Controls::SwipeHandlerFinishData(); - }, nullptr); + }); } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp index 92af8aec9e..ef88f0b384 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp @@ -420,7 +420,7 @@ void Widget::setupShortcuts() { } void Widget::setupSwipeReply() { - Ui::Controls::SetupSwipeHandler(this, _scroll.data(), [=]( + Ui::Controls::SetupSwipeHandler(_inner.data(), _scroll.data(), [=]( Ui::Controls::SwipeContextData data) { if (data.translation > 0) { if (!_swipeBackData.callback) { @@ -446,7 +446,7 @@ void Widget::setupSwipeReply() { }); } return Ui::Controls::SwipeHandlerFinishData(); - }, nullptr); + }); } std::shared_ptr Widget::createMemento() { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index ae56287868..643a59998c 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1525,6 +1525,11 @@ void HistoryInner::touchEvent(QTouchEvent *e) { } break; case QEvent::TouchUpdate: { + LOG(("UPDATE: %1,%2 -> %3,%4" + ).arg(_touchStart.x() + ).arg(_touchStart.y() + ).arg(_touchPos.x() + ).arg(_touchPos.y())); if (!_touchInProgress) { return; } else if (_touchSelect) { diff --git a/Telegram/SourceFiles/history/history_view_swipe_back_session.cpp b/Telegram/SourceFiles/history/history_view_swipe_back_session.cpp index e39ee4d4d7..05dfa6df6c 100644 --- a/Telegram/SourceFiles/history/history_view_swipe_back_session.cpp +++ b/Telegram/SourceFiles/history/history_view_swipe_back_session.cpp @@ -21,7 +21,7 @@ void SetupSwipeBackSection( not_null list) { const auto swipeBackData = list->lifetime().make_state(); - Ui::Controls::SetupSwipeHandler(parent, scroll, [=]( + Ui::Controls::SetupSwipeHandler(list, scroll, [=]( Ui::Controls::SwipeContextData data) { if (data.translation > 0) { if (!swipeBackData->callback) { diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index e0d0cf3eee..a6396b329d 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -77,8 +77,6 @@ ContentWidget::ContentWidget( ) | rpl::start_with_next([this] { updateControlsGeometry(); }, lifetime()); - - setupSwipeReply(); } void ContentWidget::resizeEvent(QResizeEvent *e) { @@ -157,6 +155,17 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget( object_ptr inner) { using namespace rpl::mappers; + const auto tmp = new Ui::RpWidget(this); + tmp->raise(); + tmp->show(); + tmp->setAttribute(Qt::WA_TransparentForMouseEvents); + tmp->paintRequest() | rpl::start_with_next([=] { + QPainter(tmp).fillRect(tmp->rect(), QColor(255, 0, 0, 64)); + }, tmp->lifetime()); + _scroll->geometryValue() | rpl::start_with_next([=] { + tmp->setGeometry(_scroll->geometry()); + }, tmp->lifetime()); + _innerWrap = _scroll->setOwnedWidget( object_ptr>( this, @@ -164,6 +173,8 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget( _innerWrap ? _innerWrap->padding() : style::margins())); _innerWrap->move(0, 0); + setupSwipeHandler(_innerWrap); + // MSVC BUG + REGRESSION rpl::mappers::tuple :( rpl::combine( _scroll->scrollTopValue(), @@ -179,6 +190,24 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget( _scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0)); }, _innerWrap->lifetime()); + rpl::combine( + _scroll->heightValue(), + _innerWrap->entity()->heightValue(), + _controller->wrapValue() + ) | rpl::start_with_next([=]( + int scrollHeight, + int innerHeight, + Wrap wrap) { + const auto added = (wrap == Wrap::Layer) + ? 0 + : std::max(scrollHeight - innerHeight, 0); + if (_addedHeight != added) { + _addedHeight = added; + updateInnerPadding(); + } + }, _innerWrap->lifetime()); + updateInnerPadding(); + return _innerWrap->entity(); } @@ -208,11 +237,19 @@ rpl::producer ContentWidget::scrollHeightValue() const { } void ContentWidget::applyAdditionalScroll(int additionalScroll) { - if (_innerWrap) { - _innerWrap->setPadding({ 0, 0, 0, additionalScroll }); + if (_additionalScroll != additionalScroll) { + _additionalScroll = additionalScroll; + if (_innerWrap) { + updateInnerPadding(); + } } } +void ContentWidget::updateInnerPadding() { + const auto addedToBottom = std::max(_additionalScroll, _addedHeight); + _innerWrap->setPadding({ 0, 0, 0, addedToBottom }); +} + void ContentWidget::applyMaxVisibleHeight(int maxVisibleHeight) { if (_maxVisibleHeight != maxVisibleHeight) { _maxVisibleHeight = maxVisibleHeight; @@ -384,8 +421,8 @@ not_null ContentWidget::scroll() const { return _scroll.data(); } -void ContentWidget::setupSwipeReply() { - Ui::Controls::SetupSwipeHandler(this, _scroll.data(), [=]( +void ContentWidget::setupSwipeHandler(not_null widget) { + Ui::Controls::SetupSwipeHandler(widget, _scroll.data(), [=]( Ui::Controls::SwipeContextData data) { if (data.translation > 0) { if (!_swipeBackData.callback) { @@ -412,7 +449,7 @@ void ContentWidget::setupSwipeReply() { })); }) : Ui::Controls::SwipeHandlerFinishData(); - }, nullptr); + }); } Key ContentMemento::key() const { diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index 86389fea99..84b03d2934 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -168,7 +168,8 @@ private: RpWidget *doSetInnerWidget(object_ptr inner); void updateControlsGeometry(); void refreshSearchField(bool shown); - void setupSwipeReply(); + void setupSwipeHandler(not_null widget); + void updateInnerPadding(); virtual std::shared_ptr doCreateMemento() = 0; @@ -183,6 +184,8 @@ private: base::unique_qptr _searchWrap = nullptr; QPointer _searchField; int _innerDesiredHeight = 0; + int _additionalScroll = 0; + int _addedHeight = 0; int _maxVisibleHeight = 0; bool _isStackBottom = false; diff --git a/Telegram/SourceFiles/ui/controls/swipe_handler.cpp b/Telegram/SourceFiles/ui/controls/swipe_handler.cpp index eece262ccc..3ec1f32ca6 100644 --- a/Telegram/SourceFiles/ui/controls/swipe_handler.cpp +++ b/Telegram/SourceFiles/ui/controls/swipe_handler.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/controls/swipe_handler.h" +#include "base/debug_log.h" + #include "base/platform/base_platform_haptic.h" #include "base/platform/base_platform_info.h" #include "base/qt/qt_common_adapters.h" @@ -64,7 +66,8 @@ void SetupSwipeHandler( Scroll scroll, Fn update, Fn generateFinish, - rpl::producer dontStart) { + rpl::producer dontStart, + rpl::lifetime *onLifetime) { static constexpr auto kThresholdWidth = 50; static constexpr auto kMaxRatio = 1.5; @@ -95,12 +98,23 @@ void SetupSwipeHandler( rpl::lifetime lifetime; }; - const auto state = widget->lifetime().make_state(); - std::move( - dontStart - ) | rpl::start_with_next([=](bool dontStart) { - state->dontStart = dontStart; - }, state->lifetime); + auto &useLifetime = onLifetime ? *onLifetime : widget->lifetime(); + const auto state = useLifetime.make_state(); + if (dontStart) { + std::move( + dontStart + ) | rpl::start_with_next([=](bool dontStart) { + state->dontStart = dontStart; + }, state->lifetime); + } else { + v::match(scroll, [](v::null_t) { + }, [&](const auto &scroll) { + scroll->touchMaybePressing( + ) | rpl::start_with_next([=](bool maybePressing) { + state->dontStart = maybePressing; + }, state->lifetime); + }); + } const auto updateRatio = [=](float64 ratio) { ratio = std::max(ratio, 0.); @@ -123,9 +137,11 @@ void SetupSwipeHandler( v::match(scroll, [](v::null_t) { }, [&](const auto &scroll) { if (const auto viewport = scroll->viewport()) { - viewport->setAttribute( - Qt::WA_AcceptTouchEvents, - !isHorizontal); + if (viewport != widget) { + viewport->setAttribute( + Qt::WA_AcceptTouchEvents, + !isHorizontal); + } } scroll->disableScroll(isHorizontal); }); @@ -173,37 +189,52 @@ void SetupSwipeHandler( update(state->data); }; const auto updateWith = [=](UpdateArgs args) { - if (!state->started - || state->touch != args.touch - || !state->direction) { - state->direction = (args.delta.x() == 0) - ? std::nullopt - : args.delta.x() < 0 - ? std::make_optional(Qt::RightToLeft) - : std::make_optional(Qt::LeftToRight); - state->directionInt = (!state->direction - || (*state->direction) == Qt::LeftToRight) + const auto fillFinishByTop = [&] { + if (!args.delta.x()) { + LOG(("SKIPPING fillFinishByTop.")); + return; + } + LOG(("SETTING DIRECTION")); + state->direction = (args.delta.x() < 0) + ? Qt::RightToLeft + : Qt::LeftToRight; + state->directionInt = (state->direction == Qt::LeftToRight) ? 1 : -1; - - state->started = true; - state->touch = args.touch; - state->startAt = args.position; - state->delta = QPointF(); - state->cursorTop = widget->mapFromGlobal(args.globalCursor).y(); state->finishByTopData = generateFinish( state->cursorTop, - state->direction.value_or(Qt::RightToLeft)); + *state->direction); state->threshold = style::ConvertFloatScale(kThresholdWidth) * state->finishByTopData.speedRatio; if (!state->finishByTopData.callback) { setOrientation(Qt::Vertical); } + }; + if (!state->started || state->touch != args.touch) { + LOG(("STARTING")); + state->started = true; + state->touch = args.touch; + state->startAt = args.position; + state->cursorTop = widget->mapFromGlobal(args.globalCursor).y(); + if (!state->touch) { + // args.delta already is valid. + fillFinishByTop(); + } else { + // args.delta depends on state->startAt, so it's invalid. + state->direction = std::nullopt; + } + state->delta = QPointF(); + } else if (!state->direction) { + fillFinishByTop(); } else if (!state->orientation) { state->delta = args.delta; const auto diffXtoY = std::abs(args.delta.x()) - std::abs(args.delta.y()); constexpr auto kOrientationThreshold = 1.; + LOG(("SETTING ORIENTATION WITH: %1,%2, diff %3" + ).arg(args.delta.x() + ).arg(args.delta.y() + ).arg(diffXtoY)); if (diffXtoY > kOrientationThreshold) { if (!state->dontStart) { setOrientation(Qt::Horizontal); @@ -239,6 +270,9 @@ void SetupSwipeHandler( } }; const auto filter = [=](not_null e) { + if (!widget->testAttribute(Qt::WA_AcceptTouchEvents)) { + [[maybe_unused]] int a = 0; + } const auto type = e->type(); switch (type) { case QEvent::Leave: { @@ -262,7 +296,7 @@ void SetupSwipeHandler( const auto t = static_cast(e.get()); const auto touchscreen = t->device() && (t->device()->type() == base::TouchDevice::TouchScreen); - if (!touchscreen) { + if (!touchscreen && type != QEvent::TouchCancel) { break; } else if (type == QEvent::TouchBegin) { // Reset state in case we lost some TouchEnd. @@ -292,8 +326,10 @@ void SetupSwipeHandler( .delta = state->startAt - touches[0].pos(), .touch = true, }; + LOG(("ORIENTATION UPDATING WITH: %1, %2").arg(args.delta.x()).arg(args.delta.y())); updateWith(args); } + LOG(("ORIENTATION: %1").arg(!state->orientation ? "none" : (state->orientation == Qt::Horizontal) ? "horizontal" : "vertical")); return (touchscreen && state->orientation != Qt::Horizontal) ? base::EventFilterResult::Continue : base::EventFilterResult::Cancel; @@ -326,6 +362,7 @@ void SetupSwipeHandler( } return base::EventFilterResult::Continue; }; + widget->setAttribute(Qt::WA_AcceptTouchEvents); state->filter = base::make_unique_q( base::install_event_filter(widget, filter)); } diff --git a/Telegram/SourceFiles/ui/controls/swipe_handler.h b/Telegram/SourceFiles/ui/controls/swipe_handler.h index 744f5f5b3a..79413091eb 100644 --- a/Telegram/SourceFiles/ui/controls/swipe_handler.h +++ b/Telegram/SourceFiles/ui/controls/swipe_handler.h @@ -35,7 +35,8 @@ void SetupSwipeHandler( Scroll scroll, Fn update, Fn generateFinishByTop, - rpl::producer dontStart = nullptr); + rpl::producer dontStart = nullptr, + rpl::lifetime *onLifetime = nullptr); [[nodiscard]] SwipeBackResult SetupSwipeBack( not_null widget, diff --git a/Telegram/lib_ui b/Telegram/lib_ui index ba96966730..107729e446 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit ba969667301ae4d8da2c2f6c4528bea63443f607 +Subproject commit 107729e446e8a2037ceb68374f90978fec937504