diff --git a/Telegram/Resources/iv_html/page.css b/Telegram/Resources/iv_html/page.css
index 9c0ab832ff..cdbfc3108c 100644
--- a/Telegram/Resources/iv_html/page.css
+++ b/Telegram/Resources/iv_html/page.css
@@ -12,6 +12,7 @@ body {
margin: 0;
background-color: var(--td-window-bg);
color: var(--td-window-fg);
+ zoom: var(--td-zoom-percentage);
}
html.custom_scroll ::-webkit-scrollbar {
diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp
index 5f304356dc..71d9ae0e8a 100644
--- a/Telegram/SourceFiles/core/core_settings.cpp
+++ b/Telegram/SourceFiles/core/core_settings.cpp
@@ -222,7 +222,7 @@ QByteArray Settings::serialize() const {
+ Serialize::stringSize(_customFontFamily)
+ sizeof(qint32) * 3
+ Serialize::bytearraySize(_tonsiteStorageToken)
- + sizeof(qint32);
+ + sizeof(qint32) * 2;
auto result = QByteArray();
result.reserve(size);
@@ -377,7 +377,8 @@ QByteArray Settings::serialize() const {
<< qint32(_systemUnlockEnabled ? 1 : 0)
<< qint32(!_weatherInCelsius ? 0 : *_weatherInCelsius ? 1 : 2)
<< _tonsiteStorageToken
- << qint32(_includeMutedCounterFolders ? 1 : 0);
+ << qint32(_includeMutedCounterFolders ? 1 : 0)
+ << qint32(_ivZoom.current());
}
Ensures(result.size() == size);
@@ -501,6 +502,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 systemUnlockEnabled = _systemUnlockEnabled ? 1 : 0;
qint32 weatherInCelsius = !_weatherInCelsius ? 0 : *_weatherInCelsius ? 1 : 2;
QByteArray tonsiteStorageToken = _tonsiteStorageToken;
+ qint32 ivZoom = _ivZoom.current();
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -810,6 +812,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> includeMutedCounterFolders;
}
+ if (!stream.atEnd()) {
+ stream >> ivZoom;
+ }
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -1021,6 +1026,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
? std::optional()
: (weatherInCelsius == 1);
_tonsiteStorageToken = tonsiteStorageToken;
+ _ivZoom = ivZoom;
}
QString Settings::getSoundPath(const QString &key) const {
@@ -1408,6 +1414,7 @@ void Settings::resetOnLastLogout() {
_hiddenGroupCallTooltips = 0;
_storiesClickTooltipHidden = false;
_ttlVoiceClickTooltipHidden = false;
+ _ivZoom = 100;
_recentEmojiPreload.clear();
_recentEmoji.clear();
@@ -1547,4 +1554,16 @@ bool Settings::rememberedDeleteMessageOnlyForYou() const {
return _rememberedDeleteMessageOnlyForYou;
}
+int Settings::ivZoom() const {
+ return _ivZoom.current();
+}
+rpl::producer Settings::ivZoomValue() const {
+ return _ivZoom.value();
+}
+void Settings::setIvZoom(int value) {
+ constexpr auto kMin = 30;
+ constexpr auto kMax = 200;
+ _ivZoom = std::clamp(value, kMin, kMax);
+}
+
} // namespace Core
diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h
index ab5f999a65..54199d8e5c 100644
--- a/Telegram/SourceFiles/core/core_settings.h
+++ b/Telegram/SourceFiles/core/core_settings.h
@@ -915,6 +915,10 @@ public:
_tonsiteStorageToken = value;
}
+ [[nodiscard]] int ivZoom() const;
+ [[nodiscard]] rpl::producer ivZoomValue() const;
+ void setIvZoom(int value);
+
[[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
@@ -1050,6 +1054,7 @@ private:
bool _systemUnlockEnabled = false;
std::optional _weatherInCelsius;
QByteArray _tonsiteStorageToken;
+ rpl::variable _ivZoom = 100;
bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream _tabbedReplacedWithInfoValue; // per-window
diff --git a/Telegram/SourceFiles/iv/iv.style b/Telegram/SourceFiles/iv/iv.style
index 6a5757276e..cea363566f 100644
--- a/Telegram/SourceFiles/iv/iv.style
+++ b/Telegram/SourceFiles/iv/iv.style
@@ -29,6 +29,38 @@ ivBack: IconButton(ivMenuToggle) {
iconOver: ivBackIcon;
rippleAreaPosition: point(12px, 6px);
}
+ivZoomButtonsSize: 26px;
+ivPlusMinusZoom: IconButton(ivMenuToggle) {
+ width: ivZoomButtonsSize;
+ height: ivZoomButtonsSize;
+
+ rippleAreaPosition: point(0px, 0px);
+ rippleAreaSize: ivZoomButtonsSize;
+ ripple: RippleAnimation(defaultRippleAnimation) {
+ color: windowBgOver;
+ }
+}
+ivResetZoomStyle: TextStyle(defaultTextStyle) {
+ font: font(12px);
+}
+ivResetZoom: RoundButton(defaultActiveButton) {
+ textFg: windowFg;
+ textFgOver: windowFgOver;
+ textBg: windowBg;
+ textBgOver: windowBgOver;
+
+ height: ivZoomButtonsSize;
+ padding: margins(0px, 0px, 0px, 0px);
+
+ style: ivResetZoomStyle;
+
+ ripple: defaultRippleAnimation;
+}
+ivResetZoomLabel: FlatLabel(defaultFlatLabel) {
+ textFg: windowFg;
+ style: ivResetZoomStyle;
+}
+ivResetZoomInnerPadding: 20px;
ivBackIconDisabled: icon {{ "box_button_back", menuIconFg }};
ivForwardIcon: icon {{ "box_button_back-flip_horizontal", menuIconColor }};
ivForward: IconButton(ivBack) {
diff --git a/Telegram/SourceFiles/iv/iv_controller.cpp b/Telegram/SourceFiles/iv/iv_controller.cpp
index b864833dd9..486778ef7c 100644
--- a/Telegram/SourceFiles/iv/iv_controller.cpp
+++ b/Telegram/SourceFiles/iv/iv_controller.cpp
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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/wrap/fade_wrap.h"
@@ -50,7 +51,145 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Iv {
namespace {
-[[nodiscard]] QByteArray ComputeStyles() {
+constexpr auto kZoomStep = int(10);
+constexpr auto kDefaultZoom = int(100);
+
+class ItemZoom final : public Ui::Menu::Action {
+public:
+ ItemZoom(
+ not_null parent,
+ const not_null delegate,
+ const style::Menu &st)
+ : Ui::Menu::Action(
+ parent,
+ st,
+ Ui::CreateChild(parent),
+ nullptr,
+ nullptr)
+ , _delegate(delegate)
+ , _st(st) {
+ init();
+ }
+
+ void init() {
+ enableMouseSelecting();
+
+ AbstractButton::setDisabled(true);
+
+ class SmallButton final : public Ui::IconButton {
+ public:
+ SmallButton(
+ not_null parent,
+ QChar c,
+ float64 skip,
+ const style::color &color)
+ : Ui::IconButton(parent, st::ivPlusMinusZoom)
+ , _color(color)
+ , _skip(style::ConvertFloatScale(skip))
+ , _c(c) {
+ }
+
+ void paintEvent(QPaintEvent *event) override {
+ auto p = Painter(this);
+ Ui::RippleButton::paintRipple(
+ p,
+ st::ivPlusMinusZoom.rippleAreaPosition);
+ p.setPen(_color);
+ p.setFont(st::normalFont);
+ p.drawText(
+ QRectF(rect()).translated(0, _skip),
+ _c,
+ style::al_center);
+ }
+
+ private:
+ const style::color _color;
+ const float64 _skip;
+ const QChar _c;
+
+ };
+
+ const auto reset = Ui::CreateChild(
+ this,
+ rpl::single(QString()),
+ st::ivResetZoom);
+ const auto resetLabel = Ui::CreateChild(
+ reset,
+ tr::lng_background_reset_default(),
+ st::ivResetZoomLabel);
+ resetLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
+ reset->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
+ reset->setClickedCallback([this] {
+ _delegate->ivSetZoom(kDefaultZoom);
+ });
+ reset->show();
+ const auto plus = Ui::CreateChild(
+ this,
+ '+',
+ 0,
+ _st.itemFg);
+ plus->setClickedCallback([this] {
+ _delegate->ivSetZoom(_delegate->ivZoom() + kZoomStep);
+ });
+ plus->show();
+ const auto minus = Ui::CreateChild(
+ this,
+ QChar(0x2013),
+ -1,
+ _st.itemFg);
+ minus->setClickedCallback([this] {
+ _delegate->ivSetZoom(_delegate->ivZoom() - kZoomStep);
+ });
+ minus->show();
+
+ _delegate->ivZoomValue(
+ ) | rpl::start_with_next([this](int value) {
+ _text.setText(_st.itemStyle, QString::number(value) + '%');
+ update();
+ }, lifetime());
+
+ rpl::combine(
+ sizeValue(),
+ reset->sizeValue()
+ ) | rpl::start_with_next([=, this](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 paintEvent(QPaintEvent *event) override {
+ auto p = QPainter(this);
+ p.setPen(_st.itemFg);
+ _text.draw(p, {
+ .position = QPoint(
+ _st.itemIconPosition.x(),
+ (height() - _text.minHeight()) / 2),
+ .outerWidth = width(),
+ .availableWidth = width(),
+ });
+ }
+
+private:
+ const not_null _delegate;
+ const style::Menu &_st;
+ Ui::Text::String _text;
+
+};
+
+[[nodiscard]] QByteArray ComputeStyles(int zoom) {
static const auto map = base::flat_map{
{ "shadow-fg", &st::shadowFg },
{ "scroll-bg", &st::scrollBg },
@@ -85,7 +224,7 @@ namespace {
static const auto phrases = base::flat_map>{
{ "iv-join-channel", tr::lng_iv_join_channel },
};
- return Ui::ComputeStyles(map, phrases)
+ return Ui::ComputeStyles(map, phrases, zoom)
+ ';'
+ Ui::ComputeSemiTransparentOverStyle(
"light-button-bg-over",
@@ -93,7 +232,7 @@ namespace {
st::windowBg);
}
-[[nodiscard]] QByteArray WrapPage(const Prepared &page) {
+[[nodiscard]] QByteArray WrapPage(const Prepared &page, int zoom) {
#ifdef Q_OS_MAC
const auto classAttribute = ""_q;
#else // Q_OS_MAC
@@ -110,7 +249,7 @@ namespace {
@@ -206,7 +345,8 @@ Controller::Controller(
Fn showShareBox)
: _delegate(delegate)
, _updateStyles([=] {
- const auto str = Ui::EscapeForScriptString(ComputeStyles());
+ const auto zoom = _delegate->ivZoom();
+ const auto str = Ui::EscapeForScriptString(ComputeStyles(zoom));
if (_webview) {
_webview->eval("IV.updateStyles('" + str + "');");
}
@@ -484,6 +624,16 @@ void Controller::createWebview(const Webview::StorageId &storageId) {
if (event->key() == Qt::Key_Escape) {
escape();
}
+ if (event->modifiers() & Qt::ControlModifier) {
+ if (event->key() == Qt::Key_Plus
+ || event->key() == Qt::Key_Equal) {
+ _delegate->ivSetZoom(_delegate->ivZoom() + kZoomStep);
+ } else if (event->key() == Qt::Key_Minus) {
+ _delegate->ivSetZoom(_delegate->ivZoom() - kZoomStep);
+ } else if (event->key() == Qt::Key_0) {
+ _delegate->ivSetZoom(kDefaultZoom);
+ }
+ }
}
}, window->lifetime());
@@ -595,7 +745,8 @@ void Controller::createWebview(const Webview::StorageId &storageId) {
rpl::merge(
Lang::Updated(),
- style::PaletteChanged()
+ style::PaletteChanged(),
+ _delegate->ivZoomValue() | rpl::to_empty
) | rpl::start_with_next([=] {
_updateStyles.call();
}, _webview->lifetime());
@@ -611,7 +762,8 @@ void Controller::createWebview(const Webview::StorageId &storageId) {
return Webview::DataResult::Failed;
}
return finishWith(
- WrapPage(_pages[index]), "text/html; charset=utf-8");
+ WrapPage(_pages[index], _delegate->ivZoom()),
+ "text/html; charset=utf-8");
} else if (id.starts_with("page") && id.ends_with(".json")) {
auto index = 0;
const auto result = std::from_chars(
@@ -897,6 +1049,10 @@ void Controller::showMenu() {
showShareMenu();
}, &st::menuIconShare);
+ _menu->addSeparator();
+ _menu->addAction(
+ base::make_unique_q(_menu, _delegate, _menu->menu()->st()));
+
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
_menu->popup(_window->body()->mapToGlobal(
QPoint(_window->body()->width(), 0) + st::ivMenuPosition));
diff --git a/Telegram/SourceFiles/iv/iv_delegate.h b/Telegram/SourceFiles/iv/iv_delegate.h
index 09374fa174..d722d19f07 100644
--- a/Telegram/SourceFiles/iv/iv_delegate.h
+++ b/Telegram/SourceFiles/iv/iv_delegate.h
@@ -18,6 +18,10 @@ public:
virtual void ivSetLastSourceWindow(not_null window) = 0;
[[nodiscard]] virtual QRect ivGeometry() const = 0;
virtual void ivSaveGeometry(not_null window) = 0;
+
+ [[nodiscard]] virtual int ivZoom() const = 0;
+ [[nodiscard]] virtual rpl::producer ivZoomValue() const = 0;
+ virtual void ivSetZoom(int value) = 0;
};
} // namespace Iv
diff --git a/Telegram/SourceFiles/iv/iv_delegate_impl.cpp b/Telegram/SourceFiles/iv/iv_delegate_impl.cpp
index 4de0fa03c8..e97de87bf8 100644
--- a/Telegram/SourceFiles/iv/iv_delegate_impl.cpp
+++ b/Telegram/SourceFiles/iv/iv_delegate_impl.cpp
@@ -117,4 +117,15 @@ void DelegateImpl::ivSaveGeometry(not_null window) {
}
}
+int DelegateImpl::ivZoom() const {
+ return Core::App().settings().ivZoom();
+}
+rpl::producer DelegateImpl::ivZoomValue() const {
+ return Core::App().settings().ivZoomValue();
+}
+void DelegateImpl::ivSetZoom(int value) {
+ Core::App().settings().setIvZoom(value);
+ Core::App().saveSettingsDelayed();
+}
+
} // namespace Iv
diff --git a/Telegram/SourceFiles/iv/iv_delegate_impl.h b/Telegram/SourceFiles/iv/iv_delegate_impl.h
index 9c7c0fb9d2..f564f237a3 100644
--- a/Telegram/SourceFiles/iv/iv_delegate_impl.h
+++ b/Telegram/SourceFiles/iv/iv_delegate_impl.h
@@ -19,6 +19,10 @@ public:
[[nodiscard]] QRect ivGeometry() const override;
void ivSaveGeometry(not_null window) override;
+ [[nodiscard]] int ivZoom() const;
+ [[nodiscard]] rpl::producer ivZoomValue() const;
+ void ivSetZoom(int value);
+
private:
QPointer _lastSourceWindow;
diff --git a/Telegram/SourceFiles/ui/controls/location_picker.cpp b/Telegram/SourceFiles/ui/controls/location_picker.cpp
index 945e5cf8fe..71b3c6f42a 100644
--- a/Telegram/SourceFiles/ui/controls/location_picker.cpp
+++ b/Telegram/SourceFiles/ui/controls/location_picker.cpp
@@ -379,7 +379,7 @@ void VenuesController::rowPaintIcon(
static const auto phrases = base::flat_map>{
{ "maps-places-in-area", tr::lng_maps_places_in_area },
};
- return Ui::ComputeStyles(map, phrases, Window::Theme::IsNightMode());
+ return Ui::ComputeStyles(map, phrases, 100, Window::Theme::IsNightMode());
}
[[nodiscard]] QByteArray ReadResource(const QString &name) {
diff --git a/Telegram/SourceFiles/ui/webview_helpers.cpp b/Telegram/SourceFiles/ui/webview_helpers.cpp
index 3be7a6359b..a0369421b0 100644
--- a/Telegram/SourceFiles/ui/webview_helpers.cpp
+++ b/Telegram/SourceFiles/ui/webview_helpers.cpp
@@ -31,6 +31,7 @@ namespace {
QByteArray ComputeStyles(
const base::flat_map &colors,
const base::flat_map> &phrases,
+ int zoom,
bool nightTheme) {
static const auto serialize = [](const style::color *color) {
return Serialize((*color)->c);
@@ -66,6 +67,9 @@ QByteArray ComputeStyles(
result += "--td-"_q + name + ':' + serialize(color) + ';';
}
result += "--td-night:"_q + (nightTheme ? "1" : "0") + ';';
+ result += "--td-zoom-percentage:"_q
+ + (QString::number(zoom).toUtf8() + '%')
+ + ';';
return result;
}
diff --git a/Telegram/SourceFiles/ui/webview_helpers.h b/Telegram/SourceFiles/ui/webview_helpers.h
index 5180964795..c75f69e891 100644
--- a/Telegram/SourceFiles/ui/webview_helpers.h
+++ b/Telegram/SourceFiles/ui/webview_helpers.h
@@ -19,6 +19,7 @@ namespace Ui {
[[nodiscard]] QByteArray ComputeStyles(
const base::flat_map &colors,
const base::flat_map> &phrases,
+ int zoom,
bool nightTheme = false);
[[nodiscard]] QByteArray ComputeSemiTransparentOverStyle(
const QByteArray &name,