/* 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 "settings/settings_notifications.h" #include "settings/settings_common.h" #include "ui/effects/animations.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" #include "ui/widgets/discrete_sliders.h" #include "lang/lang_keys.h" #include "window/notifications_manager.h" #include "window/window_session_controller.h" #include "platform/platform_notifications_manager.h" #include "base/platform/base_platform_info.h" #include "mainwindow.h" #include "core/application.h" #include "main/main_session.h" #include "main/main_account.h" #include "main/main_domain.h" #include "apiwrap.h" #include "facades.h" #include "app.h" #include "styles/style_settings.h" #include "styles/style_boxes.h" #include "styles/style_window.h" #include "styles/style_dialogs.h" #include namespace Settings { namespace { constexpr auto kMaxNotificationsCount = 5; [[nodiscard]] int CurrentCount() { return snap( Core::App().settings().notificationsCount(), 1, kMaxNotificationsCount); } using ChangeType = Window::Notifications::ChangeType; class NotificationsCount : public Ui::RpWidget { public: NotificationsCount( QWidget *parent, not_null controller); void setCount(int count); ~NotificationsCount(); protected: void paintEvent(QPaintEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void leaveEventHook(QEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; int resizeGetHeight(int newWidth) override; private: using ScreenCorner = Core::Settings::ScreenCorner; void setOverCorner(ScreenCorner corner); void clearOverCorner(); class SampleWidget; void removeSample(SampleWidget *widget); QRect getScreenRect() const; QRect getScreenRect(int width) const; int getContentLeft() const; void prepareNotificationSampleSmall(); void prepareNotificationSampleLarge(); void prepareNotificationSampleUserpic(); const not_null _controller; QPixmap _notificationSampleUserpic; QPixmap _notificationSampleSmall; QPixmap _notificationSampleLarge; ScreenCorner _chosenCorner; std::vector _sampleOpacities; bool _isOverCorner = false; ScreenCorner _overCorner = ScreenCorner::TopLeft; bool _isDownCorner = false; ScreenCorner _downCorner = ScreenCorner::TopLeft; int _oldCount; QVector _cornerSamples[4]; }; class NotificationsCount::SampleWidget : public QWidget { public: SampleWidget(NotificationsCount *owner, const QPixmap &cache); void detach(); void showFast(); void hideFast(); protected: void paintEvent(QPaintEvent *e) override; private: void startAnimation(); void animationCallback(); void destroyDelayed(); NotificationsCount *_owner; QPixmap _cache; Ui::Animations::Simple _opacity; bool _hiding = false; bool _deleted = false; }; NotificationsCount::NotificationsCount( QWidget *parent, not_null controller) : _controller(controller) , _chosenCorner(Core::App().settings().notificationsCorner()) , _oldCount(CurrentCount()) { setMouseTracking(true); _sampleOpacities.resize(kMaxNotificationsCount); prepareNotificationSampleSmall(); prepareNotificationSampleLarge(); } void NotificationsCount::paintEvent(QPaintEvent *e) { Painter p(this); auto contentLeft = getContentLeft(); auto screenRect = getScreenRect(); p.fillRect( screenRect.x(), screenRect.y(), st::notificationsBoxScreenSize.width(), st::notificationsBoxScreenSize.height(), st::notificationsBoxScreenBg); auto monitorTop = 0; st::notificationsBoxMonitor.paint(p, contentLeft, monitorTop, width()); for (int corner = 0; corner != 4; ++corner) { auto screenCorner = static_cast(corner); auto isLeft = Core::Settings::IsLeftCorner(screenCorner); auto isTop = Core::Settings::IsTopCorner(screenCorner); auto sampleLeft = isLeft ? (screenRect.x() + st::notificationsSampleSkip) : (screenRect.x() + screenRect.width() - st::notificationsSampleSkip - st::notificationSampleSize.width()); auto sampleTop = isTop ? (screenRect.y() + st::notificationsSampleTopSkip) : (screenRect.y() + screenRect.height() - st::notificationsSampleBottomSkip - st::notificationSampleSize.height()); if (corner == static_cast(_chosenCorner)) { auto count = _oldCount; for (int i = 0; i != kMaxNotificationsCount; ++i) { auto opacity = _sampleOpacities[i].value((i < count) ? 1. : 0.); p.setOpacity(opacity); p.drawPixmapLeft(sampleLeft, sampleTop, width(), _notificationSampleSmall); sampleTop += (isTop ? 1 : -1) * (st::notificationSampleSize.height() + st::notificationsSampleMargin); } p.setOpacity(1.); } else { p.setOpacity(st::notificationSampleOpacity); p.drawPixmapLeft(sampleLeft, sampleTop, width(), _notificationSampleSmall); p.setOpacity(1.); } } } void NotificationsCount::setCount(int count) { auto moreSamples = (count > _oldCount); auto from = moreSamples ? 0. : 1.; auto to = moreSamples ? 1. : 0.; auto indexDelta = moreSamples ? 1 : -1; auto animatedDelta = moreSamples ? 0 : -1; for (; _oldCount != count; _oldCount += indexDelta) { _sampleOpacities[_oldCount + animatedDelta].start([this] { update(); }, from, to, st::notifyFastAnim); } if (count != Core::App().settings().notificationsCount()) { Core::App().settings().setNotificationsCount(count); Core::App().saveSettingsDelayed(); Core::App().notifications().settingsChanged().notify( ChangeType::MaxCount); } } int NotificationsCount::getContentLeft() const { return (width() - st::notificationsBoxMonitor.width()) / 2; } QRect NotificationsCount::getScreenRect() const { return getScreenRect(width()); } QRect NotificationsCount::getScreenRect(int width) const { auto screenLeft = (width - st::notificationsBoxScreenSize.width()) / 2; auto screenTop = st::notificationsBoxScreenTop; return QRect(screenLeft, screenTop, st::notificationsBoxScreenSize.width(), st::notificationsBoxScreenSize.height()); } int NotificationsCount::resizeGetHeight(int newWidth) { update(); return st::notificationsBoxMonitor.height(); } void NotificationsCount::prepareNotificationSampleSmall() { auto width = st::notificationSampleSize.width(); auto height = st::notificationSampleSize.height(); auto sampleImage = QImage(width * cIntRetinaFactor(), height * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); sampleImage.setDevicePixelRatio(cRetinaFactor()); sampleImage.fill(st::notificationBg->c); { Painter p(&sampleImage); PainterHighQualityEnabler hq(p); p.setPen(Qt::NoPen); auto padding = height / 8; auto userpicSize = height - 2 * padding; p.setBrush(st::notificationSampleUserpicFg); p.drawEllipse(style::rtlrect(padding, padding, userpicSize, userpicSize, width)); auto rowLeft = height; auto rowHeight = padding; auto nameTop = (height - 5 * padding) / 2; auto nameWidth = height; p.setBrush(st::notificationSampleNameFg); p.drawRoundedRect(style::rtlrect(rowLeft, nameTop, nameWidth, rowHeight, width), rowHeight / 2, rowHeight / 2); auto rowWidth = (width - rowLeft - 3 * padding); auto rowTop = nameTop + rowHeight + padding; p.setBrush(st::notificationSampleTextFg); p.drawRoundedRect(style::rtlrect(rowLeft, rowTop, rowWidth, rowHeight, width), rowHeight / 2, rowHeight / 2); rowTop += rowHeight + padding; p.drawRoundedRect(style::rtlrect(rowLeft, rowTop, rowWidth, rowHeight, width), rowHeight / 2, rowHeight / 2); auto closeLeft = width - 2 * padding; p.fillRect(style::rtlrect(closeLeft, padding, padding, padding, width), st::notificationSampleCloseFg); } _notificationSampleSmall = App::pixmapFromImageInPlace(std::move(sampleImage)); _notificationSampleSmall.setDevicePixelRatio(cRetinaFactor()); } void NotificationsCount::prepareNotificationSampleUserpic() { if (_notificationSampleUserpic.isNull()) { _notificationSampleUserpic = App::pixmapFromImageInPlace( Core::App().logoNoMargin().scaled( st::notifyPhotoSize * cIntRetinaFactor(), st::notifyPhotoSize * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); _notificationSampleUserpic.setDevicePixelRatio(cRetinaFactor()); } } void NotificationsCount::prepareNotificationSampleLarge() { int w = st::notifyWidth, h = st::notifyMinHeight; auto sampleImage = QImage( w * cIntRetinaFactor(), h * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); sampleImage.setDevicePixelRatio(cRetinaFactor()); sampleImage.fill(st::notificationBg->c); { Painter p(&sampleImage); p.fillRect(0, 0, w - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder->b); p.fillRect(w - st::notifyBorderWidth, 0, st::notifyBorderWidth, h - st::notifyBorderWidth, st::notifyBorder->b); p.fillRect(st::notifyBorderWidth, h - st::notifyBorderWidth, w - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder->b); p.fillRect(0, st::notifyBorderWidth, st::notifyBorderWidth, h - st::notifyBorderWidth, st::notifyBorder->b); prepareNotificationSampleUserpic(); p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), _notificationSampleUserpic); int itemWidth = w - st::notifyPhotoPos.x() - st::notifyPhotoSize - st::notifyTextLeft - st::notifyClosePos.x() - st::notifyClose.width; auto rectForName = style::rtlrect(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyTextTop, itemWidth, st::msgNameFont->height, w); auto notifyText = st::dialogsTextFont->elided(tr::lng_notification_sample(tr::now), itemWidth); p.setFont(st::dialogsTextFont); p.setPen(st::dialogsTextFgService); p.drawText(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyItemTop + st::msgNameFont->height + st::dialogsTextFont->ascent, notifyText); p.setPen(st::dialogsNameFg); p.setFont(st::msgNameFont); auto notifyTitle = st::msgNameFont->elided(qsl("Telegram Desktop"), rectForName.width()); p.drawText(rectForName.left(), rectForName.top() + st::msgNameFont->ascent, notifyTitle); st::notifyClose.icon.paint(p, w - st::notifyClosePos.x() - st::notifyClose.width + st::notifyClose.iconPosition.x(), st::notifyClosePos.y() + st::notifyClose.iconPosition.y(), w); } _notificationSampleLarge = App::pixmapFromImageInPlace(std::move(sampleImage)); } void NotificationsCount::removeSample(SampleWidget *widget) { for (auto &samples : _cornerSamples) { for (int i = 0, size = samples.size(); i != size; ++i) { if (samples[i] == widget) { for (int j = i + 1; j != size; ++j) { samples[j]->detach(); } samples.resize(i); break; } } } } void NotificationsCount::mouseMoveEvent(QMouseEvent *e) { auto screenRect = getScreenRect(); auto cornerWidth = screenRect.width() / 3; auto cornerHeight = screenRect.height() / 3; auto topLeft = style::rtlrect(screenRect.x(), screenRect.y(), cornerWidth, cornerHeight, width()); auto topRight = style::rtlrect(screenRect.x() + screenRect.width() - cornerWidth, screenRect.y(), cornerWidth, cornerHeight, width()); auto bottomRight = style::rtlrect(screenRect.x() + screenRect.width() - cornerWidth, screenRect.y() + screenRect.height() - cornerHeight, cornerWidth, cornerHeight, width()); auto bottomLeft = style::rtlrect(screenRect.x(), screenRect.y() + screenRect.height() - cornerHeight, cornerWidth, cornerHeight, width()); if (topLeft.contains(e->pos())) { setOverCorner(ScreenCorner::TopLeft); } else if (topRight.contains(e->pos())) { setOverCorner(ScreenCorner::TopRight); } else if (bottomRight.contains(e->pos())) { setOverCorner(ScreenCorner::BottomRight); } else if (bottomLeft.contains(e->pos())) { setOverCorner(ScreenCorner::BottomLeft); } else { clearOverCorner(); } } void NotificationsCount::leaveEventHook(QEvent *e) { clearOverCorner(); } void NotificationsCount::setOverCorner(ScreenCorner corner) { if (_isOverCorner) { if (corner == _overCorner) { return; } for_const (auto widget, _cornerSamples[static_cast(_overCorner)]) { widget->hideFast(); } } else { _isOverCorner = true; setCursor(style::cur_pointer); Global::SetNotificationsDemoIsShown(true); Core::App().notifications().settingsChanged().notify( ChangeType::DemoIsShown); } _overCorner = corner; auto &samples = _cornerSamples[static_cast(_overCorner)]; auto samplesAlready = samples.size(); auto samplesNeeded = _oldCount; auto samplesLeave = qMin(samplesAlready, samplesNeeded); for (int i = 0; i != samplesLeave; ++i) { samples[i]->showFast(); } if (samplesNeeded > samplesLeave) { auto r = psDesktopRect(); auto isLeft = Core::Settings::IsLeftCorner(_overCorner); auto isTop = Core::Settings::IsTopCorner(_overCorner); auto sampleLeft = (isLeft == rtl()) ? (r.x() + r.width() - st::notifyWidth - st::notifyDeltaX) : (r.x() + st::notifyDeltaX); auto sampleTop = isTop ? (r.y() + st::notifyDeltaY) : (r.y() + r.height() - st::notifyDeltaY - st::notifyMinHeight); for (int i = samplesLeave; i != samplesNeeded; ++i) { auto widget = std::make_unique(this, _notificationSampleLarge); widget->move(sampleLeft, sampleTop + (isTop ? 1 : -1) * i * (st::notifyMinHeight + st::notifyDeltaY)); widget->showFast(); samples.push_back(widget.release()); } } else { for (int i = samplesLeave; i != samplesAlready; ++i) { samples[i]->hideFast(); } } } void NotificationsCount::clearOverCorner() { if (_isOverCorner) { _isOverCorner = false; setCursor(style::cur_default); Global::SetNotificationsDemoIsShown(false); Core::App().notifications().settingsChanged().notify(ChangeType::DemoIsShown); for_const (const auto &samples, _cornerSamples) { for_const (const auto widget, samples) { widget->hideFast(); } } } } void NotificationsCount::mousePressEvent(QMouseEvent *e) { _isDownCorner = _isOverCorner; _downCorner = _overCorner; } void NotificationsCount::mouseReleaseEvent(QMouseEvent *e) { auto isDownCorner = base::take(_isDownCorner); if (isDownCorner && _isOverCorner && _downCorner == _overCorner && _downCorner != _chosenCorner) { _chosenCorner = _downCorner; update(); if (_chosenCorner != Core::App().settings().notificationsCorner()) { Core::App().settings().setNotificationsCorner(_chosenCorner); Core::App().saveSettingsDelayed(); Core::App().notifications().settingsChanged().notify( ChangeType::Corner); } } } NotificationsCount::~NotificationsCount() { for_const (auto &samples, _cornerSamples) { for_const (auto widget, samples) { widget->detach(); } } clearOverCorner(); } NotificationsCount::SampleWidget::SampleWidget( NotificationsCount *owner, const QPixmap &cache) : QWidget(Core::App().getModalParent()) , _owner(owner) , _cache(cache) { const QSize size( cache.width() / cache.devicePixelRatio(), cache.height() / cache.devicePixelRatio()); resize(size); setMinimumSize(size); setMaximumSize(size); setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Tool); setAttribute(Qt::WA_MacAlwaysShowToolWindow); setAttribute(Qt::WA_TransparentForMouseEvents); setAttribute(Qt::WA_OpaquePaintEvent); setWindowOpacity(0.); show(); } void NotificationsCount::SampleWidget::detach() { _owner = nullptr; hideFast(); } void NotificationsCount::SampleWidget::showFast() { _hiding = false; startAnimation(); } void NotificationsCount::SampleWidget::hideFast() { _hiding = true; startAnimation(); } void NotificationsCount::SampleWidget::paintEvent(QPaintEvent *e) { Painter p(this); p.drawPixmap(0, 0, _cache); } void NotificationsCount::SampleWidget::startAnimation() { _opacity.start( [=] { animationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., st::notifyFastAnim); } void NotificationsCount::SampleWidget::animationCallback() { setWindowOpacity(_opacity.value(_hiding ? 0. : 1.)); if (!_opacity.animating() && _hiding) { if (_owner) { _owner->removeSample(this); } hide(); destroyDelayed(); } } void NotificationsCount::SampleWidget::destroyDelayed() { if (_deleted) return; _deleted = true; // Ubuntu has a lag if deleteLater() called immediately. #if defined Q_OS_UNIX && !defined Q_OS_MAC QTimer::singleShot(1000, [this] { delete this; }); #else // Q_OS_UNIX && !Q_OS_MAC deleteLater(); #endif // Q_OS_UNIX && !Q_OS_MAC } void SetupAdvancedNotifications( not_null controller, not_null container) { AddSkip(container, st::settingsCheckboxesSkip); AddDivider(container); AddSkip(container, st::settingsCheckboxesSkip); AddSubsectionTitle(container, tr::lng_settings_notifications_position()); AddSkip(container, st::settingsCheckboxesSkip); const auto position = container->add( object_ptr(container, controller)); AddSkip(container, st::settingsCheckboxesSkip); AddSubsectionTitle(container, tr::lng_settings_notifications_count()); const auto count = container->add( object_ptr(container, st::settingsSlider), st::settingsBigScalePadding); for (int i = 0; i != kMaxNotificationsCount; ++i) { count->addSection(QString::number(i + 1)); } count->setActiveSectionFast(CurrentCount() - 1); count->sectionActivated( ) | rpl::start_with_next([=](int section) { position->setCount(section + 1); }, count->lifetime()); AddSkip(container, st::settingsCheckboxesSkip); } void SetupMultiAccountNotifications( not_null controller, not_null container) { if (Core::App().domain().accounts().size() < 2) { return; } AddSubsectionTitle(container, tr::lng_settings_show_from()); const auto fromAll = container->add( object_ptr( container, tr::lng_settings_notify_all(tr::now), Core::App().settings().notifyFromAll(), st::settingsCheckbox), st::settingsCheckboxPadding); fromAll->checkedChanges( ) | rpl::filter([](bool checked) { return (checked != Core::App().settings().notifyFromAll()); }) | rpl::start_with_next([=](bool checked) { Core::App().settings().setNotifyFromAll(checked); Core::App().saveSettingsDelayed(); if (!checked) { auto ¬ifications = Core::App().notifications(); const auto &list = Core::App().domain().accounts(); for (const auto &[index, account] : list) { if (account.get() == &Core::App().domain().active()) { continue; } else if (const auto session = account->maybeSession()) { notifications.clearFromSession(session); } } } }, fromAll->lifetime()); AddSkip(container); AddDividerText(container, tr::lng_settings_notify_all_about()); AddSkip(container); } void SetupNotificationsContent( not_null controller, not_null container) { SetupMultiAccountNotifications(controller, container); AddSubsectionTitle(container, tr::lng_settings_notify_title()); const auto session = &controller->session(); const auto checkbox = [&](const QString &label, bool checked) { return object_ptr( container, label, checked, st::settingsCheckbox); }; const auto addCheckbox = [&](const QString &label, bool checked) { return container->add( checkbox(label, checked), st::settingsCheckboxPadding); }; const auto addSlidingCheckbox = [&](const QString &label, bool checked) { return container->add( object_ptr>( container, checkbox(label, checked), st::settingsCheckboxPadding)); }; const auto &settings = Core::App().settings(); const auto desktop = addCheckbox( tr::lng_settings_desktop_notify(tr::now), settings.desktopNotify()); const auto name = addSlidingCheckbox( tr::lng_settings_show_name(tr::now), (settings.notifyView() <= dbinvShowName)); const auto preview = addSlidingCheckbox( tr::lng_settings_show_preview(tr::now), (settings.notifyView() <= dbinvShowPreview)); const auto sound = addCheckbox( tr::lng_settings_sound_notify(tr::now), settings.soundNotify()); const auto flashbounce = addCheckbox( (Platform::IsWindows() ? tr::lng_settings_alert_windows : Platform::IsMac() ? tr::lng_settings_alert_mac : tr::lng_settings_alert_linux)(tr::now), settings.flashBounceNotify()); AddSkip(container, st::settingsCheckboxesSkip); AddDivider(container); AddSkip(container, st::settingsCheckboxesSkip); AddSubsectionTitle(container, tr::lng_settings_badge_title()); const auto muted = addCheckbox( tr::lng_settings_include_muted(tr::now), settings.includeMutedCounter()); const auto count = addCheckbox( tr::lng_settings_count_unread(tr::now), settings.countUnreadMessages()); AddSkip(container, st::settingsCheckboxesSkip); AddDivider(container); AddSkip(container, st::settingsCheckboxesSkip); AddSubsectionTitle(container, tr::lng_settings_events_title()); const auto joined = addCheckbox( tr::lng_settings_events_joined(tr::now), !session->api().contactSignupSilentCurrent().value_or(false)); session->api().contactSignupSilent( ) | rpl::start_with_next([=](bool silent) { joined->setChecked(!silent); }, joined->lifetime()); joined->checkedChanges( ) | rpl::filter([=](bool enabled) { const auto silent = session->api().contactSignupSilentCurrent(); return (enabled == silent.value_or(false)); }) | rpl::start_with_next([=](bool enabled) { session->api().saveContactSignupSilent(!enabled); }, joined->lifetime()); const auto pinned = addCheckbox( tr::lng_settings_events_pinned(tr::now), settings.notifyAboutPinned()); settings.notifyAboutPinnedChanges( ) | rpl::start_with_next([=](bool notify) { pinned->setChecked(notify); }, pinned->lifetime()); pinned->checkedChanges( ) | rpl::filter([=](bool notify) { return (notify != Core::App().settings().notifyAboutPinned()); }) | rpl::start_with_next([=](bool notify) { Core::App().settings().setNotifyAboutPinned(notify); Core::App().saveSettingsDelayed(); }, joined->lifetime()); const auto nativeText = [&] { if (!Platform::Notifications::Supported()) { return QString(); } else if (Platform::IsWindows()) { return tr::lng_settings_use_windows(tr::now); } else if (Platform::IsLinux() && !Platform::IsWayland()) { return tr::lng_settings_use_native_notifications(tr::now); } return QString(); }(); const auto native = [&]() -> Ui::Checkbox* { if (nativeText.isEmpty()) { return nullptr; } AddSkip(container, st::settingsCheckboxesSkip); AddDivider(container); AddSkip(container, st::settingsCheckboxesSkip); AddSubsectionTitle(container, tr::lng_settings_native_title()); return addCheckbox(nativeText, settings.nativeNotifications()); }(); const auto advancedSlide = !Platform::IsMac10_8OrGreater() && !Platform::IsWayland() ? container->add( object_ptr>( container, object_ptr(container))) : nullptr; const auto advancedWrap = advancedSlide ? advancedSlide->entity() : nullptr; if (advancedWrap) { SetupAdvancedNotifications(controller, advancedWrap); } if (!name->entity()->checked()) { preview->hide(anim::type::instant); } if (!desktop->checked()) { name->hide(anim::type::instant); preview->hide(anim::type::instant); } if (native && advancedSlide && settings.nativeNotifications()) { advancedSlide->hide(anim::type::instant); } using Change = Window::Notifications::ChangeType; const auto changed = [=](Change change) { Core::App().saveSettingsDelayed(); Core::App().notifications().settingsChanged().notify(change); }; desktop->checkedChanges( ) | rpl::filter([](bool checked) { return (checked != Core::App().settings().desktopNotify()); }) | rpl::start_with_next([=](bool checked) { Core::App().settings().setDesktopNotify(checked); changed(Change::DesktopEnabled); }, desktop->lifetime()); name->entity()->checkedChanges( ) | rpl::map([=](bool checked) { if (!checked) { return dbinvShowNothing; } else if (!preview->entity()->checked()) { return dbinvShowName; } return dbinvShowPreview; }) | rpl::filter([=](DBINotifyView value) { return (value != Core::App().settings().notifyView()); }) | rpl::start_with_next([=](DBINotifyView value) { Core::App().settings().setNotifyView(value); changed(Change::ViewParams); }, name->lifetime()); preview->entity()->checkedChanges( ) | rpl::map([=](bool checked) { if (checked) { return dbinvShowPreview; } else if (name->entity()->checked()) { return dbinvShowName; } return dbinvShowNothing; }) | rpl::filter([=](DBINotifyView value) { return (value != Core::App().settings().notifyView()); }) | rpl::start_with_next([=](DBINotifyView value) { Core::App().settings().setNotifyView(value); changed(Change::ViewParams); }, preview->lifetime()); sound->checkedChanges( ) | rpl::filter([](bool checked) { return (checked != Core::App().settings().soundNotify()); }) | rpl::start_with_next([=](bool checked) { Core::App().settings().setSoundNotify(checked); changed(Change::SoundEnabled); }, sound->lifetime()); flashbounce->checkedChanges( ) | rpl::filter([](bool checked) { return (checked != Core::App().settings().flashBounceNotify()); }) | rpl::start_with_next([=](bool checked) { Core::App().settings().setFlashBounceNotify(checked); changed(Change::FlashBounceEnabled); }, flashbounce->lifetime()); muted->checkedChanges( ) | rpl::filter([=](bool checked) { return (checked != Core::App().settings().includeMutedCounter()); }) | rpl::start_with_next([=](bool checked) { Core::App().settings().setIncludeMutedCounter(checked); changed(Change::IncludeMuted); }, muted->lifetime()); count->checkedChanges( ) | rpl::filter([=](bool checked) { return (checked != Core::App().settings().countUnreadMessages()); }) | rpl::start_with_next([=](bool checked) { Core::App().settings().setCountUnreadMessages(checked); changed(Change::CountMessages); }, count->lifetime()); base::ObservableViewer( Core::App().notifications().settingsChanged() ) | rpl::start_with_next([=](Change change) { if (change == Change::DesktopEnabled) { desktop->setChecked(Core::App().settings().desktopNotify()); name->toggle( Core::App().settings().desktopNotify(), anim::type::normal); preview->toggle( (Core::App().settings().desktopNotify() && name->entity()->checked()), anim::type::normal); } else if (change == Change::ViewParams) { preview->toggle(name->entity()->checked(), anim::type::normal); } else if (change == Change::SoundEnabled) { sound->setChecked(Core::App().settings().soundNotify()); } else if (change == Change::FlashBounceEnabled) { flashbounce->setChecked(Core::App().settings().flashBounceNotify()); } }, desktop->lifetime()); if (native) { native->checkedChanges( ) | rpl::filter([](bool checked) { return (checked != Core::App().settings().nativeNotifications()); }) | rpl::start_with_next([=](bool checked) { Core::App().settings().setNativeNotifications(checked); Core::App().saveSettingsDelayed(); Core::App().notifications().createManager(); if (advancedSlide) { advancedSlide->toggle( !Core::App().settings().nativeNotifications(), anim::type::normal); } }, native->lifetime()); } } void SetupNotifications( not_null controller, not_null container) { AddSkip(container, st::settingsCheckboxesSkip); auto wrap = object_ptr(container); SetupNotificationsContent(controller, wrap.data()); container->add(object_ptr( container, std::move(wrap))); AddSkip(container, st::settingsCheckboxesSkip); } } // namespace Notifications::Notifications( QWidget *parent, not_null controller) : Section(parent) { setupContent(controller); } void Notifications::setupContent( not_null controller) { const auto content = Ui::CreateChild(this); SetupNotifications(controller, content); Ui::ResizeFitChild(this, content); } } // namespace Settings