mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/telegramdesktop/tdesktop
synced 2026-09-20 16:13:46 +08:00
Follow the scroll direction in swipe gestures.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -811,19 +811,27 @@ Widget::Widget(
|
||||
}
|
||||
|
||||
void Widget::setupSwipeBack() {
|
||||
const auto isMainList = [=] {
|
||||
// The main menu is dragged out from the left side of the window, so it
|
||||
// always waits past the end of the chats filters, at whichever of them
|
||||
// the swipe towards it scrolls to nothing - the first one with the
|
||||
// natural scrolling and the last one without it. Standing anywhere else
|
||||
// in the filters that swipe still has a filter to move to.
|
||||
const auto noNearChatsFilter = [=](bool isNext) {
|
||||
const auto current = controller()->activeChatsFilterCurrent();
|
||||
const auto &chatsFilters = session().data().chatsFilters();
|
||||
if (chatsFilters.has()) {
|
||||
return chatsFilters.defaultId() == current;
|
||||
if (!chatsFilters.has()) {
|
||||
return !current;
|
||||
}
|
||||
return !current;
|
||||
return !Window::CheckAndJumpToNearChatsFilter(
|
||||
controller(),
|
||||
isNext,
|
||||
false);
|
||||
};
|
||||
|
||||
auto update = [=](Ui::Controls::SwipeContextData data) {
|
||||
data.cursorTop -= _inner->y();
|
||||
if (data.translation != 0) {
|
||||
if (data.translation < 0
|
||||
if (data.visualTranslation() < 0
|
||||
&& _inner
|
||||
&& (Core::App().settings().quickDialogAction()
|
||||
!= Ui::QuickDialogAction::Disabled)) {
|
||||
@@ -862,7 +870,8 @@ void Widget::setupSwipeBack() {
|
||||
if (_childListShown.current()) {
|
||||
return Ui::Controls::SwipeHandlerFinishData();
|
||||
}
|
||||
const auto isRightToLeft = data.direction == Qt::RightToLeft;
|
||||
const auto isRightToLeft = data.fingerDirection() == Qt::RightToLeft;
|
||||
const auto scrollRightToLeft = data.direction == Qt::RightToLeft;
|
||||
const auto action = Core::App().settings().quickDialogAction();
|
||||
const auto isDisabled = action == Ui::QuickDialogAction::Disabled;
|
||||
if (_inner) {
|
||||
@@ -936,27 +945,33 @@ void Widget::setupSwipeBack() {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (isRightToLeft && isMainList()) {
|
||||
_swipeBackIconMirrored = true;
|
||||
return Ui::Controls::DefaultSwipeBackHandlerFinishData([=] {
|
||||
_swipeBackIconMirrored = false;
|
||||
_swipeBackData = {};
|
||||
if (isMainList()) {
|
||||
showMainMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
const auto next = !scrollRightToLeft;
|
||||
if (session().data().chatsFilters().has() && isDisabled) {
|
||||
_swipeBackMirrored = !isRightToLeft;
|
||||
using namespace Window;
|
||||
const auto next = !isRightToLeft;
|
||||
if (CheckAndJumpToNearChatsFilter(controller(), next, false)) {
|
||||
_swipeBackMirrored = !scrollRightToLeft;
|
||||
return Ui::Controls::DefaultSwipeBackHandlerFinishData([=] {
|
||||
_swipeBackData = {};
|
||||
CheckAndJumpToNearChatsFilter(controller(), next, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
// With a quick action other than moving between the chats filters the
|
||||
// swipe never moves between them at all, so the main menu is not
|
||||
// waiting past their end - it is dragged out from any of them.
|
||||
const auto mainMenuHere = [=] {
|
||||
return !isDisabled || noNearChatsFilter(next);
|
||||
};
|
||||
if (isRightToLeft && mainMenuHere()) {
|
||||
_swipeBackIconMirrored = true;
|
||||
return Ui::Controls::DefaultSwipeBackHandlerFinishData([=] {
|
||||
_swipeBackIconMirrored = false;
|
||||
_swipeBackData = {};
|
||||
if (mainMenuHere()) {
|
||||
showMainMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Ui::Controls::SwipeHandlerFinishData();
|
||||
};
|
||||
|
||||
@@ -476,8 +476,8 @@ void PaintRow(
|
||||
&& !context.quickActionContext->ripple
|
||||
&& (history->peer->id.value
|
||||
== context.quickActionContext->data.msgBareId)) {
|
||||
swipeTranslation = context.quickActionContext->data.exactTranslation
|
||||
* -2;
|
||||
swipeTranslation
|
||||
= context.quickActionContext->data.visualExactTranslation() * -2;
|
||||
}
|
||||
if (swipeTranslation) {
|
||||
p.translate(-swipeTranslation, 0);
|
||||
|
||||
@@ -1707,15 +1707,14 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
const auto hasTranslation = context.gestureHorizontal.translation
|
||||
&& (context.gestureHorizontal.msgBareId
|
||||
== item->fullId().msg.bare);
|
||||
const auto shift = context.gestureHorizontal.visualTranslation();
|
||||
if (hasTranslation) {
|
||||
p.translate(context.gestureHorizontal.translation, 0);
|
||||
p.translate(shift, 0);
|
||||
update(
|
||||
QRect(
|
||||
st::historyPhotoLeft
|
||||
+ context.gestureHorizontal.translation,
|
||||
st::historyPhotoLeft + std::min(shift, 0),
|
||||
userpicTop,
|
||||
st::msgPhotoSize
|
||||
- context.gestureHorizontal.translation,
|
||||
st::msgPhotoSize + std::abs(shift),
|
||||
st::msgPhotoSize));
|
||||
}
|
||||
if (const auto from = item->displayFrom()) {
|
||||
@@ -1754,7 +1753,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
Unexpected("Corrupt forwarded information in message.");
|
||||
}
|
||||
if (hasTranslation) {
|
||||
p.translate(-_gestureHorizontal.translation, 0);
|
||||
p.translate(-shift, 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -3241,15 +3241,14 @@ void ListWidget::paintUserpics(
|
||||
const auto hasTranslation = context.gestureHorizontal.translation
|
||||
&& (context.gestureHorizontal.msgBareId
|
||||
== item->fullId().msg.bare);
|
||||
const auto shift = context.gestureHorizontal.visualTranslation();
|
||||
if (hasTranslation) {
|
||||
p.translate(context.gestureHorizontal.translation, 0);
|
||||
p.translate(shift, 0);
|
||||
update(
|
||||
QRect(
|
||||
st::historyPhotoLeft
|
||||
+ context.gestureHorizontal.translation,
|
||||
st::historyPhotoLeft + std::min(shift, 0),
|
||||
userpicTop,
|
||||
st::msgPhotoSize
|
||||
- context.gestureHorizontal.translation,
|
||||
st::msgPhotoSize + std::abs(shift),
|
||||
st::msgPhotoSize));
|
||||
}
|
||||
if (const auto from = view->displayFrom()) {
|
||||
@@ -3288,7 +3287,7 @@ void ListWidget::paintUserpics(
|
||||
Unexpected("Corrupt forwarded information in message.");
|
||||
}
|
||||
if (hasTranslation) {
|
||||
p.translate(-context.gestureHorizontal.translation, 0);
|
||||
p.translate(-shift, 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1723,8 +1723,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||
|
||||
const auto hasGesture = context.gestureHorizontal.translation
|
||||
&& (context.gestureHorizontal.msgBareId == item->fullId().msg.bare);
|
||||
const auto gestureShift = context.gestureHorizontal.visualTranslation();
|
||||
if (hasGesture) {
|
||||
p.translate(context.gestureHorizontal.translation, 0);
|
||||
p.translate(gestureShift, 0);
|
||||
}
|
||||
const auto selectionModeResult = delegate()->elementInSelectionMode(this);
|
||||
const auto selectionTranslation = (selectionModeResult.progress > 0)
|
||||
@@ -2228,10 +2229,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||
}
|
||||
}
|
||||
if (hasGesture) {
|
||||
p.translate(-context.gestureHorizontal.translation, 0);
|
||||
p.translate(-gestureShift, 0);
|
||||
if (context.reactionInfo && context.reactionInfo->effectPaint) {
|
||||
const auto shift = context.gestureHorizontal.translation;
|
||||
context.reactionInfo->effectOffset += QPoint(shift, 0);
|
||||
context.reactionInfo->effectOffset += QPoint(gestureShift, 0);
|
||||
}
|
||||
|
||||
constexpr auto kShiftRatio = 1.5;
|
||||
@@ -2239,13 +2239,17 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||
constexpr auto kMaxHeightRatio = 3.5;
|
||||
constexpr auto kStrokeWidth = 2.;
|
||||
constexpr auto kWaveWidth = 10.;
|
||||
const auto mirrored = !context.gestureHorizontal.inverted;
|
||||
const auto isLeftSize = !context.outbg
|
||||
|| (delegate()->elementChatMode() == ElementChatMode::Wide);
|
||||
const auto ratio = std::min(context.gestureHorizontal.ratio, 1.);
|
||||
const auto reachRatio = context.gestureHorizontal.reachRatio;
|
||||
const auto size = st::historyFastShareSize;
|
||||
const auto bubbleRight = mirrored
|
||||
? (width() - g.x())
|
||||
: rect::right(g);
|
||||
const auto outerWidth = st::historySwipeIconSkip
|
||||
+ (isLeftSize ? rect::right(g) : width())
|
||||
+ (isLeftSize ? bubbleRight : width())
|
||||
+ ((g.height() < size * kMaxHeightRatio)
|
||||
? rightActionSize().value_or(QSize()).width()
|
||||
: 0);
|
||||
@@ -2274,6 +2278,10 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||
pen.setWidthF(strokeWidth - (1. * (reachScale / kBouncePart)));
|
||||
const auto arcRect = rect - Margins(strokeWidth);
|
||||
p.save();
|
||||
if (mirrored) {
|
||||
p.translate(width(), 0);
|
||||
p.scale(-1., 1.);
|
||||
}
|
||||
{
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
@@ -88,6 +88,7 @@ void SetupSwipeHandler(SwipeHandlerArgs &&args) {
|
||||
QPointF position;
|
||||
QPointF delta;
|
||||
bool touch = false;
|
||||
bool inverted = false;
|
||||
};
|
||||
struct State {
|
||||
base::unique_qptr<QObject> filter;
|
||||
@@ -107,6 +108,7 @@ void SetupSwipeHandler(SwipeHandlerArgs &&args) {
|
||||
bool started = false;
|
||||
bool reached = false;
|
||||
bool touch = false;
|
||||
bool inverted = false;
|
||||
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
@@ -147,6 +149,7 @@ void SetupSwipeHandler(SwipeHandlerArgs &&args) {
|
||||
state->data.exactTranslation = exactTranslation
|
||||
* state->directionInt;
|
||||
state->data.cursorTop = state->cursorPosition.y();
|
||||
state->data.inverted = state->inverted;
|
||||
update(state->data);
|
||||
};
|
||||
const auto setOrientation = [=](std::optional<Qt::Orientation> o) {
|
||||
@@ -207,6 +210,7 @@ void SetupSwipeHandler(SwipeHandlerArgs &&args) {
|
||||
update(state->data);
|
||||
};
|
||||
const auto updateWith = [=, generateFinish = args.init](UpdateArgs args) {
|
||||
state->inverted = args.inverted;
|
||||
const auto fillFinishByTop = [&] {
|
||||
if (!args.delta.x()) {
|
||||
return;
|
||||
@@ -220,6 +224,7 @@ void SetupSwipeHandler(SwipeHandlerArgs &&args) {
|
||||
state->finishByTopData = generateFinish({
|
||||
.cursorPosition = state->cursorPosition,
|
||||
.direction = *state->direction,
|
||||
.inverted = state->inverted,
|
||||
});
|
||||
state->threshold = style::ConvertFloatScale(kThresholdWidth)
|
||||
* state->finishByTopData.speedRatio;
|
||||
@@ -352,6 +357,7 @@ void SetupSwipeHandler(SwipeHandlerArgs &&args) {
|
||||
.position = touches[0].pos(),
|
||||
.delta = state->startAt - touches[0].pos(),
|
||||
.touch = true,
|
||||
.inverted = true,
|
||||
};
|
||||
updateWith(args);
|
||||
}
|
||||
@@ -378,13 +384,12 @@ void SetupSwipeHandler(SwipeHandlerArgs &&args) {
|
||||
if (cancel) {
|
||||
processEnd();
|
||||
} else {
|
||||
const auto invert = (w->inverted() ? -1 : 1);
|
||||
const auto delta = Ui::ScrollDeltaF(w) * invert;
|
||||
updateWith({
|
||||
.globalCursor = w->globalPosition().toPoint(),
|
||||
.position = QPointF(),
|
||||
.delta = state->delta + delta * kSwipeSlow,
|
||||
.delta = state->delta - Ui::ScrollDeltaF(w) * kSwipeSlow,
|
||||
.touch = false,
|
||||
.inverted = w->inverted(),
|
||||
});
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -20,17 +20,44 @@ struct SwipeContextData final {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
// The system reports the gesture deltas in the scroll direction, so with
|
||||
// the natural scrolling turned off they are the opposite of the finger
|
||||
// movement. The translation sign follows the gesture and tells which of
|
||||
// the two actions was started, while the content should follow the
|
||||
// finger, so these give the direction to paint it moving in.
|
||||
[[nodiscard]] int visualTranslation() const {
|
||||
return inverted ? translation : -translation;
|
||||
}
|
||||
[[nodiscard]] float64 visualExactTranslation() const {
|
||||
return inverted ? exactTranslation : -exactTranslation;
|
||||
}
|
||||
|
||||
float64 ratio = 0.;
|
||||
float64 reachRatio = 0.;
|
||||
float64 exactTranslation = 0.;
|
||||
int64 msgBareId = 0;
|
||||
int translation = 0;
|
||||
int cursorTop = 0;
|
||||
bool inverted = false;
|
||||
};
|
||||
|
||||
struct SwipeHandlerInitData final {
|
||||
// The direction follows the scroll, which is what a gesture that just
|
||||
// moves between two equal things should use. A gesture that drags out
|
||||
// something living at a fixed side of the window, like the main menu,
|
||||
// has to be started by moving the finger towards that side instead, so
|
||||
// it should look at this one and stay the same with any scroll setting.
|
||||
[[nodiscard]] Qt::LayoutDirection fingerDirection() const {
|
||||
return inverted
|
||||
? direction
|
||||
: ((direction == Qt::LeftToRight)
|
||||
? Qt::RightToLeft
|
||||
: Qt::LeftToRight);
|
||||
}
|
||||
|
||||
QPoint cursorPosition;
|
||||
Qt::LayoutDirection direction = Qt::LeftToRight;
|
||||
bool inverted = false;
|
||||
};
|
||||
|
||||
struct SwipeBackResult final {
|
||||
|
||||
@@ -1011,7 +1011,7 @@ void MainMenu::setupSwipe() {
|
||||
}
|
||||
|
||||
auto update = [=](Ui::Controls::SwipeContextData data) {
|
||||
if (data.translation < 0) {
|
||||
if (data.visualTranslation() < 0) {
|
||||
if (!_swipeBackData.callback) {
|
||||
_swipeBackData = Ui::Controls::SetupSwipeBack(
|
||||
this,
|
||||
@@ -1030,7 +1030,7 @@ void MainMenu::setupSwipe() {
|
||||
};
|
||||
|
||||
auto init = [=](Ui::Controls::SwipeHandlerInitData data) {
|
||||
if (data.direction != Qt::LeftToRight) {
|
||||
if (data.fingerDirection() != Qt::LeftToRight) {
|
||||
return Ui::Controls::SwipeHandlerFinishData();
|
||||
}
|
||||
if (_emojiStatusPanel && _emojiStatusPanel->hasFocus()) {
|
||||
|
||||
Reference in New Issue
Block a user