feat: add message bubble radius slider

This commit is contained in:
Ireina
2026-04-04 03:14:17 +03:00
committed by AlexeyZavar
parent b83533637d
commit 969cc0778b
10 changed files with 173 additions and 33 deletions
+1
View File
@@ -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";
+2
View File
@@ -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() {
@@ -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());
+5
View File
@@ -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<bool> collapseSimilarChannelsChanges() const { return _collapseSimilarChannels.changes(); }
[[nodiscard]] rpl::producer<bool> hideSimilarChannelsValue() const { return _hideSimilarChannels.value(); }
[[nodiscard]] rpl::producer<bool> hideSimilarChannelsChanges() const { return _hideSimilarChannels.changes(); }
[[nodiscard]] rpl::producer<int> messageBubbleRadiusValue() const { return _messageBubbleRadius.value(); }
[[nodiscard]] rpl::producer<int> messageBubbleRadiusChanges() const { return _messageBubbleRadius.changes(); }
[[nodiscard]] rpl::producer<bool> disableOpenLinkWarningValue() const { return _disableOpenLinkWarning.value(); }
[[nodiscard]] rpl::producer<bool> disableOpenLinkWarningChanges() const { return _disableOpenLinkWarning.changes(); }
[[nodiscard]] rpl::producer<double> wideMultiplierValue() const { return _wideMultiplier.value(); }
@@ -591,6 +595,7 @@ private:
rpl::variable<bool> _showOnlyAddedEmojisAndStickers = false;
rpl::variable<bool> _collapseSimilarChannels = true;
rpl::variable<bool> _hideSimilarChannels = false;
rpl::variable<int> _messageBubbleRadius = 16;
rpl::variable<bool> _disableOpenLinkWarning = false;
rpl::variable<double> _wideMultiplier = 1.0;
rpl::variable<bool> _spoofWebviewAsAndroid = false;
@@ -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<Ui::ChatTheme> theme;
int currentHeight = 0;
int bubbleRadius = 16;
};
MessagePreview::MessagePreview(
@@ -53,6 +55,7 @@ MessagePreview::MessagePreview(
: RpWidget(parent)
, _controller(controller)
, _state(lifetime().make_state<State>()) {
_state->bubbleRadius = AyuSettings::getInstance().messageBubbleRadius();
_state->delegate = std::make_unique<PreviewDelegate>(
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) {
@@ -18,6 +18,8 @@ public:
QWidget *parent,
not_null<Window::SessionController*> controller);
void setBubbleRadius(int radius);
protected:
void paintEvent(QPaintEvent *e) override;
@@ -30,4 +32,5 @@ private:
class PreviewDelegate;
struct State;
const not_null<State*> _state;
};
@@ -23,6 +23,8 @@
#include "ui/wrap/vertical_layout.h"
#include "window/window_session_controller.h"
#include <memory>
namespace Settings {
using namespace Builder;
@@ -30,6 +32,21 @@ using namespace AyBuilder;
namespace {
struct PreviewState {
MessagePreview *widget = nullptr;
};
void ShowRestartPrompt(not_null<Window::SessionController*> 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> 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<MessagePreview>(ctx.container, controller);
previewState->widget = preview.data();
return {
.widget = object_ptr<MessagePreview>(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> 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<PreviewState>();
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);
@@ -28,6 +28,9 @@ rpl::lifetime PaletteChangedLifetime;
std::array<std::array<QImage, 4>, kCachedCornerRadiusCount> CachedMasks;
[[nodiscard]] std::array<QImage, 4> 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);
@@ -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<base::options::details::ScopeFlag>(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;
}
}
@@ -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();