Let wide tables scroll horizontally in the AI boxes

This commit is contained in:
John Preston
2026-07-02 19:06:03 +04:00
parent 4afeb71027
commit 72a75bed0c
5 changed files with 371 additions and 0 deletions
@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/stickers/data_custom_emoji.h"
#include "info/channel_statistics/boosts/giveaway/boost_badge.h" // InfiniteRadialAnimationWidget.
#include "iv/markdown/iv_markdown_article.h"
#include "iv/markdown/iv_markdown_article_scroll_forwarder.h"
#include "iv/markdown/iv_markdown_common.h"
#include "iv/markdown/iv_markdown_prepare.h"
#include "iv/iv_cached_media.h"
@@ -344,15 +345,22 @@ public:
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
void wheelEvent(QWheelEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
bool eventHook(QEvent *e) override;
private:
void requestRepaint(QRect rect);
[[nodiscard]] Iv::Markdown::MarkdownArticle *scrollTarget();
const not_null<Main::Session*> _session;
std::shared_ptr<Iv::Markdown::MediaRuntime> _mediaRuntime;
Iv::Markdown::MarkdownArticle _article;
std::unique_ptr<Ui::ChatTheme> _theme;
std::unique_ptr<Ui::ChatStyle> _style;
Iv::Markdown::MarkdownArticleScrollForwarder _scrollForwarder;
int _paletteVersion = -1;
bool _hasArticle = false;
@@ -1049,6 +1057,7 @@ ComposeAiRichBody::ComposeAiRichBody(
, _style(std::make_unique<Ui::ChatStyle>(session->colorIndicesValue())) {
_style->apply(_theme.get());
_paletteVersion = _style->paletteVersion();
setAttribute(Qt::WA_AcceptTouchEvents);
const auto weak = base::make_weak(this);
_article.setTextRepaintCallbacks(
@@ -1076,6 +1085,7 @@ ComposeAiRichBody::~ComposeAiRichBody() {
}
void ComposeAiRichBody::setPage(std::shared_ptr<const Iv::RichPage> page) {
_scrollForwarder.reset(scrollTarget());
const auto richLimits = Iv::ResolveRichMessageLimits(_session);
auto prepared = Iv::Markdown::TryPrepareNativeInstantView({
.richPage = page,
@@ -1105,6 +1115,39 @@ void ComposeAiRichBody::requestRepaint(QRect rect) {
});
}
Iv::Markdown::MarkdownArticle *ComposeAiRichBody::scrollTarget() {
return _hasArticle ? &_article : nullptr;
}
void ComposeAiRichBody::wheelEvent(QWheelEvent *e) {
_scrollForwarder.handleWheel(scrollTarget(), e, QPoint());
}
void ComposeAiRichBody::mousePressEvent(QMouseEvent *e) {
if (!_scrollForwarder.handleMousePress(scrollTarget(), e, QPoint())) {
RpWidget::mousePressEvent(e);
}
}
void ComposeAiRichBody::mouseMoveEvent(QMouseEvent *e) {
if (!_scrollForwarder.handleMouseMove(scrollTarget(), e, QPoint())) {
RpWidget::mouseMoveEvent(e);
}
}
void ComposeAiRichBody::mouseReleaseEvent(QMouseEvent *e) {
if (!_scrollForwarder.handleMouseRelease(scrollTarget(), e, QPoint())) {
RpWidget::mouseReleaseEvent(e);
}
}
bool ComposeAiRichBody::eventHook(QEvent *e) {
if (_scrollForwarder.handleTouchHook(scrollTarget(), this, e, QPoint())) {
return true;
}
return RpWidget::eventHook(e);
}
int ComposeAiRichBody::resizeGetHeight(int newWidth) {
return (_hasArticle && newWidth > 0)
? _article.resizeGetHeight(newWidth)
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "iv/iv_cached_media.h"
#include "iv/iv_rich_page.h"
#include "iv/markdown/iv_markdown_article.h"
#include "iv/markdown/iv_markdown_article_scroll_forwarder.h"
#include "iv/markdown/iv_markdown_common.h"
#include "iv/markdown/iv_markdown_prepare.h"
#include "lang/lang_keys.h"
@@ -84,6 +85,11 @@ public:
private:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void wheelEvent(QWheelEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
bool eventHook(QEvent *e) override;
void paintArticle(Painter &p, QRect clip);
void requestArticleRepaint(QRect articleRect);
@@ -91,6 +97,7 @@ private:
[[nodiscard]] int controlRowHeight() const;
[[nodiscard]] QRect articleRect() const;
[[nodiscard]] Iv::Markdown::MarkdownArticle *scrollTarget();
const not_null<Main::Session*> _session;
const object_ptr<Ui::FlatLabel> _selector;
@@ -100,6 +107,7 @@ private:
Iv::Markdown::MarkdownArticle _article;
std::unique_ptr<Ui::ChatTheme> _theme;
std::unique_ptr<Ui::ChatStyle> _style;
Iv::Markdown::MarkdownArticleScrollForwarder _scrollForwarder;
int _articleHeight = 0;
int _paletteVersion = -1;
bool _hasArticle = false;
@@ -128,6 +136,7 @@ ResponseIsland::ResponseIsland(
, _style(std::make_unique<Ui::ChatStyle>(session->colorIndicesValue())) {
_style->apply(_theme.get());
_paletteVersion = _style->paletteVersion();
setAttribute(Qt::WA_AcceptTouchEvents);
_selector->setMarkedText(SelectorTitle(language));
_selector->setClickHandlerFilter([=](const auto &...) {
@@ -210,6 +219,52 @@ QRect ResponseIsland::articleRect() const {
return QRect(0, top, width(), _articleHeight);
}
Iv::Markdown::MarkdownArticle *ResponseIsland::scrollTarget() {
return _hasArticle ? &_article : nullptr;
}
void ResponseIsland::wheelEvent(QWheelEvent *e) {
_scrollForwarder.handleWheel(scrollTarget(), e, articleRect().topLeft());
}
void ResponseIsland::mousePressEvent(QMouseEvent *e) {
if (!_scrollForwarder.handleMousePress(
scrollTarget(),
e,
articleRect().topLeft())) {
RpWidget::mousePressEvent(e);
}
}
void ResponseIsland::mouseMoveEvent(QMouseEvent *e) {
if (!_scrollForwarder.handleMouseMove(
scrollTarget(),
e,
articleRect().topLeft())) {
RpWidget::mouseMoveEvent(e);
}
}
void ResponseIsland::mouseReleaseEvent(QMouseEvent *e) {
if (!_scrollForwarder.handleMouseRelease(
scrollTarget(),
e,
articleRect().topLeft())) {
RpWidget::mouseReleaseEvent(e);
}
}
bool ResponseIsland::eventHook(QEvent *e) {
if (_scrollForwarder.handleTouchHook(
scrollTarget(),
this,
e,
articleRect().topLeft())) {
return true;
}
return RpWidget::eventHook(e);
}
void ResponseIsland::paintArticle(Painter &p, QRect clip) {
if (!_hasArticle) {
return;
@@ -0,0 +1,207 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "iv/markdown/iv_markdown_article_scroll_forwarder.h"
#include "base/qt/qt_common_adapters.h"
#include "base/algorithm.h"
#include "iv/markdown/iv_markdown_article.h"
#include <QtGui/QMouseEvent>
#include <QtGui/QTouchEvent>
#include <QtGui/QWheelEvent>
#include <QtWidgets/QApplication>
#include <cmath>
namespace Iv::Markdown {
namespace {
[[nodiscard]] QPoint LocalPosition(QWheelEvent *e) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
return e->position().toPoint();
#else // Qt >= 6.0
return e->pos();
#endif // Qt >= 6.0
}
} // namespace
void MarkdownArticleScrollForwarder::handleWheel(
MarkdownArticle *article,
QWheelEvent *e,
QPoint articleTopLeft) {
if (!article) {
(void)_scrollDirectionLock.update(e->phase(), {});
e->ignore();
return;
}
const auto delta = Ui::ScrollDeltaF(e);
const auto locked = _scrollDirectionLock.update(e->phase(), delta);
const auto horizontal = locked
? (*locked == Qt::Horizontal)
: (std::abs(delta.x()) > std::abs(delta.y()));
const auto local = LocalPosition(e) - articleTopLeft;
if (!article->horizontalScrollHit(local).scrollable) {
e->ignore();
return;
}
if (horizontal) {
(void)article->consumeHorizontalScroll(
local,
int(std::round(delta.x())));
e->accept();
} else {
e->ignore();
}
}
bool MarkdownArticleScrollForwarder::handleMousePress(
MarkdownArticle *article,
QMouseEvent *e,
QPoint articleTopLeft) {
if (!article || e->button() != Qt::LeftButton) {
return false;
}
if (!article->beginHorizontalScroll(e->pos() - articleTopLeft, false)) {
return false;
}
_mouseDrag = true;
e->accept();
return true;
}
bool MarkdownArticleScrollForwarder::handleMouseMove(
MarkdownArticle *article,
QMouseEvent *e,
QPoint articleTopLeft) {
if (!_mouseDrag) {
return false;
}
if (article) {
(void)article->updateHorizontalScroll(e->pos() - articleTopLeft);
}
e->accept();
return true;
}
bool MarkdownArticleScrollForwarder::handleMouseRelease(
MarkdownArticle *article,
QMouseEvent *e,
QPoint articleTopLeft) {
if (!_mouseDrag || e->button() != Qt::LeftButton) {
return false;
}
_mouseDrag = false;
if (article) {
(void)article->updateHorizontalScroll(e->pos() - articleTopLeft);
article->endHorizontalScroll();
}
e->accept();
return true;
}
bool MarkdownArticleScrollForwarder::handleTouchHook(
MarkdownArticle *article,
QWidget *widget,
QEvent *e,
QPoint articleTopLeft) {
if (e->type() != QEvent::TouchBegin
&& e->type() != QEvent::TouchUpdate
&& e->type() != QEvent::TouchEnd
&& e->type() != QEvent::TouchCancel) {
return false;
}
const auto touch = static_cast<QTouchEvent*>(e);
if (touch->device()->type() != base::TouchDevice::TouchScreen) {
return false;
}
return handleTouch(article, widget, touch, articleTopLeft);
}
bool MarkdownArticleScrollForwarder::handleTouch(
MarkdownArticle *article,
QWidget *widget,
QTouchEvent *e,
QPoint articleTopLeft) {
const auto wasActive = _touchScroll;
if (e->type() == QEvent::TouchCancel) {
_pendingTouchPoint = std::nullopt;
if (!base::take(_touchScroll)) {
return false;
}
if (article) {
article->endHorizontalScroll();
}
e->accept();
return true;
}
if (!article || e->touchPoints().isEmpty()) {
return wasActive;
}
const auto local = widget->mapFromGlobal(
e->touchPoints().cbegin()->screenPos().toPoint()) - articleTopLeft;
switch (e->type()) {
case QEvent::TouchBegin: {
_pendingTouchPoint = std::nullopt;
const auto hit = article->horizontalScrollHit(local);
_touchScroll = hit.overScrollbar
&& article->beginHorizontalScroll(local, false);
if (!_touchScroll && hit.overViewport) {
_pendingTouchPoint = local;
}
if (_touchScroll) {
e->accept();
}
} break;
case QEvent::TouchUpdate:
if (_touchScroll) {
(void)article->updateHorizontalScroll(local);
e->accept();
} else if (_pendingTouchPoint) {
const auto delta = local - *_pendingTouchPoint;
if (delta.manhattanLength() < QApplication::startDragDistance()) {
break;
}
const auto horizontal = (std::abs(delta.x()) > std::abs(delta.y()));
if (!horizontal) {
_pendingTouchPoint = std::nullopt;
break;
}
_touchScroll = article->beginHorizontalScroll(
*base::take(_pendingTouchPoint),
true);
if (_touchScroll) {
(void)article->updateHorizontalScroll(local);
e->accept();
}
}
break;
case QEvent::TouchEnd:
_pendingTouchPoint = std::nullopt;
if (base::take(_touchScroll)) {
article->endHorizontalScroll();
e->accept();
}
break;
default:
break;
}
return wasActive || _touchScroll;
}
void MarkdownArticleScrollForwarder::reset(MarkdownArticle *article) {
const auto mouse = base::take(_mouseDrag);
const auto touch = base::take(_touchScroll);
if (article && (mouse || touch)) {
article->endHorizontalScroll();
}
_pendingTouchPoint = std::nullopt;
_scrollDirectionLock.reset();
}
} // namespace Iv::Markdown
@@ -0,0 +1,64 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/ui_utility.h"
#include <optional>
#include <QtCore/QPoint>
class QEvent;
class QMouseEvent;
class QTouchEvent;
class QWheelEvent;
class QWidget;
namespace Iv::Markdown {
class MarkdownArticle;
class MarkdownArticleScrollForwarder final {
public:
void handleWheel(
MarkdownArticle *article,
QWheelEvent *e,
QPoint articleTopLeft);
[[nodiscard]] bool handleMousePress(
MarkdownArticle *article,
QMouseEvent *e,
QPoint articleTopLeft);
[[nodiscard]] bool handleMouseMove(
MarkdownArticle *article,
QMouseEvent *e,
QPoint articleTopLeft);
[[nodiscard]] bool handleMouseRelease(
MarkdownArticle *article,
QMouseEvent *e,
QPoint articleTopLeft);
[[nodiscard]] bool handleTouchHook(
MarkdownArticle *article,
QWidget *widget,
QEvent *e,
QPoint articleTopLeft);
void reset(MarkdownArticle *article);
private:
[[nodiscard]] bool handleTouch(
MarkdownArticle *article,
QWidget *widget,
QTouchEvent *e,
QPoint articleTopLeft);
Ui::ScrollDirectionLock _scrollDirectionLock;
std::optional<QPoint> _pendingTouchPoint;
bool _mouseDrag = false;
bool _touchScroll = false;
};
} // namespace Iv::Markdown
+2
View File
@@ -48,6 +48,8 @@ PRIVATE
iv/markdown/iv_markdown_article_layout_structure.h
iv/markdown/iv_markdown_article_paint.cpp
iv/markdown/iv_markdown_article_paint.h
iv/markdown/iv_markdown_article_scroll_forwarder.cpp
iv/markdown/iv_markdown_article_scroll_forwarder.h
iv/markdown/iv_markdown_article_selection.cpp
iv/markdown/iv_markdown_article_selection.h
iv/markdown/iv_markdown_article_text.cpp