/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "window/layer_widget.h" #include "base/unique_qptr.h" #include "ui/rp_widget.h" namespace style { struct RoundButton; struct IconButton; struct ScrollArea; } // namespace style namespace Ui { class RoundButton; class IconButton; class ScrollArea; class FlatLabel; class FadeShadow; } // namespace Ui class BoxContent; class BoxContentDelegate { public: virtual void setLayerType(bool layerType) = 0; virtual void setTitle(Fn titleFactory) = 0; virtual void setAdditionalTitle(Fn additionalFactory) = 0; virtual void setCloseByOutsideClick(bool close) = 0; virtual void clearButtons() = 0; virtual QPointer addButton(Fn textFactory, Fn clickCallback, const style::RoundButton &st) = 0; virtual QPointer addLeftButton(Fn textFactory, Fn clickCallback, const style::RoundButton &st) = 0; virtual QPointer addTopButton(const style::IconButton &st, Fn clickCallback) = 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; 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 QPointer outerContainer() = 0; }; class BoxContent : public Ui::RpWidget, protected base::Subscriber { Q_OBJECT public: BoxContent() { setAttribute(Qt::WA_OpaquePaintEvent); } bool isBoxShown() const { return getDelegate()->isBoxShown(); } void closeBox() { getDelegate()->closeBox(); } void setTitle(Fn titleFactory) { if (titleFactory) { getDelegate()->setTitle([titleFactory] { return TextWithEntities { titleFactory(), EntitiesInText() }; }); } else { getDelegate()->setTitle(Fn()); } } void setTitle(Fn titleFactory) { getDelegate()->setTitle(std::move(titleFactory)); } void setAdditionalTitle(Fn additional) { getDelegate()->setAdditionalTitle(std::move(additional)); } void setCloseByEscape(bool close) { _closeByEscape = close; } void setCloseByOutsideClick(bool close) { getDelegate()->setCloseByOutsideClick(close); } void scrollToWidget(not_null widget); void clearButtons() { getDelegate()->clearButtons(); } QPointer addButton(Fn textFactory, Fn clickCallback = nullptr); QPointer addLeftButton(Fn textFactory, Fn clickCallback = nullptr); QPointer addTopButton(const style::IconButton &st, Fn clickCallback = nullptr) { return getDelegate()->addTopButton(st, std::move(clickCallback)); } QPointer addButton(Fn textFactory, const style::RoundButton &st) { return getDelegate()->addButton(std::move(textFactory), nullptr, st); } QPointer addButton(Fn textFactory, Fn clickCallback, const style::RoundButton &st) { return getDelegate()->addButton(std::move(textFactory), std::move(clickCallback), st); } void updateButtonsGeometry() { getDelegate()->updateButtonsPositions(); } virtual void setInnerFocus() { setFocus(); } rpl::producer<> boxClosing() const { return _boxClosingStream.events(); } void notifyBoxClosing() { _boxClosingStream.fire({}); } void setDelegate(not_null newDelegate) { _delegate = newDelegate; _preparing = true; prepare(); finishPrepare(); } not_null getDelegate() const { return _delegate; } public slots: void onScrollToY(int top, int bottom = -1); void onDraggingScrollDelta(int delta); protected: virtual void prepare() = 0; void setLayerType(bool layerType) { getDelegate()->setLayerType(layerType); } void setNoContentMargin(bool noContentMargin) { if (_noContentMargin != noContentMargin) { _noContentMargin = noContentMargin; setAttribute(Qt::WA_OpaquePaintEvent, !_noContentMargin); } getDelegate()->setNoContentMargin(noContentMargin); } 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) { auto result = QPointer(inner.data()); setInnerTopSkip(topSkip); setInnerBottomSkip(bottomSkip); setInner(std::move(inner)); return result; } template object_ptr takeInnerWidget() { return static_object_cast(doTakeInnerWidget()); } void setInnerVisible(bool scrollAreaVisible); QPixmap grabInnerCache(); void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; void keyPressEvent(QKeyEvent *e) override; private slots: void onScroll(); void onInnerResize(); void onDraggingScrollTimer(); private: void finishPrepare(); void finishScrollCreate(); void setInner(object_ptr inner); void setInner(object_ptr inner, const style::ScrollArea &st); void updateScrollAreaGeometry(); void updateInnerVisibleTopBottom(); void updateShadowsVisibility(); object_ptr doTakeInnerWidget(); BoxContentDelegate *_delegate = nullptr; bool _preparing = false; bool _noContentMargin = false; bool _closeByEscape = true; int _innerTopSkip = 0; int _innerBottomSkip = 0; object_ptr _scroll = { nullptr }; object_ptr _topShadow = { nullptr }; object_ptr _bottomShadow = { nullptr }; object_ptr _draggingScrollTimer = { nullptr }; int _draggingScrollDelta = 0; rpl::event_stream<> _boxClosingStream; }; class AbstractBox : public Window::LayerWidget , public BoxContentDelegate , protected base::Subscriber { public: AbstractBox( not_null layer, object_ptr content); void parentResized() override; void setLayerType(bool layerType) override; void setTitle(Fn titleFactory) override; void setAdditionalTitle(Fn additionalFactory) override; void showBox( object_ptr box, LayerOptions options, anim::type animated) override; void clearButtons() override; QPointer addButton(Fn textFactory, Fn clickCallback, const style::RoundButton &st) override; QPointer addLeftButton(Fn textFactory, Fn clickCallback, const style::RoundButton &st) override; QPointer addTopButton(const style::IconButton &st, Fn clickCallback) override; void updateButtonsPositions() override; QPointer outerContainer() override; void setDimensions( int newWidth, int maxHeight, bool forceCenterPosition = false) override; void setNoContentMargin(bool noContentMargin) override { if (_noContentMargin != noContentMargin) { _noContentMargin = noContentMargin; updateSize(); } } bool isBoxShown() const override { return !isHidden(); } void closeBox() override { closeLayer(); } void setCloseByOutsideClick(bool close) override; bool closeByOutsideClick() const override; protected: void keyPressEvent(QKeyEvent *e) override; void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; void doSetInnerFocus() override { _content->setInnerFocus(); } void closeHook() override { _content->notifyBoxClosing(); } private: void paintAdditionalTitle(Painter &p); void updateTitlePosition(); void refreshTitle(); void refreshAdditionalTitle(); void refreshLang(); bool hasTitle() const; int titleHeight() const; int buttonsHeight() const; int buttonsTop() const; int contentTop() const; int countFullHeight() const; int countRealHeight() const; void updateSize(); not_null _layer; int _fullHeight = 0; bool _noContentMargin = false; int _maxContentHeight = 0; object_ptr _content; object_ptr _title = { nullptr }; Fn _titleFactory; QString _additionalTitle; Fn _additionalTitleFactory; int _titleLeft = 0; int _titleTop = 0; bool _layerType = false; bool _closeByOutsideClick = true; std::vector> _buttons; object_ptr _leftButton = { nullptr }; base::unique_qptr _topButton = { nullptr }; }; class BoxContentDivider : public Ui::RpWidget { public: BoxContentDivider(QWidget *parent); BoxContentDivider(QWidget *parent, int height); protected: void paintEvent(QPaintEvent *e) override; }; class BoxPointer { public: BoxPointer() = default; BoxPointer(const BoxPointer &other) = default; BoxPointer(BoxPointer &&other) : _value(base::take(other._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(); } private: void destroy() { if (const auto value = base::take(_value)) { value->closeBox(); } } QPointer _value; };