Improve touchscreen swipt-to-reply.

This commit is contained in:
John Preston 2024-09-04 15:56:16 +02:00
parent 8d0f66d562
commit b82fa3112c
7 changed files with 77 additions and 19 deletions

View file

@ -640,7 +640,7 @@ void HistoryInner::setupSwipeReply() {
return false;
});
return result;
});
}, _touchMaybeSelecting.value());
}
bool HistoryInner::hasSelectRestriction() const {
@ -1497,6 +1497,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
_touchScroll = _touchSelect = false;
_horizontalScrollLocked = false;
_touchScrollState = Ui::TouchScrollState::Manual;
_touchMaybeSelecting = false;
mouseActionCancel();
return;
}
@ -1519,6 +1520,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
_touchInProgress = true;
_horizontalScrollLocked = false;
if (_touchScrollState == Ui::TouchScrollState::Auto) {
_touchMaybeSelecting = false;
_touchScrollState = Ui::TouchScrollState::Acceleration;
_touchWaitingAcceleration = true;
_touchAccelerationTime = crl::now();
@ -1526,6 +1528,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
_touchStart = _touchPos;
} else {
_touchScroll = false;
_touchMaybeSelecting = true;
_touchSelectTimer.callOnce(QApplication::startDragTime());
}
_touchSelect = false;
@ -1539,6 +1542,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
mouseActionUpdate(_touchPos);
} else if (!_touchScroll && (_touchPos - _touchStart).manhattanLength() >= QApplication::startDragDistance()) {
_touchSelectTimer.cancel();
_touchMaybeSelecting = false;
_touchScroll = true;
touchUpdateSpeed();
}
@ -1560,11 +1564,18 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
return;
}
_touchInProgress = false;
const auto notMoved = (_touchPos - _touchStart).manhattanLength()
< QApplication::startDragDistance();
auto weak = Ui::MakeWeak(this);
if (_touchSelect) {
mouseActionFinish(_touchPos, Qt::RightButton);
QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos);
showContextMenu(&contextMenu, true);
if (notMoved || _touchMaybeSelecting.current()) {
mouseActionFinish(_touchPos, Qt::RightButton);
auto contextMenu = QContextMenuEvent(
QContextMenuEvent::Mouse,
mapFromGlobal(_touchPos),
_touchPos);
showContextMenu(&contextMenu, true);
}
_touchScroll = false;
} else if (_touchScroll) {
if (_touchScrollState == Ui::TouchScrollState::Manual) {
@ -1582,12 +1593,13 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
_touchWaitingAcceleration = false;
_touchPrevPosValid = false;
}
} else { // One short tap is like left mouse click.
} else if (notMoved) { // One short tap is like left mouse click.
mouseActionStart(_touchPos, Qt::LeftButton);
mouseActionFinish(_touchPos, Qt::LeftButton);
}
if (weak) {
_touchSelectTimer.cancel();
_touchMaybeSelecting = false;
_touchSelect = false;
}
} break;
@ -3789,6 +3801,7 @@ MessageIdsList HistoryInner::getSelectedItems() const {
void HistoryInner::onTouchSelect() {
_touchSelect = true;
_touchMaybeSelecting = true;
mouseActionStart(_touchPos, Qt::LeftButton);
}

View file

@ -513,6 +513,7 @@ private:
bool _touchSelect = false;
bool _touchInProgress = false;
QPoint _touchStart, _touchPrevPos, _touchPos;
rpl::variable<bool> _touchMaybeSelecting;
base::Timer _touchSelectTimer;
Ui::DraggingScrollManager _selectScroll;

View file

@ -25,7 +25,8 @@ void SetupSwipeHandler(
not_null<Ui::RpWidget*> widget,
not_null<Ui::ScrollArea*> scroll,
Fn<void(ChatPaintGestureHorizontalData)> update,
Fn<SwipeHandlerFinishData(int)> generateFinishByTop) {
Fn<SwipeHandlerFinishData(int)> generateFinishByTop,
rpl::producer<bool> dontStart) {
constexpr auto kThresholdWidth = 50;
const auto threshold = style::ConvertFloatScale(kThresholdWidth);
struct State {
@ -37,6 +38,7 @@ void SetupSwipeHandler(
QPointF startAt;
QPointF delta;
int cursorTop = 0;
bool dontStart = false;
bool started = false;
bool reached = false;
bool touch = false;
@ -44,6 +46,12 @@ 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);
const auto updateRatio = [=](float64 ratio) {
update({
.ratio = std::clamp(ratio, 0., 1.5),
@ -83,12 +91,15 @@ void SetupSwipeHandler(
state->reached = false;
};
scroll->scrolls() | rpl::start_with_next([=] {
processEnd();
if (state->orientation != Qt::Vertical) {
processEnd();
}
}, state->lifetime);
const auto animationReachCallback = [=] {
updateRatio(state->delta.x() / threshold);
};
struct UpdateArgs {
QPoint globalCursor;
QPointF position;
QPointF delta;
bool touch = false;
@ -99,8 +110,7 @@ void SetupSwipeHandler(
state->touch = args.touch;
state->startAt = args.position;
state->delta = QPointF();
state->cursorTop = widget->mapFromGlobal(
QCursor::pos()).y();
state->cursorTop = widget->mapFromGlobal(args.globalCursor).y();
state->finishByTopData = generateFinishByTop(
state->cursorTop);
if (!state->finishByTopData.callback) {
@ -112,7 +122,9 @@ void SetupSwipeHandler(
- std::abs(args.delta.y());
constexpr auto kOrientationThreshold = 1.;
if (diffXtoY > kOrientationThreshold) {
setOrientation(Qt::Horizontal);
if (!state->dontStart) {
setOrientation(Qt::Horizontal);
}
} else if (diffXtoY < -kOrientationThreshold) {
setOrientation(Qt::Vertical);
} else {
@ -143,12 +155,12 @@ void SetupSwipeHandler(
const auto type = e->type();
switch (type) {
case QEvent::Leave: {
if (state->orientation) {
if (state->orientation == Qt::Horizontal) {
processEnd();
}
} break;
case QEvent::MouseMove: {
if (state->orientation) {
if (state->orientation == Qt::Horizontal) {
const auto m = static_cast<QMouseEvent*>(e.get());
if (std::abs(m->pos().y() - state->cursorTop)
> QApplication::startDragDistance()) {
@ -165,6 +177,9 @@ void SetupSwipeHandler(
&& (t->device()->type() == base::TouchDevice::TouchScreen);
if (!Platform::IsMac() && !touchscreen) {
break;
} else if (type == QEvent::TouchBegin) {
// Reset state in case we lost some TouchEnd.
processEnd();
}
const auto &touches = t->touchPoints();
const auto released = [&](int index) {
@ -184,6 +199,9 @@ void SetupSwipeHandler(
: (state->startAt - touches[0].pos()));
} else {
updateWith({
.globalCursor = (touchscreen
? touches[0].screenPos().toPoint()
: QCursor::pos()),
.position = touches[0].pos(),
.delta = state->startAt - touches[0].pos(),
.touch = true,
@ -198,6 +216,9 @@ void SetupSwipeHandler(
const auto phase = w->phase();
if (Platform::IsMac() || phase == Qt::NoScrollPhase) {
break;
} else if (phase == Qt::ScrollBegin) {
// Reset state in case we lost some TouchEnd.
processEnd();
}
const auto cancel = w->buttons()
|| (phase == Qt::ScrollEnd)
@ -206,6 +227,7 @@ void SetupSwipeHandler(
processEnd();
} else {
updateWith({
.globalCursor = w->globalPos(),
.position = QPointF(),
.delta = state->delta - Ui::ScrollDelta(w),
.touch = false,

View file

@ -25,6 +25,7 @@ void SetupSwipeHandler(
not_null<Ui::RpWidget*> widget,
not_null<Ui::ScrollArea*> scroll,
Fn<void(ChatPaintGestureHorizontalData)> update,
Fn<SwipeHandlerFinishData(int)> generateFinishByTop);
Fn<SwipeHandlerFinishData(int)> generateFinishByTop,
rpl::producer<bool> dontStart = nullptr);
} // namespace HistoryView

View file

@ -2940,6 +2940,7 @@ void ListWidget::touchEvent(QTouchEvent *e) {
_touchSelectTimer.cancel();
_touchScroll = _touchSelect = false;
_touchScrollState = Ui::TouchScrollState::Manual;
_touchMaybeSelecting = false;
mouseActionCancel();
return;
}
@ -2960,6 +2961,7 @@ void ListWidget::touchEvent(QTouchEvent *e) {
_touchInProgress = true;
if (_touchScrollState == Ui::TouchScrollState::Auto) {
_touchMaybeSelecting = false;
_touchScrollState = Ui::TouchScrollState::Acceleration;
_touchWaitingAcceleration = true;
_touchAccelerationTime = crl::now();
@ -2967,6 +2969,7 @@ void ListWidget::touchEvent(QTouchEvent *e) {
_touchStart = _touchPos;
} else {
_touchScroll = false;
_touchMaybeSelecting = true;
_touchSelectTimer.callOnce(QApplication::startDragTime());
}
_touchSelect = false;
@ -2979,6 +2982,7 @@ void ListWidget::touchEvent(QTouchEvent *e) {
mouseActionUpdate(_touchPos);
} else if (!_touchScroll && (_touchPos - _touchStart).manhattanLength() >= QApplication::startDragDistance()) {
_touchSelectTimer.cancel();
_touchMaybeSelecting = false;
_touchScroll = true;
touchUpdateSpeed();
}
@ -2996,13 +3000,22 @@ void ListWidget::touchEvent(QTouchEvent *e) {
} break;
case QEvent::TouchEnd: {
if (!_touchInProgress) return;
if (!_touchInProgress) {
return;
}
_touchInProgress = false;
auto weak = Ui::MakeWeak(this);
const auto notMoved = (_touchPos - _touchStart).manhattanLength()
< QApplication::startDragDistance();
if (_touchSelect) {
mouseActionFinish(_touchPos, Qt::RightButton);
QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos);
showContextMenu(&contextMenu, true);
if (notMoved || _touchMaybeSelecting.current()) {
mouseActionFinish(_touchPos, Qt::RightButton);
auto contextMenu = QContextMenuEvent(
QContextMenuEvent::Mouse,
mapFromGlobal(_touchPos),
_touchPos);
showContextMenu(&contextMenu, true);
}
_touchScroll = false;
} else if (_touchScroll) {
if (_touchScrollState == Ui::TouchScrollState::Manual) {
@ -3019,12 +3032,13 @@ void ListWidget::touchEvent(QTouchEvent *e) {
_touchWaitingAcceleration = false;
_touchPrevPosValid = false;
}
} else { // One short tap is like left mouse click.
} else if (notMoved) { // One short tap is like left mouse click.
mouseActionStart(_touchPos, Qt::LeftButton);
mouseActionFinish(_touchPos, Qt::LeftButton);
}
if (weak) {
_touchSelectTimer.cancel();
_touchMaybeSelecting = false;
_touchSelect = false;
}
} break;
@ -3064,6 +3078,10 @@ void ListWidget::touchScrollUpdated(const QPoint &screenPos) {
touchUpdateSpeed();
}
rpl::producer<bool> ListWidget::touchMaybeSelectingValue() const {
return _touchMaybeSelecting.value();
}
void ListWidget::enterEventHook(QEnterEvent *e) {
mouseActionUpdate(QCursor::pos());
return TWidget::enterEventHook(e);
@ -3120,6 +3138,7 @@ void ListWidget::updateDragSelection() {
void ListWidget::onTouchSelect() {
_touchSelect = true;
_touchMaybeSelecting = true;
mouseActionStart(_touchPos, Qt::LeftButton);
}

View file

@ -327,6 +327,7 @@ public:
void selectItemAsGroup(not_null<HistoryItem*> item);
void touchScrollUpdated(const QPoint &screenPos);
[[nodiscard]] rpl::producer<bool> touchMaybeSelectingValue() const;
[[nodiscard]] bool loadedAtTopKnown() const;
[[nodiscard]] bool loadedAtTop() const;
@ -830,6 +831,7 @@ private:
bool _touchSelect = false;
bool _touchInProgress = false;
QPoint _touchStart, _touchPrevPos, _touchPos;
rpl::variable<bool> _touchMaybeSelecting;
base::Timer _touchSelectTimer;
Ui::DraggingScrollManager _selectScroll;

View file

@ -923,7 +923,7 @@ void RepliesWidget::setupSwipeReply() {
});
};
return result;
});
}, _inner->touchMaybeSelectingValue());
}
void RepliesWidget::chooseAttach(