diff --git a/Telegram/SourceFiles/boxes/contacts_box.cpp b/Telegram/SourceFiles/boxes/contacts_box.cpp index 0b428469c..234f9ecb3 100644 --- a/Telegram/SourceFiles/boxes/contacts_box.cpp +++ b/Telegram/SourceFiles/boxes/contacts_box.cpp @@ -630,7 +630,7 @@ ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent) void ContactsBox::Inner::init() { subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); connect(_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); - connect(_allAdmins, SIGNAL(changed()), this, SLOT(onAllAdminsChanged())); + subscribe(_allAdmins->checkedChanged, [this](bool checked) { onAllAdminsChanged(); }); _rowsTop = st::contactsMarginTop; setAttribute(Qt::WA_OpaquePaintEvent); diff --git a/Telegram/SourceFiles/boxes/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/edit_participant_box.cpp index 38cd4c295..3ec84a363 100644 --- a/Telegram/SourceFiles/boxes/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_participant_box.cpp @@ -196,9 +196,9 @@ void EditAdminBox::prepare() { } auto checked = (prepareRights.c_channelAdminRights().vflags.v & flags) != 0; auto control = addControl(object_ptr(this, text, checked, st::defaultBoxCheckbox)); - connect(control, &Ui::Checkbox::changed, this, [this, control] { - applyDependencies(control); - }, Qt::QueuedConnection); + subscribe(control->checkedChanged, [this, control](bool checked) { + InvokeQueued(this, [this, control] { applyDependencies(control); }); + }); _checkboxes.emplace(flags, control); }; if (channel()->isMegagroup()) { @@ -221,7 +221,7 @@ void EditAdminBox::prepare() { if (addAdmins != _checkboxes.end()) { _aboutAddAdmins = addControl(object_ptr(this, st::boxLabel)); t_assert(addAdmins != _checkboxes.end()); - connect(addAdmins->second, &Ui::Checkbox::changed, this, [this] { + subscribe(addAdmins->second->checkedChanged, [this](bool checked) { refreshAboutAddAdminsText(); }); refreshAboutAddAdminsText(); @@ -298,9 +298,9 @@ void EditRestrictedBox::prepare() { auto addCheckbox = [this, &prepareRights](Flags flags, const QString &text) { auto checked = (prepareRights.c_channelBannedRights().vflags.v & flags) == 0; auto control = addControl(object_ptr(this, text, checked, st::defaultBoxCheckbox)); - connect(control, &Ui::Checkbox::changed, this, [this, control] { - applyDependencies(control); - }, Qt::QueuedConnection); + subscribe(control->checkedChanged, [this, control](bool checked) { + InvokeQueued(this, [this, control] { applyDependencies(control); }); + }); _checkboxes.emplace(flags, control); }; addCheckbox(Flag::f_view_messages, lang(lng_rights_chat_read)); diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 12aad319d..097441b6f 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -244,7 +244,7 @@ void SendFilesBox::prepare() { auto compressed = (_compressConfirm == CompressConfirm::Auto) ? cCompressPastedImage() : (_compressConfirm == CompressConfirm::Yes); auto text = lng_send_images_compress(lt_count, _files.size()); _compressed.create(this, text, compressed, st::defaultBoxCheckbox); - connect(_compressed, SIGNAL(changed()), this, SLOT(onCompressedChange())); + subscribe(_compressed->checkedChanged, [this](bool checked) { onCompressedChange(); }); } if (_caption) { _caption->setMaxLength(MaxPhotoCaption); diff --git a/Telegram/SourceFiles/history/history_admin_log_filter.cpp b/Telegram/SourceFiles/history/history_admin_log_filter.cpp index 26137b886..07872e941 100644 --- a/Telegram/SourceFiles/history/history_admin_log_filter.cpp +++ b/Telegram/SourceFiles/history/history_admin_log_filter.cpp @@ -30,11 +30,13 @@ namespace { class UserCheckbox : public Ui::RippleButton { public: - UserCheckbox(QWidget *parent, gsl::not_null user, bool checked, base::lambda changedCallback); + UserCheckbox(QWidget *parent, gsl::not_null user, bool checked); bool checked() const { - return _checked; + return _check->checked(); } + base::Observable checkedChanged; + enum class NotifyAboutChange { Notify, DontNotify, @@ -57,24 +59,20 @@ protected: private: const style::Checkbox &_st; + std::unique_ptr _check; QRect _checkRect; - bool _checked = false; - Animation _a_checked; - gsl::not_null _user; - base::lambda _changedCallback; QString _statusText; bool _statusOnline = false; }; -UserCheckbox::UserCheckbox(QWidget *parent, gsl::not_null user, bool checked, base::lambda changedCallback) : Ui::RippleButton(parent, st::defaultBoxCheckbox.ripple) +UserCheckbox::UserCheckbox(QWidget *parent, gsl::not_null user, bool checked) : Ui::RippleButton(parent, st::defaultBoxCheckbox.ripple) , _st(st::adminLogFilterUserCheckbox) -, _checked(checked) -, _user(user) -, _changedCallback(std::move(changedCallback)) { +, _check(std::make_unique(st::defaultCheck, checked, [this] { rtlupdate(_checkRect); })) +, _user(user) { setCursor(style::cur_pointer); setClickedCallback([this] { if (isDisabled()) return; @@ -83,15 +81,15 @@ UserCheckbox::UserCheckbox(QWidget *parent, gsl::not_null user, bool auto now = unixtime(); _statusText = App::onlineText(_user, now); _statusOnline = App::onlineColorUse(_user, now); - _checkRect = myrtlrect(_st.margin.left(), (st::contactsPhotoSize - _st.diameter) / 2, _st.diameter, _st.diameter); + auto checkSize = _check->getSize(); + _checkRect = { QPoint(_st.margin.left(), (st::contactsPhotoSize - checkSize.height()) / 2), checkSize }; } void UserCheckbox::setChecked(bool checked, NotifyAboutChange notify) { - if (_checked != checked) { - _checked = checked; - _a_checked.start([this] { update(_checkRect); }, _checked ? 0. : 1., _checked ? 1. : 0., _st.duration); - if (notify == NotifyAboutChange::Notify && _changedCallback) { - _changedCallback(); + if (_check->checked() != checked) { + _check->setCheckedAnimated(checked); + if (notify == NotifyAboutChange::Notify) { + checkedChanged.notify(checked, true); } } } @@ -100,25 +98,15 @@ void UserCheckbox::paintEvent(QPaintEvent *e) { Painter p(this); auto ms = getms(); - auto active = _a_checked.current(ms, _checked ? 1. : 0.); + auto active = _check->currentAnimationValue(ms); auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active); paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y() + (_checkRect.y() - st::defaultBoxCheckbox.margin.top()), ms, &color); - if (_checkRect.intersects(e->rect())) { - auto pen = anim::pen(_st.checkFg, _st.checkFgActive, active); - pen.setWidth(_st.thickness); - p.setPen(pen); - p.setBrush(anim::brush(_st.checkBg, anim::color(_st.checkFg, _st.checkFgActive, active), active)); - - { - PainterHighQualityEnabler hq(p); - p.drawRoundedRect(QRectF(_checkRect).marginsRemoved(QMarginsF(_st.thickness / 2., _st.thickness / 2., _st.thickness / 2., _st.thickness / 2.)), st::buttonRadius - (_st.thickness / 2.), st::buttonRadius - (_st.thickness / 2.)); - } - - if (active > 0) { - _st.checkIcon.paint(p, _checkRect.topLeft(), width()); - } + auto realCheckRect = myrtlrect(_checkRect); + if (realCheckRect.intersects(e->rect())) { + _check->paint(p, _checkRect.left(), _checkRect.top(), width()); } + if (realCheckRect.contains(e->rect())) return; auto userpicLeft = _checkRect.x() + _checkRect.width() + st::adminLogFilterUserpicLeft; auto userpicTop = 0; @@ -138,7 +126,7 @@ void UserCheckbox::paintEvent(QPaintEvent *e) { } void UserCheckbox::finishAnimations() { - _a_checked.finish(); + _check->finishAnimation(); } int UserCheckbox::resizeGetHeight(int newWidth) { @@ -159,7 +147,7 @@ QPoint UserCheckbox::prepareRippleStartPosition() const { } // namespace -class FilterBox::Inner : public TWidget { +class FilterBox::Inner : public TWidget, private base::Subscriber { public: Inner(QWidget *parent, gsl::not_null channel, const std::vector> &admins, const FilterValue &filter, base::lambda changedCallback); @@ -223,7 +211,7 @@ void FilterBox::Inner::createControls(const std::vector void FilterBox::Inner::createAllActionsCheckbox(const FilterValue &filter) { auto checked = (filter.flags == 0); _allFlags = addRow(object_ptr(this, lang(lng_admin_log_filter_all_actions), checked, st::adminLogFilterCheckbox), st::adminLogFilterCheckbox.margin.top()); - connect(_allFlags, &Ui::Checkbox::changed, this, [this] { + subscribe(_allFlags->checkedChanged, [this](bool checked) { if (!std::exchange(_restoringInvariant, true)) { auto allChecked = _allFlags->checked(); for_const (auto &&checkbox, _filterFlags) { @@ -244,7 +232,7 @@ void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) { auto checked = (filter.flags == 0) || (filter.flags & flag); auto checkbox = addRow(object_ptr(this, std::move(text), checked, st::defaultBoxCheckbox), st::adminLogFilterLittleSkip); _filterFlags.insert(flag, checkbox); - connect(checkbox, &Ui::Checkbox::changed, this, [this] { + subscribe(checkbox->checkedChanged, [this](bool checked) { if (!std::exchange(_restoringInvariant, true)) { auto allChecked = true; for_const (auto &&checkbox, _filterFlags) { @@ -278,8 +266,8 @@ void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) { void FilterBox::Inner::createAllUsersCheckbox(const FilterValue &filter) { _allUsers = addRow(object_ptr(this, lang(lng_admin_log_filter_all_admins), filter.allUsers, st::adminLogFilterCheckbox), st::adminLogFilterSkip); - connect(_allUsers, &Ui::Checkbox::changed, this, [this] { - if (_allUsers->checked() && !std::exchange(_restoringInvariant, true)) { + subscribe(_allUsers->checkedChanged, [this](bool checked) { + if (checked && !std::exchange(_restoringInvariant, true)) { for_const (auto &&checkbox, _admins) { checkbox->setChecked(true); } @@ -294,7 +282,8 @@ void FilterBox::Inner::createAllUsersCheckbox(const FilterValue &filter) { void FilterBox::Inner::createAdminsCheckboxes(const std::vector> &admins, const FilterValue &filter) { for (auto user : admins) { auto checked = filter.allUsers || base::contains(filter.admins, user); - auto checkbox = addRow(object_ptr(this, user, checked, [this] { + auto checkbox = addRow(object_ptr(this, user, checked), st::adminLogFilterLittleSkip); + subscribe(checkbox->checkedChanged, [this](bool checked) { if (!std::exchange(_restoringInvariant, true)) { auto allChecked = true; for_const (auto &&checkbox, _admins) { @@ -311,7 +300,7 @@ void FilterBox::Inner::createAdminsCheckboxes(const std::vectorselecting) { - QRect check(rthumb.topLeft() + QPoint(rtl() ? 0 : (rthumb.width() - st::defaultCheckbox.diameter), rthumb.height() - st::defaultCheckbox.diameter), QSize(st::defaultCheckbox.diameter, st::defaultCheckbox.diameter)); + QRect check(rthumb.topLeft() + QPoint(rtl() ? 0 : (rthumb.width() - st::defaultCheck.diameter), rthumb.height() - st::defaultCheck.diameter), QSize(st::defaultCheck.diameter, st::defaultCheck.diameter)); p.fillRect(check, selected ? st::overviewFileChecked : st::overviewFileCheck); - st::defaultCheckbox.checkIcon.paint(p, QPoint(rthumb.width() - st::defaultCheckbox.diameter, rthumb.y() + rthumb.height() - st::defaultCheckbox.diameter), _width); + st::defaultCheck.icon.paint(p, QPoint(rthumb.width() - st::defaultCheck.diameter, rthumb.y() + rthumb.height() - st::defaultCheck.diameter), _width); } } } diff --git a/Telegram/SourceFiles/profile/profile_block_settings.cpp b/Telegram/SourceFiles/profile/profile_block_settings.cpp index 25a572a23..78c74e123 100644 --- a/Telegram/SourceFiles/profile/profile_block_settings.cpp +++ b/Telegram/SourceFiles/profile/profile_block_settings.cpp @@ -39,7 +39,7 @@ using UpdateFlag = Notify::PeerUpdate::Flag; SettingsWidget::SettingsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_settings_section)) , _enableNotifications(this, lang(lng_profile_enable_notifications), true, st::defaultCheckbox) { - connect(_enableNotifications, SIGNAL(changed()), this, SLOT(onNotificationsChange())); + subscribe(_enableNotifications->checkedChanged, [this](bool checked) { onNotificationsChange(); }); Notify::PeerUpdate::Flags observeEvents = UpdateFlag::NotificationsEnabled; if (auto chat = peer->asChat()) { diff --git a/Telegram/SourceFiles/settings/settings_background_widget.cpp b/Telegram/SourceFiles/settings/settings_background_widget.cpp index 339bed22f..b9ea8150f 100644 --- a/Telegram/SourceFiles/settings/settings_background_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_background_widget.cpp @@ -222,8 +222,8 @@ void BackgroundWidget::createControls() { connect(_background, SIGNAL(editTheme()), this, SLOT(onEditTheme())); connect(_background, SIGNAL(useDefault()), this, SLOT(onUseDefaultTheme())); - addChildRow(_tile, margin, lang(lng_settings_bg_tile), SLOT(onTile()), Window::Theme::Background()->tile()); - addChildRow(_adaptive, margin, slidedPadding, lang(lng_settings_adaptive_wide), SLOT(onAdaptive()), Global::AdaptiveForWide()); + addChildRow(_tile, margin, lang(lng_settings_bg_tile), [this](bool) { onTile(); }, Window::Theme::Background()->tile()); + addChildRow(_adaptive, margin, slidedPadding, lang(lng_settings_adaptive_wide), [this](bool) { onAdaptive(); }, Global::AdaptiveForWide()); if (Global::AdaptiveChatLayout() != Adaptive::ChatLayout::Wide) { _adaptive->hideFast(); } diff --git a/Telegram/SourceFiles/settings/settings_block_widget.cpp b/Telegram/SourceFiles/settings/settings_block_widget.cpp index 7bc950126..36da0365f 100644 --- a/Telegram/SourceFiles/settings/settings_block_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_block_widget.cpp @@ -85,9 +85,9 @@ void BlockWidget::rowHeightUpdated() { } } -void BlockWidget::createChildRow(object_ptr &child, style::margins &margin, const QString &text, const char *slot, bool checked) { +void BlockWidget::createChildRow(object_ptr &child, style::margins &margin, const QString &text, base::lambda callback, bool checked) { child.create(this, text, checked, st::defaultBoxCheckbox); - connect(child, SIGNAL(changed()), this, slot); + subscribe(child->checkedChanged, std::move(callback)); } void BlockWidget::createChildRow(object_ptr &child, style::margins &margin, const QString &text, const char *slot, const style::LinkButton &st) { diff --git a/Telegram/SourceFiles/settings/settings_block_widget.h b/Telegram/SourceFiles/settings/settings_block_widget.h index 4b6ed598f..77cdfe689 100644 --- a/Telegram/SourceFiles/settings/settings_block_widget.h +++ b/Telegram/SourceFiles/settings/settings_block_widget.h @@ -91,7 +91,7 @@ private: margin.setRight(margin.right() - padding.right()); margin.setBottom(margin.bottom() - padding.bottom()); } - void createChildRow(object_ptr &child, style::margins &margin, const QString &text, const char *slot, bool checked); + void createChildRow(object_ptr &child, style::margins &margin, const QString &text, base::lambda callback, bool checked); void createChildRow(object_ptr &child, style::margins &margin, const QString &text, const char *slot, const style::LinkButton &st = st::boxLinkButton); template diff --git a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp index d8aeaa592..68ca21d32 100644 --- a/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_chat_settings_widget.cpp @@ -154,7 +154,7 @@ void ChatSettingsWidget::createControls() { style::margins marginSub(0, 0, 0, st::settingsSubSkip); style::margins slidedPadding(0, marginSub.bottom() / 2, 0, marginSub.bottom() - (marginSub.bottom() / 2)); - addChildRow(_replaceEmoji, marginSub, lang(lng_settings_replace_emojis), SLOT(onReplaceEmoji()), cReplaceEmojis()); + addChildRow(_replaceEmoji, marginSub, lang(lng_settings_replace_emojis), [this](bool) { onReplaceEmoji(); }, cReplaceEmojis()); style::margins marginList(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip); addChildRow(_viewList, marginList, slidedPadding, lang(lng_settings_view_emojis), SLOT(onViewList()), st::defaultLinkButton); if (!cReplaceEmojis()) { @@ -166,7 +166,7 @@ void ChatSettingsWidget::createControls() { #else // OS_WIN_STORE auto pathMargin = marginSkip; #endif // OS_WIN_STORE - addChildRow(_dontAskDownloadPath, pathMargin, lang(lng_download_path_dont_ask), SLOT(onDontAskDownloadPath()), !Global::AskDownloadPath()); + addChildRow(_dontAskDownloadPath, pathMargin, lang(lng_download_path_dont_ask), [this](bool) { onDontAskDownloadPath(); }, !Global::AskDownloadPath()); #ifndef OS_WIN_STORE style::margins marginPath(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip); diff --git a/Telegram/SourceFiles/settings/settings_general_widget.cpp b/Telegram/SourceFiles/settings/settings_general_widget.cpp index abbd90807..6737e856d 100644 --- a/Telegram/SourceFiles/settings/settings_general_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_general_widget.cpp @@ -175,7 +175,7 @@ void GeneralWidget::refreshControls() { style::margins slidedPadding(0, marginSmall.bottom() / 2, 0, marginSmall.bottom() - (marginSmall.bottom() / 2)); #ifndef TDESKTOP_DISABLE_AUTOUPDATE - addChildRow(_updateAutomatically, marginSub, lang(lng_settings_update_automatically), SLOT(onUpdateAutomatically()), cAutoUpdate()); + addChildRow(_updateAutomatically, marginSub, lang(lng_settings_update_automatically), [this](bool) { onUpdateAutomatically(); }, cAutoUpdate()); style::margins marginLink(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip); addChildRow(_updateRow, marginLink, slidedPadding); connect(_updateRow->entity(), SIGNAL(restart()), this, SLOT(onRestart())); @@ -186,20 +186,20 @@ void GeneralWidget::refreshControls() { if (cPlatform() == dbipWindows || cSupportTray()) { auto workMode = Global::WorkMode().value(); - addChildRow(_enableTrayIcon, marginSmall, lang(lng_settings_workmode_tray), SLOT(onEnableTrayIcon()), (workMode == dbiwmTrayOnly || workMode == dbiwmWindowAndTray)); + addChildRow(_enableTrayIcon, marginSmall, lang(lng_settings_workmode_tray), [this](bool) { onEnableTrayIcon(); }, (workMode == dbiwmTrayOnly || workMode == dbiwmWindowAndTray)); if (cPlatform() == dbipWindows) { - addChildRow(_enableTaskbarIcon, marginLarge, lang(lng_settings_workmode_window), SLOT(onEnableTaskbarIcon()), (workMode == dbiwmWindowOnly || workMode == dbiwmWindowAndTray)); + addChildRow(_enableTaskbarIcon, marginLarge, lang(lng_settings_workmode_window), [this](bool) { onEnableTaskbarIcon(); }, (workMode == dbiwmWindowOnly || workMode == dbiwmWindowAndTray)); #ifndef OS_WIN_STORE - addChildRow(_autoStart, marginSmall, lang(lng_settings_auto_start), SLOT(onAutoStart()), cAutoStart()); - addChildRow(_startMinimized, marginLarge, slidedPadding, lang(lng_settings_start_min), SLOT(onStartMinimized()), (cStartMinimized() && !Global::LocalPasscode())); + addChildRow(_autoStart, marginSmall, lang(lng_settings_auto_start), [this](bool) { onAutoStart(); }, cAutoStart()); + addChildRow(_startMinimized, marginLarge, slidedPadding, lang(lng_settings_start_min), [this](bool) { onStartMinimized(); }, (cStartMinimized() && !Global::LocalPasscode())); subscribe(Global::RefLocalPasscodeChanged(), [this] { _startMinimized->entity()->setChecked(cStartMinimized() && !Global::LocalPasscode()); }); if (!cAutoStart()) { _startMinimized->hideFast(); } - addChildRow(_addInSendTo, marginSmall, lang(lng_settings_add_sendto), SLOT(onAddInSendTo()), cSendToMenu()); + addChildRow(_addInSendTo, marginSmall, lang(lng_settings_add_sendto), [this](bool) { onAddInSendTo(); }, cSendToMenu()); #endif // OS_WIN_STORE } } diff --git a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp index 60f4268fa..4e7ccd7be 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp @@ -56,9 +56,9 @@ NotificationsWidget::NotificationsWidget(QWidget *parent, UserData *self) : Bloc void NotificationsWidget::createControls() { style::margins margin(0, 0, 0, st::settingsSkip); style::margins slidedPadding(0, margin.bottom() / 2, 0, margin.bottom() - (margin.bottom() / 2)); - addChildRow(_desktopNotifications, margin, lang(lng_settings_desktop_notify), SLOT(onDesktopNotifications()), Global::DesktopNotify()); - addChildRow(_showSenderName, margin, slidedPadding, lang(lng_settings_show_name), SLOT(onShowSenderName()), Global::NotifyView() <= dbinvShowName); - addChildRow(_showMessagePreview, margin, slidedPadding, lang(lng_settings_show_preview), SLOT(onShowMessagePreview()), Global::NotifyView() <= dbinvShowPreview); + addChildRow(_desktopNotifications, margin, lang(lng_settings_desktop_notify), [this](bool) { onDesktopNotifications(); }, Global::DesktopNotify()); + addChildRow(_showSenderName, margin, slidedPadding, lang(lng_settings_show_name), [this](bool) { onShowSenderName(); }, Global::NotifyView() <= dbinvShowName); + addChildRow(_showMessagePreview, margin, slidedPadding, lang(lng_settings_show_preview), [this](bool) { onShowMessagePreview(); }, Global::NotifyView() <= dbinvShowPreview); if (!_showSenderName->entity()->checked()) { _showMessagePreview->hideFast(); } @@ -66,8 +66,8 @@ void NotificationsWidget::createControls() { _showSenderName->hideFast(); _showMessagePreview->hideFast(); } - addChildRow(_playSound, margin, lang(lng_settings_sound_notify), SLOT(onPlaySound()), Global::SoundNotify()); - addChildRow(_includeMuted, margin, lang(lng_settings_include_muted), SLOT(onIncludeMuted()), Global::IncludeMuted()); + addChildRow(_playSound, margin, lang(lng_settings_sound_notify), [this](bool) { onPlaySound(); }, Global::SoundNotify()); + addChildRow(_includeMuted, margin, lang(lng_settings_include_muted), [this](bool) { onIncludeMuted(); }, Global::IncludeMuted()); if (cPlatform() != dbipMac) { createNotificationsControls(); @@ -87,7 +87,7 @@ void NotificationsWidget::createNotificationsControls() { #endif // Q_OS_WIN || Q_OS_LINUX64 || Q_OS_LINUX32 } if (!nativeNotificationsLabel.isEmpty()) { - addChildRow(_nativeNotifications, margin, nativeNotificationsLabel, SLOT(onNativeNotifications()), Global::NativeNotifications()); + addChildRow(_nativeNotifications, margin, nativeNotificationsLabel, [this](bool) { onNativeNotifications(); }, Global::NativeNotifications()); } addChildRow(_advanced, margin, slidedPadding, lang(lng_settings_advanced_notifications), SLOT(onAdvanced())); if (!nativeNotificationsLabel.isEmpty() && Global::NativeNotifications()) { diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp index a0b8d01fb..02e96dd28 100644 --- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp @@ -52,7 +52,7 @@ ScaleWidget::ScaleWidget(QWidget *parent, UserData *self) : BlockWidget(parent, void ScaleWidget::createControls() { style::margins margin(0, 0, 0, st::settingsSmallSkip); - addChildRow(_auto, margin, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), SLOT(onAutoChanged()), (cConfigScale() == dbisAuto)); + addChildRow(_auto, margin, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), [this](bool) { onAutoChanged(); }, (cConfigScale() == dbisAuto)); addChildRow(_scale, style::margins(0, 0, 0, 0)); _scale->addSection(scaleLabel(dbisOne)); diff --git a/Telegram/SourceFiles/ui/style/style_core.h b/Telegram/SourceFiles/ui/style/style_core.h index 40a6fa2fa..f2520347e 100644 --- a/Telegram/SourceFiles/ui/style/style_core.h +++ b/Telegram/SourceFiles/ui/style/style_core.h @@ -28,12 +28,18 @@ inline QPoint rtlpoint(int x, int y, int outerw) { inline QPoint rtlpoint(const QPoint &p, int outerw) { return rtl() ? QPoint(outerw - p.x(), p.y()) : p; } +inline QPointF rtlpoint(const QPointF &p, int outerw) { + return rtl() ? QPointF(outerw - p.x(), p.y()) : p; +} inline QRect rtlrect(int x, int y, int w, int h, int outerw) { return QRect(rtl() ? (outerw - x - w) : x, y, w, h); } inline QRect rtlrect(const QRect &r, int outerw) { return rtl() ? QRect(outerw - r.x() - r.width(), r.y(), r.width(), r.height()) : r; } +inline QRectF rtlrect(const QRectF &r, int outerw) { + return rtl() ? QRectF(outerw - r.x() - r.width(), r.y(), r.width(), r.height()) : r; +} inline QRect centerrect(const QRect &inRect, const QRect &rect) { return QRect(inRect.x() + (inRect.width() - rect.width()) / 2, inRect.y() + (inRect.height() - rect.height()) / 2, rect.width(), rect.height()); } diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.cpp b/Telegram/SourceFiles/ui/widgets/checkbox.cpp index ad62d2113..dba2fc5a0 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.cpp +++ b/Telegram/SourceFiles/ui/widgets/checkbox.cpp @@ -35,14 +35,158 @@ TextParseOptions _checkboxOptions = { } // namespace -Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st) : RippleButton(parent, st.ripple) +AbstractCheckView::AbstractCheckView(int duration, bool checked, base::lambda updateCallback) +: _duration(duration) +, _checked(checked) +, _updateCallback(std::move(updateCallback)) { +} + +void AbstractCheckView::setCheckedFast(bool checked) { + _checked = checked; + finishAnimation(); + if (_updateCallback) { + _updateCallback(); + } +} + +void AbstractCheckView::setUpdateCallback(base::lambda updateCallback) { + _updateCallback = std::move(updateCallback); + if (_toggleAnimation.animating()) { + _toggleAnimation.setUpdateCallback(_updateCallback); + } +} + +void AbstractCheckView::setCheckedAnimated(bool checked) { + if (_checked != checked) { + _checked = checked; + _toggleAnimation.start(_updateCallback, _checked ? 0. : 1., _checked ? 1. : 0., _duration); + } +} + +void AbstractCheckView::finishAnimation() { + _toggleAnimation.finish(); +} + +float64 AbstractCheckView::currentAnimationValue(TimeMs ms) { + return ms ? _toggleAnimation.current(ms, _checked ? 1. : 0.) : _toggleAnimation.current(_checked ? 1. : 0.); +} + +ToggleView::ToggleView(const style::Toggle &st, bool checked, base::lambda updateCallback) : AbstractCheckView(st.duration, checked, std::move(updateCallback)) +, _st(&st) { +} + +QSize ToggleView::getSize() { + return QSize(_st->diameter + _st->width, _st->diameter); +} + +void ToggleView::setStyle(const style::Toggle &st) { + _st = &st; +} + +void ToggleView::paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) { + PainterHighQualityEnabler hq(p); + auto toggled = currentAnimationValue(ms); + auto fullWidth = _st->diameter + _st->width; + auto innerDiameter = _st->diameter - 2 * _st->shift; + auto innerRadius = float64(innerDiameter) / 2.; + auto bgRect = rtlrect(left + _st->shift, top + _st->shift, fullWidth - 2 * _st->shift, innerDiameter, outerWidth); + auto fgRect = rtlrect(left + anim::interpolate(0, fullWidth - _st->diameter, toggled), top, _st->diameter, _st->diameter, outerWidth); + + p.setPen(Qt::NoPen); + p.setBrush(anim::brush(_st->untoggledFg, _st->toggledFg, toggled)); + p.drawRoundedRect(bgRect, innerRadius, innerRadius); + + auto pen = anim::pen(_st->untoggledFg, _st->toggledFg, toggled); + pen.setWidth(_st->border); + p.setPen(pen); + p.setBrush(anim::brush(_st->untoggledBg, _st->toggledBg, toggled)); + p.drawEllipse(fgRect); +} + +CheckView::CheckView(const style::Check &st, bool checked, base::lambda updateCallback) : AbstractCheckView(st.duration, checked, std::move(updateCallback)) +, _st(&st) { +} + +QSize CheckView::getSize() { + return QSize(_st->diameter, _st->diameter); +} + +void CheckView::setStyle(const style::Check &st) { + _st = &st; +} + +void CheckView::paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) { + auto toggled = currentAnimationValue(ms); + auto pen = anim::pen(_st->untoggledFg, _st->toggledFg, toggled); + pen.setWidth(_st->thickness); + p.setPen(pen); + p.setBrush(anim::brush(_st->bg, anim::color(_st->untoggledFg, _st->toggledFg, toggled), toggled)); + + { + PainterHighQualityEnabler hq(p); + p.drawRoundedRect(rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(_st->thickness / 2., _st->thickness / 2., _st->thickness / 2., _st->thickness / 2.)), outerWidth), st::buttonRadius - (_st->thickness / 2.), st::buttonRadius - (_st->thickness / 2.)); + } + + if (toggled > 0) { + _st->icon.paint(p, QPoint(left, top), outerWidth); + } +} + +RadioView::RadioView(const style::Radio &st, bool checked, base::lambda updateCallback) : AbstractCheckView(st.duration, checked, std::move(updateCallback)) +, _st(&st) { +} + +QSize RadioView::getSize() { + return QSize(_st->diameter, _st->diameter); +} + +void RadioView::setStyle(const style::Radio &st) { + _st = &st; +} + +void RadioView::paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) { + PainterHighQualityEnabler hq(p); + + auto toggled = currentAnimationValue(ms); + auto pen = anim::pen(_st->untoggledFg, _st->toggledFg, toggled); + pen.setWidth(_st->thickness); + p.setPen(pen); + p.setBrush(_st->bg); + //int32 skip = qCeil(_st->thickness / 2.); + //p.drawEllipse(_checkRect.marginsRemoved(QMargins(skip, skip, skip, skip))); + p.drawEllipse(rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(_st->thickness / 2., _st->thickness / 2., _st->thickness / 2., _st->thickness / 2.)), outerWidth)); + + if (toggled > 0) { + p.setPen(Qt::NoPen); + p.setBrush(anim::brush(_st->untoggledFg, _st->toggledFg, toggled)); + + auto skip0 = _st->diameter / 2., skip1 = _st->skip / 10., checkSkip = skip0 * (1. - toggled) + skip1 * toggled; + p.drawEllipse(rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(checkSkip, checkSkip, checkSkip, checkSkip)), outerWidth)); + //int32 fskip = qFloor(checkSkip), cskip = qCeil(checkSkip); + //if (2 * fskip < _checkRect.width()) { + // if (fskip != cskip) { + // p.setOpacity(float64(cskip) - checkSkip); + // p.drawEllipse(_checkRect.marginsRemoved(QMargins(fskip, fskip, fskip, fskip))); + // p.setOpacity(1.); + // } + // if (2 * cskip < _checkRect.width()) { + // p.drawEllipse(_checkRect.marginsRemoved(QMargins(cskip, cskip, cskip, cskip))); + // } + //} + } +} + +Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st, const style::Check &checkSt) : Checkbox(parent, text, st, std::make_unique(checkSt, checked, [this] { updateCheck(); })) { +} + +Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st, const style::Toggle &toggleSt) : Checkbox(parent, text, st, std::make_unique(toggleSt, checked, [this] { updateCheck(); })) { +} + +Checkbox::Checkbox(QWidget *parent, const QString &text, const style::Checkbox &st, std::unique_ptr check) : RippleButton(parent, st.ripple) , _st(st) -, _text(_st.style, text, _checkboxOptions) -, _checked(checked) { +, _check(std::move(check)) +, _text(_st.style, text, _checkboxOptions) { resizeToText(); - - connect(this, SIGNAL(clicked()), this, SLOT(onClicked())); - setCursor(style::cur_pointer); } @@ -53,7 +197,7 @@ void Checkbox::setText(const QString &text) { } bool Checkbox::checked() const { - return _checked; + return _check->checked(); } void Checkbox::resizeToText() { @@ -62,21 +206,20 @@ void Checkbox::resizeToText() { } else { resizeToWidth(_st.width); } - _checkRect = myrtlrect(_st.margin.left(), _st.margin.top(), _st.diameter, _st.diameter); + _checkRect = { QPoint(_st.margin.left(), _st.margin.top()), _check->getSize() }; } void Checkbox::setChecked(bool checked, NotifyAboutChange notify) { - if (_checked != checked) { - _checked = checked; - _a_checked.start([this] { update(_checkRect); }, _checked ? 0. : 1., _checked ? 1. : 0., _st.duration); + if (_check->checked() != checked) { + _check->setCheckedAnimated(checked); if (notify == NotifyAboutChange::Notify) { - emit changed(); + checkedChanged.notify(checked, true); } } } void Checkbox::finishAnimations() { - _a_checked.finish(); + _check->finishAnimation(); } int Checkbox::naturalWidth() const { @@ -87,38 +230,22 @@ void Checkbox::paintEvent(QPaintEvent *e) { Painter p(this); auto ms = getms(); - auto active = _a_checked.current(ms, _checked ? 1. : 0.); + auto active = _check->currentAnimationValue(ms); auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active); paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms, &color); - if (_checkRect.intersects(e->rect())) { - auto pen = anim::pen(_st.checkFg, _st.checkFgActive, active); - pen.setWidth(_st.thickness); - p.setPen(pen); - p.setBrush(anim::brush(_st.checkBg, anim::color(_st.checkFg, _st.checkFgActive, active), active)); - - { - PainterHighQualityEnabler hq(p); - p.drawRoundedRect(QRectF(_checkRect).marginsRemoved(QMarginsF(_st.thickness / 2., _st.thickness / 2., _st.thickness / 2., _st.thickness / 2.)), st::buttonRadius - (_st.thickness / 2.), st::buttonRadius - (_st.thickness / 2.)); - } - - if (active > 0) { - _st.checkIcon.paint(p, QPoint(_st.margin.left(), _st.margin.top()), width()); - } + auto realCheckRect = myrtlrect(_checkRect); + if (realCheckRect.intersects(e->rect())) { + _check->paint(p, _checkRect.left(), _checkRect.top(), width()); } - if (_checkRect.contains(e->rect())) return; + if (realCheckRect.contains(e->rect())) return; - auto textWidth = qMax(width() - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1); + auto textWidth = qMax(width() - (_st.textPosition.x() + (_st.textPosition.x() - _check->getSize().width())), 1); p.setPen(_st.textFg); _text.drawLeftElided(p, _st.margin.left() + _st.textPosition.x(), _st.margin.top() + _st.textPosition.y(), textWidth, width()); } -void Checkbox::onClicked() { - if (isDisabled()) return; - setChecked(!checked()); -} - void Checkbox::onStateChanged(State was, StateChangeSource source) { RippleButton::onStateChanged(was, source); @@ -127,6 +254,13 @@ void Checkbox::onStateChanged(State was, StateChangeSource source) { } else if (!isDisabled() && (was & StateFlag::Disabled)) { setCursor(style::cur_pointer); } + + auto now = state(); + if (!isDisabled() && (was & StateFlag::Over) && (now & StateFlag::Over)) { + if ((was & StateFlag::Down) && !(now & StateFlag::Down)) { + setChecked(!checked()); + } + } } int Checkbox::resizeGetHeight(int newWidth) { @@ -159,88 +293,26 @@ void RadiobuttonGroup::setValue(int value) { } } -Radiobutton::Radiobutton(QWidget *parent, const std::shared_ptr &group, int value, const QString &text, const style::Checkbox &st) : RippleButton(parent, st.ripple) +Radiobutton::Radiobutton(QWidget *parent, const std::shared_ptr &group, int value, const QString &text, const style::Checkbox &st, const style::Radio &radioSt) : Checkbox(parent, text, st, std::make_unique(radioSt, (group->hasValue() && group->value() == value), [this] { updateCheck(); })) , _group(group) -, _value(value) -, _st(st) -, _text(_st.style, text, _checkboxOptions) -, _checked(_group->hasValue() && _group->value() == _value) { +, _value(value) { _group->registerButton(this); - - if (_st.width <= 0) { - resizeToWidth(_text.maxWidth() - _st.width); - } else { - resizeToWidth(_st.width); - } - _checkRect = myrtlrect(_st.margin.left(), _st.margin.top(), _st.diameter, _st.diameter); + subscribe(checkbox()->checkedChanged, [this](bool checked) { + if (checked) { + _group->setValue(_value); + } + }); } void Radiobutton::handleNewGroupValue(int value) { auto checked = (value == _value); - if (_checked != checked) { - _checked = checked; - _a_checked.start([this] { update(_checkRect); }, _checked ? 0. : 1., _checked ? 1. : 0., _st.duration); + if (checkbox()->checked() != checked) { + checkbox()->setChecked(checked, Ui::Checkbox::NotifyAboutChange::DontNotify); } } -int Radiobutton::naturalWidth() const { - return _st.textPosition.x() + _text.maxWidth(); -} - -void Radiobutton::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto ms = getms(); - auto active = _a_checked.current(ms, _checked ? 1. : 0.); - auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active); - paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms, &color); - - if (_checkRect.intersects(e->rect())) { - PainterHighQualityEnabler hq(p); - - auto pen = anim::pen(_st.checkFg, _st.checkFgActive, active); - pen.setWidth(_st.thickness); - p.setPen(pen); - p.setBrush(_st.checkBg); - //int32 skip = qCeil(_st.thickness / 2.); - //p.drawEllipse(_checkRect.marginsRemoved(QMargins(skip, skip, skip, skip))); - p.drawEllipse(QRectF(_checkRect).marginsRemoved(QMarginsF(_st.thickness / 2., _st.thickness / 2., _st.thickness / 2., _st.thickness / 2.))); - - if (active > 0) { - p.setPen(Qt::NoPen); - p.setBrush(anim::brush(_st.checkFg, _st.checkFgActive, active)); - - auto skip0 = _checkRect.width() / 2., skip1 = _st.radioSkip / 10., checkSkip = skip0 * (1. - active) + skip1 * active; - p.drawEllipse(QRectF(_checkRect).marginsRemoved(QMarginsF(checkSkip, checkSkip, checkSkip, checkSkip))); - //int32 fskip = qFloor(checkSkip), cskip = qCeil(checkSkip); - //if (2 * fskip < _checkRect.width()) { - // if (fskip != cskip) { - // p.setOpacity(float64(cskip) - checkSkip); - // p.drawEllipse(_checkRect.marginsRemoved(QMargins(fskip, fskip, fskip, fskip))); - // p.setOpacity(1.); - // } - // if (2 * cskip < _checkRect.width()) { - // p.drawEllipse(_checkRect.marginsRemoved(QMargins(cskip, cskip, cskip, cskip))); - // } - //} - } - } - if (_checkRect.contains(e->rect())) return; - - auto textWidth = qMax(width() - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1); - - p.setPen(_st.textFg); - _text.drawLeftElided(p, _st.margin.left() + _st.textPosition.x(), _st.margin.top() + _st.textPosition.y(), textWidth, width()); -} - void Radiobutton::onStateChanged(State was, StateChangeSource source) { - RippleButton::onStateChanged(was, source); - - if (isDisabled() && !(was & StateFlag::Disabled)) { - setCursor(style::cur_default); - } else if (!isDisabled() && (was & StateFlag::Disabled)) { - setCursor(style::cur_pointer); - } + Checkbox::onStateChanged(was, source); auto now = state(); if (!isDisabled() && (was & StateFlag::Over) && (now & StateFlag::Over)) { @@ -250,80 +322,8 @@ void Radiobutton::onStateChanged(State was, StateChangeSource source) { } } -int Radiobutton::resizeGetHeight(int newWidth) { - return _st.height; -} - -QImage Radiobutton::prepareRippleMask() const { - return RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize)); -} - -QPoint Radiobutton::prepareRippleStartPosition() const { - auto position = mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition; - if (QRect(0, 0, _st.rippleAreaSize, _st.rippleAreaSize).contains(position)) { - return position; - } - return disabledRippleStartPosition(); -} - Radiobutton::~Radiobutton() { _group->unregisterButton(this); } -ToggleView::ToggleView(const style::Toggle &st, bool toggled, base::lambda updateCallback) -: _st(&st) -, _toggled(toggled) -, _updateCallback(std::move(updateCallback)) { -} - -void ToggleView::setStyle(const style::Toggle &st) { - _st = &st; -} - -void ToggleView::setToggledFast(bool toggled) { - _toggled = toggled; - finishAnimation(); - if (_updateCallback) { - _updateCallback(); - } -} - -void ToggleView::setUpdateCallback(base::lambda updateCallback) { - _updateCallback = std::move(updateCallback); - if (_toggleAnimation.animating()) { - _toggleAnimation.setUpdateCallback(_updateCallback); - } -} - -void ToggleView::setToggledAnimated(bool toggled) { - if (_toggled != toggled) { - _toggled = toggled; - _toggleAnimation.start(_updateCallback, _toggled ? 0. : 1., _toggled ? 1. : 0., _st->duration); - } -} - -void ToggleView::finishAnimation() { - _toggleAnimation.finish(); -} - -void ToggleView::paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) { - PainterHighQualityEnabler hq(p); - auto toggled = _toggleAnimation.current(ms, _toggled ? 1. : 0.); - auto fullWidth = _st->diameter + _st->width; - auto innerDiameter = _st->diameter - 2 * _st->shift; - auto innerRadius = float64(innerDiameter) / 2.; - auto bgRect = rtlrect(left + _st->shift, top + _st->shift, fullWidth - 2 * _st->shift, innerDiameter, outerWidth); - auto fgRect = rtlrect(left + anim::interpolate(0, fullWidth - _st->diameter, toggled), top, _st->diameter, _st->diameter, outerWidth); - - p.setPen(Qt::NoPen); - p.setBrush(anim::brush(_st->untoggledFg, _st->toggledFg, toggled)); - p.drawRoundedRect(bgRect, innerRadius, innerRadius); - - auto pen = anim::pen(_st->untoggledFg, _st->toggledFg, toggled); - pen.setWidth(_st->border); - p.setPen(pen); - p.setBrush(anim::brush(_st->untoggledBg, _st->toggledBg, toggled)); - p.drawEllipse(fgRect); -} - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.h b/Telegram/SourceFiles/ui/widgets/checkbox.h index e140e31c1..2465bc299 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.h +++ b/Telegram/SourceFiles/ui/widgets/checkbox.h @@ -25,11 +25,87 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Ui { -class Checkbox : public RippleButton { - Q_OBJECT - +class AbstractCheckView { public: - Checkbox(QWidget *parent, const QString &text, bool checked = false, const style::Checkbox &st = st::defaultCheckbox); + AbstractCheckView(int duration, bool checked, base::lambda updateCallback); + + void setCheckedFast(bool checked); + void setCheckedAnimated(bool checked); + void finishAnimation(); + void setUpdateCallback(base::lambda updateCallback); + bool checked() const { + return _checked; + } + float64 currentAnimationValue(TimeMs ms); + + virtual QSize getSize() = 0; + + // Zero instead of ms value means that animation was already updated for this time. + // It can be passed to currentAnimationValue() safely. + virtual void paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) = 0; + + void paint(Painter &p, int left, int top, int outerWidth) { + // Pass zero in ms if the animation was already updated for this time. + paint(p, left, top, outerWidth, 0); + } + + virtual ~AbstractCheckView() = default; + +private: + int _duration = 0; + bool _checked = false; + base::lambda _updateCallback; + Animation _toggleAnimation; + +}; + +class CheckView : public AbstractCheckView { +public: + CheckView(const style::Check &st, bool checked, base::lambda updateCallback); + + void setStyle(const style::Check &st); + + QSize getSize() override; + void paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) override; + +private: + gsl::not_null _st; + +}; + +class RadioView : public AbstractCheckView { +public: + RadioView(const style::Radio &st, bool checked, base::lambda updateCallback); + + void setStyle(const style::Radio &st); + + QSize getSize() override; + void paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) override; + +private: + gsl::not_null _st; + +}; + +class ToggleView : public AbstractCheckView { +public: + ToggleView(const style::Toggle &st, bool checked, base::lambda updateCallback); + + void setStyle(const style::Toggle &st); + + QSize getSize() override; + void paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) override; + +private: + gsl::not_null _st; + +}; + +class Checkbox : public RippleButton { +public: + Checkbox(QWidget *parent, const QString &text, bool checked = false, const style::Checkbox &st = st::defaultCheckbox, const style::Check &checkSt = st::defaultCheck); + Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st, const style::Toggle &toggleSt); + Checkbox(QWidget *parent, const QString &text, const style::Checkbox &st, std::unique_ptr check); void setText(const QString &text); @@ -39,6 +115,7 @@ public: DontNotify, }; void setChecked(bool checked, NotifyAboutChange notify = NotifyAboutChange::Notify); + base::Observable checkedChanged; void finishAnimations(); @@ -56,23 +133,19 @@ protected: QImage prepareRippleMask() const override; QPoint prepareRippleStartPosition() const override; -public slots: - void onClicked(); - -signals: - void changed(); + void updateCheck() { + rtlupdate(_checkRect); + } private: void resizeToText(); const style::Checkbox &_st; + std::unique_ptr _check; Text _text; QRect _checkRect; - bool _checked; - Animation _a_checked; - }; class Radiobutton; @@ -113,41 +186,32 @@ private: }; -class Radiobutton : public RippleButton { +class Radiobutton : public Checkbox, private base::Subscriber { public: - Radiobutton(QWidget *parent, const std::shared_ptr &group, int value, const QString &text, const style::Checkbox &st = st::defaultCheckbox); - - QMargins getMargins() const override { - return _st.margin; - } - int naturalWidth() const override; - + Radiobutton(QWidget *parent, const std::shared_ptr &group, int value, const QString &text, const style::Checkbox &st = st::defaultCheckbox, const style::Radio &radioSt = st::defaultRadio); ~Radiobutton(); protected: - void paintEvent(QPaintEvent *e) override; - void onStateChanged(State was, StateChangeSource source) override; - int resizeGetHeight(int newWidth) override; - - QImage prepareRippleMask() const override; - QPoint prepareRippleStartPosition() const override; private: + // Hide the names from Checkbox. + bool checked() const; + void setChecked(bool checked, NotifyAboutChange notify); + void checkedChanged(); + Checkbox *checkbox() { + return this; + } + const Checkbox *checkbox() const { + return this; + } + friend class RadiobuttonGroup; void handleNewGroupValue(int value); std::shared_ptr _group; int _value = 0; - const style::Checkbox &_st; - - Text _text; - QRect _checkRect; - - bool _checked = false; - Animation _a_checked; - }; template @@ -194,24 +258,4 @@ public: }; -class ToggleView { -public: - ToggleView(const style::Toggle &st, bool toggled, base::lambda updateCallback); - - void setToggledFast(bool toggled); - void setToggledAnimated(bool toggled); - void finishAnimation(); - void setStyle(const style::Toggle &st); - void setUpdateCallback(base::lambda updateCallback); - - void paint(Painter &p, int left, int top, int outerWidth, TimeMs ms); - -private: - gsl::not_null _st; - bool _toggled = false; - base::lambda _updateCallback; - Animation _toggleAnimation; - -}; - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/menu.cpp b/Telegram/SourceFiles/ui/widgets/menu.cpp index 4b74e0614..b67ac2683 100644 --- a/Telegram/SourceFiles/ui/widgets/menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/menu.cpp @@ -140,13 +140,13 @@ int Menu::processAction(QAction *action, int index, int width) { } if (action->isCheckable()) { auto updateCallback = [this, index] { updateItem(index); }; - goodw += _st.itemPadding.left() + _st.itemToggle.diameter + _st.itemToggle.width - _st.itemToggleShift; if (data.toggle) { data.toggle->setUpdateCallback(updateCallback); - data.toggle->setToggledAnimated(action->isChecked()); + data.toggle->setCheckedAnimated(action->isChecked()); } else { data.toggle = std::make_unique(_st.itemToggle, action->isChecked(), updateCallback); } + goodw += _st.itemPadding.left() + data.toggle->getSize().width() - _st.itemToggleShift; } else { data.toggle.reset(); } @@ -231,8 +231,8 @@ void Menu::paintEvent(QPaintEvent *e) { p.setPen(selected ? _st.itemFgShortcutOver : (enabled ? _st.itemFgShortcut : _st.itemFgShortcutDisabled)); p.drawTextRight(_st.itemPadding.right(), _st.itemPadding.top(), width(), data.shortcut); } else if (data.toggle) { - auto toggleSize = _st.itemToggle.diameter + _st.itemToggle.width; - data.toggle->paint(p, width() - _st.itemPadding.right() - toggleSize + _st.itemToggleShift, (_itemHeight - _st.itemToggle.diameter) / 2, width(), ms); + auto toggleSize = data.toggle->getSize(); + data.toggle->paint(p, width() - _st.itemPadding.right() - toggleSize.width() + _st.itemToggleShift, (_itemHeight - toggleSize.height()) / 2, width(), ms); } } } diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index bb5fa69c4..a10626410 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -92,25 +92,48 @@ RoundButton { ripple: RippleAnimation; } +Toggle { + toggledBg: color; + toggledFg: color; + untoggledBg: color; + untoggledFg: color; + duration: int; + border: pixels; + shift: pixels; + diameter: pixels; + width: pixels; +} + +Check { + bg: color; + untoggledFg: color; + toggledFg: color; + diameter: pixels; + thickness: pixels; + icon: icon; + duration: int; +} + +Radio { + bg: color; + untoggledFg: color; + toggledFg: color; + diameter: pixels; + thickness: pixels; + skip: pixels; + duration: int; +} + Checkbox { textFg: color; - checkBg: color; - checkFg: color; - checkFgActive: color; - width: pixels; height: pixels; margin: margins; textPosition: point; - diameter: pixels; - thickness: pixels; - radioSkip: pixels; - checkIcon: icon; style: TextStyle; - duration: int; rippleAreaPosition: point; rippleAreaSize: pixels; @@ -360,18 +383,6 @@ CallButton { outerBg: color; } -Toggle { - toggledBg: color; - toggledFg: color; - untoggledBg: color; - untoggledFg: color; - duration: int; - border: pixels; - shift: pixels; - diameter: pixels; - width: pixels; -} - Menu { skip: pixels; @@ -660,25 +671,45 @@ defaultInputField: InputField { defaultCheckboxIcon: icon {{ "default_checkbox_check", windowFgActive, point(4px, 7px) }}; +defaultCheck: Check { + bg: transparent; + untoggledFg: checkboxFg; + toggledFg: windowBgActive; + diameter: 22px; + thickness: 2px; + icon: defaultCheckboxIcon; + duration: 120; +} +defaultRadio: Radio { + bg: transparent; + untoggledFg: checkboxFg; + toggledFg: windowBgActive; + diameter: 22px; + thickness: 2px; + skip: 65px; // * 0.1 + duration: 120; +} +defaultToggle: Toggle { + toggledBg: windowBg; + toggledFg: windowBgActive; + untoggledBg: windowBg; + untoggledFg: checkboxFg; + duration: 120; + border: 2px; + shift: 1px; + diameter: 20px; + width: 16px; +} defaultCheckbox: Checkbox { textFg: windowFg; - checkBg: transparent; - checkFg: checkboxFg; - checkFgActive: windowBgActive; - width: -44px; height: 22px; margin: margins(8px, 8px, 8px, 8px); textPosition: point(32px, 2px); - diameter: 22px; - thickness: 2px; - checkIcon: defaultCheckboxIcon; - radioSkip: 65px; // * 0.1 style: defaultTextStyle; - duration: 120; rippleAreaSize: 38px; rippleAreaPosition: point(0px, 0px); @@ -781,18 +812,6 @@ defaultRoundCheckbox: RoundCheckbox { duration: 150; } -defaultToggle: Toggle { - toggledBg: windowBg; - toggledFg: windowBgActive; - untoggledBg: windowBg; - untoggledFg: checkboxFg; - duration: 200; - border: 2px; - shift: 1px; - diameter: 20px; - width: 16px; -} - defaultMenuArrow: icon {{ "dropdown_submenu_arrow", menuSubmenuArrowFg }}; defaultMenuToggle: Toggle(defaultToggle) { untoggledFg: menuIconFg;