#pragma once #include "widgets/basewidget.hpp" #include "widgets/helper/droppreview.hpp" #include "widgets/helper/notebooktab.hpp" #include "widgets/split.hpp" #include #include #include #include #include #include #include #include #include #include #include class QJsonObject; namespace chatterino { namespace widgets { // // Note: This class is a spaghetti container. There is a lot of spaghetti code inside but it doesn't // expose any of it publicly. // class SplitContainer : public BaseWidget, pajlada::Signals::SignalHolder { Q_OBJECT public: struct Node; // fourtf: !!! preserve the order of left, up, right and down enum Direction { Left, Above, Right, Below }; struct Position final { private: Position() = default; Position(Node *_relativeNode, Direction _direcion) : relativeNode(_relativeNode) , direction(_direcion) { } Node *relativeNode; Direction direction; friend struct Node; friend class SplitContainer; }; private: struct DropRect final { QRect rect; Position position; DropRect(const QRect &_rect, const Position &_position) : rect(_rect) , position(_position) { } }; struct ResizeRect final { QRect rect; Node *node; bool vertical; ResizeRect(const QRect &_rect, Node *_node, bool _vertical) : rect(_rect) , node(_node) , vertical(_vertical) { } }; public: struct Node final { enum Type { EmptyRoot, _Split, VerticalContainer, HorizontalContainer }; Type getType(); Split *getSplit(); Node *getParent(); qreal getHorizontalFlex(); qreal getVerticalFlex(); const std::vector> &getChildren(); private: Type type; Split *split; Node *preferedFocusTarget; Node *parent; QRectF geometry; qreal flexH = 1; qreal flexV = 1; std::vector> children; Node(); Node(Split *_split, Node *_parent); bool isOrContainsNode(Node *_node); Node *findNodeContainingSplit(Split *_split); void insertSplitRelative(Split *_split, Direction _direction); void nestSplitIntoCollection(Split *_split, Direction _direction); void _insertNextToThis(Split *_split, Direction _direction); void setSplit(Split *_split); Position releaseSplit(); qreal getFlex(bool isVertical); qreal getSize(bool isVertical); qreal getChildrensTotalFlex(bool isVertical); void layout(bool addSpacing, float _scale, std::vector &dropRects, std::vector &resizeRects); static Type toContainerType(Direction _dir); friend class SplitContainer; }; private: class DropOverlay final : public QWidget { public: DropOverlay(SplitContainer *_parent = nullptr); void setRects(std::vector _rects); pajlada::Signals::NoArgSignal dragEnded; protected: void paintEvent(QPaintEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; void dragLeaveEvent(QDragLeaveEvent *event) override; void dropEvent(QDropEvent *event) override; private: std::vector rects; QPoint mouseOverPoint; SplitContainer *parent; }; class ResizeHandle final : public QWidget { public: SplitContainer *parent; Node *node; void setVertical(bool isVertical); ResizeHandle(SplitContainer *_parent = nullptr); void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; friend class SplitContainer; private: bool vertical; bool isMouseDown = false; }; public: SplitContainer(Notebook *parent); void appendNewSplit(bool openChannelNameDialog); void appendSplit(Split *split); void insertSplit(Split *split, const Position &position); void insertSplit(Split *split, Direction direction, Split *relativeTo); void insertSplit(Split *split, Direction direction, Node *relativeTo = nullptr); Position releaseSplit(Split *split); Position deleteSplit(Split *split); void selectNextSplit(Direction direction); void decodeFromJson(QJsonObject &obj); int getSplitCount(); const std::vector getSplits() const; void refreshTabTitle(); NotebookTab *getTab() const; Node *getBaseNode(); void setTab(NotebookTab *tab); void hideResizeHandles(); void resetMouseStatus(); static bool isDraggingSplit; static Split *draggingSplit; protected: void paintEvent(QPaintEvent *event) override; void focusInEvent(QFocusEvent *event) override; void leaveEvent(QEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: struct DropRegion { QRect rect; std::pair position; DropRegion(QRect rect, std::pair position) { this->rect = rect; this->position = position; } }; void addSplit(Split *split); std::vector dropRects; std::vector dropRegions; NotebookPageDropPreview dropPreview; DropOverlay overlay; std::vector> resizeHandles; QPoint mouseOverPoint; void layout(); Node baseNode; Split *selected; void setSelected(Split *selected); void selectSplitRecursive(Node *node, Direction direction); void focusSplitRecursive(Node *node, Direction direction); void setPreferedTargetRecursive(Node *node); NotebookTab *tab; std::vector splits; bool isDragging = false; void decodeNodeRecusively(QJsonObject &obj, Node *node); }; } // namespace widgets } // namespace chatterino