Improve style, add zoom controls.

This commit is contained in:
John Preston
2026-05-15 15:08:40 +04:00
parent 7188ab0644
commit e467bc7da9
29 changed files with 1760 additions and 1572 deletions
+21 -1
View File
@@ -27,6 +27,10 @@ private:
};
```
## No consecutive empty lines
Use at most one empty line between declarations, definitions, include groups, or logical blocks. Two or more empty lines in a row add visual noise without adding structure.
## Multi-line expressions — operators at the start of continuation lines
When splitting an expression across multiple lines, place operators (like `&&`, `||`, `;`, `+`, etc.) at the **beginning** of continuation lines, not at the end of the previous line. This makes it immediately obvious from the left edge whether a line is a continuation or new code.
@@ -355,6 +359,22 @@ void MyWidget::paintEvent(QPaintEvent *e) {
When there are multiple local classes, put **all class definitions first**, then **all method definitions** after. This keeps the declarations readable as an overview.
## Do not repeat [[nodiscard]] on method definitions
Put `[[nodiscard]]` on the method declaration inside the class. Do not repeat it on the out-of-class method definition. Free functions may keep `[[nodiscard]]` on their definition when that is the only declaration.
```cpp
// BAD - duplicated attribute on the definition:
[[nodiscard]] int MyClass::value() const {
return _value;
}
// GOOD - declaration carries the attribute, definition stays clean:
int MyClass::value() const {
return _value;
}
```
## Use RAII for resource cleanup
When working with raw resources (Win32 HANDLEs, file descriptors, COM objects), use `gsl::finally` or a dedicated RAII wrapper for cleanup instead of calling release functions manually. Manual cleanup breaks when early returns are added later.
@@ -472,7 +492,7 @@ const auto state = lifetime.make_state<State>();
## Use trailing return type when the return type doesn't fit on one line
When a function's return type is long enough that the declaration would need a line break between the return type and the function name, use trailing return type syntax (`auto ... -> Type`) to keep the function name on the opening line.
When a function's return type is long enough that the declaration or definition would need a line break between the return type and the function name, use trailing return type syntax (`auto ... -> Type`) to keep the function name on the opening line.
```cpp
// BAD - return type orphaned on its own line:
+2
View File
@@ -1292,6 +1292,8 @@ PRIVATE
intro/intro_step.h
intro/intro_widget.cpp
intro/intro_widget.h
iv/iv_cached_media.cpp
iv/iv_cached_media.h
iv/iv_delegate_impl.cpp
iv/iv_delegate_impl.h
iv/iv_instance.cpp
+17 -11
View File
@@ -24,6 +24,8 @@ namespace Core {
namespace {
constexpr auto kInitialVideoQuality = 480; // Start with SD.
constexpr auto kMinIvZoom = 25;
constexpr auto kMaxIvZoom = 400;
[[nodiscard]] int DefaultIvZoom() {
const auto exact = cScale() * 100 / cScreenScale();
@@ -35,7 +37,10 @@ constexpr auto kInitialVideoQuality = 480; // Start with SD.
}
[[nodiscard]] int ResolveIvZoom(int value) {
return (value > 0) ? value : DefaultIvZoom();
return std::clamp(
(value > 0) ? value : DefaultIvZoom(),
kMinIvZoom,
kMaxIvZoom);
}
[[nodiscard]] WindowPosition Deserialize(const QByteArray &data) {
@@ -1798,25 +1803,26 @@ rpl::producer<int> Settings::ivZoomValue() const {
}
void Settings::setIvZoom(int value) {
if (!value || value == DefaultIvZoom()) {
const auto resolved = ResolveIvZoom(value);
if (!value || resolved == ResolveIvZoom(0)) {
_ivZoom = 0;
return;
}
#ifdef Q_OS_WIN
constexpr auto kMin = 25;
constexpr auto kMax = 500;
#else
constexpr auto kMin = 30;
constexpr auto kMax = 200;
#endif
_ivZoom = std::clamp(value, kMin, kMax);
_ivZoom = resolved;
}
bool Settings::normalizeIvZoom() {
const auto value = _ivZoom.current();
if (value && value == DefaultIvZoom()) {
if (!value) {
return false;
}
const auto resolved = ResolveIvZoom(value);
if (resolved == ResolveIvZoom(0)) {
_ivZoom = 0;
return true;
} else if (resolved != value) {
_ivZoom = resolved;
return true;
}
return false;
}
+24 -21
View File
@@ -268,6 +268,7 @@ MarkdownEmbedOverlay {
Markdown {
textPalette: TextPalette;
textColor: color;
supplementaryTextColor: color;
body: TextStyle;
heading1: TextStyle;
heading2: TextStyle;
@@ -315,22 +316,22 @@ defaultMarkdownTableHeaderStyle: TextStyle(defaultMarkdownBodyStyle) {
lineHeight: 24px;
}
defaultMarkdownBlockSkips: MarkdownBlockSkips {
paragraph: 16px;
heading: 24px;
code: 20px;
rule: 24px;
quote: 18px;
displayMath: 20px;
table: 20px;
photo: 20px;
video: 20px;
audio: 20px;
map: 20px;
channel: 20px;
groupedMedia: 20px;
paragraph: 12px;
heading: 16px;
code: 12px;
rule: 12px;
quote: 12px;
displayMath: 12px;
table: 12px;
photo: 12px;
video: 12px;
audio: 12px;
map: 12px;
channel: 8px;
groupedMedia: 12px;
relatedArticle: 0px;
embedPost: 20px;
placeholder: 20px;
embedPost: 12px;
placeholder: 12px;
}
defaultMarkdownList: MarkdownList {
indent: 28px;
@@ -452,8 +453,9 @@ defaultMarkdownChannelSubtitleStyle: TextStyle(defaultMarkdownDetailsSummaryStyl
}
defaultMarkdownChannelButtonStyle: TextStyle(defaultMarkdownChannelTitleStyle) {
}
defaultMarkdownSidesMargin: margins(18px, 0px, 18px, 0px);
defaultMarkdownChannelButton: MarkdownChannelButton {
padding: margins(44px, 0px, 44px, 0px);
padding: defaultMarkdownSidesMargin;
border: 0px;
borderFg: windowActiveTextFg;
bg: windowBgOver;
@@ -462,7 +464,7 @@ defaultMarkdownChannelButton: MarkdownChannelButton {
textFg: windowActiveTextFg;
}
defaultMarkdownChannel: MarkdownChannel {
padding: margins(44px, 8px, 44px, 8px);
padding: margins(18px, 8px, 18px, 8px);
border: 0px;
borderFg: inputBorderFg;
bg: windowBgOver;
@@ -486,12 +488,12 @@ defaultMarkdownRelatedArticleSubtitleStyle: TextStyle(defaultMarkdownBodyStyle)
defaultMarkdownRelatedArticleFooterStyle: TextStyle(defaultMarkdownDetailsSummaryStyle) {
}
defaultMarkdownRelatedArticle: MarkdownRelatedArticle {
padding: margins(44px, 16px, 44px, 16px);
padding: margins(18px, 8px, 18px, 8px);
border: 0px;
borderFg: boxDividerBg;
bg: windowBg;
radius: 0px;
headerPadding: margins(44px, 14px, 44px, 14px);
headerPadding: margins(18px, 8px, 18px, 8px);
headerBg: windowBgOver;
separator: 1px;
separatorFg: boxDividerBg;
@@ -566,6 +568,7 @@ markdownEmbedOverlay: MarkdownEmbedOverlay {
defaultMarkdown: Markdown {
textPalette: defaultMarkdownTextPalette;
textColor: windowFg;
supplementaryTextColor: windowSubTextFg;
body: defaultMarkdownBodyStyle;
heading1: TextStyle(defaultMarkdownBodyStyle) {
font: font(30px semibold);
@@ -592,9 +595,9 @@ defaultMarkdown: Markdown {
lineHeight: 21px;
}
code: defaultMarkdownCodeStyle;
pagePadding: margins(0px, 26px, 0px, 32px);
pagePadding: margins(0px, 0px, 0px, 16px);
pageMaxWidth: 732px;
textPadding: margins(44px, 0px, 44px, 0px);
textPadding: defaultMarkdownSidesMargin;
mediaPadding: margins(0px, 0px, 0px, 0px);
blockSkips: defaultMarkdownBlockSkips;
list: defaultMarkdownList;
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
/*
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 "base/basic_types.h"
#include <QtCore/QString>
#include <memory>
struct WebPageData;
namespace Main {
class Session;
} // namespace Main
namespace Iv::Markdown {
class MediaRuntime;
} // namespace Iv::Markdown
namespace Iv {
[[nodiscard]] auto CreateCachedPageMediaRuntime(
not_null<Main::Session*> session,
not_null<WebPageData*> page,
Fn<void(QString)> openChannel,
Fn<void(QString)> joinChannel)
-> std::shared_ptr<Markdown::MediaRuntime>;
} // namespace Iv
+2 -192
View File
@@ -15,15 +15,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/qthelp_url.h"
#include "core/file_utilities.h"
#include "iv/iv_data.h"
#include "iv/iv_zoom_controls.h"
#include "lang/lang_keys.h"
#include "ui/chat/attach/attach_bot_webview.h"
#include "ui/platform/ui_platform_window_title.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/menu/menu_action.h"
#include "ui/widgets/rp_window.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/tooltip.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/basic_click_handlers.h"
#include "ui/painter.h"
@@ -37,7 +36,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_iv.h"
#include "styles/style_menu_icons.h"
#include "styles/style_payments.h" // paymentsCriticalError
#include "styles/style_widgets.h"
#include "styles/style_window.h"
#include <QtCore/QRegularExpression>
@@ -57,195 +55,8 @@ namespace Iv {
namespace {
constexpr auto kZoomStep = int(10);
constexpr auto kZoomSmallStep = int(5);
constexpr auto kZoomTinyStep = int(1);
constexpr auto kDefaultZoom = int(100);
class ItemZoom final
: public Ui::Menu::Action
, public Ui::AbstractTooltipShower {
public:
ItemZoom(
not_null<Ui::PopupMenu*> parent,
const not_null<Delegate*> delegate,
const style::Menu &st);
void init();
void paintEvent(QPaintEvent *event) override;
QString tooltipText() const override;
QPoint tooltipPos() const override;
bool tooltipWindowActive() const override;
private:
const not_null<Delegate*> _delegate;
const style::Menu &_st;
Ui::Text::String _text;
};
ItemZoom::ItemZoom(
not_null<Ui::PopupMenu*> parent,
const not_null<Delegate*> delegate,
const style::Menu &st)
: Ui::Menu::Action(
parent->menu(),
st,
Ui::CreateChild<QAction>(parent),
nullptr,
nullptr)
, _delegate(delegate)
, _st(st) {
init();
}
void ItemZoom::init() {
enableMouseSelecting();
AbstractButton::setDisabled(true);
const auto processTooltip = [=](not_null<Ui::RpWidget*> w) {
w->events() | rpl::on_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Enter) {
Ui::Tooltip::Show(1000, this);
} else if (e->type() == QEvent::Leave) {
Ui::Tooltip::Hide();
}
}, w->lifetime());
};
const auto reset = Ui::CreateChild<Ui::RoundButton>(
this,
rpl::single<QString>(QString()),
st::ivResetZoom);
processTooltip(reset);
const auto resetLabel = Ui::CreateChild<Ui::FlatLabel>(
reset,
tr::lng_background_reset_default(),
st::ivResetZoomLabel);
resetLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
reset->setClickedCallback([this] {
_delegate->ivSetZoom(0);
});
reset->show();
const auto plus = Ui::CreateSimpleCircleButton(
this,
st::defaultRippleAnimationBgOver);
plus->resize(Size(st::ivZoomButtonsSize));
plus->paintRequest() | rpl::on_next([=, fg = _st.itemFg] {
auto p = QPainter(plus);
p.setPen(fg);
p.setFont(st::normalFont);
p.drawText(plus->rect(), QChar('+'), style::al_center);
}, plus->lifetime());
processTooltip(plus);
const auto step = [] {
return base::IsAltPressed()
? kZoomTinyStep
: base::IsCtrlPressed()
? kZoomSmallStep
: kZoomStep;
};
plus->setClickedCallback([this, step] {
_delegate->ivSetZoom(_delegate->ivZoom() + step());
});
plus->show();
const auto minus = Ui::CreateSimpleCircleButton(
this,
st::defaultRippleAnimationBgOver);
minus->resize(Size(st::ivZoomButtonsSize));
minus->paintRequest() | rpl::on_next([=, fg = _st.itemFg] {
auto p = QPainter(minus);
const auto r = minus->rect();
p.setPen(fg);
p.setFont(st::normalFont);
p.drawText(
QRectF(r).translated(0, style::ConvertFloatScale(-1)),
QChar(0x2013),
style::al_center);
}, minus->lifetime());
processTooltip(minus);
minus->setClickedCallback([this, step] {
_delegate->ivSetZoom(_delegate->ivZoom() - step());
});
minus->show();
{
const auto maxWidthText = u"000%"_q;
_text.setText(_st.itemStyle, maxWidthText);
Ui::Menu::ItemBase::setMinWidth(
_text.maxWidth()
+ st::ivResetZoomInnerPadding
+ resetLabel->width()
+ plus->width()
+ minus->width()
+ _st.itemPadding.right() * 2);
}
_delegate->ivZoomValue(
) | rpl::on_next([this](int value) {
_text.setText(_st.itemStyle, QString::number(value) + '%');
update();
}, lifetime());
rpl::combine(
sizeValue(),
reset->sizeValue()
) | rpl::on_next([=](const QSize &size, const QSize &) {
reset->setFullWidth(0
+ resetLabel->width()
+ st::ivResetZoomInnerPadding);
resetLabel->moveToLeft(
(reset->width() - resetLabel->width()) / 2,
(reset->height() - resetLabel->height()) / 2);
reset->moveToRight(
_st.itemPadding.right(),
(size.height() - reset->height()) / 2);
plus->moveToRight(
_st.itemPadding.right() + reset->width(),
(size.height() - plus->height()) / 2);
minus->moveToRight(
_st.itemPadding.right() + plus->width() + reset->width(),
(size.height() - minus->height()) / 2);
}, lifetime());
}
void ItemZoom::paintEvent(QPaintEvent *event) {
auto p = QPainter(this);
p.setPen(_st.itemFg);
_text.draw(p, {
.position = QPoint(
_st.itemIconPosition.x(),
(height() - _text.minHeight()) / 2),
.outerWidth = width(),
.availableWidth = width(),
});
}
QString ItemZoom::tooltipText() const {
#ifdef Q_OS_MAC
return tr::lng_iv_zoom_tooltip_cmd(tr::now);
#else
return tr::lng_iv_zoom_tooltip_ctrl(tr::now);
#endif
}
QPoint ItemZoom::tooltipPos() const {
return QCursor::pos();
}
bool ItemZoom::tooltipWindowActive() const {
return true;
}
[[nodiscard]] QByteArray ComputeStyles(int zoom) {
static const auto map = base::flat_map<QByteArray, const style::color*>{
{ "shadow-fg", &st::shadowFg },
@@ -1154,8 +965,7 @@ void Controller::showMenu() {
}, &st::menuIconShare);
_menu->addSeparator();
_menu->addAction(
base::make_unique_q<ItemZoom>(_menu, _delegate, _menu->menu()->st()));
_menu->addAction(CreateZoomMenuAction(_menu, _delegate));
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
_menu->popup(_window->body()->mapToGlobal(
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,225 @@
/*
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/iv_zoom_controls.h"
#include "base/qt/qt_key_modifiers.h"
#include "iv/iv_delegate.h"
#include "lang/lang_keys.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/menu/menu_action.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/tooltip.h"
#include "ui/rect.h"
#include "styles/style_iv.h"
#include "styles/style_widgets.h"
#include <QtCore/QEvent>
#include <QtWidgets/QAction>
#include <QtGui/QCursor>
#include <QtGui/QPainter>
namespace Iv {
namespace {
constexpr auto kZoomStep = int(10);
constexpr auto kZoomSmallStep = int(5);
constexpr auto kZoomTinyStep = int(1);
class ZoomMenuAction final
: public Ui::Menu::Action
, public Ui::AbstractTooltipShower {
public:
ZoomMenuAction(
not_null<Ui::PopupMenu*> parent,
not_null<Delegate*> delegate,
const style::Menu &st);
void init();
void paintEvent(QPaintEvent *event) override;
QString tooltipText() const override;
QPoint tooltipPos() const override;
bool tooltipWindowActive() const override;
private:
const not_null<Delegate*> _delegate;
const style::Menu &_st;
Ui::Text::String _text;
};
ZoomMenuAction::ZoomMenuAction(
not_null<Ui::PopupMenu*> parent,
not_null<Delegate*> delegate,
const style::Menu &st)
: Ui::Menu::Action(
parent->menu(),
st,
Ui::CreateChild<QAction>(parent),
nullptr,
nullptr)
, _delegate(delegate)
, _st(st) {
init();
}
void ZoomMenuAction::init() {
enableMouseSelecting();
AbstractButton::setDisabled(true);
const auto processTooltip = [=](not_null<Ui::RpWidget*> w) {
w->events() | rpl::on_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Enter) {
Ui::Tooltip::Show(1000, this);
} else if (e->type() == QEvent::Leave) {
Ui::Tooltip::Hide();
}
}, w->lifetime());
};
const auto reset = Ui::CreateChild<Ui::RoundButton>(
this,
rpl::single<QString>(QString()),
st::ivResetZoom);
processTooltip(reset);
const auto resetLabel = Ui::CreateChild<Ui::FlatLabel>(
reset,
tr::lng_background_reset_default(),
st::ivResetZoomLabel);
resetLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
reset->setClickedCallback([this] {
_delegate->ivSetZoom(0);
});
reset->show();
const auto plus = Ui::CreateSimpleCircleButton(
this,
st::defaultRippleAnimationBgOver);
plus->resize(Size(st::ivZoomButtonsSize));
plus->paintRequest() | rpl::on_next([=, fg = _st.itemFg] {
auto p = QPainter(plus);
p.setPen(fg);
p.setFont(st::normalFont);
p.drawText(plus->rect(), QChar('+'), style::al_center);
}, plus->lifetime());
processTooltip(plus);
const auto step = [] {
return base::IsAltPressed()
? kZoomTinyStep
: base::IsCtrlPressed()
? kZoomSmallStep
: kZoomStep;
};
plus->setClickedCallback([this, step] {
_delegate->ivSetZoom(_delegate->ivZoom() + step());
});
plus->show();
const auto minus = Ui::CreateSimpleCircleButton(
this,
st::defaultRippleAnimationBgOver);
minus->resize(Size(st::ivZoomButtonsSize));
minus->paintRequest() | rpl::on_next([=, fg = _st.itemFg] {
auto p = QPainter(minus);
const auto r = minus->rect();
p.setPen(fg);
p.setFont(st::normalFont);
p.drawText(
QRectF(r).translated(0, style::ConvertFloatScale(-1)),
QChar(0x2013),
style::al_center);
}, minus->lifetime());
processTooltip(minus);
minus->setClickedCallback([this, step] {
_delegate->ivSetZoom(_delegate->ivZoom() - step());
});
minus->show();
{
const auto maxWidthText = u"000%"_q;
_text.setText(_st.itemStyle, maxWidthText);
Ui::Menu::ItemBase::setMinWidth(
_text.maxWidth()
+ st::ivResetZoomInnerPadding
+ resetLabel->width()
+ plus->width()
+ minus->width()
+ _st.itemPadding.right() * 2);
}
_delegate->ivZoomValue(
) | rpl::on_next([this](int value) {
_text.setText(_st.itemStyle, QString::number(value) + '%');
update();
}, lifetime());
rpl::combine(
sizeValue(),
reset->sizeValue()
) | rpl::on_next([=](const QSize &size, const QSize &) {
reset->setFullWidth(0
+ resetLabel->width()
+ st::ivResetZoomInnerPadding);
resetLabel->moveToLeft(
(reset->width() - resetLabel->width()) / 2,
(reset->height() - resetLabel->height()) / 2);
reset->moveToRight(
_st.itemPadding.right(),
(size.height() - reset->height()) / 2);
plus->moveToRight(
_st.itemPadding.right() + reset->width(),
(size.height() - plus->height()) / 2);
minus->moveToRight(
_st.itemPadding.right() + plus->width() + reset->width(),
(size.height() - minus->height()) / 2);
}, lifetime());
}
void ZoomMenuAction::paintEvent(QPaintEvent *event) {
auto p = QPainter(this);
p.setPen(_st.itemFg);
_text.draw(p, {
.position = QPoint(
_st.itemIconPosition.x(),
(height() - _text.minHeight()) / 2),
.outerWidth = width(),
.availableWidth = width(),
});
}
QString ZoomMenuAction::tooltipText() const {
#ifdef Q_OS_MAC
return tr::lng_iv_zoom_tooltip_cmd(tr::now);
#else
return tr::lng_iv_zoom_tooltip_ctrl(tr::now);
#endif
}
QPoint ZoomMenuAction::tooltipPos() const {
return QCursor::pos();
}
bool ZoomMenuAction::tooltipWindowActive() const {
return true;
}
} // namespace
base::unique_qptr<Ui::Menu::ItemBase> CreateZoomMenuAction(
not_null<Ui::PopupMenu*> parent,
not_null<Delegate*> delegate) {
return base::make_unique_q<ZoomMenuAction>(
parent,
delegate,
parent->menu()->st());
}
} // namespace Iv
@@ -0,0 +1,29 @@
/*
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 "base/basic_types.h"
#include "base/unique_qptr.h"
namespace Ui {
class PopupMenu;
} // namespace Ui
namespace Ui::Menu {
class ItemBase;
} // namespace Ui::Menu
namespace Iv {
class Delegate;
[[nodiscard]] base::unique_qptr<Ui::Menu::ItemBase> CreateZoomMenuAction(
not_null<Ui::PopupMenu*> parent,
not_null<Delegate*> delegate);
} // namespace Iv
@@ -41,7 +41,7 @@ struct PendingHighlightKeyHasher {
const PendingHighlightKey &key) const noexcept;
};
[[nodiscard]] size_t PendingHighlightKeyHasher::operator()(
size_t PendingHighlightKeyHasher::operator()(
const PendingHighlightKey &key) const noexcept {
auto result = size_t(qHash(key.text));
result = (result * 1315423911U) ^ size_t(qHash(key.language));
@@ -386,8 +386,8 @@ public:
void setMediaBlockHost(MediaBlockHost *host);
void setTextRepaintCallbacks(
Fn<void()> repaint,
Fn<void(QRect)> repaintRect);
Fn<void()> repaint,
Fn<void(QRect)> repaintRect);
void setContent(MarkdownArticleContent content);
@@ -398,15 +398,15 @@ public:
void setVisibleTopBottom(int visibleTop, int visibleBottom);
void paint(
Painter &p,
QRect clip,
MarkdownArticlePaintCaches caches,
MarkdownArticleSelection selection,
const MarkdownArticleSelectionEndpoints *endpoints);
Painter &p,
QRect clip,
MarkdownArticlePaintCaches caches,
MarkdownArticleSelection selection,
const MarkdownArticleSelectionEndpoints *endpoints);
[[nodiscard]] MarkdownArticleHitTestResult hitTest(
QPoint point,
Ui::Text::StateRequest::Flags flags) const;
QPoint point,
Ui::Text::StateRequest::Flags flags) const;
[[nodiscard]] int anchorTop(const QString &anchorId) const;
@@ -417,28 +417,28 @@ public:
[[nodiscard]] int segmentLength(int index) const;
[[nodiscard]] int selectionOffsetFromHit(
const MarkdownArticleHitTestResult &result,
TextSelectType selectionType) const;
const MarkdownArticleHitTestResult &result,
TextSelectType selectionType) const;
[[nodiscard]] TextSelection adjustSelection(
int segmentIndex,
TextSelection selection,
TextSelectType selectionType) const;
int segmentIndex,
TextSelection selection,
TextSelectType selectionType) const;
[[nodiscard]] bool selectionContains(
MarkdownArticleSelection selection,
const MarkdownArticleSelectionEndpoints *endpoints,
const MarkdownArticleHitTestResult &result) const;
MarkdownArticleSelection selection,
const MarkdownArticleSelectionEndpoints *endpoints,
const MarkdownArticleHitTestResult &result) const;
[[nodiscard]] TextForMimeData textForContext(
const MarkdownArticleHitTestResult &result) const;
const MarkdownArticleHitTestResult &result) const;
[[nodiscard]] TextForMimeData textForSelection(
MarkdownArticleSelection selection,
const MarkdownArticleSelectionEndpoints *endpoints) const;
MarkdownArticleSelection selection,
const MarkdownArticleSelectionEndpoints *endpoints) const;
[[nodiscard]] bool highlightProcessDone(
Spellchecker::HighlightProcessId processId);
Spellchecker::HighlightProcessId processId);
void invalidatePaletteCache();
@@ -460,25 +460,25 @@ private:
void refreshMediaBlockHosts();
[[nodiscard]] std::shared_ptr<MediaBlock> getOrCreateMediaBlock(
const PreparedBlock &prepared);
const PreparedBlock &prepared);
template <typename Factory>
[[nodiscard]] std::shared_ptr<MediaBlock> getOrCreateMediaBlock(
PreparedMediaBlockId id,
Factory &&factory);
PreparedMediaBlockId id,
Factory &&factory);
[[nodiscard]] Spellchecker::HighlightProcessId tryHighlightSyntax(
const QString &displayText,
const QString &language,
TextWithEntities &marked) override;
const QString &displayText,
const QString &language,
TextWithEntities &marked) override;
[[nodiscard]] SegmentSpan candidateSegmentSpan(QPoint point) const;
void clearPendingHighlightBlockPointers();
void registerPendingHighlightProcess(
const PendingHighlightKey &key,
Spellchecker::HighlightProcessId processId);
const PendingHighlightKey &key,
Spellchecker::HighlightProcessId processId);
void registerPendingHighlightBlock(LaidOutBlock &block);
@@ -499,7 +499,9 @@ private:
int _height = 0;
std::vector<LaidOutBlock> _blocks;
std::unordered_map<uint64, std::shared_ptr<MediaBlock>> _mediaBlocks;
std::unordered_map<uint64, RelatedArticleThumbnailState> _relatedArticleThumbnails;
std::unordered_map<
uint64,
RelatedArticleThumbnailState> _relatedArticleThumbnails;
std::unordered_map<
PendingHighlightKey,
Spellchecker::HighlightProcessId,
@@ -521,7 +523,6 @@ MarkdownArticle::Impl::Impl(std::shared_ptr<MathRenderer> renderer)
, _inlineFormulaObjects(CreateInlineFormulaObjectCache(_renderer)) {
}
void MarkdownArticle::Impl::setRenderer(std::shared_ptr<MathRenderer> renderer) {
_renderer = std::move(renderer);
SetInlineFormulaObjectCacheRenderer(_inlineFormulaObjects, _renderer);
@@ -529,13 +530,11 @@ void MarkdownArticle::Impl::setRenderer(std::shared_ptr<MathRenderer> renderer)
invalidateLayout();
}
void MarkdownArticle::Impl::setMediaBlockHost(MediaBlockHost *host) {
_mediaBlockHost = host;
refreshMediaBlockHosts();
}
void MarkdownArticle::Impl::setTextRepaintCallbacks(
Fn<void()> repaint,
Fn<void(QRect)> repaintRect) {
@@ -543,7 +542,6 @@ void MarkdownArticle::Impl::setTextRepaintCallbacks(
_textRepaintRect = std::move(repaintRect);
}
void MarkdownArticle::Impl::setContent(MarkdownArticleContent content) {
clearMediaBlocks();
_relatedArticleThumbnails.clear();
@@ -553,8 +551,7 @@ void MarkdownArticle::Impl::setContent(MarkdownArticleContent content) {
invalidateLayout();
}
[[nodiscard]] int MarkdownArticle::Impl::maxWidth() {
int MarkdownArticle::Impl::maxWidth() {
const auto &markdown = st::defaultMarkdown;
return std::max(
markdown.pageMaxWidth,
@@ -563,13 +560,11 @@ void MarkdownArticle::Impl::setContent(MarkdownArticleContent content) {
+ 1);
}
[[nodiscard]] int MarkdownArticle::Impl::resizeGetHeight(int width) {
int MarkdownArticle::Impl::resizeGetHeight(int width) {
relayout(width);
return std::max(_height, 1);
}
void MarkdownArticle::Impl::setVisibleTopBottom(int visibleTop, int visibleBottom) {
if (visibleBottom <= visibleTop) {
_visibleRange = std::nullopt;
@@ -583,7 +578,6 @@ void MarkdownArticle::Impl::setVisibleTopBottom(int visibleTop, int visibleBotto
refreshVisibleSegmentSpan();
}
void MarkdownArticle::Impl::paint(
Painter &p,
QRect clip,
@@ -610,8 +604,7 @@ void MarkdownArticle::Impl::paint(
clip);
}
[[nodiscard]] MarkdownArticleHitTestResult MarkdownArticle::Impl::hitTest(
MarkdownArticleHitTestResult MarkdownArticle::Impl::hitTest(
QPoint point,
Ui::Text::StateRequest::Flags flags) const {
const auto span = candidateSegmentSpan(point);
@@ -635,8 +628,7 @@ void MarkdownArticle::Impl::paint(
return {};
}
[[nodiscard]] int MarkdownArticle::Impl::anchorTop(const QString &anchorId) const {
int MarkdownArticle::Impl::anchorTop(const QString &anchorId) const {
for (const auto &entry : _anchors) {
if (entry.first == anchorId) {
return entry.second;
@@ -645,8 +637,7 @@ void MarkdownArticle::Impl::paint(
return -1;
}
[[nodiscard]] bool MarkdownArticle::Impl::toggleDetails(const QString &anchorId) {
bool MarkdownArticle::Impl::toggleDetails(const QString &anchorId) {
if (!ToggleDetailsBlock(&_content.blocks.blocks, anchorId)) {
return false;
}
@@ -654,20 +645,17 @@ void MarkdownArticle::Impl::paint(
return true;
}
[[nodiscard]] bool MarkdownArticle::Impl::segmentIsText(int index) const {
bool MarkdownArticle::Impl::segmentIsText(int index) const {
const auto segment = FindSegment(&_segments, index);
return segment && segment->isTextLeaf();
}
[[nodiscard]] int MarkdownArticle::Impl::segmentLength(int index) const {
int MarkdownArticle::Impl::segmentLength(int index) const {
const auto segment = FindSegment(&_segments, index);
return segment ? SegmentLength(*segment) : 0;
}
[[nodiscard]] int MarkdownArticle::Impl::selectionOffsetFromHit(
int MarkdownArticle::Impl::selectionOffsetFromHit(
const MarkdownArticleHitTestResult &result,
TextSelectType selectionType) const {
const auto segment = FindSegment(&_segments, result.segmentIndex);
@@ -685,8 +673,7 @@ void MarkdownArticle::Impl::paint(
return std::clamp(offset, 0, SegmentLength(*segment));
}
[[nodiscard]] TextSelection MarkdownArticle::Impl::adjustSelection(
TextSelection MarkdownArticle::Impl::adjustSelection(
int segmentIndex,
TextSelection selection,
TextSelectType selectionType) const {
@@ -697,8 +684,7 @@ void MarkdownArticle::Impl::paint(
return segment->leaf->adjustSelection(selection, selectionType);
}
[[nodiscard]] bool MarkdownArticle::Impl::selectionContains(
bool MarkdownArticle::Impl::selectionContains(
MarkdownArticleSelection selection,
const MarkdownArticleSelectionEndpoints *endpoints,
const MarkdownArticleHitTestResult &result) const {
@@ -726,8 +712,7 @@ void MarkdownArticle::Impl::paint(
return (offset >= textSelection->from) && (offset < textSelection->to);
}
[[nodiscard]] TextForMimeData MarkdownArticle::Impl::textForContext(
TextForMimeData MarkdownArticle::Impl::textForContext(
const MarkdownArticleHitTestResult &result) const {
if (!result.valid() || !result.direct) {
return TextForMimeData();
@@ -736,15 +721,13 @@ void MarkdownArticle::Impl::paint(
return segment ? TextForSegment(*segment) : TextForMimeData();
}
[[nodiscard]] TextForMimeData MarkdownArticle::Impl::textForSelection(
TextForMimeData MarkdownArticle::Impl::textForSelection(
MarkdownArticleSelection selection,
const MarkdownArticleSelectionEndpoints *endpoints) const {
return TextForSelectedSegments(_segments, selection, endpoints);
}
[[nodiscard]] bool MarkdownArticle::Impl::highlightProcessDone(
bool MarkdownArticle::Impl::highlightProcessDone(
Spellchecker::HighlightProcessId processId) {
const auto i = _pendingHighlightEntries.find(processId);
if (i == end(_pendingHighlightEntries)) {
@@ -763,25 +746,21 @@ void MarkdownArticle::Impl::paint(
return rebuilt;
}
void MarkdownArticle::Impl::invalidatePaletteCache() {
InvalidateInlineFormulaPaletteCache(_inlineFormulaObjects);
ClearColorizedFormulaImages(&_blocks);
}
void MarkdownArticle::Impl::invalidateRasterCache() {
resetFormulaRasterCache();
InvalidateInlineFormulaRasterCache(_inlineFormulaObjects);
ClearColorizedFormulaImages(&_blocks);
}
[[nodiscard]] MediaBlockHost *MarkdownArticle::Impl::mediaBlockHost() const {
MediaBlockHost *MarkdownArticle::Impl::mediaBlockHost() const {
return _mediaBlockHost;
}
void MarkdownArticle::Impl::invalidateLayout() {
_width = -1;
_height = 0;
@@ -794,11 +773,10 @@ void MarkdownArticle::Impl::invalidateLayout() {
_segmentBottoms.clear();
}
[[nodiscard]] int MarkdownArticle::Impl::currentDevicePixelRatio() const {
int MarkdownArticle::Impl::currentDevicePixelRatio() const {
return std::max(style::DevicePixelRatio(), 1);
}
void MarkdownArticle::Impl::rebuildVisibleSegmentLookup() {
RebuildVisibleSegmentLookup(
_segments,
@@ -807,7 +785,6 @@ void MarkdownArticle::Impl::rebuildVisibleSegmentLookup() {
refreshVisibleSegmentSpan();
}
void MarkdownArticle::Impl::refreshVisibleSegmentSpan() {
_visibleSegmentSpan = _visibleRange
? LookupVisibleSegmentSpan(
@@ -817,7 +794,6 @@ void MarkdownArticle::Impl::refreshVisibleSegmentSpan() {
: SegmentSpan();
}
void MarkdownArticle::Impl::clearMediaBlocks() {
for (const auto &[id, block] : _mediaBlocks) {
if (block) {
@@ -827,7 +803,6 @@ void MarkdownArticle::Impl::clearMediaBlocks() {
_mediaBlocks.clear();
}
void MarkdownArticle::Impl::refreshMediaBlockHosts() {
for (const auto &[id, block] : _mediaBlocks) {
if (block) {
@@ -836,8 +811,7 @@ void MarkdownArticle::Impl::refreshMediaBlockHosts() {
}
}
[[nodiscard]] std::shared_ptr<MediaBlock> MarkdownArticle::Impl::getOrCreateMediaBlock(
std::shared_ptr<MediaBlock> MarkdownArticle::Impl::getOrCreateMediaBlock(
const PreparedBlock &prepared) {
switch (prepared.kind) {
case PreparedBlockKind::Photo:
@@ -893,9 +867,8 @@ void MarkdownArticle::Impl::refreshMediaBlockHosts() {
}
}
template <typename Factory>
[[nodiscard]] std::shared_ptr<MediaBlock> MarkdownArticle::Impl::getOrCreateMediaBlock(
std::shared_ptr<MediaBlock> MarkdownArticle::Impl::getOrCreateMediaBlock(
PreparedMediaBlockId id,
Factory &&factory) {
if (!id) {
@@ -913,8 +886,7 @@ template <typename Factory>
return block;
}
[[nodiscard]] Spellchecker::HighlightProcessId MarkdownArticle::Impl::tryHighlightSyntax(
Spellchecker::HighlightProcessId MarkdownArticle::Impl::tryHighlightSyntax(
const QString &displayText,
const QString &language,
TextWithEntities &marked) {
@@ -933,8 +905,7 @@ template <typename Factory>
return processId;
}
[[nodiscard]] SegmentSpan MarkdownArticle::Impl::candidateSegmentSpan(QPoint point) const {
SegmentSpan MarkdownArticle::Impl::candidateSegmentSpan(QPoint point) const {
if (_visibleRange
&& (_visibleRange->top <= point.y())
&& (point.y() < _visibleRange->bottom)) {
@@ -945,14 +916,12 @@ template <typename Factory>
return FullSegmentSpan(_segments);
}
void MarkdownArticle::Impl::clearPendingHighlightBlockPointers() {
for (auto &entry : _pendingHighlightEntries) {
entry.second.blocks.clear();
}
}
void MarkdownArticle::Impl::registerPendingHighlightProcess(
const PendingHighlightKey &key,
Spellchecker::HighlightProcessId processId) {
@@ -961,7 +930,6 @@ void MarkdownArticle::Impl::registerPendingHighlightProcess(
entry.key = key;
}
void MarkdownArticle::Impl::registerPendingHighlightBlock(LaidOutBlock &block) {
if (!block.syntaxHighlightProcessId) {
return;
@@ -975,7 +943,6 @@ void MarkdownArticle::Impl::registerPendingHighlightBlock(LaidOutBlock &block) {
&block);
}
void MarkdownArticle::Impl::registerPendingHighlightBlocks(std::vector<LaidOutBlock> &blocks) {
for (auto &block : blocks) {
registerPendingHighlightBlock(block);
@@ -983,13 +950,11 @@ void MarkdownArticle::Impl::registerPendingHighlightBlocks(std::vector<LaidOutBl
}
}
void MarkdownArticle::Impl::resetFormulaRasterCache() {
_formulaRenders.clear();
_formulaRenders.resize(_content.formulas.size());
}
void MarkdownArticle::Impl::relayout(int width) {
width = std::max(width, 1);
if (_width == width) {
@@ -1045,7 +1010,6 @@ void MarkdownArticle::Impl::relayout(int width) {
rebuildVisibleSegmentLookup();
}
MarkdownArticle::MarkdownArticle(std::shared_ptr<MathRenderer> renderer)
: _impl(std::make_unique<Impl>(std::move(renderer))) {
}
@@ -27,6 +27,7 @@ struct MarkdownArticlePaintCaches {
std::span<Ui::Text::SpecialColor> colors;
Fn<void()> repaint;
Fn<void(QRect)> repaintRect;
std::optional<QColor> supplementaryColorOverride;
};
struct MarkdownArticleHitTestResult {
@@ -415,6 +415,7 @@ void LayoutMediaCaption(
if (prepared.text.text.isEmpty()) {
return;
}
block->supplementary = prepared.supplementary;
const auto textBand = ArticleTextBand(left, width, markdown, context);
LayoutMediaCaptionText(
block,
@@ -469,6 +470,15 @@ bool IsFlowKind(PreparedBlockKind kind) {
|| (kind == PreparedBlockKind::Heading);
}
bool IsAnchorOnlyBlock(const PreparedBlock &block) {
return (block.kind == PreparedBlockKind::Paragraph)
&& !block.anchorId.isEmpty()
&& block.text.text.isEmpty()
&& block.text.entities.empty()
&& block.links.empty()
&& block.children.empty();
}
QString ListMarkerText(const PreparedBlock &block) {
if (block.listKind == ListKind::Ordered) {
const auto delimiter = (block.listDelimiter == ListDelimiter::Parenthesis)
@@ -545,6 +555,9 @@ int TableCellTextMinResizeWidth(
int BlockSkip(
const PreparedBlock &block,
const style::Markdown &markdown) {
if (IsAnchorOnlyBlock(block)) {
return 0;
}
const auto &skips = markdown.blockSkips;
switch (block.kind) {
case PreparedBlockKind::Paragraph:
@@ -670,7 +683,13 @@ LaidOutBlock LayoutFlowBlock(
block.kind = prepared.kind;
block.anchorId = prepared.anchorId;
block.headingLevel = prepared.headingLevel;
block.supplementary = prepared.supplementary;
block.textWidth = std::max(width, 1);
if (IsAnchorOnlyBlock(prepared)) {
block.textRect = QRect(left, top, block.textWidth, 0);
block.outer = block.textRect;
return block;
}
const auto &textStyle = TextStyleFor(prepared, markdown);
SetTextLeaf(
@@ -853,6 +872,7 @@ LaidOutBlock LayoutTableBlock(
block.anchorId = prepared.anchorId;
block.tableBordered = prepared.tableBordered;
block.tableStriped = prepared.tableStriped;
block.supplementary = prepared.supplementary;
auto tableTop = top;
if (!prepared.text.text.isEmpty()) {
@@ -1094,6 +1114,7 @@ LaidOutBlock LayoutPlaceholderBlock(
block.anchorId = prepared.anchorId;
block.copyText = prepared.placeholder.copyText;
block.labelText = prepared.placeholder.label;
block.supplementary = prepared.supplementary;
const auto &style = markdown.placeholder;
const auto blockWidth = std::max(width, 1);
@@ -1330,6 +1351,7 @@ LaidOutBlock LayoutPhotoBlock(
auto block = LaidOutBlock();
block.kind = PreparedBlockKind::Photo;
block.anchorId = prepared.anchorId;
block.supplementary = prepared.supplementary;
if (context.mediaBlockFactory) {
block.mediaBlock = context.mediaBlockFactory(prepared);
}
@@ -1400,6 +1422,7 @@ LaidOutBlock LayoutVideoBlock(
auto block = LaidOutBlock();
block.kind = PreparedBlockKind::Video;
block.anchorId = prepared.anchorId;
block.supplementary = prepared.supplementary;
if (context.mediaBlockFactory) {
block.mediaBlock = context.mediaBlockFactory(prepared);
}
@@ -1470,6 +1493,7 @@ LaidOutBlock LayoutAudioBlock(
auto block = LaidOutBlock();
block.kind = PreparedBlockKind::Audio;
block.anchorId = prepared.anchorId;
block.supplementary = prepared.supplementary;
if (context.mediaBlockFactory) {
block.mediaBlock = context.mediaBlockFactory(prepared);
}
@@ -1526,6 +1550,7 @@ LaidOutBlock LayoutMapBlock(
auto block = LaidOutBlock();
block.kind = PreparedBlockKind::Map;
block.anchorId = prepared.anchorId;
block.supplementary = prepared.supplementary;
if (context.mediaBlockFactory) {
block.mediaBlock = context.mediaBlockFactory(prepared);
}
@@ -1591,6 +1616,7 @@ LaidOutBlock LayoutChannelBlock(
auto block = LaidOutBlock();
block.kind = PreparedBlockKind::Channel;
block.anchorId = prepared.anchorId;
block.supplementary = prepared.supplementary;
if (context.mediaBlockFactory) {
block.mediaBlock = context.mediaBlockFactory(prepared);
}
@@ -1645,6 +1671,7 @@ LaidOutBlock LayoutGroupedMediaBlock(
auto block = LaidOutBlock();
block.kind = PreparedBlockKind::GroupedMedia;
block.anchorId = prepared.anchorId;
block.supplementary = prepared.supplementary;
if (context.mediaBlockFactory) {
block.mediaBlock = context.mediaBlockFactory(prepared);
}
@@ -113,6 +113,7 @@ struct LaidOutBlock {
bool overflowed = false;
bool tableBordered = true;
bool tableStriped = false;
bool supplementary = false;
int segmentIndex = -1;
int secondarySegmentIndex = -1;
int tertiarySegmentIndex = -1;
@@ -153,6 +154,7 @@ struct TableRowLayoutData {
bool header = false;
};
[[nodiscard]] bool IsAnchorOnlyBlock(const PreparedBlock &block);
[[nodiscard]] bool IsFlowKind(PreparedBlockKind kind);
[[nodiscard]] QString ListMarkerText(const PreparedBlock &block);
[[nodiscard]] int TextLineHeight(const style::TextStyle &style);
@@ -104,6 +104,17 @@ namespace {
&& (next->kind == PreparedBlockKind::RelatedArticle);
}
[[nodiscard]] const PreparedBlock *NextVisibleBlock(
const std::vector<PreparedBlock> &blocks,
int index) {
for (auto i = index + 1, count = int(blocks.size()); i != count; ++i) {
if (!IsAnchorOnlyBlock(blocks[i])) {
return &blocks[i];
}
}
return nullptr;
}
void PrepareNestedContext(
LayoutContext *context,
int left,
@@ -186,6 +197,7 @@ void PrepareNestedContext(
auto block = LaidOutBlock();
block.kind = PreparedBlockKind::ListItem;
block.anchorId = prepared.anchorId;
block.supplementary = prepared.supplementary;
block.listKind = prepared.listKind;
block.listDelimiter = prepared.listDelimiter;
block.taskState = prepared.taskState;
@@ -314,12 +326,12 @@ void PrepareNestedContext(
PrepareNestedContext(&childContext, listLeft, listWidth);
auto y = top;
auto first = true;
auto previous = static_cast<const PreparedBlock*>(nullptr);
for (const auto &child : prepared.children) {
if (!first) {
const auto anchorOnly = IsAnchorOnlyBlock(child);
if (previous && !anchorOnly) {
y += prepared.tight ? 0 : BlockSkip(child, markdown);
}
first = false;
auto laidOut = (child.kind == PreparedBlockKind::ListItem)
? LayoutListItemBlock(
@@ -349,6 +361,9 @@ void PrepareNestedContext(
childContext);
y = BlockBottom(laidOut);
block.children.push_back(std::move(laidOut));
if (!anchorOnly) {
previous = &child;
}
}
block.outer = QRect(
@@ -441,6 +456,7 @@ void PrepareNestedContext(
block.kind = PreparedBlockKind::Details;
block.anchorId = prepared.anchorId;
block.collapsed = prepared.collapsed;
block.supplementary = prepared.supplementary;
const auto &details = markdown.details;
const auto headerWidth = std::max(width, 1);
const auto iconWidth = details.icon.width();
@@ -553,6 +569,7 @@ void PrepareNestedContext(
auto block = LaidOutBlock();
block.kind = PreparedBlockKind::EmbedPost;
block.anchorId = prepared.anchorId;
block.supplementary = prepared.supplementary;
block.thumbnailPhotoId = prepared.embedPost.authorPhotoId;
if (prepared.embedPost.authorPhotoId && mediaRuntime) {
block.photoRuntime = mediaRuntime->resolvePhoto(
@@ -937,8 +954,9 @@ int LayoutBlocks(
auto previous = static_cast<const PreparedBlock*>(nullptr);
for (auto i = 0, count = int(prepared.size()); i != count; ++i) {
const auto &block = prepared[i];
const auto next = (i + 1 < count) ? &prepared[i + 1] : nullptr;
if (previous) {
const auto anchorOnly = IsAnchorOnlyBlock(block);
const auto next = NextVisibleBlock(prepared, i);
if (previous && !anchorOnly) {
y += BlockSkip(*previous, block, context, markdown);
}
const auto band = BlockBand(
@@ -987,7 +1005,9 @@ int LayoutBlocks(
}
y = BlockBottom(laidOut);
blocks->push_back(std::move(laidOut));
previous = &block;
if (!anchorOnly) {
previous = &block;
}
}
return y;
}
@@ -134,7 +134,9 @@ void PaintTextLeaf(
leaf.draw(p, {
.position = rect.topLeft(),
.availableWidth = availableWidth,
.geometry = TextGeometry(availableWidth),
.geometry = elisionLines
? Ui::Text::SimpleGeometry(availableWidth, elisionLines, 0, true)
: TextGeometry(availableWidth),
.align = align,
.clip = clip,
.palette = &p.textPalette(),
@@ -148,6 +150,27 @@ void PaintTextLeaf(
});
}
[[nodiscard]] std::optional<QColor> QuoteSupplementaryColor(
const MarkdownArticlePaintCaches &caches) {
if (!caches.blockquote) {
return {};
}
return anim::color(
caches.blockquote->bg,
caches.blockquote->icon,
0.75);
}
void SetTextLeafPen(
Painter &p,
const LaidOutBlock &block,
const style::Markdown &markdown,
const MarkdownArticlePaintCaches &caches) {
p.setPen(!block.supplementary
? markdown.textColor->c
: caches.supplementaryColorOverride.value_or(markdown.textColor->c));
}
void PaintRelatedArticleTextLeaf(
Painter &p,
const Ui::Text::String &leaf,
@@ -302,7 +325,7 @@ void PaintTableBlock(
const PaintSelectionState &selectionState,
QRect clip) {
if (!block.textRect.isEmpty()) {
p.setPen(markdown.textColor->c);
SetTextLeafPen(p, block, markdown, caches);
PaintTextLeaf(
p,
block.leaf,
@@ -586,6 +609,8 @@ void PaintQuoteBlock(
p.restore();
}
auto overriden = caches;
overriden.supplementaryColorOverride = QuoteSupplementaryColor(caches);
PaintBlocks(
p,
block.children,
@@ -595,7 +620,7 @@ void PaintQuoteBlock(
devicePixelRatio,
outerWidth,
markdown,
caches,
overriden,
selectionState,
clip.intersected(block.contentRect));
}
@@ -641,7 +666,7 @@ void PaintPlaceholderBlock(
p.restore();
}
if (!block.textRect.isEmpty()) {
p.setPen(markdown.textColor->c);
SetTextLeafPen(p, block, markdown, caches);
PaintTextLeaf(
p,
block.leaf,
@@ -752,7 +777,7 @@ void PaintEmbedPostBlock(
clip.intersected(block.bodyRect));
}
if (!block.textRect.isEmpty()) {
p.setPen(markdown.textColor->c);
SetTextLeafPen(p, block, markdown, caches);
PaintTextLeaf(
p,
block.leaf,
@@ -777,7 +802,7 @@ void PaintMediaCaption(
if (block.textRect.isEmpty()) {
return;
}
p.setPen(markdown.textColor->c);
SetTextLeafPen(p, block, markdown, caches);
PaintTextLeaf(
p,
block.leaf,
@@ -914,7 +939,7 @@ void PaintRelatedArticleBlock(
}
}
if (!block.labelRect.isEmpty()) {
p.setPen(style.titleFg->c);
p.setPen(markdown.textColor->c);
PaintRelatedArticleTextLeaf(
p,
block.labelLeaf,
@@ -925,7 +950,7 @@ void PaintRelatedArticleBlock(
style.titleLines);
}
if (!block.subtitleRect.isEmpty()) {
p.setPen(style.subtitleFg->c);
p.setPen(markdown.textColor->c);
PaintRelatedArticleTextLeaf(
p,
block.subtitleLeaf,
@@ -936,7 +961,7 @@ void PaintRelatedArticleBlock(
style.subtitleLines);
}
if (!block.actionRect.isEmpty()) {
p.setPen(style.footerFg->c);
p.setPen(markdown.supplementaryTextColor->c);
PaintRelatedArticleTextLeaf(
p,
block.actionLeaf,
@@ -1133,7 +1158,7 @@ void PaintBlock(
if (!block.headerRect.isEmpty()) {
p.fillRect(block.headerRect, markdown.relatedArticle.headerBg->c);
}
p.setPen(markdown.textColor->c);
SetTextLeafPen(p, block, markdown, caches);
PaintTextLeaf(
p,
block.leaf,
@@ -338,6 +338,9 @@ void CollectSelectableSegments(
case PreparedBlockKind::Paragraph:
case PreparedBlockKind::Heading:
case PreparedBlockKind::Details: {
if (block.leaf.isEmpty() && block.textRect.isEmpty()) {
break;
}
auto segment = SelectableSegment();
segment.kind = SelectableSegmentKind::TextLeaf;
segment.leaf = &block.leaf;
@@ -152,33 +152,27 @@ PreparedLinkClickHandler::PreparedLinkClickHandler(PreparedLink link)
: _link(std::move(link)) {
}
void PreparedLinkClickHandler::onClick(ClickContext) const {
}
[[nodiscard]] const PreparedLink &PreparedLinkClickHandler::link() const {
const PreparedLink &PreparedLinkClickHandler::link() const {
return _link;
}
QString PreparedLinkClickHandler::url() const {
return _link.target;
}
QString PreparedLinkClickHandler::copyToClipboardText() const {
return CopyTextForLink(_link);
}
QString PreparedLinkClickHandler::copyToClipboardContextItemText() const {
return copyToClipboardText().isEmpty()
? QString()
: CopyLabelForLink(_link);
}
ClickHandler::TextEntity PreparedLinkClickHandler::getTextEntity() const {
return TextEntityForLink(_link);
}
@@ -713,8 +707,8 @@ bool InlineFormulaSharedState::failed() const {
return !measured().success;
}
std::optional<Ui::Text::CustomEmojiVerticalMetrics>
InlineFormulaSharedState::vertical(const style::TextStyle &textStyle) const {
auto InlineFormulaSharedState::vertical(const style::TextStyle &textStyle) const
-> std::optional<Ui::Text::CustomEmojiVerticalMetrics> {
const auto &formula = measured();
const auto geometry = InlineFormulaGeometryFrom(formula);
if (formula.success && (geometry.imageHeight > 0)) {
@@ -879,8 +873,8 @@ QString InlineFormulaObject::entityData() {
return QString();
}
std::optional<Ui::Text::CustomEmojiVerticalMetrics>
InlineFormulaObject::vertical(const style::TextStyle &textStyle) {
auto InlineFormulaObject::vertical(const style::TextStyle &textStyle)
-> std::optional<Ui::Text::CustomEmojiVerticalMetrics> {
return _state ? _state->vertical(textStyle) : std::nullopt;
}
@@ -970,8 +964,8 @@ QString InlineIvImageObject::entityData() {
return QString();
}
std::optional<Ui::Text::CustomEmojiVerticalMetrics>
InlineIvImageObject::vertical(const style::TextStyle &textStyle) {
auto InlineIvImageObject::vertical(const style::TextStyle &textStyle)
-> std::optional<Ui::Text::CustomEmojiVerticalMetrics> {
if (_height > 0) {
const auto line = textStyle.font->height;
const auto above = _height - (_height / 2);
@@ -1077,11 +1071,11 @@ std::unique_ptr<Ui::Text::CustomEmoji> InlineFormulaObjectCache::create(
std::move(state));
}
std::shared_ptr<InlineFormulaSharedState>
InlineFormulaObjectCache::lookupOrCreate(
auto InlineFormulaObjectCache::lookupOrCreate(
const PreparedFormulaMeasurementSignature &signature,
const style::TextStyle &textStyle,
const std::vector<PreparedFormulaSlot> *formulas) {
const std::vector<PreparedFormulaSlot> *formulas)
-> std::shared_ptr<InlineFormulaSharedState> {
if (const auto i = _states.find(signature); i != end(_states)) {
return i->second;
}
@@ -162,8 +162,8 @@ inline rpl::producer<uint64> MediaRuntime::channelJoinedChanges() const {
return rpl::never<uint64>();
}
inline std::shared_ptr<HostedMediaBlockFactory>
MediaRuntime::hostedMediaBlockFactory() const {
inline auto MediaRuntime::hostedMediaBlockFactory() const
-> std::shared_ptr<HostedMediaBlockFactory> {
return nullptr;
}
@@ -217,9 +217,13 @@ struct OpenOptions {
std::shared_ptr<QVariant> clickHandlerContextRef;
std::function<void()> openSource;
std::function<void(std::shared_ptr<Ui::Show>)> share;
std::function<Webview::DataResult(QByteArray, Webview::DataRequest)>
ivWebviewDataRequest;
std::function<bool(const MediaActivation &, Qt::MouseButton)> activateMedia;
std::function<Webview::DataResult(
QByteArray,
Webview::DataRequest)> ivWebviewDataRequest;
std::function<bool(
const MediaActivation &,
Qt::MouseButton)> activateMedia;
rpl::producer<> downloadTaskFinished;
};
struct ParseOptions {
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "iv/markdown/iv_markdown_parse.h"
#include "iv/markdown/iv_markdown_view.h"
#include "iv/iv_delegate_impl.h"
#include "iv/iv_zoom_controls.h"
#include "lang/lang_keys.h"
#include "ui/layers/layer_manager.h"
#include "ui/layers/show.h"
@@ -849,6 +850,9 @@ void Controller::showMenu() {
&st::menuIconShare);
}
_menu->addSeparator();
_menu->addAction(CreateZoomMenuAction(_menu, _delegate));
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
_menu->popup(_window->body()->mapToGlobal(
QPoint(_window->body()->width(), 0) + st::ivMenuPosition));
@@ -87,29 +87,24 @@ IvHistoryViewDelegate::IvHistoryViewDelegate(
, _session(session) {
}
HistoryView::Context IvHistoryViewDelegate::elementContext() {
return HistoryView::Context::TTLViewer;
}
HistoryView::ElementChatMode IvHistoryViewDelegate::elementChatMode() {
return HistoryView::ElementChatMode::Default;
}
bool IvHistoryViewDelegate::elementAnimationsPaused() {
return false;
}
void IvHistoryViewDelegate::elementOpenPhoto(
not_null<PhotoData*> photo,
FullMsgId context) {
controller()->openPhoto(photo, { .id = context });
}
void IvHistoryViewDelegate::elementOpenDocument(
not_null<DocumentData*> document,
FullMsgId context,
@@ -120,14 +115,12 @@ void IvHistoryViewDelegate::elementOpenDocument(
{ .id = context });
}
void IvHistoryViewDelegate::elementCancelUpload(const FullMsgId &context) {
if (const auto item = _session->message(context)) {
controller()->cancelUploadLayer(item);
}
}
void IvHistoryViewDelegate::elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) {
@@ -286,18 +279,15 @@ IvHistoryViewBlock::IvHistoryViewBlock(
}, _lifetime);
}
[[nodiscard]] uint64 IvHistoryViewBlock::stableId() const {
uint64 IvHistoryViewBlock::stableId() const {
return _stableId;
}
[[nodiscard]] bool IvHistoryViewBlock::supported() const {
bool IvHistoryViewBlock::supported() const {
return _supported;
}
[[nodiscard]] int IvHistoryViewBlock::resizeGetHeight(int width) {
int IvHistoryViewBlock::resizeGetHeight(int width) {
if (!_media) {
return 0;
}
@@ -305,7 +295,6 @@ IvHistoryViewBlock::IvHistoryViewBlock(
return _media->resizeGetHeight(_requestedWidth);
}
void IvHistoryViewBlock::setGeometry(QRect geometry) {
if (!_media) {
_geometry = geometry;
@@ -319,17 +308,14 @@ void IvHistoryViewBlock::setGeometry(QRect geometry) {
_geometry = QRect(geometry.topLeft(), _media->currentSize());
}
[[nodiscard]] QRect IvHistoryViewBlock::geometry() const {
QRect IvHistoryViewBlock::geometry() const {
return _geometry;
}
[[nodiscard]] int IvHistoryViewBlock::firstLineBaseline() const {
int IvHistoryViewBlock::firstLineBaseline() const {
return _geometry.y();
}
void IvHistoryViewBlock::paint(
Painter &p,
QRect clip,
@@ -357,24 +343,21 @@ void IvHistoryViewBlock::paint(
p.restore();
}
[[nodiscard]] ClickHandlerPtr IvHistoryViewBlock::linkAt(QPoint point) const {
ClickHandlerPtr IvHistoryViewBlock::linkAt(QPoint point) const {
return resolveHit(point).link;
}
[[nodiscard]] MediaActivation IvHistoryViewBlock::activationAt(QPoint point) const {
MediaActivation IvHistoryViewBlock::activationAt(QPoint point) const {
return resolveHit(point).activation;
}
[[nodiscard]] MediaBlockSelectionData IvHistoryViewBlock::selectionData() const {
MediaBlockSelectionData IvHistoryViewBlock::selectionData() const {
return {
.copyText = _copyText,
};
}
[[nodiscard]] IvHistoryViewHit IvHistoryViewBlock::resolveHit(QPoint point) const {
IvHistoryViewHit IvHistoryViewBlock::resolveHit(QPoint point) const {
auto result = IvHistoryViewHit();
if (!_supported || !_media || !_geometry.contains(point)) {
return result;
@@ -382,8 +365,7 @@ void IvHistoryViewBlock::paint(
return resolveLocalHit(point - _geometry.topLeft());
}
[[nodiscard]] IvHistoryViewHit IvHistoryViewBlock::resolveLocalHit(QPoint point) const {
IvHistoryViewHit IvHistoryViewBlock::resolveLocalHit(QPoint point) const {
auto result = IvHistoryViewHit();
if (!_media) {
return result;
@@ -397,14 +379,12 @@ void IvHistoryViewBlock::paint(
return classifyState(state);
}
[[nodiscard]] IvHistoryViewHit IvHistoryViewBlock::classifyState(
IvHistoryViewHit IvHistoryViewBlock::classifyState(
const HistoryView::TextState &state) const {
return classifyHandler(state.link);
}
[[nodiscard]] IvHistoryViewHit IvHistoryViewBlock::classifyHandler(
IvHistoryViewHit IvHistoryViewBlock::classifyHandler(
const ClickHandlerPtr &handler) const {
auto result = IvHistoryViewHit();
if (!handler) {
@@ -459,8 +439,7 @@ void IvHistoryViewBlock::paint(
return result;
}
[[nodiscard]] bool IvHistoryViewBlock::probeSupport() {
bool IvHistoryViewBlock::probeSupport() {
if (!_media) {
return false;
}
@@ -476,8 +455,7 @@ void IvHistoryViewBlock::paint(
return false;
}
[[nodiscard]] bool IvHistoryViewBlock::supportsHitClassification() {
bool IvHistoryViewBlock::supportsHitClassification() {
const auto width = std::max(_layoutHint.width(), 1);
_media->resizeGetHeight(width);
const auto size = _media->currentSize();
@@ -501,18 +479,15 @@ void IvHistoryViewBlock::paint(
return true;
}
void IvHistoryViewBlock::handleViewRepaint(QRect rect) {
Q_UNUSED(rect);
requestRepaint(QRect());
}
void IvHistoryViewBlock::handleItemRepaint() {
requestRepaint(QRect());
}
void IvHistoryViewBlock::handleViewResize() {
if (!_media) {
return;
@@ -474,7 +474,6 @@ ImageBackedMediaBlock::ImageBackedMediaBlock(
}
}
ImageBackedMediaBlock::ImageBackedMediaBlock(
const PreparedVideoBlockData &prepared,
std::shared_ptr<MediaRuntime> mediaRuntime)
@@ -487,7 +486,6 @@ ImageBackedMediaBlock::ImageBackedMediaBlock(
, _documentId(prepared.media.id) {
}
ImageBackedMediaBlock::ImageBackedMediaBlock(
const PreparedMapBlockData &prepared,
std::shared_ptr<MediaRuntime> mediaRuntime)
@@ -510,11 +508,11 @@ ImageBackedMediaBlock::ImageBackedMediaBlock(
}
}
[[nodiscard]] uint64 ImageBackedMediaBlock::stableId() const {
uint64 ImageBackedMediaBlock::stableId() const {
return _stableId;
}
[[nodiscard]] int ImageBackedMediaBlock::resizeGetHeight(int width) {
int ImageBackedMediaBlock::resizeGetHeight(int width) {
return MediaHeightForWidth(width, _aspectWidth, _aspectHeight);
}
@@ -523,11 +521,11 @@ void ImageBackedMediaBlock::setGeometry(QRect geometry) {
ensureResolved(geometry.size());
}
[[nodiscard]] QRect ImageBackedMediaBlock::geometry() const {
QRect ImageBackedMediaBlock::geometry() const {
return _geometry;
}
[[nodiscard]] int ImageBackedMediaBlock::firstLineBaseline() const {
int ImageBackedMediaBlock::firstLineBaseline() const {
return _geometry.y();
}
@@ -567,17 +565,17 @@ void ImageBackedMediaBlock::paint(
p.restore();
}
[[nodiscard]] ClickHandlerPtr ImageBackedMediaBlock::linkAt(QPoint point) const {
ClickHandlerPtr ImageBackedMediaBlock::linkAt(QPoint point) const {
Q_UNUSED(point);
return nullptr;
}
[[nodiscard]] MediaActivation ImageBackedMediaBlock::activationAt(
MediaActivation ImageBackedMediaBlock::activationAt(
QPoint point) const {
return _geometry.contains(point) ? _activation : MediaActivation();
}
[[nodiscard]] MediaBlockSelectionData ImageBackedMediaBlock::selectionData() const {
MediaBlockSelectionData ImageBackedMediaBlock::selectionData() const {
return {
.copyText = _copyText,
};
@@ -677,7 +675,7 @@ void ImageBackedMediaBlock::subscribeImage(const std::shared_ptr<Ui::DynamicImag
});
}
[[nodiscard]] bool ImageBackedMediaBlock::loading() const {
bool ImageBackedMediaBlock::loading() const {
if (_photoRuntime) {
return _photoRuntime->loading();
} else if (_documentRuntime) {
@@ -688,7 +686,7 @@ void ImageBackedMediaBlock::subscribeImage(const std::shared_ptr<Ui::DynamicImag
return false;
}
[[nodiscard]] double ImageBackedMediaBlock::progress() const {
double ImageBackedMediaBlock::progress() const {
if (_photoRuntime) {
return _photoRuntime->progress();
} else if (_documentRuntime) {
@@ -766,18 +764,15 @@ AudioMediaBlock::AudioMediaBlock(
}
}
[[nodiscard]] uint64 AudioMediaBlock::stableId() const {
uint64 AudioMediaBlock::stableId() const {
return _stableId;
}
[[nodiscard]] int AudioMediaBlock::resizeGetHeight(int width) {
int AudioMediaBlock::resizeGetHeight(int width) {
rebuildLayout(width);
return _height;
}
void AudioMediaBlock::setGeometry(QRect geometry) {
if (_layoutWidth != std::max(geometry.width(), 1)) {
rebuildLayout(geometry.width());
@@ -788,17 +783,14 @@ void AudioMediaBlock::setGeometry(QRect geometry) {
applyGeometry();
}
[[nodiscard]] QRect AudioMediaBlock::geometry() const {
QRect AudioMediaBlock::geometry() const {
return _geometry;
}
[[nodiscard]] int AudioMediaBlock::firstLineBaseline() const {
int AudioMediaBlock::firstLineBaseline() const {
return _firstLineBaseline;
}
void AudioMediaBlock::paint(
Painter &p,
QRect clip,
@@ -838,19 +830,16 @@ void AudioMediaBlock::paint(
p.restore();
}
[[nodiscard]] ClickHandlerPtr AudioMediaBlock::linkAt(QPoint point) const {
ClickHandlerPtr AudioMediaBlock::linkAt(QPoint point) const {
Q_UNUSED(point);
return nullptr;
}
[[nodiscard]] MediaActivation AudioMediaBlock::activationAt(QPoint point) const {
MediaActivation AudioMediaBlock::activationAt(QPoint point) const {
return _geometry.contains(point) ? _activation : MediaActivation();
}
[[nodiscard]] MediaBlockSelectionData AudioMediaBlock::selectionData() const {
MediaBlockSelectionData AudioMediaBlock::selectionData() const {
return {
.copyText = _copyText,
};
@@ -901,7 +890,6 @@ void AudioMediaBlock::rebuildLayout(int width) {
+ padding.bottom();
}
void AudioMediaBlock::applyGeometry() {
const auto &card = st::defaultMarkdown.audio;
const auto &padding = card.padding;
@@ -1020,18 +1008,15 @@ ChannelMediaBlock::ChannelMediaBlock(
}
}
[[nodiscard]] uint64 ChannelMediaBlock::stableId() const {
uint64 ChannelMediaBlock::stableId() const {
return _stableId;
}
[[nodiscard]] int ChannelMediaBlock::resizeGetHeight(int width) {
int ChannelMediaBlock::resizeGetHeight(int width) {
rebuildLayout(width);
return _height;
}
void ChannelMediaBlock::setGeometry(QRect geometry) {
if (_layoutWidth != std::max(geometry.width(), 1)) {
rebuildLayout(geometry.width());
@@ -1042,17 +1027,14 @@ void ChannelMediaBlock::setGeometry(QRect geometry) {
applyGeometry();
}
[[nodiscard]] QRect ChannelMediaBlock::geometry() const {
QRect ChannelMediaBlock::geometry() const {
return _geometry;
}
[[nodiscard]] int ChannelMediaBlock::firstLineBaseline() const {
int ChannelMediaBlock::firstLineBaseline() const {
return _firstLineBaseline;
}
void ChannelMediaBlock::paint(
Painter &p,
QRect clip,
@@ -1099,8 +1081,7 @@ void ChannelMediaBlock::paint(
p.restore();
}
[[nodiscard]] ClickHandlerPtr ChannelMediaBlock::linkAt(QPoint point) const {
ClickHandlerPtr ChannelMediaBlock::linkAt(QPoint point) const {
if (_joinVisible
&& _joinLink
&& !_actionRect.isEmpty()
@@ -1110,8 +1091,7 @@ void ChannelMediaBlock::paint(
return nullptr;
}
[[nodiscard]] MediaActivation ChannelMediaBlock::activationAt(QPoint point) const {
MediaActivation ChannelMediaBlock::activationAt(QPoint point) const {
if (!_geometry.contains(point)) {
return {};
}
@@ -1121,8 +1101,7 @@ void ChannelMediaBlock::paint(
return _openActivation;
}
[[nodiscard]] MediaBlockSelectionData ChannelMediaBlock::selectionData() const {
MediaBlockSelectionData ChannelMediaBlock::selectionData() const {
return {
.copyText = _copyText,
};
@@ -1145,7 +1124,6 @@ void ChannelMediaBlock::resolveChannel() {
}
}
void ChannelMediaBlock::rebuildLayout(int width) {
resolveChannel();
const auto &card = st::defaultMarkdown.channel;
@@ -1212,7 +1190,6 @@ void ChannelMediaBlock::rebuildLayout(int width) {
_height = padding.top() + _cardContentHeight + padding.bottom();
}
void ChannelMediaBlock::applyGeometry() {
const auto &card = st::defaultMarkdown.channel;
const auto &padding = card.padding;
@@ -1244,7 +1221,6 @@ void ChannelMediaBlock::applyGeometry() {
}
}
void ChannelMediaBlock::handleJoinedChange() {
if (_geometry.width() <= 0 && _layoutWidth <= 0) {
_channelResolved = false;
@@ -1405,18 +1381,15 @@ GroupedMediaBlock::GroupedMediaBlock(
}
}
[[nodiscard]] uint64 GroupedMediaBlock::stableId() const {
uint64 GroupedMediaBlock::stableId() const {
return _stableId;
}
[[nodiscard]] int GroupedMediaBlock::resizeGetHeight(int width) {
int GroupedMediaBlock::resizeGetHeight(int width) {
rebuildLayout(width);
return _height;
}
void GroupedMediaBlock::setGeometry(QRect geometry) {
rebuildLayout(geometry.width());
const auto contentWidth = std::max(_contentWidth, 1);
@@ -1430,17 +1403,14 @@ void GroupedMediaBlock::setGeometry(QRect geometry) {
applyGeometry();
}
[[nodiscard]] QRect GroupedMediaBlock::geometry() const {
QRect GroupedMediaBlock::geometry() const {
return _geometry;
}
[[nodiscard]] int GroupedMediaBlock::firstLineBaseline() const {
int GroupedMediaBlock::firstLineBaseline() const {
return _geometry.y();
}
void GroupedMediaBlock::paint(
Painter &p,
QRect clip,
@@ -1473,8 +1443,7 @@ void GroupedMediaBlock::paint(
p.restore();
}
[[nodiscard]] ClickHandlerPtr GroupedMediaBlock::linkAt(QPoint point) const {
ClickHandlerPtr GroupedMediaBlock::linkAt(QPoint point) const {
if (_intent == PreparedGroupedMediaIntent::Slideshow) {
if (_previousRect.contains(point)) {
return _previousLink;
@@ -1485,8 +1454,7 @@ void GroupedMediaBlock::paint(
return nullptr;
}
[[nodiscard]] MediaActivation GroupedMediaBlock::activationAt(QPoint point) const {
MediaActivation GroupedMediaBlock::activationAt(QPoint point) const {
if (!_geometry.contains(point)) {
return {};
} else if (_intent == PreparedGroupedMediaIntent::Slideshow) {
@@ -1508,14 +1476,12 @@ void GroupedMediaBlock::paint(
return {};
}
[[nodiscard]] MediaBlockSelectionData GroupedMediaBlock::selectionData() const {
MediaBlockSelectionData GroupedMediaBlock::selectionData() const {
return {
.copyText = _copyText,
};
}
void GroupedMediaBlock::rebuildLayout(int width) {
_layoutWidth = std::max(width, 1);
_contentWidth = _layoutWidth;
@@ -1572,7 +1538,6 @@ void GroupedMediaBlock::rebuildLayout(int width) {
_useCollageLayout = true;
}
void GroupedMediaBlock::clearCollageLayout() {
_contentWidth = _layoutWidth;
_height = fallbackHeight(_layoutWidth);
@@ -1583,8 +1548,7 @@ void GroupedMediaBlock::clearCollageLayout() {
}
}
[[nodiscard]] int GroupedMediaBlock::fallbackHeight(int width) const {
int GroupedMediaBlock::fallbackHeight(int width) const {
if (_fallbackSize.isEmpty()) {
return std::max(st::defaultMarkdown.placeholder.minHeight, 1);
}
@@ -1594,7 +1558,6 @@ void GroupedMediaBlock::clearCollageLayout() {
_fallbackSize.height());
}
void GroupedMediaBlock::applyGeometry() {
_previousRect = QRect();
_nextRect = QRect();
@@ -1624,7 +1587,6 @@ void GroupedMediaBlock::applyGeometry() {
}
}
void GroupedMediaBlock::resolveRuntime(ItemState &item) {
if (item.runtimeResolved) {
return;
@@ -1649,7 +1611,6 @@ void GroupedMediaBlock::resolveRuntime(ItemState &item) {
}
}
void GroupedMediaBlock::resolveImages(ItemState &item) {
if (item.rect.isEmpty()) {
return;
@@ -1681,7 +1642,6 @@ void GroupedMediaBlock::resolveImages(ItemState &item) {
}
}
void GroupedMediaBlock::subscribeImage(
const std::shared_ptr<Ui::DynamicImage> &image,
const ItemState *item) {
@@ -1698,7 +1658,6 @@ void GroupedMediaBlock::subscribeImage(
});
}
void GroupedMediaBlock::handleImageUpdate(int index) {
if (index < 0 || index >= int(_items.size())) {
return;
@@ -1711,7 +1670,6 @@ void GroupedMediaBlock::handleImageUpdate(int index) {
requestRepaint(_items[index].rect);
}
void GroupedMediaBlock::paintItem(Painter &p, const ItemState &item) const {
if (item.rect.isEmpty()) {
return;
@@ -1739,8 +1697,7 @@ void GroupedMediaBlock::paintItem(Painter &p, const ItemState &item) const {
}
}
[[nodiscard]] bool GroupedMediaBlock::itemLoading(const ItemState &item) const {
bool GroupedMediaBlock::itemLoading(const ItemState &item) const {
if (item.photoRuntime) {
return item.photoRuntime->loading();
} else if (item.documentRuntime) {
@@ -1749,8 +1706,7 @@ void GroupedMediaBlock::paintItem(Painter &p, const ItemState &item) const {
return false;
}
[[nodiscard]] double GroupedMediaBlock::itemProgress(const ItemState &item) const {
double GroupedMediaBlock::itemProgress(const ItemState &item) const {
if (item.photoRuntime) {
return item.photoRuntime->progress();
} else if (item.documentRuntime) {
@@ -1759,7 +1715,6 @@ void GroupedMediaBlock::paintItem(Painter &p, const ItemState &item) const {
return 0.;
}
void GroupedMediaBlock::paintActiveItem(Painter &p) const {
const auto item = activeItem();
if (!item) {
@@ -1794,7 +1749,6 @@ void GroupedMediaBlock::paintActiveItem(Painter &p) const {
}
}
void GroupedMediaBlock::paintNavigation(Painter &p) const {
if ((_intent != PreparedGroupedMediaIntent::Slideshow)
|| (_items.size() < 2)) {
@@ -1821,7 +1775,6 @@ void GroupedMediaBlock::paintNavigation(Painter &p) const {
}
}
void GroupedMediaBlock::ensureNavigationLinks() {
if ((_intent != PreparedGroupedMediaIntent::Slideshow)
|| (_items.size() < 2)
@@ -1842,7 +1795,6 @@ void GroupedMediaBlock::ensureNavigationLinks() {
});
}
void GroupedMediaBlock::updateNavigationRects() {
if ((_intent != PreparedGroupedMediaIntent::Slideshow)
|| (_items.size() < 2)
@@ -1874,7 +1826,6 @@ void GroupedMediaBlock::updateNavigationRects() {
size);
}
void GroupedMediaBlock::stepActiveIndex(int delta) {
if ((_intent != PreparedGroupedMediaIntent::Slideshow)
|| (_items.size() < 2)) {
@@ -1906,8 +1857,7 @@ void GroupedMediaBlock::stepActiveIndex(int delta) {
requestRepaint(previousGeometry.united(_geometry));
}
[[nodiscard]] int GroupedMediaBlock::activeItemHeight(int width) const {
int GroupedMediaBlock::activeItemHeight(int width) const {
if (const auto item = activeItem()) {
return MediaHeightForWidth(
width,
@@ -1917,15 +1867,13 @@ void GroupedMediaBlock::stepActiveIndex(int delta) {
return fallbackHeight(width);
}
[[nodiscard]] GroupedMediaBlock::ItemState *GroupedMediaBlock::activeItem() {
GroupedMediaBlock::ItemState *GroupedMediaBlock::activeItem() {
return (_activeIndex >= 0 && _activeIndex < int(_items.size()))
? &_items[_activeIndex]
: nullptr;
}
[[nodiscard]] const GroupedMediaBlock::ItemState *GroupedMediaBlock::activeItem() const {
const GroupedMediaBlock::ItemState *GroupedMediaBlock::activeItem() const {
return (_activeIndex >= 0 && _activeIndex < int(_items.size()))
? &_items[_activeIndex]
: nullptr;
@@ -250,6 +250,7 @@ struct PreparedBlock {
bool collapsed = false;
bool depthClamped = false;
bool tight = false;
bool supplementary = false;
};
struct PreparedRenderDocument {
@@ -564,7 +564,6 @@ void PrepareFootnotes(PrepareState *state) {
return PrepareChildren(node, {}, state);
}
[[nodiscard]] std::vector<PreparedBlock> PrepareFlowBlock(
const MarkdownNode &node,
PreparedBlockKind kind,
@@ -736,6 +736,7 @@ using NativeIvHtmlAttributes = std::vector<NativeIvHtmlAttribute>;
SortPreparedIvRichText(&caption);
block.text = std::move(caption.text);
block.links = std::move(caption.links);
block.supplementary = true;
if (data.vblocks().v.isEmpty()) {
block.children.push_back(
PrepareNativeIvEmbedPostFallbackParagraph(block.embedPost.url));
@@ -841,7 +842,10 @@ using NativeIvHtmlAttributes = std::vector<NativeIvHtmlAttribute>;
&block.children,
PreparedBlockKind::Paragraph,
0,
std::move(cite))) {
std::move(cite),
QString(),
false,
true)) {
return false;
}
if (block.children.empty()) {
@@ -1441,7 +1445,9 @@ void MarkNativeIvTableSlots(
PreparedBlockKind::Paragraph,
0,
std::move(prepared),
std::move(anchorId));
std::move(anchorId),
false,
true);
}, [&](const MTPDpageBlockHeader &data) {
return AppendNativeIvFlowBlock(
result,
@@ -581,7 +581,8 @@ bool AppendPreparedIvRichBlock(
int headingLevel,
PreparedIvRichText prepared,
QString anchorId,
bool allowEmpty) {
bool allowEmpty,
bool supplementary) {
SortPreparedIvRichText(&prepared);
if (prepared.text.text.isEmpty() && !allowEmpty) {
return true;
@@ -592,6 +593,7 @@ bool AppendPreparedIvRichBlock(
block.text = std::move(prepared.text);
block.links = std::move(prepared.links);
block.anchorId = std::move(anchorId);
block.supplementary = supplementary;
result->push_back(std::move(block));
return true;
}
@@ -626,6 +628,7 @@ bool PrepareNativeIvPhotoBlock(
block.text = std::move(caption.text);
block.links = std::move(caption.links);
block.anchorId = std::move(anchorId);
block.supplementary = true;
block.photo.id = GeneratePreparedMediaBlockId(state);
block.photo.photoId = info->id;
block.photo.width = info->width;
@@ -658,6 +661,7 @@ bool PrepareNativeIvVideoBlock(
block.text = std::move(caption.text);
block.links = std::move(caption.links);
block.anchorId = std::move(anchorId);
block.supplementary = true;
block.video.id = GeneratePreparedMediaBlockId(state);
block.video.media.kind = PreparedMediaItemKind::Document;
block.video.media.id = info->id;
@@ -686,6 +690,7 @@ bool PrepareNativeIvAudioBlock(
block.text = std::move(caption.text);
block.links = std::move(caption.links);
block.anchorId = std::move(anchorId);
block.supplementary = true;
block.audio.id = GeneratePreparedMediaBlockId(state);
block.audio.documentId = info->id;
block.audio.title = info->title;
@@ -730,6 +735,7 @@ bool PrepareNativeIvMapBlock(
block.text = std::move(caption.text);
block.links = std::move(caption.links);
block.anchorId = std::move(anchorId);
block.supplementary = true;
prepared.id = GeneratePreparedMediaBlockId(state);
block.map = std::move(prepared);
result->push_back(std::move(block));
@@ -807,6 +813,7 @@ bool PrepareNativeIvGroupedMediaBlock(
SortPreparedIvRichText(&preparedCaption);
block.text = std::move(preparedCaption.text);
block.links = std::move(preparedCaption.links);
block.supplementary = true;
result->push_back(std::move(block));
return true;
}
@@ -842,6 +849,7 @@ bool PrepareNativeIvPlaceholderBlock(
block.text = std::move(prepared.text);
block.links = std::move(prepared.links);
block.anchorId = std::move(anchorId);
block.supplementary = true;
block.placeholder.label = label;
block.placeholder.embed = std::move(embed);
block.placeholder.copyText = NativeIvPlaceholderCopyText(
@@ -71,6 +71,7 @@ void RememberNativeIvDocument(
int headingLevel,
PreparedIvRichText prepared,
QString anchorId = QString(),
bool allowEmpty = false);
bool allowEmpty = false,
bool supplementary = false);
} // namespace Iv::Markdown
@@ -336,6 +336,12 @@ void MarkdownPreviewRoot::setup() {
}, lifetime());
}
rpl::duplicate(
_options.downloadTaskFinished
) | rpl::on_next([=] {
update();
}, lifetime());
_devicePixelRatio = style::DevicePixelRatio();
prepareArticle();
}
+2
View File
@@ -19,6 +19,8 @@ PRIVATE
iv/iv_pch.h
iv/iv_prepare.cpp
iv/iv_prepare.h
iv/iv_zoom_controls.cpp
iv/iv_zoom_controls.h
)
nice_target_sources(td_iv ${src_loc}