// 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 // #pragma once #include "base/unique_qptr.h" #include "base/flags.h" #include "ui/dragging_scroll_manager.h" #include "ui/wrap/padding_wrap.h" #include "ui/widgets/labels.h" #include "ui/layers/layer_widget.h" #include "ui/layers/show.h" #include "ui/effects/animation_value.h" #include "ui/text/text_entity.h" #include "ui/rp_widget.h" enum class RectPart; using RectParts = base::flags; namespace base { class Timer; } // namespace base namespace style { struct RoundButton; struct IconButton; struct ScrollArea; struct Box; } // namespace style namespace st { extern const style::ScrollArea &boxScroll; } // namespace st namespace Ui::Toast { struct Config; class Instance; } // namespace Ui::Toast namespace Ui { class GenericBox; } // namespace Ui template inline object_ptr Box(Args &&...args) { const auto parent = static_cast(nullptr); return object_ptr(parent, std::forward(args)...); } namespace Ui { class AbstractButton; class RoundButton; class IconButton; class ScrollArea; class FlatLabel; class FadeShadow; class BoxContent; class BoxContentDelegate { public: virtual void setLayerType(bool layerType) = 0; virtual void setStyle(const style::Box &st) = 0; virtual const style::Box &style() = 0; virtual void setTitle(rpl::producer title) = 0; virtual void setAdditionalTitle(rpl::producer additional) = 0; virtual void setCloseByOutsideClick(bool close) = 0; virtual void clearButtons() = 0; virtual void addButton(object_ptr button) = 0; virtual void addLeftButton(object_ptr button) = 0; virtual void addTopButton(object_ptr button) = 0; virtual void showLoading(bool show) = 0; virtual void updateButtonsPositions() = 0; virtual void showBox( object_ptr box, LayerOptions options, anim::type animated) = 0; virtual void setDimensions( int newWidth, int maxHeight, bool forceCenterPosition = false) = 0; virtual void setNoContentMargin(bool noContentMargin) = 0; virtual bool isBoxShown() const = 0; virtual void closeBox() = 0; virtual void hideLayer() = 0; virtual void triggerButton(int index) = 0; template QPointer show( object_ptr content, LayerOptions options = LayerOption::KeepOther, anim::type animated = anim::type::normal) { auto result = QPointer(content.data()); showBox(std::move(content), options, animated); return result; } virtual ShowFactory showFactory() = 0; virtual QPointer outerContainer() = 0; }; class BoxContent : public RpWidget { public: BoxContent() { setAttribute(Qt::WA_OpaquePaintEvent); } bool isBoxShown() const { return getDelegate()->isBoxShown(); } void closeBox() { getDelegate()->closeBox(); } void triggerButton(int index) { getDelegate()->triggerButton(index); } void setTitle(rpl::producer title); void setTitle(rpl::producer title) { getDelegate()->setTitle(std::move(title)); } void setAdditionalTitle(rpl::producer additional) { getDelegate()->setAdditionalTitle(std::move(additional)); } void setCloseByEscape(bool close) { _closeByEscape = close; } void setCloseByOutsideClick(bool close) { getDelegate()->setCloseByOutsideClick(close); } void scrollToWidget(not_null widget); virtual void showFinished() { } virtual RectParts customCornersFilling(); void clearButtons() { getDelegate()->clearButtons(); } QPointer addButton(object_ptr button); QPointer addButton( rpl::producer text, Fn clickCallback = nullptr); QPointer addButton( rpl::producer text, const style::RoundButton &st); QPointer addButton( rpl::producer text, Fn clickCallback, const style::RoundButton &st); QPointer addLeftButton( object_ptr button); QPointer addLeftButton( rpl::producer text, Fn clickCallback = nullptr); QPointer addLeftButton( rpl::producer text, Fn clickCallback, const style::RoundButton& st); QPointer addTopButton( object_ptr button); QPointer addTopButton( const style::IconButton &st, Fn clickCallback = nullptr); void showLoading(bool show) { getDelegate()->showLoading(show); } void updateButtonsGeometry() { getDelegate()->updateButtonsPositions(); } void setStyle(const style::Box &st) { getDelegate()->setStyle(st); } virtual void setInnerFocus() { setFocus(); } [[nodiscard]] rpl::producer<> boxClosing() const { return _boxClosingStream.events(); } void notifyBoxClosing() { _boxClosingStream.fire({}); } void setDelegate(not_null newDelegate) { _delegate = newDelegate; _preparing = true; prepare(); finishPrepare(); } [[nodiscard]] bool hasDelegate() const { return _delegate != nullptr; } [[nodiscard]] not_null getDelegate() const { return _delegate; } void setNoContentMargin(bool noContentMargin) { if (_noContentMargin != noContentMargin) { _noContentMargin = noContentMargin; setAttribute(Qt::WA_OpaquePaintEvent, !_noContentMargin); } getDelegate()->setNoContentMargin(noContentMargin); } void scrollByDraggingDelta(int delta); void scrollToY(int top, int bottom = -1); void sendScrollViewportEvent(not_null event); [[nodiscard]] rpl::producer<> scrolls() const; [[nodiscard]] int scrollTop() const; [[nodiscard]] int scrollHeight() const; base::weak_ptr showToast(Toast::Config &&config); base::weak_ptr showToast( TextWithEntities &&text, crl::time duration = 0); base::weak_ptr showToast( const QString &text, crl::time duration = 0); [[nodiscard]] std::shared_ptr uiShow(); protected: virtual void prepare() = 0; void setLayerType(bool layerType) { getDelegate()->setLayerType(layerType); } void setTopShadowWithSkip(bool enabled) { _topShadowWithSkip = enabled; } void setBottomShadowWithSkip(bool enabled) { _bottomShadowWithSkip = enabled; } void setDimensions( int newWidth, int maxHeight, bool forceCenterPosition = false) { getDelegate()->setDimensions( newWidth, maxHeight, forceCenterPosition); } void setDimensionsToContent( int newWidth, not_null content); void setInnerTopSkip(int topSkip, bool scrollBottomFixed = false); void setInnerBottomSkip(int bottomSkip); template QPointer setInnerWidget( object_ptr inner, const style::ScrollArea &st, int topSkip = 0, int bottomSkip = 0) { auto result = QPointer(inner.data()); setInnerTopSkip(topSkip); setInnerBottomSkip(bottomSkip); setInner(std::move(inner), st); return result; } template QPointer setInnerWidget( object_ptr inner, int topSkip = 0, int bottomSkip = 0) { return setInnerWidget( std::move(inner), st::boxScroll, topSkip, bottomSkip); } template object_ptr takeInnerWidget() { return object_ptr::fromRaw( static_cast(doTakeInnerWidget().release())); } void setInnerVisible(bool scrollAreaVisible); QPixmap grabInnerCache(); void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; void keyPressEvent(QKeyEvent *e) override; private: void finishPrepare(); void finishScrollCreate(); void setInner(object_ptr inner, const style::ScrollArea &st); void updateScrollAreaGeometry(); void updateInnerVisibleTopBottom(); void updateShadowsVisibility(anim::type animated = anim::type::normal); object_ptr doTakeInnerWidget(); BoxContentDelegate *_delegate = nullptr; bool _preparing = false; bool _noContentMargin = false; bool _closeByEscape = true; bool _topShadowWithSkip = false; bool _bottomShadowWithSkip = false; int _innerTopSkip = 0; int _innerBottomSkip = 0; object_ptr _scroll = { nullptr }; object_ptr _topShadow = { nullptr }; object_ptr _bottomShadow = { nullptr }; Ui::DraggingScrollManager _draggingScroll; rpl::event_stream<> _boxClosingStream; }; class BoxPointer { public: BoxPointer() = default; BoxPointer(const BoxPointer &other) = default; BoxPointer(BoxPointer &&other) : _value(base::take(other._value)) { } BoxPointer(BoxContent *value) : _value(value) { } BoxPointer &operator=(const BoxPointer &other) { if (_value != other._value) { destroy(); _value = other._value; } return *this; } BoxPointer &operator=(BoxPointer &&other) { if (_value != other._value) { destroy(); _value = base::take(other._value); } return *this; } BoxPointer &operator=(BoxContent *other) { if (_value != other) { destroy(); _value = other; } return *this; } ~BoxPointer() { destroy(); } BoxContent *get() const { return _value.data(); } operator BoxContent*() const { return get(); } explicit operator bool() const { return get(); } BoxContent *operator->() const { return get(); } private: void destroy() { if (const auto value = base::take(_value)) { value->closeBox(); } } QPointer _value; }; } // namespace Ui