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() {
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);

View file

@ -196,9 +196,9 @@ void EditAdminBox::prepare() {
}
auto checked = (prepareRights.c_channelAdminRights().vflags.v & flags) != 0;
auto control = addControl(object_ptr<Ui::Checkbox>(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<Ui::FlatLabel>(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<Ui::Checkbox>(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));

View file

@ -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);

View file

@ -30,11 +30,13 @@ namespace {
class UserCheckbox : public Ui::RippleButton {
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 {
return _checked;
return _check->checked();
}
base::Observable<bool> checkedChanged;
enum class NotifyAboutChange {
Notify,
DontNotify,
@ -57,24 +59,20 @@ protected:
private:
const style::Checkbox &_st;
std::unique_ptr<Ui::AbstractCheckView> _check;
QRect _checkRect;
bool _checked = false;
Animation _a_checked;
gsl::not_null<UserData*> _user;
base::lambda<void()> _changedCallback;
QString _statusText;
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)
, _checked(checked)
, _user(user)
, _changedCallback(std::move(changedCallback)) {
, _check(std::make_unique<Ui::CheckView>(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<UserData*> 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<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) {
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());
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<Ui::Checkbox>(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<Ui::Checkbox>(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<gsl::not_null<UserData*>> &admins, const FilterValue &filter) {
for (auto user : admins) {
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)) {
auto allChecked = true;
for_const (auto &&checkbox, _admins) {
@ -311,7 +300,7 @@ void FilterBox::Inner::createAdminsCheckboxes(const std::vector<gsl::not_null<Us
_changedCallback();
}
}
}), st::adminLogFilterLittleSkip);
});
_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) {
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);
}
}
}

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))
, _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()) {

View file

@ -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();
}

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);
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) {

View file

@ -91,7 +91,7 @@ private:
margin.setRight(margin.right() - padding.right());
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);
template <typename Enum>

View file

@ -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);

View file

@ -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
}
}

View file

@ -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()) {

View file

@ -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));

View file

@ -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());
}

View file

@ -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<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)
, _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<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)
, _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<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

View file

@ -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<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);
@ -39,6 +115,7 @@ public:
DontNotify,
};
void setChecked(bool checked, NotifyAboutChange notify = NotifyAboutChange::Notify);
base::Observable<bool> 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<AbstractCheckView> _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<RadiobuttonGroup> &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<RadiobuttonGroup> &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<RadiobuttonGroup> _group;
int _value = 0;
const style::Checkbox &_st;
Text _text;
QRect _checkRect;
bool _checked = false;
Animation _a_checked;
};
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

View file

@ -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<ToggleView>(_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);
}
}
}

View file

@ -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;