diff --git a/Telegram/Resources/icons/settings/dino.svg b/Telegram/Resources/icons/settings/dino.svg
new file mode 100644
index 000000000..24ebe83cf
--- /dev/null
+++ b/Telegram/Resources/icons/settings/dino.svg
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 0bbda64fe..177419c16 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -324,8 +324,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_notify_all_about" = "Turn this off if you want to receive notifications only from the account you are currently using.";
"lng_settings_notify_title" = "Notifications for chats";
"lng_settings_desktop_notify" = "Desktop notifications";
-"lng_settings_show_name" = "Show sender's name";
-"lng_settings_show_preview" = "Show message preview";
"lng_settings_native_title" = "Native notifications";
"lng_settings_use_windows" = "Use Windows notifications";
"lng_settings_use_native_notifications" = "Use native notifications";
@@ -343,6 +341,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_events_pinned" = "Pinned messages";
"lng_settings_notifications_calls_title" = "Calls";
+"lng_notification_preview_title" = "Dino Rex";
+"lng_notification_preview_text" = "It's morning in Tokyo 😎";
+"lng_notification_show_name" = "Name";
+"lng_notification_show_text" = "Text";
"lng_notification_preview" = "You have a new message";
"lng_notification_reply" = "Reply";
"lng_notification_hide_all" = "Hide all";
diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc
index d8d40fbd3..9826d3107 100644
--- a/Telegram/Resources/qrc/telegram/telegram.qrc
+++ b/Telegram/Resources/qrc/telegram/telegram.qrc
@@ -67,6 +67,7 @@
../../art/recording/recording_info_audio.svg
../../art/recording/recording_info_video_landscape.svg
../../art/recording/recording_info_video_portrait.svg
+ ../../icons/settings/dino.svg
../../icons/calls/hands.lottie
diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp
index 52bb145ac..1ca6faf39 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp
@@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "window/themes/window_theme.h"
+#include "ui/boxes/confirm_box.h"
+#include "ui/controls/chat_service_checkbox.h"
#include "ui/chat/chat_theme.h"
#include "ui/chat/chat_style.h"
#include "ui/toast/toast.h"
@@ -28,9 +30,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document_resolver.h"
#include "data/data_file_origin.h"
#include "base/unixtime.h"
-#include "ui/boxes/confirm_box.h"
#include "boxes/background_preview_box.h"
#include "window/window_session_controller.h"
+#include "settings/settings_common.h"
#include "styles/style_chat.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -42,231 +44,6 @@ namespace {
constexpr auto kMaxWallPaperSlugLength = 255;
-class ServiceCheck : public Ui::AbstractCheckView {
-public:
- ServiceCheck(const style::ServiceCheck &st, bool checked);
-
- QSize getSize() const override;
- void paint(
- Painter &p,
- int left,
- int top,
- int outerWidth) override;
- QImage prepareRippleMask() const override;
- bool checkRippleStartPosition(QPoint position) const override;
-
-private:
- class Generator {
- public:
- Generator();
-
- void paintFrame(
- Painter &p,
- int left,
- int top,
- not_null st,
- float64 toggled);
- void invalidate();
-
- private:
- struct Frames {
- QImage image;
- std::vector ready;
- };
-
- not_null framesForStyle(
- not_null st);
- static void FillFrame(
- QImage &image,
- not_null st,
- int index,
- int count);
- static void PaintFillingFrame(
- Painter &p,
- not_null st,
- float64 progress);
- static void PaintCheckingFrame(
- Painter &p,
- not_null st,
- float64 progress);
-
- base::flat_map, Frames> _data;
- rpl::lifetime _lifetime;
-
- };
- static Generator &Frames();
-
- const style::ServiceCheck &_st;
-
-};
-
-ServiceCheck::Generator::Generator() {
- style::PaletteChanged(
- ) | rpl::start_with_next([=] {
- invalidate();
- }, _lifetime);
-}
-
-auto ServiceCheck::Generator::framesForStyle(
- not_null st) -> not_null {
- if (const auto i = _data.find(st); i != _data.end()) {
- return &i->second;
- }
- const auto result = &_data.emplace(st, Frames()).first->second;
- const auto size = st->diameter;
- const auto count = (st->duration / AnimationTimerDelta) + 2;
- result->image = QImage(
- QSize(count * size, size) * cIntRetinaFactor(),
- QImage::Format_ARGB32_Premultiplied);
- result->image.fill(Qt::transparent);
- result->image.setDevicePixelRatio(cRetinaFactor());
- result->ready.resize(count);
- return result;
-}
-
-void ServiceCheck::Generator::FillFrame(
- QImage &image,
- not_null st,
- int index,
- int count) {
- Expects(count > 1);
- Expects(index >= 0 && index < count);
-
- Painter p(&image);
- PainterHighQualityEnabler hq(p);
-
- p.translate(index * st->diameter, 0);
- const auto progress = index / float64(count - 1);
- if (progress > 0.5) {
- PaintCheckingFrame(p, st, (progress - 0.5) * 2);
- } else {
- PaintFillingFrame(p, st, progress * 2);
- }
-}
-
-void ServiceCheck::Generator::PaintFillingFrame(
- Painter &p,
- not_null st,
- float64 progress) {
- const auto shift = progress * st->shift;
- p.setBrush(st->color);
- p.setPen(Qt::NoPen);
- p.drawEllipse(QRectF(
- shift,
- shift,
- st->diameter - 2 * shift,
- st->diameter - 2 * shift));
- if (progress < 1.) {
- const auto remove = progress * (st->diameter / 2. - st->thickness);
- p.setCompositionMode(QPainter::CompositionMode_Source);
- p.setPen(Qt::NoPen);
- p.setBrush(Qt::transparent);
- p.drawEllipse(QRectF(
- st->thickness + remove,
- st->thickness + remove,
- st->diameter - 2 * (st->thickness + remove),
- st->diameter - 2 * (st->thickness + remove)));
- }
-}
-
-void ServiceCheck::Generator::PaintCheckingFrame(
- Painter &p,
- not_null st,
- float64 progress) {
- const auto shift = (1. - progress) * st->shift;
- p.setBrush(st->color);
- p.setPen(Qt::NoPen);
- p.drawEllipse(QRectF(
- shift,
- shift,
- st->diameter - 2 * shift,
- st->diameter - 2 * shift));
- if (progress > 0.) {
- const auto tip = QPointF(st->tip.x(), st->tip.y());
- const auto left = tip - QPointF(st->small, st->small) * progress;
- const auto right = tip - QPointF(-st->large, st->large) * progress;
-
- p.setCompositionMode(QPainter::CompositionMode_Source);
- p.setBrush(Qt::NoBrush);
- auto pen = QPen(Qt::transparent);
- pen.setWidth(st->stroke);
- pen.setCapStyle(Qt::RoundCap);
- pen.setJoinStyle(Qt::RoundJoin);
- p.setPen(pen);
- auto path = QPainterPath();
- path.moveTo(left);
- path.lineTo(tip);
- path.lineTo(right);
- p.drawPath(path);
- }
-}
-
-void ServiceCheck::Generator::paintFrame(
- Painter &p,
- int left,
- int top,
- not_null st,
- float64 toggled) {
- const auto frames = framesForStyle(st);
- auto &image = frames->image;
- const auto count = int(frames->ready.size());
- const auto index = int(base::SafeRound(toggled * (count - 1)));
- Assert(index >= 0 && index < count);
- if (!frames->ready[index]) {
- frames->ready[index] = true;
- FillFrame(image, st, index, count);
- }
- const auto size = st->diameter;
- const auto part = size * cIntRetinaFactor();
- p.drawImage(
- QPoint(left, top),
- image,
- QRect(index * part, 0, part, part));
-}
-
-void ServiceCheck::Generator::invalidate() {
- _data.clear();
-}
-
-ServiceCheck::Generator &ServiceCheck::Frames() {
- static const auto Instance = Ui::CreateChild(
- QCoreApplication::instance());
- return *Instance;
-}
-
-ServiceCheck::ServiceCheck(
- const style::ServiceCheck &st,
- bool checked)
-: AbstractCheckView(st.duration, checked, nullptr)
-, _st(st) {
-}
-
-QSize ServiceCheck::getSize() const {
- const auto inner = QRect(0, 0, _st.diameter, _st.diameter);
- return inner.marginsAdded(_st.margin).size();
-}
-
-void ServiceCheck::paint(
- Painter &p,
- int left,
- int top,
- int outerWidth) {
- Frames().paintFrame(
- p,
- left + _st.margin.left(),
- top + _st.margin.top(),
- &_st,
- currentAnimationValue());
-}
-
-QImage ServiceCheck::prepareRippleMask() const {
- return QImage();
-}
-
-bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
- return false;
-}
-
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
return false;
@@ -439,13 +216,13 @@ void BackgroundPreviewBox::prepare() {
}
void BackgroundPreviewBox::createBlurCheckbox() {
- _blur.create(
+ _blur = Ui::MakeChatServiceCheckbox(
this,
tr::lng_background_blur(tr::now),
st::backgroundCheckbox,
- std::make_unique(
- st::backgroundCheck,
- _paper.isBlurred()));
+ st::backgroundCheck,
+ _paper.isBlurred(),
+ [=] { return _serviceBg.value_or(QColor(255, 255, 255, 0)); });
rpl::combine(
sizeValue(),
@@ -456,20 +233,6 @@ void BackgroundPreviewBox::createBlurCheckbox() {
outer.height() - st::historyPaddingBottom - inner.height());
}, _blur->lifetime());
- _blur->paintRequest(
- ) | rpl::filter([=] {
- return _serviceBg.has_value();
- }) | rpl::start_with_next([=] {
- Painter p(_blur.data());
- PainterHighQualityEnabler hq(p);
- p.setPen(Qt::NoPen);
- p.setBrush(*_serviceBg);
- p.drawRoundedRect(
- _blur->rect(),
- st::historyMessageRadius,
- st::historyMessageRadius);
- }, _blur->lifetime());
-
_blur->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
checkBlurAnimationStart();
diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style
index 43952c137..2525fc5be 100644
--- a/Telegram/SourceFiles/boxes/boxes.style
+++ b/Telegram/SourceFiles/boxes/boxes.style
@@ -855,14 +855,14 @@ backgroundCheckbox: Checkbox(defaultCheckbox) {
width: -50px;
margin: margins(0px, 0px, 0px, 0px);
- textPosition: point(0px, 8px);
+ textPosition: point(0px, 6px);
checkPosition: point(0px, 0px);
style: semiboldTextStyle;
}
backgroundCheck: ServiceCheck {
- margin: margins(12px, 8px, 8px, 8px);
+ margin: margins(10px, 6px, 8px, 6px);
diameter: 18px;
shift: 2px;
thickness: 2px;
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index 58558bf48..0738868a4 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -369,3 +369,11 @@ settingsUsernameTop: 58px;
settingsPeerToPeerSkip: 9px;
settingsIconRadius: 6px;
+
+notifyPreviewMargins: margins(40px, 20px, 40px, 58px);
+notifyPreviewUserpicSize: 36px;
+notifyPreviewUserpicPosition: point(14px, 11px);
+notifyPreviewTitlePosition: point(64px, 9px);
+notifyPreviewTextPosition: point(64px, 30px);
+notifyPreviewChecksSkip: 12px;
+notifyPreviewBottomSkip: 9px;
diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp
index e62f50008..70c46bf04 100644
--- a/Telegram/SourceFiles/settings/settings_notifications.cpp
+++ b/Telegram/SourceFiles/settings/settings_notifications.cpp
@@ -8,9 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_notifications.h"
#include "settings/settings_common.h"
+#include "ui/controls/chat_service_checkbox.h"
#include "ui/effects/animations.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
+#include "ui/widgets/box_content_divider.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/discrete_sliders.h"
@@ -18,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "window/notifications_manager.h"
#include "window/window_session_controller.h"
+#include "window/section_widget.h"
#include "platform/platform_specific.h"
#include "platform/platform_notifications_manager.h"
#include "base/platform/base_platform_info.h"
@@ -31,9 +34,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "facades.h"
#include "styles/style_settings.h"
#include "styles/style_boxes.h"
+#include "styles/style_layers.h"
+#include "styles/style_chat.h"
#include "styles/style_window.h"
#include "styles/style_dialogs.h"
+#include
#include
namespace Settings {
@@ -513,6 +519,199 @@ void NotificationsCount::SampleWidget::destroyDelayed() {
#endif // Q_OS_UNIX && !Q_OS_MAC
}
+class NotifyPreview final {
+public:
+ NotifyPreview(bool nameShown, bool previewShown);
+
+ void setNameShown(bool shown);
+ void setPreviewShown(bool shown);
+
+ int resizeGetHeight(int newWidth);
+ void paint(Painter &p, int x, int y);
+
+private:
+ int _width = 0;
+ int _height = 0;
+ bool _nameShown = false;
+ bool _previewShown = false;
+ Ui::RoundRect _roundRect;
+ Ui::Text::String _name, _title;
+ Ui::Text::String _text, _preview;
+ QSvgRenderer _userpic;
+ QImage _logo;
+
+};
+
+NotifyPreview::NotifyPreview(bool nameShown, bool previewShown)
+: _nameShown(nameShown)
+, _previewShown(previewShown)
+, _roundRect(st::boxRadius, st::msgInBg)
+, _userpic(u":/gui/icons/settings/dino.svg"_q)
+, _logo(Window::LogoNoMargin()) {
+ const auto ratio = style::DevicePixelRatio();
+ _logo = _logo.scaledToWidth(
+ st::notifyPreviewUserpicSize * ratio,
+ Qt::SmoothTransformation);
+ _logo.setDevicePixelRatio(ratio);
+
+ _name.setText(
+ st::settingsSubsectionTitle.style,
+ tr::lng_notification_preview_title(tr::now));
+ _title.setText(st::settingsSubsectionTitle.style, AppName.utf16());
+
+ _text.setText(
+ st::boxTextStyle,
+ tr::lng_notification_preview_text(tr::now));
+ _preview.setText(
+ st::boxTextStyle,
+ tr::lng_notification_preview(tr::now));
+}
+
+void NotifyPreview::setNameShown(bool shown) {
+ _nameShown = shown;
+}
+
+void NotifyPreview::setPreviewShown(bool shown) {
+ _previewShown = shown;
+}
+
+int NotifyPreview::resizeGetHeight(int newWidth) {
+ _width = newWidth;
+ _height = st::notifyPreviewUserpicPosition.y()
+ + st::notifyPreviewUserpicSize
+ + st::notifyPreviewUserpicPosition.y();
+ const auto available = _width
+ - st::notifyPreviewTextPosition.x()
+ - st::notifyPreviewUserpicPosition.x();
+ if (std::max(_text.maxWidth(), _preview.maxWidth()) >= available) {
+ _height += st::defaultTextStyle.font->height;
+ }
+ return _height;
+}
+
+void NotifyPreview::paint(Painter &p, int x, int y) {
+ if (!_width || !_height) {
+ return;
+ }
+ p.translate(x, y);
+ const auto guard = gsl::finally([&] { p.translate(-x, -y); });
+
+ _roundRect.paint(p, { 0, 0, _width, _height });
+ const auto userpic = QRect(
+ st::notifyPreviewUserpicPosition,
+ QSize{ st::notifyPreviewUserpicSize, st::notifyPreviewUserpicSize });
+
+ if (_nameShown) {
+ _userpic.render(&p, QRectF(userpic));
+ } else {
+ p.drawImage(userpic.topLeft(), _logo);
+ }
+
+ const auto &title = _nameShown ? _name : _title;
+ title.drawElided(
+ p,
+ st::notifyPreviewTitlePosition.x(),
+ st::notifyPreviewTitlePosition.y(),
+ _width - st::notifyPreviewTitlePosition.x() - userpic.x());
+
+ const auto &text = _previewShown ? _text : _preview;
+ text.drawElided(
+ p,
+ st::notifyPreviewTextPosition.x(),
+ st::notifyPreviewTextPosition.y(),
+ _width - st::notifyPreviewTextPosition.x() - userpic.x(),
+ 2);
+}
+
+struct NotifyViewCheckboxes {
+ not_null*> wrap;
+ not_null name;
+ not_null preview;
+};
+
+NotifyViewCheckboxes SetupNotifyViewOptions(
+ not_null controller,
+ not_null container,
+ bool nameShown,
+ bool previewShown) {
+ using namespace rpl::mappers;
+
+ auto wrap = container->add(object_ptr>(
+ container,
+ object_ptr(container)));
+ const auto widget = wrap->entity();
+
+ const auto makeCheckbox = [&](const QString &text, bool checked) {
+ return Ui::MakeChatServiceCheckbox(
+ widget,
+ text,
+ st::backgroundCheckbox,
+ st::backgroundCheck,
+ checked).release();
+ };
+ const auto name = makeCheckbox(
+ tr::lng_notification_show_name(tr::now),
+ nameShown);
+ const auto preview = makeCheckbox(
+ tr::lng_notification_show_text(tr::now),
+ previewShown);
+
+ const auto view = widget->lifetime().make_state(
+ nameShown,
+ previewShown);
+ widget->widthValue(
+ ) | rpl::filter(
+ _1 >= (st::historyMinimalWidth / 2)
+ ) | rpl::start_with_next([=](int width) {
+ const auto margins = st::notifyPreviewMargins;
+ const auto bubblew = width - margins.left() - margins.right();
+ const auto bubbleh = view->resizeGetHeight(bubblew);
+ const auto height = bubbleh + margins.top() + margins.bottom();
+ widget->resize(width, height);
+
+ const auto skip = st::notifyPreviewChecksSkip;
+ const auto checksWidth = name->width() + skip + preview->width();
+ const auto checksLeft = (width - checksWidth) / 2;
+ const auto checksTop = height
+ - (margins.bottom() + name->height()) / 2;
+ name->move(checksLeft, checksTop);
+ preview->move(checksLeft + name->width() + skip, checksTop);
+ }, widget->lifetime());
+
+ widget->paintRequest(
+ ) | rpl::start_with_next([=](QRect rect) {
+ Window::SectionWidget::PaintBackground(
+ controller,
+ controller->defaultChatTheme().get(), // #TODO themes
+ widget,
+ rect);
+
+ Painter p(widget);
+ view->paint(
+ p,
+ st::notifyPreviewMargins.left(),
+ st::notifyPreviewMargins.top());
+ }, widget->lifetime());
+
+ name->checkedChanges(
+ ) | rpl::start_with_next([=](bool checked) {
+ view->setNameShown(checked);
+ widget->update();
+ }, name->lifetime());
+
+ preview->checkedChanges(
+ ) | rpl::start_with_next([=](bool checked) {
+ view->setPreviewShown(checked);
+ widget->update();
+ }, preview->lifetime());
+
+ return {
+ .wrap = wrap,
+ .name = name,
+ .preview = preview,
+ };
+}
+
void SetupAdvancedNotifications(
not_null controller,
not_null container) {
@@ -615,18 +814,6 @@ void SetupNotificationsContent(
std::move(descriptor),
std::move(checked)));
};
- const auto addSlidingCheckbox = [&](
- rpl::producer label,
- IconDescriptor &&descriptor,
- rpl::producer checked) {
- return container->add(
- object_ptr>(
- container,
- checkbox(
- std::move(label),
- std::move(descriptor),
- std::move(checked))));
- };
const auto &settings = Core::App().settings();
const auto desktopToggles = container->lifetime(
).make_state>();
@@ -635,15 +822,6 @@ void SetupNotificationsContent(
{ &st::settingsIconNotifications, kIconRed },
desktopToggles->events_starting_with(settings.desktopNotify()));
- const auto name = addSlidingCheckbox(
- tr::lng_settings_show_name(),
- { &st::settingsIconUser, kIconLightOrange },
- rpl::single(settings.notifyView() <= NotifyView::ShowName));
- const auto preview = addSlidingCheckbox(
- tr::lng_settings_show_preview(),
- { &st::settingsIconAskQuestion, kIconGreen },
- rpl::single(settings.notifyView() <= NotifyView::ShowPreview));
-
const auto soundToggles = container->lifetime(
).make_state>();
const auto sound = addCheckbox(
@@ -663,8 +841,23 @@ void SetupNotificationsContent(
settings.flashBounceNotify()));
AddSkip(container);
- AddDivider(container);
- AddSkip(container);
+
+ const auto checkboxes = SetupNotifyViewOptions(
+ controller,
+ container,
+ (settings.notifyView() <= NotifyView::ShowName),
+ (settings.notifyView() <= NotifyView::ShowPreview));
+ const auto name = checkboxes.name;
+ const auto preview = checkboxes.preview;
+ const auto previewWrap = checkboxes.wrap;
+ const auto previewDivider = container->add(
+ object_ptr>(
+ container,
+ object_ptr(container)));
+ previewWrap->toggle(settings.desktopNotify(), anim::type::instant);
+ previewDivider->toggle(!settings.desktopNotify(), anim::type::instant);
+
+ AddSkip(container, st::notifyPreviewBottomSkip);
AddSubsectionTitle(container, tr::lng_settings_events_title());
auto joinSilent = rpl::single(
@@ -768,13 +961,6 @@ void SetupNotificationsContent(
SetupAdvancedNotifications(controller, advancedWrap);
}
- if (!name->entity()->toggled()) {
- preview->hide(anim::type::instant);
- }
- if (!desktop->toggled()) {
- name->hide(anim::type::instant);
- preview->hide(anim::type::instant);
- }
if (native && advancedSlide && settings.nativeNotifications()) {
advancedSlide->hide(anim::type::instant);
}
@@ -792,11 +978,12 @@ void SetupNotificationsContent(
changed(Change::DesktopEnabled);
}, desktop->lifetime());
- name->entity()->toggledChanges(
+ name->checkedChanges(
) | rpl::map([=](bool checked) {
if (!checked) {
+ preview->setChecked(false);
return NotifyView::ShowNothing;
- } else if (!preview->entity()->toggled()) {
+ } else if (!preview->checked()) {
return NotifyView::ShowName;
}
return NotifyView::ShowPreview;
@@ -807,11 +994,12 @@ void SetupNotificationsContent(
changed(Change::ViewParams);
}, name->lifetime());
- preview->entity()->toggledChanges(
+ preview->checkedChanges(
) | rpl::map([=](bool checked) {
if (checked) {
+ name->setChecked(true);
return NotifyView::ShowPreview;
- } else if (name->entity()->toggled()) {
+ } else if (name->checked()) {
return NotifyView::ShowName;
}
return NotifyView::ShowNothing;
@@ -858,15 +1046,14 @@ void SetupNotificationsContent(
) | rpl::start_with_next([=](Change change) {
if (change == Change::DesktopEnabled) {
desktopToggles->fire(Core::App().settings().desktopNotify());
- name->toggle(
+ previewWrap->toggle(
Core::App().settings().desktopNotify(),
anim::type::normal);
- preview->toggle(
- (Core::App().settings().desktopNotify()
- && name->entity()->toggled()),
+ previewDivider->toggle(
+ !Core::App().settings().desktopNotify(),
anim::type::normal);
} else if (change == Change::ViewParams) {
- preview->toggle(name->entity()->toggled(), anim::type::normal);
+ //
} else if (change == Change::SoundEnabled) {
soundToggles->fire(Core::App().settings().soundNotify());
} else if (change == Change::FlashBounceEnabled) {
diff --git a/Telegram/SourceFiles/ui/controls/chat_service_checkbox.cpp b/Telegram/SourceFiles/ui/controls/chat_service_checkbox.cpp
new file mode 100644
index 000000000..253b5177e
--- /dev/null
+++ b/Telegram/SourceFiles/ui/controls/chat_service_checkbox.cpp
@@ -0,0 +1,279 @@
+/*
+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 "ui/controls/chat_service_checkbox.h"
+
+#include "ui/widgets/checkbox.h"
+#include "styles/style_layers.h"
+
+#include
+
+namespace Ui {
+namespace {
+
+constexpr auto kAnimationTimerDelta = crl::time(7);
+
+class ServiceCheck final : public AbstractCheckView {
+public:
+ ServiceCheck(const style::ServiceCheck &st, bool checked);
+
+ QSize getSize() const override;
+ void paint(
+ Painter &p,
+ int left,
+ int top,
+ int outerWidth) override;
+ QImage prepareRippleMask() const override;
+ bool checkRippleStartPosition(QPoint position) const override;
+
+private:
+ class Generator {
+ public:
+ Generator();
+
+ void paintFrame(
+ Painter &p,
+ int left,
+ int top,
+ not_null st,
+ float64 toggled);
+ void invalidate();
+
+ private:
+ struct Frames {
+ QImage image;
+ std::vector ready;
+ };
+
+ not_null framesForStyle(
+ not_null st);
+ static void FillFrame(
+ QImage &image,
+ not_null st,
+ int index,
+ int count);
+ static void PaintFillingFrame(
+ Painter &p,
+ not_null st,
+ float64 progress);
+ static void PaintCheckingFrame(
+ Painter &p,
+ not_null st,
+ float64 progress);
+
+ base::flat_map, Frames> _data;
+ rpl::lifetime _lifetime;
+
+ };
+ static Generator &Frames();
+
+ const style::ServiceCheck &_st;
+
+};
+
+ServiceCheck::Generator::Generator() {
+ style::PaletteChanged(
+ ) | rpl::start_with_next([=] {
+ invalidate();
+ }, _lifetime);
+}
+
+auto ServiceCheck::Generator::framesForStyle(
+ not_null st) -> not_null {
+ if (const auto i = _data.find(st); i != _data.end()) {
+ return &i->second;
+ }
+ const auto result = &_data.emplace(st, Frames()).first->second;
+ const auto size = st->diameter;
+ const auto count = (st->duration / kAnimationTimerDelta) + 2;
+ result->image = QImage(
+ QSize(count * size, size) * style::DevicePixelRatio(),
+ QImage::Format_ARGB32_Premultiplied);
+ result->image.fill(Qt::transparent);
+ result->image.setDevicePixelRatio(style::DevicePixelRatio());
+ result->ready.resize(count);
+ return result;
+}
+
+void ServiceCheck::Generator::FillFrame(
+ QImage &image,
+ not_null st,
+ int index,
+ int count) {
+ Expects(count > 1);
+ Expects(index >= 0 && index < count);
+
+ Painter p(&image);
+ PainterHighQualityEnabler hq(p);
+
+ p.translate(index * st->diameter, 0);
+ const auto progress = index / float64(count - 1);
+ if (progress > 0.5) {
+ PaintCheckingFrame(p, st, (progress - 0.5) * 2);
+ } else {
+ PaintFillingFrame(p, st, progress * 2);
+ }
+}
+
+void ServiceCheck::Generator::PaintFillingFrame(
+ Painter &p,
+ not_null st,
+ float64 progress) {
+ const auto shift = progress * st->shift;
+ p.setBrush(st->color);
+ p.setPen(Qt::NoPen);
+ p.drawEllipse(QRectF(
+ shift,
+ shift,
+ st->diameter - 2 * shift,
+ st->diameter - 2 * shift));
+ if (progress < 1.) {
+ const auto remove = progress * (st->diameter / 2. - st->thickness);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.setPen(Qt::NoPen);
+ p.setBrush(Qt::transparent);
+ p.drawEllipse(QRectF(
+ st->thickness + remove,
+ st->thickness + remove,
+ st->diameter - 2 * (st->thickness + remove),
+ st->diameter - 2 * (st->thickness + remove)));
+ }
+}
+
+void ServiceCheck::Generator::PaintCheckingFrame(
+ Painter &p,
+ not_null st,
+ float64 progress) {
+ const auto shift = (1. - progress) * st->shift;
+ p.setBrush(st->color);
+ p.setPen(Qt::NoPen);
+ p.drawEllipse(QRectF(
+ shift,
+ shift,
+ st->diameter - 2 * shift,
+ st->diameter - 2 * shift));
+ if (progress > 0.) {
+ const auto tip = QPointF(st->tip.x(), st->tip.y());
+ const auto left = tip - QPointF(st->small, st->small) * progress;
+ const auto right = tip - QPointF(-st->large, st->large) * progress;
+
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.setBrush(Qt::NoBrush);
+ auto pen = QPen(Qt::transparent);
+ pen.setWidth(st->stroke);
+ pen.setCapStyle(Qt::RoundCap);
+ pen.setJoinStyle(Qt::RoundJoin);
+ p.setPen(pen);
+ auto path = QPainterPath();
+ path.moveTo(left);
+ path.lineTo(tip);
+ path.lineTo(right);
+ p.drawPath(path);
+ }
+}
+
+void ServiceCheck::Generator::paintFrame(
+ Painter &p,
+ int left,
+ int top,
+ not_null st,
+ float64 toggled) {
+ const auto frames = framesForStyle(st);
+ auto &image = frames->image;
+ const auto count = int(frames->ready.size());
+ const auto index = int(base::SafeRound(toggled * (count - 1)));
+ Assert(index >= 0 && index < count);
+ if (!frames->ready[index]) {
+ frames->ready[index] = true;
+ FillFrame(image, st, index, count);
+ }
+ const auto size = st->diameter;
+ const auto part = size * style::DevicePixelRatio();
+ p.drawImage(
+ QPoint(left, top),
+ image,
+ QRect(index * part, 0, part, part));
+}
+
+void ServiceCheck::Generator::invalidate() {
+ _data.clear();
+}
+
+ServiceCheck::Generator &ServiceCheck::Frames() {
+ static const auto Instance = Ui::CreateChild(
+ QCoreApplication::instance());
+ return *Instance;
+}
+
+ServiceCheck::ServiceCheck(
+ const style::ServiceCheck &st,
+ bool checked)
+: AbstractCheckView(st.duration, checked, nullptr)
+, _st(st) {
+}
+
+QSize ServiceCheck::getSize() const {
+ const auto inner = QRect(0, 0, _st.diameter, _st.diameter);
+ return inner.marginsAdded(_st.margin).size();
+}
+
+void ServiceCheck::paint(
+ Painter &p,
+ int left,
+ int top,
+ int outerWidth) {
+ Frames().paintFrame(
+ p,
+ left + _st.margin.left(),
+ top + _st.margin.top(),
+ &_st,
+ currentAnimationValue());
+}
+
+QImage ServiceCheck::prepareRippleMask() const {
+ return QImage();
+}
+
+bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
+ return false;
+}
+
+void SetupBackground(not_null checkbox, Fn bg) {
+ checkbox->paintRequest(
+ ) | rpl::map(
+ bg ? bg : [] { return st::msgServiceBg->c; }
+ ) | rpl::filter([=](const QColor &color) {
+ return color.alpha() > 0;
+ }) | rpl::start_with_next([=](const QColor &color) {
+ Painter p(checkbox);
+ PainterHighQualityEnabler hq(p);
+ p.setPen(Qt::NoPen);
+ p.setBrush(color);
+ const auto radius = checkbox->height() / 2.;
+ p.drawRoundedRect(checkbox->rect(), radius, radius);
+ }, checkbox->lifetime());
+}
+
+} // namespace
+
+[[nodiscard]] object_ptr MakeChatServiceCheckbox(
+ QWidget *parent,
+ const QString &text,
+ const style::Checkbox &st,
+ const style::ServiceCheck &stCheck,
+ bool checked,
+ Fn bg) {
+ auto result = object_ptr(
+ parent,
+ text,
+ st,
+ std::make_unique(stCheck, checked));
+ SetupBackground(result.data(), std::move(bg));
+ return result;
+}
+
+} // namespace Ui
diff --git a/Telegram/SourceFiles/ui/controls/chat_service_checkbox.h b/Telegram/SourceFiles/ui/controls/chat_service_checkbox.h
new file mode 100644
index 000000000..2c4533b03
--- /dev/null
+++ b/Telegram/SourceFiles/ui/controls/chat_service_checkbox.h
@@ -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/object_ptr.h"
+
+namespace style {
+struct Checkbox;
+struct ServiceCheck;
+} // namespace style
+
+namespace Ui {
+
+class Checkbox;
+
+[[nodiscard]] object_ptr MakeChatServiceCheckbox(
+ QWidget *parent,
+ const QString &text,
+ const style::Checkbox &st,
+ const style::ServiceCheck &stCheck,
+ bool checked,
+ Fn bg = nullptr);
+
+} // namespace Ui
diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp
index c4945f70b..83ce22f13 100644
--- a/Telegram/SourceFiles/window/notifications_manager.cpp
+++ b/Telegram/SourceFiles/window/notifications_manager.cpp
@@ -954,7 +954,7 @@ void NativeManager::doShowNotification(NotificationFields &&fields) {
&& (item->out() || peer->isSelf())
&& item->isFromScheduled();
const auto title = options.hideNameAndPhoto
- ? qsl("Telegram Desktop")
+ ? AppName.utf16()
: (scheduled && peer->isSelf())
? tr::lng_notification_reminder(tr::now)
: peer->name;
diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake
index 3df88554b..5c558bb57 100644
--- a/Telegram/cmake/td_ui.cmake
+++ b/Telegram/cmake/td_ui.cmake
@@ -190,6 +190,8 @@ PRIVATE
ui/chat/select_scroll_manager.h
ui/controls/call_mute_button.cpp
ui/controls/call_mute_button.h
+ ui/controls/chat_service_checkbox.cpp
+ ui/controls/chat_service_checkbox.h
ui/controls/delete_message_context_action.cpp
ui/controls/delete_message_context_action.h
ui/controls/download_bar.cpp