mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-10 13:43:54 +00:00
Improve style, add zoom controls.
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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 = █
|
||||
if (!anchorOnly) {
|
||||
previous = █
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user