Generalize Checkbox layout.

Now any Checkbox can have Check, Radio or Toggle layout.
Radiobutton is now a subclass of Checkbox with default Radio layout.
This commit is contained in:
John Preston 2017-07-07 14:16:37 +03:00
parent 0ecef54e2b
commit 21d2f6a44f
18 changed files with 408 additions and 350 deletions

View File

@ -630,7 +630,7 @@ ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent)
void ContactsBox::Inner::init() { void ContactsBox::Inner::init() {
subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); });
connect(_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); 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; _rowsTop = st::contactsMarginTop;
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);

View File

@ -196,9 +196,9 @@ void EditAdminBox::prepare() {
} }
auto checked = (prepareRights.c_channelAdminRights().vflags.v & flags) != 0; auto checked = (prepareRights.c_channelAdminRights().vflags.v & flags) != 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::defaultBoxCheckbox)); auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::defaultBoxCheckbox));
connect(control, &Ui::Checkbox::changed, this, [this, control] { subscribe(control->checkedChanged, [this, control](bool checked) {
applyDependencies(control); InvokeQueued(this, [this, control] { applyDependencies(control); });
}, Qt::QueuedConnection); });
_checkboxes.emplace(flags, control); _checkboxes.emplace(flags, control);
}; };
if (channel()->isMegagroup()) { if (channel()->isMegagroup()) {
@ -221,7 +221,7 @@ void EditAdminBox::prepare() {
if (addAdmins != _checkboxes.end()) { if (addAdmins != _checkboxes.end()) {
_aboutAddAdmins = addControl(object_ptr<Ui::FlatLabel>(this, st::boxLabel)); _aboutAddAdmins = addControl(object_ptr<Ui::FlatLabel>(this, st::boxLabel));
t_assert(addAdmins != _checkboxes.end()); t_assert(addAdmins != _checkboxes.end());
connect(addAdmins->second, &Ui::Checkbox::changed, this, [this] { subscribe(addAdmins->second->checkedChanged, [this](bool checked) {
refreshAboutAddAdminsText(); refreshAboutAddAdminsText();
}); });
refreshAboutAddAdminsText(); refreshAboutAddAdminsText();
@ -298,9 +298,9 @@ void EditRestrictedBox::prepare() {
auto addCheckbox = [this, &prepareRights](Flags flags, const QString &text) { auto addCheckbox = [this, &prepareRights](Flags flags, const QString &text) {
auto checked = (prepareRights.c_channelBannedRights().vflags.v & flags) == 0; auto checked = (prepareRights.c_channelBannedRights().vflags.v & flags) == 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::defaultBoxCheckbox)); auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::defaultBoxCheckbox));
connect(control, &Ui::Checkbox::changed, this, [this, control] { subscribe(control->checkedChanged, [this, control](bool checked) {
applyDependencies(control); InvokeQueued(this, [this, control] { applyDependencies(control); });
}, Qt::QueuedConnection); });
_checkboxes.emplace(flags, control); _checkboxes.emplace(flags, control);
}; };
addCheckbox(Flag::f_view_messages, lang(lng_rights_chat_read)); addCheckbox(Flag::f_view_messages, lang(lng_rights_chat_read));

View File

@ -244,7 +244,7 @@ void SendFilesBox::prepare() {
auto compressed = (_compressConfirm == CompressConfirm::Auto) ? cCompressPastedImage() : (_compressConfirm == CompressConfirm::Yes); auto compressed = (_compressConfirm == CompressConfirm::Auto) ? cCompressPastedImage() : (_compressConfirm == CompressConfirm::Yes);
auto text = lng_send_images_compress(lt_count, _files.size()); auto text = lng_send_images_compress(lt_count, _files.size());
_compressed.create(this, text, compressed, st::defaultBoxCheckbox); _compressed.create(this, text, compressed, st::defaultBoxCheckbox);
connect(_compressed, SIGNAL(changed()), this, SLOT(onCompressedChange())); subscribe(_compressed->checkedChanged, [this](bool checked) { onCompressedChange(); });
} }
if (_caption) { if (_caption) {
_caption->setMaxLength(MaxPhotoCaption); _caption->setMaxLength(MaxPhotoCaption);

View File

@ -30,11 +30,13 @@ namespace {
class UserCheckbox : public Ui::RippleButton { class UserCheckbox : public Ui::RippleButton {
public: public:
UserCheckbox(QWidget *parent, gsl::not_null<UserData*> user, bool checked, base::lambda<void()> changedCallback); UserCheckbox(QWidget *parent, gsl::not_null<UserData*> user, bool checked);
bool checked() const { bool checked() const {
return _checked; return _check->checked();
} }
base::Observable<bool> checkedChanged;
enum class NotifyAboutChange { enum class NotifyAboutChange {
Notify, Notify,
DontNotify, DontNotify,
@ -57,24 +59,20 @@ protected:
private: private:
const style::Checkbox &_st; const style::Checkbox &_st;
std::unique_ptr<Ui::AbstractCheckView> _check;
QRect _checkRect; QRect _checkRect;
bool _checked = false;
Animation _a_checked;
gsl::not_null<UserData*> _user; gsl::not_null<UserData*> _user;
base::lambda<void()> _changedCallback;
QString _statusText; QString _statusText;
bool _statusOnline = false; bool _statusOnline = false;
}; };
UserCheckbox::UserCheckbox(QWidget *parent, gsl::not_null<UserData*> user, bool checked, base::lambda<void()> changedCallback) : Ui::RippleButton(parent, st::defaultBoxCheckbox.ripple) UserCheckbox::UserCheckbox(QWidget *parent, gsl::not_null<UserData*> user, bool checked) : Ui::RippleButton(parent, st::defaultBoxCheckbox.ripple)
, _st(st::adminLogFilterUserCheckbox) , _st(st::adminLogFilterUserCheckbox)
, _checked(checked) , _check(std::make_unique<Ui::CheckView>(st::defaultCheck, checked, [this] { rtlupdate(_checkRect); }))
, _user(user) , _user(user) {
, _changedCallback(std::move(changedCallback)) {
setCursor(style::cur_pointer); setCursor(style::cur_pointer);
setClickedCallback([this] { setClickedCallback([this] {
if (isDisabled()) return; if (isDisabled()) return;
@ -83,15 +81,15 @@ UserCheckbox::UserCheckbox(QWidget *parent, gsl::not_null<UserData*> user, bool
auto now = unixtime(); auto now = unixtime();
_statusText = App::onlineText(_user, now); _statusText = App::onlineText(_user, now);
_statusOnline = App::onlineColorUse(_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) { void UserCheckbox::setChecked(bool checked, NotifyAboutChange notify) {
if (_checked != checked) { if (_check->checked() != checked) {
_checked = checked; _check->setCheckedAnimated(checked);
_a_checked.start([this] { update(_checkRect); }, _checked ? 0. : 1., _checked ? 1. : 0., _st.duration); if (notify == NotifyAboutChange::Notify) {
if (notify == NotifyAboutChange::Notify && _changedCallback) { checkedChanged.notify(checked, true);
_changedCallback();
} }
} }
} }
@ -100,25 +98,15 @@ void UserCheckbox::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
auto ms = getms(); 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); 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); paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y() + (_checkRect.y() - st::defaultBoxCheckbox.margin.top()), ms, &color);
if (_checkRect.intersects(e->rect())) { auto realCheckRect = myrtlrect(_checkRect);
auto pen = anim::pen(_st.checkFg, _st.checkFgActive, active); if (realCheckRect.intersects(e->rect())) {
pen.setWidth(_st.thickness); _check->paint(p, _checkRect.left(), _checkRect.top(), width());
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());
}
} }
if (realCheckRect.contains(e->rect())) return;
auto userpicLeft = _checkRect.x() + _checkRect.width() + st::adminLogFilterUserpicLeft; auto userpicLeft = _checkRect.x() + _checkRect.width() + st::adminLogFilterUserpicLeft;
auto userpicTop = 0; auto userpicTop = 0;
@ -138,7 +126,7 @@ void UserCheckbox::paintEvent(QPaintEvent *e) {
} }
void UserCheckbox::finishAnimations() { void UserCheckbox::finishAnimations() {
_a_checked.finish(); _check->finishAnimation();
} }
int UserCheckbox::resizeGetHeight(int newWidth) { int UserCheckbox::resizeGetHeight(int newWidth) {
@ -159,7 +147,7 @@ QPoint UserCheckbox::prepareRippleStartPosition() const {
} // namespace } // namespace
class FilterBox::Inner : public TWidget { class FilterBox::Inner : public TWidget, private base::Subscriber {
public: public:
Inner(QWidget *parent, gsl::not_null<ChannelData*> channel, const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter, base::lambda<void()> changedCallback); Inner(QWidget *parent, gsl::not_null<ChannelData*> channel, const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter, base::lambda<void()> changedCallback);
@ -223,7 +211,7 @@ void FilterBox::Inner::createControls(const std::vector<gsl::not_null<UserData*>
void FilterBox::Inner::createAllActionsCheckbox(const FilterValue &filter) { void FilterBox::Inner::createAllActionsCheckbox(const FilterValue &filter) {
auto checked = (filter.flags == 0); auto checked = (filter.flags == 0);
_allFlags = addRow(object_ptr<Ui::Checkbox>(this, lang(lng_admin_log_filter_all_actions), checked, st::adminLogFilterCheckbox), st::adminLogFilterCheckbox.margin.top()); _allFlags = addRow(object_ptr<Ui::Checkbox>(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)) { if (!std::exchange(_restoringInvariant, true)) {
auto allChecked = _allFlags->checked(); auto allChecked = _allFlags->checked();
for_const (auto &&checkbox, _filterFlags) { 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 checked = (filter.flags == 0) || (filter.flags & flag);
auto checkbox = addRow(object_ptr<Ui::Checkbox>(this, std::move(text), checked, st::defaultBoxCheckbox), st::adminLogFilterLittleSkip); auto checkbox = addRow(object_ptr<Ui::Checkbox>(this, std::move(text), checked, st::defaultBoxCheckbox), st::adminLogFilterLittleSkip);
_filterFlags.insert(flag, checkbox); _filterFlags.insert(flag, checkbox);
connect(checkbox, &Ui::Checkbox::changed, this, [this] { subscribe(checkbox->checkedChanged, [this](bool checked) {
if (!std::exchange(_restoringInvariant, true)) { if (!std::exchange(_restoringInvariant, true)) {
auto allChecked = true; auto allChecked = true;
for_const (auto &&checkbox, _filterFlags) { for_const (auto &&checkbox, _filterFlags) {
@ -278,8 +266,8 @@ void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) {
void FilterBox::Inner::createAllUsersCheckbox(const FilterValue &filter) { void FilterBox::Inner::createAllUsersCheckbox(const FilterValue &filter) {
_allUsers = addRow(object_ptr<Ui::Checkbox>(this, lang(lng_admin_log_filter_all_admins), filter.allUsers, st::adminLogFilterCheckbox), st::adminLogFilterSkip); _allUsers = addRow(object_ptr<Ui::Checkbox>(this, lang(lng_admin_log_filter_all_admins), filter.allUsers, st::adminLogFilterCheckbox), st::adminLogFilterSkip);
connect(_allUsers, &Ui::Checkbox::changed, this, [this] { subscribe(_allUsers->checkedChanged, [this](bool checked) {
if (_allUsers->checked() && !std::exchange(_restoringInvariant, true)) { if (checked && !std::exchange(_restoringInvariant, true)) {
for_const (auto &&checkbox, _admins) { for_const (auto &&checkbox, _admins) {
checkbox->setChecked(true); checkbox->setChecked(true);
} }
@ -294,7 +282,8 @@ void FilterBox::Inner::createAllUsersCheckbox(const FilterValue &filter) {
void FilterBox::Inner::createAdminsCheckboxes(const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter) { void FilterBox::Inner::createAdminsCheckboxes(const std::vector<gsl::not_null<UserData*>> &admins, const FilterValue &filter) {
for (auto user : admins) { for (auto user : admins) {
auto checked = filter.allUsers || base::contains(filter.admins, user); auto checked = filter.allUsers || base::contains(filter.admins, user);
auto checkbox = addRow(object_ptr<UserCheckbox>(this, user, checked, [this] { auto checkbox = addRow(object_ptr<UserCheckbox>(this, user, checked), st::adminLogFilterLittleSkip);
subscribe(checkbox->checkedChanged, [this](bool checked) {
if (!std::exchange(_restoringInvariant, true)) { if (!std::exchange(_restoringInvariant, true)) {
auto allChecked = true; auto allChecked = true;
for_const (auto &&checkbox, _admins) { for_const (auto &&checkbox, _admins) {
@ -311,7 +300,7 @@ void FilterBox::Inner::createAdminsCheckboxes(const std::vector<gsl::not_null<Us
_changedCallback(); _changedCallback();
} }
} }
}), st::adminLogFilterLittleSkip); });
_admins.insert(user, checkbox); _admins.insert(user, checkbox);
} }
} }

View File

@ -858,9 +858,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con
} }
} }
if (selected || context->selecting) { if (selected || context->selecting) {
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); 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);
} }
} }
} }

View File

@ -39,7 +39,7 @@ using UpdateFlag = Notify::PeerUpdate::Flag;
SettingsWidget::SettingsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_settings_section)) SettingsWidget::SettingsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_settings_section))
, _enableNotifications(this, lang(lng_profile_enable_notifications), true, st::defaultCheckbox) { , _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; Notify::PeerUpdate::Flags observeEvents = UpdateFlag::NotificationsEnabled;
if (auto chat = peer->asChat()) { if (auto chat = peer->asChat()) {

View File

@ -222,8 +222,8 @@ void BackgroundWidget::createControls() {
connect(_background, SIGNAL(editTheme()), this, SLOT(onEditTheme())); connect(_background, SIGNAL(editTheme()), this, SLOT(onEditTheme()));
connect(_background, SIGNAL(useDefault()), this, SLOT(onUseDefaultTheme())); connect(_background, SIGNAL(useDefault()), this, SLOT(onUseDefaultTheme()));
addChildRow(_tile, margin, lang(lng_settings_bg_tile), SLOT(onTile()), Window::Theme::Background()->tile()); addChildRow(_tile, margin, lang(lng_settings_bg_tile), [this](bool) { onTile(); }, Window::Theme::Background()->tile());
addChildRow(_adaptive, margin, slidedPadding, lang(lng_settings_adaptive_wide), SLOT(onAdaptive()), Global::AdaptiveForWide()); addChildRow(_adaptive, margin, slidedPadding, lang(lng_settings_adaptive_wide), [this](bool) { onAdaptive(); }, Global::AdaptiveForWide());
if (Global::AdaptiveChatLayout() != Adaptive::ChatLayout::Wide) { if (Global::AdaptiveChatLayout() != Adaptive::ChatLayout::Wide) {
_adaptive->hideFast(); _adaptive->hideFast();
} }

View File

@ -85,9 +85,9 @@ void BlockWidget::rowHeightUpdated() {
} }
} }
void BlockWidget::createChildRow(object_ptr<Ui::Checkbox> &child, style::margins &margin, const QString &text, const char *slot, bool checked) { void BlockWidget::createChildRow(object_ptr<Ui::Checkbox> &child, style::margins &margin, const QString &text, base::lambda<void(bool checked)> callback, bool checked) {
child.create(this, text, checked, st::defaultBoxCheckbox); child.create(this, text, checked, st::defaultBoxCheckbox);
connect(child, SIGNAL(changed()), this, slot); subscribe(child->checkedChanged, std::move(callback));
} }
void BlockWidget::createChildRow(object_ptr<Ui::LinkButton> &child, style::margins &margin, const QString &text, const char *slot, const style::LinkButton &st) { void BlockWidget::createChildRow(object_ptr<Ui::LinkButton> &child, style::margins &margin, const QString &text, const char *slot, const style::LinkButton &st) {

View File

@ -91,7 +91,7 @@ private:
margin.setRight(margin.right() - padding.right()); margin.setRight(margin.right() - padding.right());
margin.setBottom(margin.bottom() - padding.bottom()); margin.setBottom(margin.bottom() - padding.bottom());
} }
void createChildRow(object_ptr<Ui::Checkbox> &child, style::margins &margin, const QString &text, const char *slot, bool checked); void createChildRow(object_ptr<Ui::Checkbox> &child, style::margins &margin, const QString &text, base::lambda<void(bool checked)> callback, bool checked);
void createChildRow(object_ptr<Ui::LinkButton> &child, style::margins &margin, const QString &text, const char *slot, const style::LinkButton &st = st::boxLinkButton); void createChildRow(object_ptr<Ui::LinkButton> &child, style::margins &margin, const QString &text, const char *slot, const style::LinkButton &st = st::boxLinkButton);
template <typename Enum> template <typename Enum>

View File

@ -154,7 +154,7 @@ void ChatSettingsWidget::createControls() {
style::margins marginSub(0, 0, 0, st::settingsSubSkip); style::margins marginSub(0, 0, 0, st::settingsSubSkip);
style::margins slidedPadding(0, marginSub.bottom() / 2, 0, marginSub.bottom() - (marginSub.bottom() / 2)); 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); style::margins marginList(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip);
addChildRow(_viewList, marginList, slidedPadding, lang(lng_settings_view_emojis), SLOT(onViewList()), st::defaultLinkButton); addChildRow(_viewList, marginList, slidedPadding, lang(lng_settings_view_emojis), SLOT(onViewList()), st::defaultLinkButton);
if (!cReplaceEmojis()) { if (!cReplaceEmojis()) {
@ -166,7 +166,7 @@ void ChatSettingsWidget::createControls() {
#else // OS_WIN_STORE #else // OS_WIN_STORE
auto pathMargin = marginSkip; auto pathMargin = marginSkip;
#endif // OS_WIN_STORE #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 #ifndef OS_WIN_STORE
style::margins marginPath(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip); style::margins marginPath(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip);

View File

@ -175,7 +175,7 @@ void GeneralWidget::refreshControls() {
style::margins slidedPadding(0, marginSmall.bottom() / 2, 0, marginSmall.bottom() - (marginSmall.bottom() / 2)); style::margins slidedPadding(0, marginSmall.bottom() / 2, 0, marginSmall.bottom() - (marginSmall.bottom() / 2));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE #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); style::margins marginLink(st::defaultBoxCheckbox.textPosition.x(), 0, 0, st::settingsSkip);
addChildRow(_updateRow, marginLink, slidedPadding); addChildRow(_updateRow, marginLink, slidedPadding);
connect(_updateRow->entity(), SIGNAL(restart()), this, SLOT(onRestart())); connect(_updateRow->entity(), SIGNAL(restart()), this, SLOT(onRestart()));
@ -186,20 +186,20 @@ void GeneralWidget::refreshControls() {
if (cPlatform() == dbipWindows || cSupportTray()) { if (cPlatform() == dbipWindows || cSupportTray()) {
auto workMode = Global::WorkMode().value(); 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) { 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 #ifndef OS_WIN_STORE
addChildRow(_autoStart, marginSmall, lang(lng_settings_auto_start), SLOT(onAutoStart()), cAutoStart()); addChildRow(_autoStart, marginSmall, lang(lng_settings_auto_start), [this](bool) { onAutoStart(); }, cAutoStart());
addChildRow(_startMinimized, marginLarge, slidedPadding, lang(lng_settings_start_min), SLOT(onStartMinimized()), (cStartMinimized() && !Global::LocalPasscode())); addChildRow(_startMinimized, marginLarge, slidedPadding, lang(lng_settings_start_min), [this](bool) { onStartMinimized(); }, (cStartMinimized() && !Global::LocalPasscode()));
subscribe(Global::RefLocalPasscodeChanged(), [this] { subscribe(Global::RefLocalPasscodeChanged(), [this] {
_startMinimized->entity()->setChecked(cStartMinimized() && !Global::LocalPasscode()); _startMinimized->entity()->setChecked(cStartMinimized() && !Global::LocalPasscode());
}); });
if (!cAutoStart()) { if (!cAutoStart()) {
_startMinimized->hideFast(); _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 #endif // OS_WIN_STORE
} }
} }

View File

@ -56,9 +56,9 @@ NotificationsWidget::NotificationsWidget(QWidget *parent, UserData *self) : Bloc
void NotificationsWidget::createControls() { void NotificationsWidget::createControls() {
style::margins margin(0, 0, 0, st::settingsSkip); style::margins margin(0, 0, 0, st::settingsSkip);
style::margins slidedPadding(0, margin.bottom() / 2, 0, margin.bottom() - (margin.bottom() / 2)); 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(_desktopNotifications, margin, lang(lng_settings_desktop_notify), [this](bool) { onDesktopNotifications(); }, Global::DesktopNotify());
addChildRow(_showSenderName, margin, slidedPadding, lang(lng_settings_show_name), SLOT(onShowSenderName()), Global::NotifyView() <= dbinvShowName); addChildRow(_showSenderName, margin, slidedPadding, lang(lng_settings_show_name), [this](bool) { onShowSenderName(); }, Global::NotifyView() <= dbinvShowName);
addChildRow(_showMessagePreview, margin, slidedPadding, lang(lng_settings_show_preview), SLOT(onShowMessagePreview()), Global::NotifyView() <= dbinvShowPreview); addChildRow(_showMessagePreview, margin, slidedPadding, lang(lng_settings_show_preview), [this](bool) { onShowMessagePreview(); }, Global::NotifyView() <= dbinvShowPreview);
if (!_showSenderName->entity()->checked()) { if (!_showSenderName->entity()->checked()) {
_showMessagePreview->hideFast(); _showMessagePreview->hideFast();
} }
@ -66,8 +66,8 @@ void NotificationsWidget::createControls() {
_showSenderName->hideFast(); _showSenderName->hideFast();
_showMessagePreview->hideFast(); _showMessagePreview->hideFast();
} }
addChildRow(_playSound, margin, lang(lng_settings_sound_notify), SLOT(onPlaySound()), Global::SoundNotify()); addChildRow(_playSound, margin, lang(lng_settings_sound_notify), [this](bool) { onPlaySound(); }, Global::SoundNotify());
addChildRow(_includeMuted, margin, lang(lng_settings_include_muted), SLOT(onIncludeMuted()), Global::IncludeMuted()); addChildRow(_includeMuted, margin, lang(lng_settings_include_muted), [this](bool) { onIncludeMuted(); }, Global::IncludeMuted());
if (cPlatform() != dbipMac) { if (cPlatform() != dbipMac) {
createNotificationsControls(); createNotificationsControls();
@ -87,7 +87,7 @@ void NotificationsWidget::createNotificationsControls() {
#endif // Q_OS_WIN || Q_OS_LINUX64 || Q_OS_LINUX32 #endif // Q_OS_WIN || Q_OS_LINUX64 || Q_OS_LINUX32
} }
if (!nativeNotificationsLabel.isEmpty()) { 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())); addChildRow(_advanced, margin, slidedPadding, lang(lng_settings_advanced_notifications), SLOT(onAdvanced()));
if (!nativeNotificationsLabel.isEmpty() && Global::NativeNotifications()) { if (!nativeNotificationsLabel.isEmpty() && Global::NativeNotifications()) {

View File

@ -52,7 +52,7 @@ ScaleWidget::ScaleWidget(QWidget *parent, UserData *self) : BlockWidget(parent,
void ScaleWidget::createControls() { void ScaleWidget::createControls() {
style::margins margin(0, 0, 0, st::settingsSmallSkip); 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)); addChildRow(_scale, style::margins(0, 0, 0, 0));
_scale->addSection(scaleLabel(dbisOne)); _scale->addSection(scaleLabel(dbisOne));

View File

@ -28,12 +28,18 @@ inline QPoint rtlpoint(int x, int y, int outerw) {
inline QPoint rtlpoint(const QPoint &p, int outerw) { inline QPoint rtlpoint(const QPoint &p, int outerw) {
return rtl() ? QPoint(outerw - p.x(), p.y()) : p; 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) { inline QRect rtlrect(int x, int y, int w, int h, int outerw) {
return QRect(rtl() ? (outerw - x - w) : x, y, w, h); return QRect(rtl() ? (outerw - x - w) : x, y, w, h);
} }
inline QRect rtlrect(const QRect &r, int outerw) { inline QRect rtlrect(const QRect &r, int outerw) {
return rtl() ? QRect(outerw - r.x() - r.width(), r.y(), r.width(), r.height()) : r; 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) { 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()); return QRect(inRect.x() + (inRect.width() - rect.width()) / 2, inRect.y() + (inRect.height() - rect.height()) / 2, rect.width(), rect.height());
} }

View File

@ -35,14 +35,158 @@ TextParseOptions _checkboxOptions = {
} // namespace } // 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<void()> 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<void()> 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<void()> 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<void()> 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<void()> 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<CheckView>(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<ToggleView>(toggleSt, checked, [this] { updateCheck(); })) {
}
Checkbox::Checkbox(QWidget *parent, const QString &text, const style::Checkbox &st, std::unique_ptr<AbstractCheckView> check) : RippleButton(parent, st.ripple)
, _st(st) , _st(st)
, _text(_st.style, text, _checkboxOptions) , _check(std::move(check))
, _checked(checked) { , _text(_st.style, text, _checkboxOptions) {
resizeToText(); resizeToText();
connect(this, SIGNAL(clicked()), this, SLOT(onClicked()));
setCursor(style::cur_pointer); setCursor(style::cur_pointer);
} }
@ -53,7 +197,7 @@ void Checkbox::setText(const QString &text) {
} }
bool Checkbox::checked() const { bool Checkbox::checked() const {
return _checked; return _check->checked();
} }
void Checkbox::resizeToText() { void Checkbox::resizeToText() {
@ -62,21 +206,20 @@ void Checkbox::resizeToText() {
} else { } else {
resizeToWidth(_st.width); 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) { void Checkbox::setChecked(bool checked, NotifyAboutChange notify) {
if (_checked != checked) { if (_check->checked() != checked) {
_checked = checked; _check->setCheckedAnimated(checked);
_a_checked.start([this] { update(_checkRect); }, _checked ? 0. : 1., _checked ? 1. : 0., _st.duration);
if (notify == NotifyAboutChange::Notify) { if (notify == NotifyAboutChange::Notify) {
emit changed(); checkedChanged.notify(checked, true);
} }
} }
} }
void Checkbox::finishAnimations() { void Checkbox::finishAnimations() {
_a_checked.finish(); _check->finishAnimation();
} }
int Checkbox::naturalWidth() const { int Checkbox::naturalWidth() const {
@ -87,38 +230,22 @@ void Checkbox::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
auto ms = getms(); 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); auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active);
paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms, &color); paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms, &color);
if (_checkRect.intersects(e->rect())) { auto realCheckRect = myrtlrect(_checkRect);
auto pen = anim::pen(_st.checkFg, _st.checkFgActive, active); if (realCheckRect.intersects(e->rect())) {
pen.setWidth(_st.thickness); _check->paint(p, _checkRect.left(), _checkRect.top(), width());
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 (realCheckRect.contains(e->rect())) return;
if (active > 0) { auto textWidth = qMax(width() - (_st.textPosition.x() + (_st.textPosition.x() - _check->getSize().width())), 1);
_st.checkIcon.paint(p, QPoint(_st.margin.left(), _st.margin.top()), width());
}
}
if (_checkRect.contains(e->rect())) return;
auto textWidth = qMax(width() - (_st.textPosition.x() + (_st.textPosition.x() - _st.diameter)), 1);
p.setPen(_st.textFg); p.setPen(_st.textFg);
_text.drawLeftElided(p, _st.margin.left() + _st.textPosition.x(), _st.margin.top() + _st.textPosition.y(), textWidth, width()); _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) { void Checkbox::onStateChanged(State was, StateChangeSource source) {
RippleButton::onStateChanged(was, source); RippleButton::onStateChanged(was, source);
@ -127,6 +254,13 @@ void Checkbox::onStateChanged(State was, StateChangeSource source) {
} else if (!isDisabled() && (was & StateFlag::Disabled)) { } else if (!isDisabled() && (was & StateFlag::Disabled)) {
setCursor(style::cur_pointer); 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) { int Checkbox::resizeGetHeight(int newWidth) {
@ -159,88 +293,26 @@ void RadiobuttonGroup::setValue(int value) {
} }
} }
Radiobutton::Radiobutton(QWidget *parent, const std::shared_ptr<RadiobuttonGroup> &group, int value, const QString &text, const style::Checkbox &st) : RippleButton(parent, st.ripple) Radiobutton::Radiobutton(QWidget *parent, const std::shared_ptr<RadiobuttonGroup> &group, int value, const QString &text, const style::Checkbox &st, const style::Radio &radioSt) : Checkbox(parent, text, st, std::make_unique<RadioView>(radioSt, (group->hasValue() && group->value() == value), [this] { updateCheck(); }))
, _group(group) , _group(group)
, _value(value) , _value(value) {
, _st(st)
, _text(_st.style, text, _checkboxOptions)
, _checked(_group->hasValue() && _group->value() == _value) {
_group->registerButton(this); _group->registerButton(this);
subscribe(checkbox()->checkedChanged, [this](bool checked) {
if (_st.width <= 0) { if (checked) {
resizeToWidth(_text.maxWidth() - _st.width); _group->setValue(_value);
} else {
resizeToWidth(_st.width);
} }
_checkRect = myrtlrect(_st.margin.left(), _st.margin.top(), _st.diameter, _st.diameter); });
} }
void Radiobutton::handleNewGroupValue(int value) { void Radiobutton::handleNewGroupValue(int value) {
auto checked = (value == _value); auto checked = (value == _value);
if (_checked != checked) { if (checkbox()->checked() != checked) {
_checked = checked; checkbox()->setChecked(checked, Ui::Checkbox::NotifyAboutChange::DontNotify);
_a_checked.start([this] { update(_checkRect); }, _checked ? 0. : 1., _checked ? 1. : 0., _st.duration);
} }
} }
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) { void Radiobutton::onStateChanged(State was, StateChangeSource source) {
RippleButton::onStateChanged(was, source); Checkbox::onStateChanged(was, source);
if (isDisabled() && !(was & StateFlag::Disabled)) {
setCursor(style::cur_default);
} else if (!isDisabled() && (was & StateFlag::Disabled)) {
setCursor(style::cur_pointer);
}
auto now = state(); auto now = state();
if (!isDisabled() && (was & StateFlag::Over) && (now & StateFlag::Over)) { 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() { Radiobutton::~Radiobutton() {
_group->unregisterButton(this); _group->unregisterButton(this);
} }
ToggleView::ToggleView(const style::Toggle &st, bool toggled, base::lambda<void()> 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<void()> 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 } // namespace Ui

View File

@ -25,11 +25,87 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Ui { namespace Ui {
class Checkbox : public RippleButton { class AbstractCheckView {
Q_OBJECT
public: public:
Checkbox(QWidget *parent, const QString &text, bool checked = false, const style::Checkbox &st = st::defaultCheckbox); AbstractCheckView(int duration, bool checked, base::lambda<void()> updateCallback);
void setCheckedFast(bool checked);
void setCheckedAnimated(bool checked);
void finishAnimation();
void setUpdateCallback(base::lambda<void()> 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<void()> _updateCallback;
Animation _toggleAnimation;
};
class CheckView : public AbstractCheckView {
public:
CheckView(const style::Check &st, bool checked, base::lambda<void()> 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<const style::Check*> _st;
};
class RadioView : public AbstractCheckView {
public:
RadioView(const style::Radio &st, bool checked, base::lambda<void()> 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<const style::Radio*> _st;
};
class ToggleView : public AbstractCheckView {
public:
ToggleView(const style::Toggle &st, bool checked, base::lambda<void()> 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<const style::Toggle*> _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<AbstractCheckView> check);
void setText(const QString &text); void setText(const QString &text);
@ -39,6 +115,7 @@ public:
DontNotify, DontNotify,
}; };
void setChecked(bool checked, NotifyAboutChange notify = NotifyAboutChange::Notify); void setChecked(bool checked, NotifyAboutChange notify = NotifyAboutChange::Notify);
base::Observable<bool> checkedChanged;
void finishAnimations(); void finishAnimations();
@ -56,23 +133,19 @@ protected:
QImage prepareRippleMask() const override; QImage prepareRippleMask() const override;
QPoint prepareRippleStartPosition() const override; QPoint prepareRippleStartPosition() const override;
public slots: void updateCheck() {
void onClicked(); rtlupdate(_checkRect);
}
signals:
void changed();
private: private:
void resizeToText(); void resizeToText();
const style::Checkbox &_st; const style::Checkbox &_st;
std::unique_ptr<AbstractCheckView> _check;
Text _text; Text _text;
QRect _checkRect; QRect _checkRect;
bool _checked;
Animation _a_checked;
}; };
class Radiobutton; class Radiobutton;
@ -113,41 +186,32 @@ private:
}; };
class Radiobutton : public RippleButton { class Radiobutton : public Checkbox, private base::Subscriber {
public: public:
Radiobutton(QWidget *parent, const std::shared_ptr<RadiobuttonGroup> &group, int value, const QString &text, const style::Checkbox &st = st::defaultCheckbox); Radiobutton(QWidget *parent, const std::shared_ptr<RadiobuttonGroup> &group, int value, const QString &text, const style::Checkbox &st = st::defaultCheckbox, const style::Radio &radioSt = st::defaultRadio);
QMargins getMargins() const override {
return _st.margin;
}
int naturalWidth() const override;
~Radiobutton(); ~Radiobutton();
protected: protected:
void paintEvent(QPaintEvent *e) override;
void onStateChanged(State was, StateChangeSource source) override; void onStateChanged(State was, StateChangeSource source) override;
int resizeGetHeight(int newWidth) override;
QImage prepareRippleMask() const override;
QPoint prepareRippleStartPosition() const override;
private: 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; friend class RadiobuttonGroup;
void handleNewGroupValue(int value); void handleNewGroupValue(int value);
std::shared_ptr<RadiobuttonGroup> _group; std::shared_ptr<RadiobuttonGroup> _group;
int _value = 0; int _value = 0;
const style::Checkbox &_st;
Text _text;
QRect _checkRect;
bool _checked = false;
Animation _a_checked;
}; };
template <typename Enum> template <typename Enum>
@ -194,24 +258,4 @@ public:
}; };
class ToggleView {
public:
ToggleView(const style::Toggle &st, bool toggled, base::lambda<void()> updateCallback);
void setToggledFast(bool toggled);
void setToggledAnimated(bool toggled);
void finishAnimation();
void setStyle(const style::Toggle &st);
void setUpdateCallback(base::lambda<void()> updateCallback);
void paint(Painter &p, int left, int top, int outerWidth, TimeMs ms);
private:
gsl::not_null<const style::Toggle*> _st;
bool _toggled = false;
base::lambda<void()> _updateCallback;
Animation _toggleAnimation;
};
} // namespace Ui } // namespace Ui

View File

@ -140,13 +140,13 @@ int Menu::processAction(QAction *action, int index, int width) {
} }
if (action->isCheckable()) { if (action->isCheckable()) {
auto updateCallback = [this, index] { updateItem(index); }; auto updateCallback = [this, index] { updateItem(index); };
goodw += _st.itemPadding.left() + _st.itemToggle.diameter + _st.itemToggle.width - _st.itemToggleShift;
if (data.toggle) { if (data.toggle) {
data.toggle->setUpdateCallback(updateCallback); data.toggle->setUpdateCallback(updateCallback);
data.toggle->setToggledAnimated(action->isChecked()); data.toggle->setCheckedAnimated(action->isChecked());
} else { } else {
data.toggle = std::make_unique<ToggleView>(_st.itemToggle, action->isChecked(), updateCallback); data.toggle = std::make_unique<ToggleView>(_st.itemToggle, action->isChecked(), updateCallback);
} }
goodw += _st.itemPadding.left() + data.toggle->getSize().width() - _st.itemToggleShift;
} else { } else {
data.toggle.reset(); data.toggle.reset();
} }
@ -231,8 +231,8 @@ void Menu::paintEvent(QPaintEvent *e) {
p.setPen(selected ? _st.itemFgShortcutOver : (enabled ? _st.itemFgShortcut : _st.itemFgShortcutDisabled)); p.setPen(selected ? _st.itemFgShortcutOver : (enabled ? _st.itemFgShortcut : _st.itemFgShortcutDisabled));
p.drawTextRight(_st.itemPadding.right(), _st.itemPadding.top(), width(), data.shortcut); p.drawTextRight(_st.itemPadding.right(), _st.itemPadding.top(), width(), data.shortcut);
} else if (data.toggle) { } else if (data.toggle) {
auto toggleSize = _st.itemToggle.diameter + _st.itemToggle.width; auto toggleSize = data.toggle->getSize();
data.toggle->paint(p, width() - _st.itemPadding.right() - toggleSize + _st.itemToggleShift, (_itemHeight - _st.itemToggle.diameter) / 2, width(), ms); data.toggle->paint(p, width() - _st.itemPadding.right() - toggleSize.width() + _st.itemToggleShift, (_itemHeight - toggleSize.height()) / 2, width(), ms);
} }
} }
} }

View File

@ -92,25 +92,48 @@ RoundButton {
ripple: RippleAnimation; 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 { Checkbox {
textFg: color; textFg: color;
checkBg: color;
checkFg: color;
checkFgActive: color;
width: pixels; width: pixels;
height: pixels; height: pixels;
margin: margins; margin: margins;
textPosition: point; textPosition: point;
diameter: pixels;
thickness: pixels;
radioSkip: pixels;
checkIcon: icon;
style: TextStyle; style: TextStyle;
duration: int;
rippleAreaPosition: point; rippleAreaPosition: point;
rippleAreaSize: pixels; rippleAreaSize: pixels;
@ -360,18 +383,6 @@ CallButton {
outerBg: color; outerBg: color;
} }
Toggle {
toggledBg: color;
toggledFg: color;
untoggledBg: color;
untoggledFg: color;
duration: int;
border: pixels;
shift: pixels;
diameter: pixels;
width: pixels;
}
Menu { Menu {
skip: pixels; skip: pixels;
@ -660,25 +671,45 @@ defaultInputField: InputField {
defaultCheckboxIcon: icon {{ "default_checkbox_check", windowFgActive, point(4px, 7px) }}; 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 { defaultCheckbox: Checkbox {
textFg: windowFg; textFg: windowFg;
checkBg: transparent;
checkFg: checkboxFg;
checkFgActive: windowBgActive;
width: -44px; width: -44px;
height: 22px; height: 22px;
margin: margins(8px, 8px, 8px, 8px); margin: margins(8px, 8px, 8px, 8px);
textPosition: point(32px, 2px); textPosition: point(32px, 2px);
diameter: 22px;
thickness: 2px;
checkIcon: defaultCheckboxIcon;
radioSkip: 65px; // * 0.1
style: defaultTextStyle; style: defaultTextStyle;
duration: 120;
rippleAreaSize: 38px; rippleAreaSize: 38px;
rippleAreaPosition: point(0px, 0px); rippleAreaPosition: point(0px, 0px);
@ -781,18 +812,6 @@ defaultRoundCheckbox: RoundCheckbox {
duration: 150; 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 }}; defaultMenuArrow: icon {{ "dropdown_submenu_arrow", menuSubmenuArrowFg }};
defaultMenuToggle: Toggle(defaultToggle) { defaultMenuToggle: Toggle(defaultToggle) {
untoggledFg: menuIconFg; untoggledFg: menuIconFg;