mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
feat: add message bubble radius slider
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user