| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993 |
- // This file is part of Desktop App Toolkit,
- // a set of libraries for developing nice desktop applications.
- //
- // For license and copyright information please follow this link:
- // https://github.com/desktop-app/legal/blob/master/LEGAL
- //
- #include "ui/widgets/checkbox.h"
- #include "ui/effects/ripple_animation.h"
- #include "ui/basic_click_handlers.h"
- #include "ui/qt_weak_factory.h"
- #include "ui/ui_utility.h"
- #include "ui/painter.h"
- #include "styles/palette.h"
- #include <QtGui/QtEvents>
- #include <QtCore/QtMath>
- namespace Ui {
- namespace {
- TextParseOptions _checkboxOptions = {
- TextParseMultiline, // flags
- 0, // maxw
- 0, // maxh
- Qt::LayoutDirectionAuto, // dir
- };
- TextParseOptions _checkboxRichOptions = {
- TextParseMultiline, // flags
- 0, // maxw
- 0, // maxh
- Qt::LayoutDirectionAuto, // dir
- };
- } // namespace
- AbstractCheckView::AbstractCheckView(int duration, bool checked, Fn<void()> updateCallback)
- : _duration(duration)
- , _checked(checked)
- , _updateCallback(std::move(updateCallback)) {
- }
- void AbstractCheckView::setChecked(bool checked, anim::type animated) {
- const auto changed = (_checked != checked);
- _checked = checked;
- if (animated == anim::type::instant) {
- finishAnimating();
- if (_updateCallback) {
- _updateCallback();
- }
- } else if (changed) {
- _toggleAnimation.start(
- [=] { if (_updateCallback) _updateCallback(); },
- _checked ? 0. : 1.,
- _checked ? 1. : 0.,
- _duration);
- }
- checkedChangedHook(animated);
- if (changed) {
- _checks.fire_copy(_checked);
- }
- }
- void AbstractCheckView::setUpdateCallback(Fn<void()> updateCallback) {
- _updateCallback = std::move(updateCallback);
- }
- void AbstractCheckView::update() {
- if (_updateCallback) {
- _updateCallback();
- }
- }
- void AbstractCheckView::finishAnimating() {
- _toggleAnimation.stop();
- }
- float64 AbstractCheckView::currentAnimationValue() {
- return _toggleAnimation.value(_checked ? 1. : 0.);
- }
- bool AbstractCheckView::animating() const {
- return _toggleAnimation.animating();
- }
- ToggleView::ToggleView(
- const style::Toggle &st,
- bool checked,
- Fn<void()> updateCallback)
- : AbstractCheckView(st.duration, checked, std::move(updateCallback))
- , _st(&st) {
- }
- QSize ToggleView::getSize() const {
- return QSize(2 * _st->border + _st->diameter + _st->width, 2 * _st->border + _st->diameter);
- }
- void ToggleView::setStyle(const style::Toggle &st) {
- _st = &st;
- }
- void ToggleView::paint(QPainter &p, int left, int top, int outerWidth) {
- left += _st->border;
- top += _st->border;
- PainterHighQualityEnabler hq(p);
- auto toggled = currentAnimationValue();
- auto fullWidth = _st->diameter + _st->width;
- auto innerDiameter = _st->diameter - 2 * _st->shift;
- auto innerRadius = float64(innerDiameter) / 2.;
- auto toggleLeft = left + anim::interpolate(0, fullWidth - _st->diameter, toggled);
- auto bgRect = style::rtlrect(left + _st->shift, top + _st->shift, fullWidth - 2 * _st->shift, innerDiameter, outerWidth);
- auto fgRect = style::rtlrect(toggleLeft, top, _st->diameter, _st->diameter, outerWidth);
- auto fgBrush = anim::brush(_st->untoggledFg, _st->toggledFg, toggled);
- p.setPen(Qt::NoPen);
- p.setBrush(fgBrush);
- 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);
- if (_locked || _st->xsize > 0) {
- p.setPen(Qt::NoPen);
- p.setBrush(fgBrush);
- if (_locked) {
- const auto color = anim::color(_st->untoggledFg, _st->toggledFg, toggled);
- _st->lockIcon.paint(p, toggleLeft, top, outerWidth, color);
- } else {
- paintXV(p, toggleLeft, top, outerWidth, toggled, fgBrush);
- }
- }
- }
- void ToggleView::paintXV(QPainter &p, int left, int top, int outerWidth, float64 toggled, const QBrush &brush) {
- Expects(_st->vsize > 0);
- Expects(_st->stroke > 0);
- const auto stroke = (0. + _st->stroke) / M_SQRT2;
- if (toggled < 1) {
- // Just X or X->V.
- const auto xSize = 0. + _st->xsize;
- const auto xLeft = left + (_st->diameter - xSize) / 2.;
- const auto xTop = top + (_st->diameter - xSize) / 2.;
- QPointF pathX[] = {
- { xLeft, xTop + stroke },
- { xLeft + stroke, xTop },
- { xLeft + (xSize / 2.), xTop + (xSize / 2.) - stroke },
- { xLeft + xSize - stroke, xTop },
- { xLeft + xSize, xTop + stroke },
- { xLeft + (xSize / 2.) + stroke, xTop + (xSize / 2.) },
- { xLeft + xSize, xTop + xSize - stroke },
- { xLeft + xSize - stroke, xTop + xSize },
- { xLeft + (xSize / 2.), xTop + (xSize / 2.) + stroke },
- { xLeft + stroke, xTop + xSize },
- { xLeft, xTop + xSize - stroke },
- { xLeft + (xSize / 2.) - stroke, xTop + (xSize / 2.) },
- };
- for (auto &point : pathX) {
- point = style::rtlpoint(point, outerWidth);
- }
- if (toggled > 0) {
- // X->V.
- const auto vSize = 0. + _st->vsize;
- const auto fSize = (xSize + vSize - 2. * stroke);
- const auto vLeft = left + (_st->diameter - fSize) / 2.;
- const auto vTop = 0. + xTop + _st->vshift;
- QPointF pathV[] = {
- { vLeft, vTop + xSize - vSize + stroke },
- { vLeft + stroke, vTop + xSize - vSize },
- { vLeft + vSize - stroke, vTop + xSize - 2 * stroke },
- { vLeft + fSize - stroke, vTop },
- { vLeft + fSize, vTop + stroke },
- { vLeft + vSize, vTop + xSize - stroke },
- { vLeft + vSize, vTop + xSize - stroke },
- { vLeft + vSize - stroke, vTop + xSize },
- { vLeft + vSize - stroke, vTop + xSize },
- { vLeft + vSize - stroke, vTop + xSize },
- { vLeft + vSize - 2 * stroke, vTop + xSize - stroke },
- { vLeft + vSize - 2 * stroke, vTop + xSize - stroke },
- };
- for (auto &point : pathV) {
- point = style::rtlpoint(point, outerWidth);
- }
- p.fillPath(anim::interpolate(pathX, pathV, toggled), brush);
- } else {
- // Just X.
- p.fillPath(anim::path(pathX), brush);
- }
- } else {
- // Just V.
- const auto xSize = 0. + _st->xsize;
- const auto xTop = top + (_st->diameter - xSize) / 2.;
- const auto vSize = 0. + _st->vsize;
- const auto fSize = (xSize + vSize - 2. * stroke);
- const auto vLeft = left + (_st->diameter - (_st->xsize + _st->vsize - 2. * stroke)) / 2.;
- const auto vTop = 0. + xTop + _st->vshift;
- QPointF pathV[] = {
- { vLeft, vTop + xSize - vSize + stroke },
- { vLeft + stroke, vTop + xSize - vSize },
- { vLeft + vSize - stroke, vTop + xSize - 2 * stroke },
- { vLeft + fSize - stroke, vTop },
- { vLeft + fSize, vTop + stroke },
- { vLeft + vSize, vTop + xSize - stroke },
- { vLeft + vSize, vTop + xSize - stroke },
- { vLeft + vSize - stroke, vTop + xSize },
- { vLeft + vSize - stroke, vTop + xSize },
- { vLeft + vSize - stroke, vTop + xSize },
- { vLeft + vSize - 2 * stroke, vTop + xSize - stroke },
- { vLeft + vSize - 2 * stroke, vTop + xSize - stroke },
- };
- p.fillPath(anim::path(pathV), brush);
- }
- }
- QSize ToggleView::rippleSize() const {
- return getSize() + 2 * QSize(_st->rippleAreaPadding, _st->rippleAreaPadding);
- }
- QImage ToggleView::prepareRippleMask() const {
- auto size = rippleSize();
- return RippleAnimation::RoundRectMask(size, size.height() / 2);
- }
- bool ToggleView::checkRippleStartPosition(QPoint position) const {
- return QRect(QPoint(0, 0), rippleSize()).contains(position);
- }
- void ToggleView::setLocked(bool locked) {
- if (_locked != locked) {
- _locked = locked;
- update();
- }
- }
- CheckView::CheckView(const style::Check &st, bool checked, Fn<void()> updateCallback) : AbstractCheckView(st.duration, checked, std::move(updateCallback))
- , _st(&st) {
- }
- QSize CheckView::getSize() const {
- return QSize(_st->diameter, _st->diameter);
- }
- void CheckView::setStyle(const style::Check &st) {
- _st = &st;
- }
- void CheckView::paint(QPainter &p, int left, int top, int outerWidth) {
- auto toggled = currentAnimationValue();
- auto pen = _untoggledOverride
- ? anim::pen(*_untoggledOverride, _st->toggledFg, toggled)
- : anim::pen(_st->untoggledFg, _st->toggledFg, toggled);
- pen.setWidth(_st->thickness);
- p.setPen(pen);
- p.setBrush(anim::brush(
- _st->bg,
- (_untoggledOverride
- ? anim::color(*_untoggledOverride, _st->toggledFg, toggled)
- : anim::color(_st->untoggledFg, _st->toggledFg, toggled)),
- toggled));
- {
- PainterHighQualityEnabler hq(p);
- p.drawRoundedRect(style::rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(_st->thickness / 2., _st->thickness / 2., _st->thickness / 2., _st->thickness / 2.)), outerWidth), st::roundRadiusSmall - (_st->thickness / 2.), st::roundRadiusSmall - (_st->thickness / 2.));
- }
- if (toggled > 0) {
- _st->icon.paint(p, QPoint(left, top), outerWidth);
- }
- }
- QSize CheckView::rippleSize() const {
- return getSize() + 2 * QSize(_st->rippleAreaPadding, _st->rippleAreaPadding);
- }
- QImage CheckView::prepareRippleMask() const {
- return RippleAnimation::EllipseMask(rippleSize());
- }
- bool CheckView::checkRippleStartPosition(QPoint position) const {
- return QRect(QPoint(0, 0), rippleSize()).contains(position);
- }
- void CheckView::setUntoggledOverride(
- std::optional<QColor> untoggledOverride) {
- _untoggledOverride = untoggledOverride;
- update();
- }
- Fn<void()> CheckView::PrepareNonToggledError(
- not_null<CheckView*> view,
- rpl::lifetime &lifetime) {
- struct State {
- bool error = false;
- Ui::Animations::Simple errorAnimation;
- };
- const auto state = lifetime.make_state<State>();
- view->checkedChanges(
- ) | rpl::filter([=](bool checked) {
- return checked;
- }) | rpl::start_with_next([=] {
- state->error = false;
- view->setUntoggledOverride(std::nullopt);
- }, lifetime);
- return [=] {
- const auto callback = [=] {
- const auto error = state->errorAnimation.value(
- state->error ? 1. : 0.);
- if (error == 0.) {
- view->setUntoggledOverride(std::nullopt);
- } else {
- const auto color = anim::color(
- st::defaultCheck.untoggledFg,
- st::boxTextFgError,
- error);
- view->setUntoggledOverride(color);
- }
- };
- state->error = true;
- state->errorAnimation.stop();
- state->errorAnimation.start(
- callback,
- 0.,
- 1.,
- st::defaultCheck.duration);
- };
- }
- RadioView::RadioView(
- const style::Radio &st,
- bool checked,
- Fn<void()> updateCallback)
- : AbstractCheckView(st.duration, checked, std::move(updateCallback))
- , _st(&st) {
- }
- QSize RadioView::getSize() const {
- return QSize(_st->diameter, _st->diameter);
- }
- void RadioView::setStyle(const style::Radio &st) {
- _st = &st;
- }
- void RadioView::paint(QPainter &p, int left, int top, int outerWidth) {
- PainterHighQualityEnabler hq(p);
- auto toggled = currentAnimationValue();
- auto pen = _toggledOverride
- ? (_untoggledOverride
- ? anim::pen(*_untoggledOverride, *_toggledOverride, toggled)
- : anim::pen(_st->untoggledFg, *_toggledOverride, toggled))
- : (_untoggledOverride
- ? anim::pen(*_untoggledOverride, _st->toggledFg, toggled)
- : 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)));
- const auto skip = (_st->outerSkip / 10.) + (_st->thickness / 2);
- p.drawEllipse(style::rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(skip, skip, skip, skip)), outerWidth));
- if (toggled > 0) {
- p.setPen(Qt::NoPen);
- p.setBrush(_toggledOverride
- ? (_untoggledOverride
- ? anim::brush(*_untoggledOverride, *_toggledOverride, toggled)
- : anim::brush(_st->untoggledFg, *_toggledOverride, toggled))
- : (_untoggledOverride
- ? anim::brush(*_untoggledOverride, _st->toggledFg, toggled)
- : anim::brush(_st->untoggledFg, _st->toggledFg, toggled)));
- const auto skip0 = _st->diameter / 2., skip1 = _st->skip / 10., checkSkip = skip0 * (1. - toggled) + skip1 * toggled;
- p.drawEllipse(style::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)));
- // }
- //}
- }
- }
- QSize RadioView::rippleSize() const {
- return getSize() + 2 * QSize(_st->rippleAreaPadding, _st->rippleAreaPadding);
- }
- QImage RadioView::prepareRippleMask() const {
- return RippleAnimation::EllipseMask(rippleSize());
- }
- bool RadioView::checkRippleStartPosition(QPoint position) const {
- return QRect(QPoint(0, 0), rippleSize()).contains(position);
- }
- void RadioView::setToggledOverride(std::optional<QColor> toggledOverride) {
- _toggledOverride = toggledOverride;
- update();
- }
- void RadioView::setUntoggledOverride(
- std::optional<QColor> untoggledOverride) {
- _untoggledOverride = untoggledOverride;
- update();
- }
- Checkbox::Checkbox(
- QWidget *parent,
- const QString &text,
- bool checked,
- const style::Checkbox &st,
- const style::Check &checkSt)
- : Checkbox(
- parent,
- rpl::single(text) | rpl::map(TextWithEntities::Simple),
- st,
- std::make_unique<CheckView>(
- checkSt,
- checked)) {
- }
- Checkbox::Checkbox(
- QWidget *parent,
- const TextWithEntities &text,
- bool checked,
- const style::Checkbox &st,
- const style::Check &checkSt)
- : Checkbox(
- parent,
- rpl::single(text),
- st,
- std::make_unique<CheckView>(
- checkSt,
- checked)) {
- }
- Checkbox::Checkbox(
- QWidget *parent,
- const QString &text,
- bool checked,
- const style::Checkbox &st,
- const style::Toggle &toggleSt)
- : Checkbox(
- parent,
- rpl::single(text) | rpl::map(TextWithEntities::Simple),
- st,
- std::make_unique<ToggleView>(
- toggleSt,
- checked)) {
- }
- Checkbox::Checkbox(
- QWidget *parent,
- rpl::producer<QString> &&text,
- bool checked,
- const style::Checkbox &st,
- const style::Check &checkSt)
- : Checkbox(
- parent,
- std::move(text) | rpl::map(TextWithEntities::Simple),
- st,
- std::make_unique<CheckView>(
- checkSt,
- checked)) {
- }
- Checkbox::Checkbox(
- QWidget *parent,
- rpl::producer<QString> &&text,
- bool checked,
- const style::Checkbox &st,
- const style::Toggle &toggleSt)
- : Checkbox(
- parent,
- std::move(text) | rpl::map(TextWithEntities::Simple),
- st,
- std::make_unique<ToggleView>(
- toggleSt,
- checked)) {
- }
- Checkbox::Checkbox(
- QWidget *parent,
- const QString &text,
- const style::Checkbox &st,
- std::unique_ptr<AbstractCheckView> check)
- : Checkbox(
- parent,
- rpl::single(text) | rpl::map(TextWithEntities::Simple),
- st,
- std::move(check)) {
- }
- Checkbox::Checkbox(
- QWidget *parent,
- rpl::producer<TextWithEntities> &&text,
- const style::Checkbox &st,
- std::unique_ptr<AbstractCheckView> check)
- : RippleButton(parent, st.ripple)
- , _st(st)
- , _check(std::move(check))
- , _text(
- _st.style,
- QString(),
- _checkboxOptions,
- _st.style.font->elidew) {
- _check->setUpdateCallback([=] { update(); });
- resizeToText();
- setCursor(style::cur_pointer);
- std::move(
- text
- ) | rpl::start_with_next([=](TextWithEntities &&value) {
- if (value.entities.empty()) {
- setText(base::take(value.text));
- } else {
- _text.setMarkedText(
- _st.style,
- std::move(value),
- _checkboxRichOptions);
- resizeToText();
- if (_text.hasLinks()) {
- setMouseTracking(true);
- }
- update();
- }
- }, lifetime());
- }
- int Checkbox::countTextMinWidth() const {
- const auto leftSkip = _st.checkPosition.x()
- + checkRect().width()
- + _st.textPosition.x();
- return (_st.width > 0)
- ? std::max(_st.width - leftSkip, 1)
- : kQFixedMax;
- }
- QRect Checkbox::checkRect() const {
- auto size = _check->getSize();
- return QRect({
- (_checkAlignment & Qt::AlignHCenter)
- ? (width() - size.width()) / 2
- : (_checkAlignment & Qt::AlignRight)
- ? (width() - _st.checkPosition.x() - size.width())
- : _st.checkPosition.x(),
- (_checkAlignment & Qt::AlignVCenter)
- ? (height() - size.height()) / 2
- : (_checkAlignment & Qt::AlignBottom)
- ? (height() - _st.checkPosition.y() - size.height())
- : _st.checkPosition.y()
- }, size);
- }
- void Checkbox::setText(const QString &text, bool rich) {
- _text.setText(_st.style, text, rich ? _checkboxRichOptions : _checkboxOptions);
- resizeToText();
- update();
- }
- void Checkbox::setCheckAlignment(style::align alignment) {
- if (_checkAlignment != alignment) {
- _checkAlignment = alignment;
- resizeToText();
- update();
- }
- }
- void Checkbox::setAllowTextLines(int lines) {
- _allowTextLines = lines;
- resizeToText();
- update();
- }
- void Checkbox::setTextBreakEverywhere(bool allow) {
- _textBreakEverywhere = allow;
- }
- void Checkbox::setLink(uint16 index, const ClickHandlerPtr &lnk) {
- _text.setLink(index, lnk);
- }
- void Checkbox::setLinksTrusted() {
- static const auto TrustedLinksFilter = [](
- const ClickHandlerPtr &link,
- Qt::MouseButton button) {
- if (const auto url = dynamic_cast<UrlClickHandler*>(link.get())) {
- url->UrlClickHandler::onClick({ button });
- return false;
- }
- return true;
- };
- setClickHandlerFilter(TrustedLinksFilter);
- }
- void Checkbox::setClickHandlerFilter(ClickHandlerFilter &&filter) {
- _clickHandlerFilter = std::move(filter);
- }
- bool Checkbox::checked() const {
- return _check->checked();
- }
- rpl::producer<bool> Checkbox::checkedChanges() const {
- return _checkedChanges.events();
- }
- rpl::producer<bool> Checkbox::checkedValue() const {
- return _checkedChanges.events_starting_with(checked());
- }
- void Checkbox::resizeToText() {
- if (_st.width <= 0) {
- resizeToWidth(_text.maxWidth() - _st.width);
- } else {
- resizeToWidth(_st.width);
- }
- }
- void Checkbox::setChecked(bool checked, NotifyAboutChange notify) {
- if (_check->checked() != checked) {
- _check->setChecked(checked, anim::type::normal);
- if (notify == NotifyAboutChange::Notify) {
- _checkedChanges.fire_copy(checked);
- }
- }
- }
- void Checkbox::finishAnimating() {
- _check->finishAnimating();
- }
- int Checkbox::naturalWidth() const {
- if (_st.width > 0) {
- return _st.width;
- }
- auto result = _st.checkPosition.x() + _check->getSize().width();
- if (!_text.isEmpty()) {
- result += _st.textPosition.x() + _text.maxWidth();
- }
- return result - _st.width;
- }
- void Checkbox::paintEvent(QPaintEvent *e) {
- Painter p(this);
- auto check = checkRect();
- auto active = _check->currentAnimationValue();
- if (isDisabled()) {
- p.setOpacity(_st.disabledOpacity);
- } else {
- auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active);
- paintRipple(p, check.topLeft() + _st.rippleAreaPosition, &color);
- }
- auto realCheckRect = myrtlrect(check);
- if (realCheckRect.intersects(e->rect())) {
- if (isDisabled()) {
- p.drawPixmapLeft(check.left(), check.top(), width(), _checkCache);
- } else {
- _check->paint(p, check.left(), check.top(), width());
- }
- }
- if (realCheckRect.contains(e->rect()) || _text.isEmpty()) {
- return;
- }
- const auto alignLeft = (_checkAlignment & Qt::AlignLeft);
- const auto alignRight = (_checkAlignment & Qt::AlignRight);
- const auto centered = ((_checkAlignment & Qt::AlignHCenter) != 0);
- const auto textSkip = _st.checkPosition.x()
- + check.width()
- + _st.textPosition.x();
- const auto availableTextWidth = centered
- ? std::max(width() - _st.margin.left() - _st.margin.right(), 1)
- : std::max(width() - textSkip, 1);
- const auto textTop = _st.margin.top() + _st.textPosition.y();
- p.setPen(anim::pen(_st.textFg, _st.textFgActive, active));
- if (alignLeft) {
- if (!_allowTextLines) {
- _text.drawLeft(
- p,
- textSkip,
- textTop,
- availableTextWidth,
- width());
- } else {
- _text.drawLeftElided(
- p,
- textSkip,
- textTop,
- availableTextWidth,
- width(),
- _allowTextLines,
- style::al_left,
- 0,
- -1,
- 0,
- _textBreakEverywhere);
- }
- } else if (alignRight) {
- if (!_allowTextLines) {
- _text.drawRight(
- p,
- textSkip,
- textTop,
- availableTextWidth,
- width());
- } else {
- _text.drawRightElided(
- p,
- textSkip,
- textTop,
- availableTextWidth,
- width(),
- _allowTextLines,
- style::al_left,
- 0,
- -1,
- 0,
- _textBreakEverywhere);
- }
- } else if (!_allowTextLines
- || (_text.countHeight(availableTextWidth)
- < (_allowTextLines + 1) * _st.style.font->height)) {
- _text.drawLeft(
- p,
- _st.margin.left(),
- textTop,
- width() - _st.margin.left() - _st.margin.right(),
- width(),
- style::al_top);
- } else {
- _text.drawLeftElided(
- p,
- _st.margin.left(),
- textTop,
- width() - _st.margin.left() - _st.margin.right(),
- width(),
- _allowTextLines,
- style::al_top,
- 0,
- -1,
- 0,
- _textBreakEverywhere);
- }
- }
- void Checkbox::mousePressEvent(QMouseEvent *e) {
- RippleButton::mousePressEvent(e);
- ClickHandler::pressed();
- }
- void Checkbox::mouseMoveEvent(QMouseEvent *e) {
- RippleButton::mouseMoveEvent(e);
- const auto state = getTextState(e->pos());
- if (state.link != ClickHandler::getActive()) {
- ClickHandler::setActive(state.link, this);
- update();
- }
- }
- void Checkbox::mouseReleaseEvent(QMouseEvent *e) {
- const auto weak = Ui::MakeWeak(this);
- if (auto activated = _activatingHandler = ClickHandler::unpressed()) {
- // _clickHandlerFilter may delete `this`. In that case we don't want
- // to try to show a context menu or smth like that.
- const auto button = e->button();
- crl::on_main(this, [=] {
- const auto guard = window();
- if (!_clickHandlerFilter
- || _clickHandlerFilter(activated, button)) {
- ActivateClickHandler(guard, activated, button);
- }
- });
- }
- RippleButton::mouseReleaseEvent(e);
- if (weak) {
- _activatingHandler = nullptr;
- }
- }
- void Checkbox::leaveEventHook(QEvent *e) {
- RippleButton::leaveEventHook(e);
- ClickHandler::clearActive(this);
- }
- Text::StateResult Checkbox::getTextState(const QPoint &m) const {
- if (!(_checkAlignment & Qt::AlignLeft)) {
- return {};
- }
- const auto check = checkRect();
- const auto textSkip = _st.checkPosition.x()
- + check.width()
- + _st.textPosition.x();
- const auto availableTextWidth = std::max(width() - textSkip, 1);
- const auto textTop = _st.margin.top() + _st.textPosition.y();
- auto request = Ui::Text::StateRequestElided();
- request.lines = _allowTextLines;
- return !_allowTextLines
- ? _text.getState(
- m - QPoint(textSkip, textTop),
- availableTextWidth,
- {})
- : _text.getStateElidedLeft(
- m - QPoint(textSkip, textTop),
- availableTextWidth,
- width(),
- request);
- }
- QPixmap Checkbox::grabCheckCache() const {
- auto checkSize = _check->getSize();
- auto image = QImage(
- checkSize * style::DevicePixelRatio(),
- QImage::Format_ARGB32_Premultiplied);
- image.fill(Qt::transparent);
- image.setDevicePixelRatio(style::DevicePixelRatio());
- {
- Painter p(&image);
- _check->paint(p, 0, 0, checkSize.width());
- }
- return PixmapFromImage(std::move(image));
- }
- void Checkbox::onStateChanged(State was, StateChangeSource source) {
- RippleButton::onStateChanged(was, source);
- if (isDisabled() && !(was & StateFlag::Disabled)) {
- setCursor(style::cur_default);
- finishAnimating();
- _checkCache = grabCheckCache();
- } else if (!isDisabled() && (was & StateFlag::Disabled)) {
- setCursor(style::cur_pointer);
- _checkCache = QPixmap();
- }
- auto now = state();
- if (!isDisabled() && (was & StateFlag::Over) && (now & StateFlag::Over)) {
- if ((was & StateFlag::Down) && !(now & StateFlag::Down)) {
- handlePress();
- }
- }
- }
- void Checkbox::handlePress() {
- if (!_activatingHandler) {
- setChecked(!checked());
- }
- }
- int Checkbox::resizeGetHeight(int newWidth) {
- const auto result = _check->getSize().height();
- const auto centered = ((_checkAlignment & Qt::AlignHCenter) != 0);
- if (!centered && _allowTextLines == 1) {
- return result;
- }
- const auto textSkip = _st.checkPosition.x()
- + checkRect().width()
- + _st.textPosition.x();
- const auto fullWidth = _st.margin.left() + newWidth + _st.margin.right();
- const auto availableTextWidth = centered
- ? std::max(newWidth, 1)
- : std::max(fullWidth - textSkip, 1);
- const auto textHeight = _text.countHeight(availableTextWidth);
- const auto textBottom = _st.textPosition.y()
- + (_allowTextLines
- ? std::min(textHeight, _allowTextLines * _st.style.font->height)
- : textHeight);
- return std::max(result, textBottom);
- }
- QImage Checkbox::prepareRippleMask() const {
- return _check->prepareRippleMask();
- }
- QPoint Checkbox::prepareRippleStartPosition() const {
- if (isDisabled()) {
- return DisabledRippleStartPosition();
- }
- auto position = myrtlpoint(mapFromGlobal(QCursor::pos()))
- - checkRect().topLeft()
- - _st.rippleAreaPosition;
- return _check->checkRippleStartPosition(position)
- ? position
- : DisabledRippleStartPosition();
- }
- void RadiobuttonGroup::setValue(int value) {
- if (_hasValue && _value == value) {
- return;
- }
- _hasValue = true;
- _value = value;
- for (const auto button : _buttons) {
- button->handleNewGroupValue(_value);
- }
- const auto guard = weak_from_this();
- _changes.fire_copy(value);
- if (guard.lock()) {
- if (const auto callback = _changedCallback) {
- callback(_value);
- }
- }
- }
- void RadiobuttonGroup::registerButton(Radiobutton *button) {
- if (!base::contains(_buttons, button)) {
- _buttons.push_back(button);
- }
- }
- void RadiobuttonGroup::unregisterButton(Radiobutton *button) {
- _buttons.erase(ranges::remove(_buttons, button), _buttons.end());
- }
- Radiobutton::Radiobutton(
- QWidget *parent,
- const std::shared_ptr<RadiobuttonGroup> &group,
- int value,
- const QString &text,
- const style::Checkbox &st,
- const style::Radio &radioSt)
- : Radiobutton(
- parent,
- group,
- value,
- text,
- st,
- std::make_unique<RadioView>(
- radioSt,
- (group->hasValue() && group->current() == value))) {
- }
- Radiobutton::Radiobutton(
- QWidget *parent,
- const std::shared_ptr<RadiobuttonGroup> &group,
- int value,
- const QString &text,
- const style::Checkbox &st,
- std::unique_ptr<AbstractCheckView> check)
- : Checkbox(
- parent,
- text,
- st,
- std::move(check))
- , _group(group)
- , _value(value) {
- using namespace rpl::mappers;
- checkbox()->setChecked(group->hasValue() && group->current() == value);
- _group->registerButton(this);
- checkbox()->checkedChanges(
- ) | rpl::filter(
- _1
- ) | rpl::start_with_next([=] {
- _group->setValue(_value);
- }, lifetime());
- }
- void Radiobutton::handleNewGroupValue(int value) {
- auto checked = (value == _value);
- if (checkbox()->checked() != checked) {
- checkbox()->setChecked(
- checked,
- Ui::Checkbox::NotifyAboutChange::DontNotify);
- }
- }
- void Radiobutton::handlePress() {
- if (!checkbox()->checked()) {
- checkbox()->setChecked(true);
- }
- }
- Radiobutton::~Radiobutton() {
- _group->unregisterButton(this);
- }
- } // namespace Ui
|