From 4df5372dab24d5d459c8a14f1059dd96e5b761e1 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 30 May 2024 22:33:24 +0400
Subject: [PATCH] Support chat preview on touchscreens.

---
 .../dialogs/dialogs_inner_widget.cpp          | 44 +++++++++++++++++++
 .../dialogs/dialogs_inner_widget.h            |  8 ++++
 .../SourceFiles/dialogs/dialogs_widget.cpp    | 13 ++++++
 Telegram/SourceFiles/dialogs/dialogs_widget.h |  1 +
 Telegram/lib_ui                               |  2 +-
 5 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
index f90993a19..7633e4d2f 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
@@ -1316,6 +1316,9 @@ void InnerWidget::paintSearchInTopic(
 }
 
 void InnerWidget::mouseMoveEvent(QMouseEvent *e) {
+	if (_chatPreviewTouchGlobal) {
+		return;
+	}
 	const auto globalPosition = e->globalPos();
 	if (!_lastMousePosition) {
 		_lastMousePosition = globalPosition;
@@ -1333,6 +1336,8 @@ void InnerWidget::mouseMoveEvent(QMouseEvent *e) {
 void InnerWidget::cancelChatPreview() {
 	_chatPreviewTimer.cancel();
 	_chatPreviewWillBeFor = {};
+	_chatPreviewTouchLocal = {};
+	_chatPreviewTouchGlobal = {};
 }
 
 void InnerWidget::clearIrrelevantState() {
@@ -2396,10 +2401,14 @@ void InnerWidget::fillArchiveSearchMenu(not_null<Ui::PopupMenu*> menu) {
 
 void InnerWidget::showChatPreview(bool onlyUserpic) {
 	const auto key = base::take(_chatPreviewWillBeFor);
+	const auto touchGlobal = base::take(_chatPreviewTouchGlobal);
 	cancelChatPreview();
 	if (!pressShowsPreview(onlyUserpic) || key != computeChatPreviewRow()) {
 		return;
 	}
+	if (onlyUserpic && touchGlobal) {
+		_touchCancelRequests.fire({});
+	}
 	ClickHandler::unpressed();
 	mousePressReleased(QCursor::pos(), Qt::NoButton, Qt::NoModifier);
 
@@ -2538,6 +2547,41 @@ void InnerWidget::parentGeometryChanged() {
 	}
 }
 
+void InnerWidget::processTouchEvent(not_null<QTouchEvent*> e) {
+	const auto point = e->touchPoints().empty()
+		? std::optional<QPoint>()
+		: e->touchPoints().front().screenPos().toPoint();
+	switch (e->type()) {
+	case QEvent::TouchBegin: {
+		if (!point) {
+			return;
+		}
+		selectByMouse(*point);
+		const auto onlyUserpic = true;
+		if (pressShowsPreview(onlyUserpic)) {
+			_chatPreviewTouchGlobal = point;
+			_chatPreviewWillBeFor = computeChatPreviewRow();
+			_chatPreviewTimer.callOnce(kChatPreviewDelay);
+		}
+	} break;
+
+	case QEvent::TouchUpdate: {
+		if (!_chatPreviewTouchGlobal || !point) {
+			return;
+		}
+		const auto delta = (*_chatPreviewTouchGlobal - *point);
+		if (delta.manhattanLength() > _st->photoSize) {
+			cancelChatPreview();
+		}
+	} break;
+
+	case QEvent::TouchEnd:
+	case QEvent::TouchCancel: if (_chatPreviewTouchGlobal) {
+		cancelChatPreview();
+	} break;
+	}
+}
+
 void InnerWidget::applySearchState(SearchState state) {
 	if (_searchState == state) {
 		return;
diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h
index b41a4aeb3..ed94e0b58 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h
+++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h
@@ -174,6 +174,11 @@ public:
 
 	void parentGeometryChanged();
 
+	void processTouchEvent(not_null<QTouchEvent*> e);
+	[[nodiscard]] rpl::producer<> touchCancelRequests() const {
+		return _touchCancelRequests.events();
+	}
+
 protected:
 	void visibleTopBottomUpdated(
 		int visibleTop,
@@ -520,6 +525,9 @@ private:
 	base::Timer _chatPreviewTimer;
 	Key _chatPreviewWillBeFor;
 	Key _chatPreviewKey;
+	std::optional<QPoint> _chatPreviewTouchLocal;
+	std::optional<QPoint> _chatPreviewTouchGlobal;
+	rpl::event_stream<> _touchCancelRequests;
 
 	rpl::variable<ChildListShown> _childListShown;
 	float64 _narrowRatio = 0.;
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
index b3b5a31f9..092f2f4cc 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
@@ -473,6 +473,7 @@ Widget::Widget(
 	updateSearchFromVisibility(true);
 	setupSupportMode();
 	setupScrollUpButton();
+	setupTouchChatPreview();
 
 	const auto overscrollBg = [=] {
 		return anim::color(
@@ -655,6 +656,18 @@ void Widget::setupScrollUpButton() {
 	updateScrollUpVisibility();
 }
 
+void Widget::setupTouchChatPreview() {
+	_scroll->setCustomTouchProcess([=](not_null<QTouchEvent*> e) {
+		_inner->processTouchEvent(e);
+		return false;
+	});
+	_inner->touchCancelRequests() | rpl::start_with_next([=] {
+		QTouchEvent ev(QEvent::TouchCancel);
+		ev.setTimestamp(crl::now());
+		QGuiApplication::sendEvent(_scroll, &ev);
+	}, _inner->lifetime());
+}
+
 void Widget::setupMoreChatsBar() {
 	if (_layout == Layout::Child) {
 		return;
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h
index ec604f61c..d7e8edaeb 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.h
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h
@@ -179,6 +179,7 @@ private:
 	[[nodiscard]] const std::vector<Data::ReactionId> &searchInTags() const;
 
 	void setupSupportMode();
+	void setupTouchChatPreview();
 	void setupConnectingWidget();
 	void setupMainMenuToggle();
 	void setupMoreChatsBar();
diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index 495ea0af5..33aac93b1 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit 495ea0af50da469fb30769ac2d78251e4279a746
+Subproject commit 33aac93b160d4cd30119c8859de722e28512902b