diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index cf00f36349..01da0281d0 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -7888,6 +7888,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "ayu_MessageFieldElementVoice" = "Voice"; "ayu_DrawerElementsHeader" = "Drawer Elements"; "ayu_TrayElementsHeader" = "Tray Elements"; +"ayu_MessageBubbleRadius" = "Message Bubble Radius"; "ayu_SettingsWideMultiplier" = "Wide Messages Multiplier"; "ayu_SettingsWideMultiplierDescription" = "You can change message width for better display on wide monitors."; "ayu_SettingsSpoofWebviewAsAndroid" = "Spoof Client as Android"; diff --git a/Telegram/SourceFiles/ayu/ayu_infra.cpp b/Telegram/SourceFiles/ayu/ayu_infra.cpp index 2321b19761..2fa8a2a3f2 100644 --- a/Telegram/SourceFiles/ayu/ayu_infra.cpp +++ b/Telegram/SourceFiles/ayu/ayu_infra.cpp @@ -14,6 +14,7 @@ #include "ayu/ui/ayu_logo.h" #include "features/translator/ayu_translator.h" #include "lang/lang_instance.h" +#include "ui/chat/chat_style_radius.h" #include "utils/rc_manager.h" #ifdef Q_OS_WIN @@ -40,6 +41,7 @@ void initUiSettings() { AyuUiSettings::setWideMultiplier(settings.wideMultiplier()); AyuUiSettings::setMaterialSwitches(settings.materialSwitches()); AyuUiSettings::setAvatarCorners(settings.avatarCorners()); + Ui::SetAppliedBubbleRadius(settings.messageBubbleRadius()); } void initDatabase() { diff --git a/Telegram/SourceFiles/ayu/ayu_settings.cpp b/Telegram/SourceFiles/ayu/ayu_settings.cpp index 8dbc9e77e4..c8fbf988d5 100644 --- a/Telegram/SourceFiles/ayu/ayu_settings.cpp +++ b/Telegram/SourceFiles/ayu/ayu_settings.cpp @@ -473,6 +473,7 @@ void AyuSettings::validate() { validateEnum(_translationProvider, defaults._translationProvider); + validateRange(_messageBubbleRadius, 0, 16, defaults._messageBubbleRadius); validateRange(_wideMultiplier, 0.5, 4.0, defaults._wideMultiplier); validateRange(_recentStickersCount, 1, 200, defaults._recentStickersCount); validateRange(_avatarCorners, 0, AyuUiSettings::kMaxAvatarCorners, defaults._avatarCorners); @@ -574,6 +575,12 @@ void AyuSettings::setHideSimilarChannels(bool val) { save(); } +void AyuSettings::setMessageBubbleRadius(int val) { + if (_messageBubbleRadius.current() == val) return; + _messageBubbleRadius = val; + save(); +} + void AyuSettings::setWideMultiplier(double val) { if (_wideMultiplier.current() == val) return; _wideMultiplier = val; @@ -1007,6 +1014,7 @@ void to_json(nlohmann::json &j, const AyuSettings &s) { {"showOnlyAddedEmojisAndStickers", s._showOnlyAddedEmojisAndStickers.current()}, {"collapseSimilarChannels", s._collapseSimilarChannels.current()}, {"hideSimilarChannels", s._hideSimilarChannels.current()}, + {"messageBubbleRadius", s._messageBubbleRadius.current()}, {"disableOpenLinkWarning", s._disableOpenLinkWarning.current()}, {"wideMultiplier", s._wideMultiplier.current()}, {"spoofWebviewAsAndroid", s._spoofWebviewAsAndroid.current()}, @@ -1104,6 +1112,7 @@ void from_json(const nlohmann::json &j, AyuSettings &s) { s._showOnlyAddedEmojisAndStickers = j.value("showOnlyAddedEmojisAndStickers", defaults._showOnlyAddedEmojisAndStickers.current()); s._collapseSimilarChannels = j.value("collapseSimilarChannels", defaults._collapseSimilarChannels.current()); s._hideSimilarChannels = j.value("hideSimilarChannels", defaults._hideSimilarChannels.current()); + s._messageBubbleRadius = j.value("messageBubbleRadius", defaults._messageBubbleRadius.current()); s._disableOpenLinkWarning = j.value("disableOpenLinkWarning", defaults._disableOpenLinkWarning.current()); s._wideMultiplier = j.value("wideMultiplier", defaults._wideMultiplier.current()); s._spoofWebviewAsAndroid = j.value("spoofWebviewAsAndroid", defaults._spoofWebviewAsAndroid.current()); diff --git a/Telegram/SourceFiles/ayu/ayu_settings.h b/Telegram/SourceFiles/ayu/ayu_settings.h index 711e8eea94..50687018f7 100644 --- a/Telegram/SourceFiles/ayu/ayu_settings.h +++ b/Telegram/SourceFiles/ayu/ayu_settings.h @@ -258,6 +258,7 @@ public: [[nodiscard]] bool showOnlyAddedEmojisAndStickers() const { return _showOnlyAddedEmojisAndStickers.current(); } [[nodiscard]] bool collapseSimilarChannels() const { return _collapseSimilarChannels.current(); } [[nodiscard]] bool hideSimilarChannels() const { return _hideSimilarChannels.current(); } + [[nodiscard]] int messageBubbleRadius() const { return _messageBubbleRadius.current(); } [[nodiscard]] bool disableOpenLinkWarning() const { return _disableOpenLinkWarning.current(); } [[nodiscard]] double wideMultiplier() const { return _wideMultiplier.current(); } [[nodiscard]] bool spoofWebviewAsAndroid() const { return _spoofWebviewAsAndroid.current(); } @@ -339,6 +340,7 @@ public: void setShowOnlyAddedEmojisAndStickers(bool val); void setCollapseSimilarChannels(bool val); void setHideSimilarChannels(bool val); + void setMessageBubbleRadius(int val); void setDisableOpenLinkWarning(bool val); void setWideMultiplier(double val); void setSpoofWebviewAsAndroid(bool val); @@ -436,6 +438,8 @@ public: [[nodiscard]] rpl::producer collapseSimilarChannelsChanges() const { return _collapseSimilarChannels.changes(); } [[nodiscard]] rpl::producer hideSimilarChannelsValue() const { return _hideSimilarChannels.value(); } [[nodiscard]] rpl::producer hideSimilarChannelsChanges() const { return _hideSimilarChannels.changes(); } + [[nodiscard]] rpl::producer messageBubbleRadiusValue() const { return _messageBubbleRadius.value(); } + [[nodiscard]] rpl::producer messageBubbleRadiusChanges() const { return _messageBubbleRadius.changes(); } [[nodiscard]] rpl::producer disableOpenLinkWarningValue() const { return _disableOpenLinkWarning.value(); } [[nodiscard]] rpl::producer disableOpenLinkWarningChanges() const { return _disableOpenLinkWarning.changes(); } [[nodiscard]] rpl::producer wideMultiplierValue() const { return _wideMultiplier.value(); } @@ -591,6 +595,7 @@ private: rpl::variable _showOnlyAddedEmojisAndStickers = false; rpl::variable _collapseSimilarChannels = true; rpl::variable _hideSimilarChannels = false; + rpl::variable _messageBubbleRadius = 16; rpl::variable _disableOpenLinkWarning = false; rpl::variable _wideMultiplier = 1.0; rpl::variable _spoofWebviewAsAndroid = false; diff --git a/Telegram/SourceFiles/ayu/ui/components/message_preview.cpp b/Telegram/SourceFiles/ayu/ui/components/message_preview.cpp index 771817584b..0a24658363 100644 --- a/Telegram/SourceFiles/ayu/ui/components/message_preview.cpp +++ b/Telegram/SourceFiles/ayu/ui/components/message_preview.cpp @@ -21,6 +21,7 @@ #include "styles/style_settings.h" #include "ui/painter.h" #include "ui/chat/chat_style.h" +#include "ui/chat/chat_style_radius.h" #include "ui/chat/chat_theme.h" #include "ui/effects/animations.h" #include "window/section_widget.h" @@ -45,6 +46,7 @@ struct MessagePreview::State { Ui::Animations::Simple heightAnimation; std::unique_ptr theme; int currentHeight = 0; + int bubbleRadius = 16; }; MessagePreview::MessagePreview( @@ -53,6 +55,7 @@ MessagePreview::MessagePreview( : RpWidget(parent) , _controller(controller) , _state(lifetime().make_state()) { + _state->bubbleRadius = AyuSettings::getInstance().messageBubbleRadius(); _state->delegate = std::make_unique( controller, crl::guard(this, [=] { update(); })); @@ -162,7 +165,9 @@ void MessagePreview::paintEvent(QPaintEvent *e) { const auto padding = st::settingsForwardPrivacyPadding; p.translate(padding / 2, padding + view->marginBottom()); + Ui::SetBubbleRadiusOverride(_state->bubbleRadius); view->draw(p, context); + Ui::ClearBubbleRadiusOverride(); if (!AyuSettings::getInstance().hideFastShare()) { const auto size = st::historyFastShareSize; @@ -191,6 +196,14 @@ void MessagePreview::paintEvent(QPaintEvent *e) { } } +void MessagePreview::setBubbleRadius(int radius) { + if (_state->bubbleRadius == radius) { + return; + } + _state->bubbleRadius = radius; + refresh(); +} + void MessagePreview::updateWidgetSize(int width, bool animate) { const auto view = _state->item.get(); if (!view) { diff --git a/Telegram/SourceFiles/ayu/ui/components/message_preview.h b/Telegram/SourceFiles/ayu/ui/components/message_preview.h index fb89b6c9cd..6172977df6 100644 --- a/Telegram/SourceFiles/ayu/ui/components/message_preview.h +++ b/Telegram/SourceFiles/ayu/ui/components/message_preview.h @@ -18,6 +18,8 @@ public: QWidget *parent, not_null controller); + void setBubbleRadius(int radius); + protected: void paintEvent(QPaintEvent *e) override; @@ -30,4 +32,5 @@ private: class PreviewDelegate; struct State; const not_null _state; + }; diff --git a/Telegram/SourceFiles/ayu/ui/settings/settings_chats.cpp b/Telegram/SourceFiles/ayu/ui/settings/settings_chats.cpp index d924bab849..ee39b992bd 100644 --- a/Telegram/SourceFiles/ayu/ui/settings/settings_chats.cpp +++ b/Telegram/SourceFiles/ayu/ui/settings/settings_chats.cpp @@ -23,6 +23,8 @@ #include "ui/wrap/vertical_layout.h" #include "window/window_session_controller.h" +#include + namespace Settings { using namespace Builder; @@ -30,6 +32,21 @@ using namespace AyBuilder; namespace { +struct PreviewState { + MessagePreview *widget = nullptr; +}; + +void ShowRestartPrompt(not_null controller) { + crl::on_main([=] { + controller->show(Ui::MakeConfirmBox({ + .text = tr::lng_settings_need_restart(), + .confirmed = [] { Core::Restart(); }, + .confirmText = tr::lng_settings_restart_now(), + .cancelText = tr::lng_settings_restart_later(), + })); + }); +} + void BuildStickersAndEmoji(SectionBuilder &builder, AyuSectionBuilder &ayu) { builder.addSubsectionTitle(tr::lng_settings_stickers_emoji()); @@ -125,15 +142,20 @@ void BuildGroupsAndChannels(SectionBuilder &builder, AyuSectionBuilder &ayu) { builder.addSkip(); } -void BuildMarks(SectionBuilder &builder, AyuSectionBuilder &ayu) { +void BuildMarks( + SectionBuilder &builder, + AyuSectionBuilder &ayu, + std::shared_ptr previewState) { auto *settings = &AyuSettings::getInstance(); const auto controller = builder.controller(); builder.addSubsectionTitle(tr::lng_settings_messages()); builder.add([=](const WidgetContext &ctx) -> SectionBuilder::WidgetToAdd { + auto preview = object_ptr(ctx.container, controller); + previewState->widget = preview.data(); return { - .widget = object_ptr(ctx.container, controller), + .widget = std::move(preview), .margin = style::margins( 0, st::defaultVerticalListSkip, @@ -223,10 +245,12 @@ void BuildMarks(SectionBuilder &builder, AyuSectionBuilder &ayu) { ayu.addSectionDivider(); } -void BuildWideMessagesMultiplier(SectionBuilder &builder, AyuSectionBuilder &ayu) { +void BuildWideMessagesMultiplier( + SectionBuilder &builder, + AyuSectionBuilder &ayu, + std::shared_ptr previewState) { auto *settings = &AyuSettings::getInstance(); - constexpr auto kSizeAmount = 61; // (4.00 - 1.00) / 0.05 + 1 constexpr auto kMinSize = 1.00; constexpr auto kStep = 0.05; @@ -235,24 +259,42 @@ void BuildWideMessagesMultiplier(SectionBuilder &builder, AyuSectionBuilder &ayu }; const auto controller = builder.controller(); + ayu.addSlider({ + .id = u"ayu/messageBubbleRadius"_q, + .title = tr::ayu_MessageBubbleRadius(), + .steps = 17, + .current = settings->messageBubbleRadius(), + .indexToValue = [](int index) { return index; }, + .onChanged = [=](int index) { + if (previewState->widget) { + previewState->widget->setBubbleRadius(index); + } + }, + .onFinalChanged = [=](int index) { + if (previewState->widget) { + previewState->widget->setBubbleRadius(index); + } + AyuSettings::getInstance().setMessageBubbleRadius(index); + ShowRestartPrompt(controller); + }, + .formatLabel = [](int index) { + return QString::number(index); + }, + }); + + ayu.addSectionDivider(); + ayu.addSlider({ .id = u"ayu/wideMultiplier"_q, .title = tr::ayu_SettingsWideMultiplier(), - .steps = kSizeAmount, + .steps = 61, // (4.00 - 1.00) / 0.05 + 1 .current = valueToIndex(settings->wideMultiplier()), .indexToValue = [](int index) { return index; }, .onChanged = nullptr, .onFinalChanged = [=](int index) { AyuSettings::getInstance().setWideMultiplier( kMinSize + index * kStep); - crl::on_main([=] { - controller->show(Ui::MakeConfirmBox({ - .text = tr::lng_settings_need_restart(), - .confirmed = [] { Core::Restart(); }, - .confirmText = tr::lng_settings_restart_now(), - .cancelText = tr::lng_settings_restart_later(), - })); - }); + ShowRestartPrompt(controller); }, .formatLabel = [=](int index) { return QString::number(kMinSize + index * kStep, 'f', 2); @@ -414,13 +456,14 @@ const auto kMeta = BuildHelper({ .icon = &st::menuIconChatBubble, }, [](SectionBuilder &builder) { auto ayu = AyuSectionBuilder(builder); + const auto previewState = std::make_shared(); builder.addSkip(); BuildStickersAndEmoji(builder, ayu); BuildRecentStickersLimit(builder, ayu); BuildGroupsAndChannels(builder, ayu); - BuildMarks(builder, ayu); - BuildWideMessagesMultiplier(builder, ayu); + BuildMarks(builder, ayu, previewState); + BuildWideMessagesMultiplier(builder, ayu, previewState); BuildContextMenuElements(builder, ayu); BuildMessageFieldElements(builder, ayu); BuildMessageFieldPopups(builder, ayu); diff --git a/Telegram/SourceFiles/ui/cached_round_corners.cpp b/Telegram/SourceFiles/ui/cached_round_corners.cpp index 7da69a2299..595aa47563 100644 --- a/Telegram/SourceFiles/ui/cached_round_corners.cpp +++ b/Telegram/SourceFiles/ui/cached_round_corners.cpp @@ -28,6 +28,9 @@ rpl::lifetime PaletteChangedLifetime; std::array, kCachedCornerRadiusCount> CachedMasks; [[nodiscard]] std::array PrepareCorners(int32 radius, const QBrush &brush, const style::color *shadow = nullptr) { + if (radius <= 0) { + return {}; + } int32 r = radius * style::DevicePixelRatio(), s = st::msgShadow * style::DevicePixelRatio(); QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied); rect.fill(Qt::transparent); diff --git a/Telegram/SourceFiles/ui/chat/chat_style_radius.cpp b/Telegram/SourceFiles/ui/chat/chat_style_radius.cpp index 0d54394fcc..d75579bd73 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style_radius.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style_radius.cpp @@ -17,10 +17,41 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { namespace { +constexpr auto kBubbleRadiusSliderMax = 16; + +int AppliedBubbleRadius = 16; +int BubbleRadiusOverride = -1; + +[[nodiscard]] int ClampBubbleRadiusValue(int value) { + return (value < 0) + ? 0 + : (value > kBubbleRadiusSliderMax) + ? kBubbleRadiusSliderMax + : value; +} + +[[nodiscard]] int EffectiveBubbleRadiusValue() { + return (BubbleRadiusOverride >= 0) + ? BubbleRadiusOverride + : AppliedBubbleRadius; +} + +[[nodiscard]] int MapBubbleRadius(int sliderValue, int maximum) { + if (sliderValue <= 0 || maximum <= 0) { + return 0; + } else if (sliderValue >= kBubbleRadiusSliderMax) { + return maximum; + } + const auto result = (sliderValue * maximum + (kBubbleRadiusSliderMax / 2)) + / kBubbleRadiusSliderMax; + return (result < 0) ? 0 : (result > maximum) ? maximum : result; +} + base::options::toggle UseSmallMsgBubbleRadius({ .id = kOptionUseSmallMsgBubbleRadius, .name = "Use small message bubble radius", .description = "Makes most message bubbles square-ish.", + .scope = static_cast(0), .restartRequired = true, }); @@ -28,34 +59,60 @@ base::options::toggle UseSmallMsgBubbleRadius({ const char kOptionUseSmallMsgBubbleRadius[] = "use-small-msg-bubble-radius"; +void SetAppliedBubbleRadius(int value) { + AppliedBubbleRadius = ClampBubbleRadiusValue(value); +} + +void SetBubbleRadiusOverride(int value) { + BubbleRadiusOverride = ClampBubbleRadiusValue(value); +} + +void ClearBubbleRadiusOverride() { + BubbleRadiusOverride = -1; +} + int BubbleRadiusSmall() { - return st::bubbleRadiusSmall; + static auto cachedValue = -1; + static auto cachedRadius = st::bubbleRadiusSmall; + const auto value = EffectiveBubbleRadiusValue(); + if (cachedValue != value) { + cachedValue = value; + cachedRadius = MapBubbleRadius(value, st::bubbleRadiusSmall); + } + return cachedRadius; } int BubbleRadiusLarge() { - static const auto result = [] { - if (UseSmallMsgBubbleRadius.value()) { - return st::bubbleRadiusSmall; - } else { - return st::bubbleRadiusLarge; - } - }(); - return result; + static auto cachedValue = -1; + static auto cachedRadius = st::bubbleRadiusLarge; + const auto value = EffectiveBubbleRadiusValue(); + if (cachedValue != value) { + cachedValue = value; + cachedRadius = MapBubbleRadius(value, st::bubbleRadiusLarge); + } + return cachedRadius; } int MsgFileThumbRadiusSmall() { - return st::msgFileThumbRadiusSmall; + static auto cachedValue = -1; + static auto cachedRadius = st::msgFileThumbRadiusSmall; + const auto value = EffectiveBubbleRadiusValue(); + if (cachedValue != value) { + cachedValue = value; + cachedRadius = MapBubbleRadius(value, st::msgFileThumbRadiusSmall); + } + return cachedRadius; } int MsgFileThumbRadiusLarge() { - static const auto result = [] { - if (UseSmallMsgBubbleRadius.value()) { - return st::msgFileThumbRadiusSmall; - } else { - return st::msgFileThumbRadiusLarge; - } - }(); - return result; + static auto cachedValue = -1; + static auto cachedRadius = st::msgFileThumbRadiusLarge; + const auto value = EffectiveBubbleRadiusValue(); + if (cachedValue != value) { + cachedValue = value; + cachedRadius = MapBubbleRadius(value, st::msgFileThumbRadiusLarge); + } + return cachedRadius; } } diff --git a/Telegram/SourceFiles/ui/chat/chat_style_radius.h b/Telegram/SourceFiles/ui/chat/chat_style_radius.h index 99c1127aa0..0ef42dde1e 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style_radius.h +++ b/Telegram/SourceFiles/ui/chat/chat_style_radius.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { +void SetAppliedBubbleRadius(int value); +void SetBubbleRadiusOverride(int value); +void ClearBubbleRadiusOverride(); + [[nodiscard]] int BubbleRadiusSmall(); [[nodiscard]] int BubbleRadiusLarge();