| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- // 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/layers/box_layer_widget.h"
- #include "ui/effects/radial_animation.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/labels.h"
- #include "ui/painter.h"
- #include "base/timer.h"
- #include "styles/style_layers.h"
- #include "styles/palette.h"
- namespace Ui {
- struct BoxLayerWidget::LoadingProgress {
- LoadingProgress(
- Fn<void()> &&callback,
- const style::InfiniteRadialAnimation &st);
- InfiniteRadialAnimation animation;
- base::Timer removeTimer;
- };
- BoxLayerWidget::LoadingProgress::LoadingProgress(
- Fn<void()> &&callback,
- const style::InfiniteRadialAnimation &st)
- : animation(std::move(callback), st) {
- }
- BoxLayerWidget::BoxLayerWidget(
- not_null<LayerStackWidget*> layer,
- object_ptr<BoxContent> content)
- : LayerWidget(layer)
- , _layer(layer)
- , _content(std::move(content))
- , _roundRect(st::boxRadius, st().bg) {
- _content->setParent(this);
- _content->setDelegate(this);
- _additionalTitle.changes(
- ) | rpl::start_with_next([=] {
- updateSize();
- update();
- }, lifetime());
- }
- BoxLayerWidget::~BoxLayerWidget() = default;
- void BoxLayerWidget::setLayerType(bool layerType) {
- if (_layerType == layerType) {
- return;
- }
- _layerType = layerType;
- updateTitlePosition();
- if (_maxContentHeight) {
- setDimensions(width(), _maxContentHeight);
- }
- }
- int BoxLayerWidget::titleHeight() const {
- return st::boxTitleHeight;
- }
- const style::Box &BoxLayerWidget::st() const {
- return _st
- ? *_st
- : _layerType
- ? (_layer->boxStyleOverrideLayer()
- ? *_layer->boxStyleOverrideLayer()
- : st::layerBox)
- : (_layer->boxStyleOverride()
- ? *_layer->boxStyleOverride()
- : st::defaultBox);
- }
- void BoxLayerWidget::setStyle(const style::Box &st) {
- _st = &st;
- _roundRect.setColor(st.bg);
- }
- const style::Box &BoxLayerWidget::style() {
- return st();
- }
- int BoxLayerWidget::buttonsHeight() const {
- const auto padding = st().buttonPadding;
- return padding.top() + st().buttonHeight + padding.bottom();
- }
- int BoxLayerWidget::buttonsTop() const {
- const auto padding = st().buttonPadding;
- return height() - padding.bottom() - st().buttonHeight;
- }
- QRect BoxLayerWidget::loadingRect() const {
- const auto padding = st().buttonPadding;
- const auto size = st::boxLoadingSize;
- const auto skipx = st::boxTitlePosition.x();
- const auto skipy = (st().buttonHeight - size) / 2;
- return QRect(
- skipx,
- height() - padding.bottom() - skipy - size,
- size,
- size);
- }
- void BoxLayerWidget::paintEvent(QPaintEvent *e) {
- Painter p(this);
- const auto clip = e->rect();
- const auto paintTopRounded = !(_customCornersFilling & RectPart::FullTop)
- && clip.intersects(QRect(0, 0, width(), st::boxRadius));
- const auto paintBottomRounded = !(_customCornersFilling
- & RectPart::FullBottom)
- && clip.intersects(
- QRect(0, height() - st::boxRadius, width(), st::boxRadius));
- if (paintTopRounded || paintBottomRounded) {
- _roundRect.paint(p, rect(), RectPart::None
- | (paintTopRounded ? RectPart::FullTop : RectPart::None)
- | (paintBottomRounded ? RectPart::FullBottom : RectPart::None));
- }
- const auto other = e->region().intersected(
- QRect(0, st::boxRadius, width(), height() - 2 * st::boxRadius));
- if (!other.isEmpty()) {
- for (const auto &rect : other) {
- p.fillRect(rect, st().bg);
- }
- }
- if (!_additionalTitle.current().isEmpty()
- && clip.intersects(QRect(0, 0, width(), titleHeight()))) {
- paintAdditionalTitle(p);
- }
- if (_loadingProgress) {
- const auto rect = loadingRect();
- _loadingProgress->animation.draw(
- p,
- rect.topLeft(),
- rect.size(),
- width());
- }
- }
- void BoxLayerWidget::paintAdditionalTitle(Painter &p) {
- p.setFont(st::boxTitleAdditionalFont);
- p.setPen(st().titleAdditionalFg);
- p.drawTextLeft(
- _titleLeft + (_title ? _title->width() : 0) + st::boxTitleAdditionalSkip,
- _titleTop + st::boxTitleFont->ascent - st::boxTitleAdditionalFont->ascent,
- width(),
- _additionalTitle.current());
- }
- void BoxLayerWidget::parentResized() {
- auto newHeight = countRealHeight();
- auto parentSize = parentWidget()->size();
- setGeometry(
- (parentSize.width() - width()) / 2,
- (parentSize.height() - newHeight) / 2,
- width(),
- newHeight);
- update();
- }
- void BoxLayerWidget::setTitle(rpl::producer<TextWithEntities> title) {
- const auto wasTitle = hasTitle();
- if (title) {
- _title.create(this, rpl::duplicate(title), st().title);
- _title->show();
- std::move(
- title
- ) | rpl::start_with_next([=] {
- updateTitlePosition();
- }, _title->lifetime());
- } else {
- _title.destroy();
- }
- if (wasTitle != hasTitle()) {
- updateSize();
- }
- }
- void BoxLayerWidget::setAdditionalTitle(rpl::producer<QString> additional) {
- _additionalTitle = std::move(additional);
- }
- void BoxLayerWidget::triggerButton(int index) {
- if (index < _buttons.size()) {
- _buttons[index]->clicked(Qt::KeyboardModifiers(), Qt::LeftButton);
- }
- }
- void BoxLayerWidget::setCloseByOutsideClick(bool close) {
- _closeByOutsideClick = close;
- }
- bool BoxLayerWidget::closeByOutsideClick() const {
- return _closeByOutsideClick;
- }
- bool BoxLayerWidget::hasTitle() const {
- return (_title != nullptr) || !_additionalTitle.current().isEmpty();
- }
- void BoxLayerWidget::showBox(
- object_ptr<BoxContent> box,
- LayerOptions options,
- anim::type animated) {
- _layer->showBox(std::move(box), options, animated);
- }
- void BoxLayerWidget::hideLayer() {
- _layer->hideLayers(anim::type::normal);
- }
- void BoxLayerWidget::updateSize() {
- setDimensions(width(), _maxContentHeight);
- }
- void BoxLayerWidget::updateButtonsPositions() {
- if (!_buttons.empty() || _leftButton) {
- auto padding = st().buttonPadding;
- auto right = padding.right();
- auto top = buttonsTop();
- if (_leftButton) {
- _leftButton->moveToLeft(right, top);
- }
- for (const auto &button : _buttons) {
- button->moveToRight(right, top);
- right += button->width() + padding.left();
- }
- }
- if (_topButton) {
- _topButton->moveToRight(0, 0);
- }
- }
- ShowFactory BoxLayerWidget::showFactory() {
- return _layer->showFactory();
- }
- QPointer<QWidget> BoxLayerWidget::outerContainer() {
- return parentWidget();
- }
- void BoxLayerWidget::updateTitlePosition() {
- _titleLeft = st::boxTitlePosition.x();
- _titleTop = st::boxTitlePosition.y();
- if (_title) {
- const auto topButtonSkip = _topButton
- ? (_topButton->width() / 2)
- : 0;
- _title->resizeToNaturalWidth(
- width() - _titleLeft * 2 - topButtonSkip);
- _title->moveToLeft(_titleLeft, _titleTop);
- }
- }
- void BoxLayerWidget::setCustomCornersFilling(RectParts corners) {
- _customCornersFilling = corners;
- update();
- }
- void BoxLayerWidget::clearButtons() {
- for (auto &button : base::take(_buttons)) {
- button.destroy();
- }
- _leftButton.destroy();
- _topButton = nullptr;
- }
- void BoxLayerWidget::addButton(object_ptr<AbstractButton> button) {
- _buttons.push_back(std::move(button));
- const auto raw = _buttons.back().data();
- raw->setParent(this);
- raw->show();
- raw->widthValue(
- ) | rpl::start_with_next([=] {
- updateButtonsPositions();
- }, raw->lifetime());
- }
- void BoxLayerWidget::addLeftButton(object_ptr<AbstractButton> button) {
- _leftButton = std::move(button);
- const auto raw = _leftButton.data();
- raw->setParent(this);
- raw->show();
- raw->widthValue(
- ) | rpl::start_with_next([=] {
- updateButtonsPositions();
- }, raw->lifetime());
- }
- void BoxLayerWidget::addTopButton(object_ptr<AbstractButton> button) {
- _topButton = base::unique_qptr<AbstractButton>(button.release());
- const auto raw = _topButton.get();
- raw->setParent(this);
- raw->show();
- updateButtonsPositions();
- updateTitlePosition();
- }
- void BoxLayerWidget::showLoading(bool show) {
- const auto &st = st::boxLoadingAnimation;
- if (!show) {
- if (_loadingProgress && !_loadingProgress->removeTimer.isActive()) {
- _loadingProgress->removeTimer.callOnce(
- st.sineDuration + st.sinePeriod);
- _loadingProgress->animation.stop();
- }
- return;
- }
- if (!_loadingProgress) {
- const auto callback = [=] {
- if (!anim::Disabled()) {
- const auto t = st::boxLoadingAnimation.thickness;
- update(loadingRect().marginsAdded({ t, t, t, t }));
- }
- };
- _loadingProgress = std::make_unique<LoadingProgress>(
- callback,
- st::boxLoadingAnimation);
- _loadingProgress->removeTimer.setCallback([=] {
- _loadingProgress = nullptr;
- });
- } else {
- _loadingProgress->removeTimer.cancel();
- }
- _loadingProgress->animation.start();
- }
- void BoxLayerWidget::setDimensions(int newWidth, int maxHeight, bool forceCenterPosition) {
- _maxContentHeight = maxHeight;
- auto fullHeight = countFullHeight();
- if (width() != newWidth || _fullHeight != fullHeight) {
- _fullHeight = fullHeight;
- if (parentWidget()) {
- auto oldGeometry = geometry();
- resize(newWidth, countRealHeight());
- auto newGeometry = geometry();
- auto parentHeight = parentWidget()->height();
- const auto bottomMargin = st().margin.bottom();
- if (newGeometry.top() + newGeometry.height() + bottomMargin > parentHeight
- || forceCenterPosition) {
- const auto top1 = parentHeight - bottomMargin - newGeometry.height();
- const auto top2 = (parentHeight - newGeometry.height()) / 2;
- const auto newTop = forceCenterPosition
- ? std::min(top1, top2)
- : std::max(top1, top2);
- if (newTop != newGeometry.top()) {
- move(newGeometry.left(), newTop);
- resizeEvent(0);
- }
- }
- parentWidget()->update(oldGeometry.united(geometry()).marginsAdded(st::boxRoundShadow.extend));
- } else {
- resize(newWidth, 0);
- }
- }
- }
- int BoxLayerWidget::countRealHeight() const {
- const auto &margin = st().margin;
- return std::min(
- _fullHeight,
- parentWidget()->height() - margin.top() - margin.bottom());
- }
- int BoxLayerWidget::countFullHeight() const {
- return contentTop() + _maxContentHeight + buttonsHeight();
- }
- int BoxLayerWidget::contentTop() const {
- return hasTitle()
- ? titleHeight()
- : _noContentMargin
- ?
- 0
- : st::boxTopMargin;
- }
- void BoxLayerWidget::resizeEvent(QResizeEvent *e) {
- updateButtonsPositions();
- updateTitlePosition();
- const auto top = contentTop();
- _content->resize(width(), height() - top - buttonsHeight());
- _content->moveToLeft(0, top);
- LayerWidget::resizeEvent(e);
- }
- void BoxLayerWidget::keyPressEvent(QKeyEvent *e) {
- if (e->key() == Qt::Key_Escape) {
- closeBox();
- } else {
- LayerWidget::keyPressEvent(e);
- }
- }
- } // namespace Ui
|