mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-03 21:54:05 +02:00
Fix swipe gestures on touchscreens.
This commit is contained in:
parent
044ef3447c
commit
34858b36c1
12 changed files with 140 additions and 51 deletions
|
@ -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<Ui::RpWidget*> 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();
|
||||
|
|
|
@ -288,7 +288,7 @@ private:
|
|||
not_null<GifsListWidget*> gifs() const;
|
||||
not_null<StickersListWidget*> masks() const;
|
||||
|
||||
void setupSwipe();
|
||||
void reinstallSwipe(not_null<Ui::RpWidget*> 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 {
|
||||
|
|
|
@ -2972,7 +2972,7 @@ bool InnerWidget::processTouchEvent(not_null<QTouchEvent*> 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<QTouchEvent*> 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 = {};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Window::SectionMemento> Widget::createMemento() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -21,7 +21,7 @@ void SetupSwipeBackSection(
|
|||
not_null<HistoryView::ListWidget*> list) {
|
||||
const auto swipeBackData
|
||||
= list->lifetime().make_state<Ui::Controls::SwipeBackResult>();
|
||||
Ui::Controls::SetupSwipeHandler(parent, scroll, [=](
|
||||
Ui::Controls::SetupSwipeHandler(list, scroll, [=](
|
||||
Ui::Controls::SwipeContextData data) {
|
||||
if (data.translation > 0) {
|
||||
if (!swipeBackData->callback) {
|
||||
|
|
|
@ -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<RpWidget> 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<Ui::PaddingWrap<Ui::RpWidget>>(
|
||||
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<int> 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<Ui::ScrollArea*> ContentWidget::scroll() const {
|
|||
return _scroll.data();
|
||||
}
|
||||
|
||||
void ContentWidget::setupSwipeReply() {
|
||||
Ui::Controls::SetupSwipeHandler(this, _scroll.data(), [=](
|
||||
void ContentWidget::setupSwipeHandler(not_null<Ui::RpWidget*> 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 {
|
||||
|
|
|
@ -168,7 +168,8 @@ private:
|
|||
RpWidget *doSetInnerWidget(object_ptr<RpWidget> inner);
|
||||
void updateControlsGeometry();
|
||||
void refreshSearchField(bool shown);
|
||||
void setupSwipeReply();
|
||||
void setupSwipeHandler(not_null<Ui::RpWidget*> widget);
|
||||
void updateInnerPadding();
|
||||
|
||||
virtual std::shared_ptr<ContentMemento> doCreateMemento() = 0;
|
||||
|
||||
|
@ -183,6 +184,8 @@ private:
|
|||
base::unique_qptr<Ui::RpWidget> _searchWrap = nullptr;
|
||||
QPointer<Ui::InputField> _searchField;
|
||||
int _innerDesiredHeight = 0;
|
||||
int _additionalScroll = 0;
|
||||
int _addedHeight = 0;
|
||||
int _maxVisibleHeight = 0;
|
||||
bool _isStackBottom = false;
|
||||
|
||||
|
|
|
@ -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<void(SwipeContextData)> update,
|
||||
Fn<SwipeHandlerFinishData(int, Qt::LayoutDirection)> generateFinish,
|
||||
rpl::producer<bool> dontStart) {
|
||||
rpl::producer<bool> 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<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<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<QEvent*> 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<QTouchEvent*>(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<QObject>(
|
||||
base::install_event_filter(widget, filter));
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ void SetupSwipeHandler(
|
|||
Scroll scroll,
|
||||
Fn<void(SwipeContextData)> update,
|
||||
Fn<SwipeHandlerFinishData(int, Qt::LayoutDirection)> generateFinishByTop,
|
||||
rpl::producer<bool> dontStart = nullptr);
|
||||
rpl::producer<bool> dontStart = nullptr,
|
||||
rpl::lifetime *onLifetime = nullptr);
|
||||
|
||||
[[nodiscard]] SwipeBackResult SetupSwipeBack(
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit ba969667301ae4d8da2c2f6c4528bea63443f607
|
||||
Subproject commit 107729e446e8a2037ceb68374f90978fec937504
|
Loading…
Add table
Reference in a new issue