#pragma once #include #include #include #include #include #include #include #include #include #include "uitypes.h" #include "json/json.h" #include "splitter.h" class DockableTabWidget; /*! \defgroup tabwidget TabWidget \ingroup uiapi */ /*! \ingroup tabwidget */ class BINARYNINJAUIAPI DockableTabCollection : public QObject { Q_OBJECT std::set m_containers; public: DockableTabCollection(QObject* parent = nullptr) : QObject(parent) {} void registerContainer(DockableTabWidget* widget); void unregisterContainer(DockableTabWidget* widget); const std::set& containers() const { return m_containers; } }; /*! \ingroup tabwidget */ class BINARYNINJAUIAPI TabDragIndicator : public QWidget { Q_OBJECT QPixmap m_pixmap; QImage m_newWindowImage; QSize m_size, m_currentSize; QPoint m_offset; bool m_newWindow; public: TabDragIndicator(QPixmap pixmap, QSize size, QPoint pt, QPoint offset, bool newWindow); void moveToMouse(QPoint pt); QPixmap pixmap() const { return m_pixmap; } QSize size() const { return m_size; } QPoint offset() const { return m_offset; } protected: virtual QSize sizeHint() const override; virtual QSize minimumSizeHint() const override; virtual void paintEvent(QPaintEvent* event) override; }; /*! \ingroup tabwidget */ enum DockableTabInteractionState { NoTabInteraction, MouseOverTab, MouseOverCloseButton, CloseButtonPressActive, CloseButtonPressInactive }; /*! \ingroup tabwidget */ struct BINARYNINJAUIAPI DockableTabInfo { QString title; QString toolTip; QRect tabRect, closeButtonRect, closeIconRect; bool modifiedIndicator; bool canClose; bool isDrag = false; }; /*! \ingroup tabwidget */ class BINARYNINJAUIAPI DockableTabStyle { public: virtual ~DockableTabStyle() {} virtual QSize sizeForTab(const QWidget* widget, const DockableTabInfo& info, int idx, int count, int active) const; virtual QRect closeButtonRect( const QWidget* widget, const DockableTabInfo& info, int idx, int count, int active) const; virtual QRect closeIconRect( const QWidget* widget, const DockableTabInfo& info, int idx, int count, int active) const; virtual void paintTab(const QWidget* widget, QStylePainter& p, const DockableTabInfo& info, int idx, int count, int active, DockableTabInteractionState state, const QRect& rect) const; virtual void paintBase(const QWidget* widget, QStylePainter& p, const QRect& rect, const QRect& activeRect) const; virtual DockableTabStyle* duplicate(); }; /*! \ingroup tabwidget */ class BINARYNINJAUIAPI DefaultDockableTabStyle : public DockableTabStyle { QStyleOptionTab styleForTab( const QWidget* widget, const DockableTabInfo& info, int idx, int count, int active) const; int closeButtonSize(const QWidget* widget) const; public: virtual QSize sizeForTab( const QWidget* widget, const DockableTabInfo& info, int idx, int count, int active) const override; virtual QRect closeButtonRect( const QWidget* widget, const DockableTabInfo& info, int idx, int count, int active) const override; virtual QRect closeIconRect( const QWidget* widget, const DockableTabInfo& info, int idx, int count, int active) const override; virtual void paintTab(const QWidget* widget, QStylePainter& p, const DockableTabInfo& info, int idx, int count, int active, DockableTabInteractionState state, const QRect& rect) const override; virtual void paintBase( const QWidget* widget, QStylePainter& p, const QRect& rect, const QRect& activeRect) const override; virtual DockableTabStyle* duplicate() override; }; /*! \ingroup tabwidget */ class BINARYNINJAUIAPI DockableTabBar : public QAbstractScrollArea { Q_OBJECT DockableTabCollection* m_collection; DockableTabStyle* m_style; std::vector m_tabs; int m_active = -1; bool m_canCreateNewWindow = false; bool m_canSplit = false; bool m_mouseInside = false; int m_tabHover = -1; int m_closeButtonHover = -1; int m_closeButtonDown = -1; std::optional m_tabDragStart; TabDragIndicator* m_tabDragIndicator = nullptr; DockableTabBar* m_tabDragTarget = nullptr; int m_tabDragWidth; int m_tabDragTargetIndex = -1; std::optional m_tabDragSplitEdge; bool m_tabDragNewWindow = false; QRubberBand* m_tabDropIndicator = nullptr; int m_placeholderIndex = -1; int m_placeholderWidth; QRect m_placeholderRect; QTimer* m_timer; QTimer* m_hoverTimer; void updateLayout(); public: DockableTabBar(DockableTabCollection* collection); virtual ~DockableTabBar(); int addTab(const QString& title); int insertTab(int idx, const QString& title); void removeTab(int idx); void setCurrentIndex(int idx); void setTabText(int idx, const QString& title); void setTabToolTip(int idx, const QString& toolTip); void setTabModifiedIndicator(int idx, bool indicator); void setCanCloseTab(int idx, bool canClose); void setCanCreateNewWindow(bool canCreate); void setCanSplit(bool canSplit); int count() const; int currentIndex() const; int tabAt(const QPoint& pt); QRect tabRect(int idx); QString tabText(int idx); QString tabToolTip(int idx); bool tabModifiedIndicator(int idx); bool canCloseTab(int idx); bool canCreateNewWindow(); bool canSplit(); void ensureCurrentTabVisible(); DockableTabStyle* tabStyle() const { return m_style; } void setTabStyle(DockableTabStyle* style); Q_SIGNALS: void currentChanged(int idx); void tabCloseRequested(int idx); void tabMoved(int oldIdx, int newIdx); void newWindowForTab(int idx, QRect rectHint); void reparentTab(int oldIdx, DockableTabWidget* target, int newIdx); void splitTab(int idx, Qt::Edge edge); private Q_SLOTS: void underMouseTimerEvent(); void tabHoverTimerEvent(); protected: virtual QSize sizeHint() const override; virtual QSize minimumSizeHint() const override; virtual void paintEvent(QPaintEvent* event) override; virtual void enterEvent(QEnterEvent* event) override; virtual void leaveEvent(QEvent* event) override; virtual void mouseMoveEvent(QMouseEvent* event) override; virtual void mousePressEvent(QMouseEvent* event) override; virtual void mouseReleaseEvent(QMouseEvent* event) override; virtual void resizeEvent(QResizeEvent* event) override; virtual void wheelEvent(QWheelEvent* event) override; }; /*! \ingroup tabwidget */ class BINARYNINJAUIAPI DockableTabBarWithCornerWidget : public QWidget { DockableTabBar* m_bar; QHBoxLayout* m_barLayout; Qt::Corner m_corner = Qt::TopRightCorner; QWidget* m_cornerWidget = nullptr; QWidget* m_cornerWidgetContainer = nullptr; protected: virtual void paintEvent(QPaintEvent* event) override; public: DockableTabBarWithCornerWidget(DockableTabBar* bar); DockableTabBar* tabBar() const { return m_bar; } void setCornerWidget(QWidget* widget, Qt::Corner corner = Qt::TopRightCorner, bool expanding = false); Qt::Corner corner() const { return m_corner; } QWidget* cornerWidget() const { return m_cornerWidget; } }; /*! \ingroup tabwidget */ class BINARYNINJAUIAPI DockableTabWidget : public QWidget { Q_OBJECT DockableTabCollection* m_collection; DockableTabBar* m_bar; DockableTabBarWithCornerWidget* m_cornerWidget; QStackedWidget* m_widgets; void addReparentedTab(DockableTabWidget* source, int idx, QWidget* widget, const QString& title, const QString& toolTip, bool canClose); public: DockableTabWidget(DockableTabCollection* collection, DockableTabBar* bar = nullptr); int addTab(QWidget* widget, const QString& title); int insertTab(int idx, QWidget* widget, const QString& title); void removeTab(int idx); int count(); int currentIndex(); QWidget* currentWidget(); QWidget* widget(int idx); DockableTabBar* tabBar() const { return m_bar; } QStackedWidget* container() const { return m_widgets; } DockableTabCollection* collection() const { return m_collection; } QString tabText(int idx); bool tabModifiedIndicator(int idx); bool canCloseTab(int idx); bool canCreateNewWindow(); bool canSplit(); int indexOf(QWidget* widget); void setCurrentIndex(int idx); void setTabText(int idx, const QString& title); void setTabToolTip(int idx, const QString& toolTip); void setTabModifiedIndicator(int idx, bool indicator); void setCanCloseTab(int idx, bool canClose); void setCanCreateNewWindow(bool canCreate); void setCanSplit(bool canSplit); void setCornerWidget(QWidget* widget, Qt::Corner corner = Qt::TopRightCorner, bool expanding = false); Qt::Corner corner() const; QWidget* cornerWidget() const; DockableTabStyle* tabStyle() const { return m_bar->tabStyle(); } void setTabStyle(DockableTabStyle* style); virtual QSize sizeHint() const override; Q_SIGNALS: void tabCloseRequested(int idx); void currentChanged(int idx); void tabMoved(int oldIdx, int newIdx); void newWindowForTab(int idx, QRect rectHint); void tabRemovedForReparent(int oldIdx, QWidget* widget, DockableTabWidget* target, int newIdx); void tabAddedForReparent(int idx, DockableTabWidget* source); void splitTab(int idx, Qt::Edge edge); private Q_SLOTS: void tabBarCurrentChanged(int idx); void tabBarCloseRequested(int idx); void tabBarTabMoved(int oldIdx, int newIdx); void tabBarNewWindowForTab(int idx, QRect rectHint); void tabBarSplitTab(int idx, Qt::Edge edge); void reparentTab(int oldIdx, DockableTabWidget* target, int newIdx); }; class BINARYNINJAUIAPI FlexibleTabBar : public QWidget { DockableTabBar* m_bar; QHBoxLayout* m_overallBarLayout; QHBoxLayout* m_barLayout; QHBoxLayout* m_leftLayout; QHBoxLayout* m_rightLayout; public: enum FlexibleTabBarWidgetLocation { LeftCorner, RightCorner, AfterTabs }; FlexibleTabBar(DockableTabBar* bar); void addWidget(QWidget* widget, FlexibleTabBarWidgetLocation corner); void insertWidget(int idx, QWidget* widget, FlexibleTabBarWidgetLocation corner); protected: void paintEvent(QPaintEvent* event) override; }; /*! \ingroup tabwidget */ class BINARYNINJAUIAPI SplitTabWidget : public QWidget { Q_OBJECT DockableTabWidget* m_tabs = nullptr; Splitter* m_splitter = nullptr; SplitTabWidget* m_first = nullptr; SplitTabWidget* m_second = nullptr; QVBoxLayout* m_layout; SplitTabWidget(DockableTabWidget* tabs); SplitTabWidget(SplitTabWidget* first, SplitTabWidget* second, Qt::Orientation orientation); void splitTabInternal(QWidget* widget, QString title, bool canClose, Qt::Edge edge); void promoteChild(SplitTabWidget* child, SplitTabWidget* other); void enumerateTabTree(const std::function& func); Json::Value savedLayoutObject() const; void restoreLayoutObject(const Json::Value& layout); void restoreLayoutObjectWithTabs( const Json::Value& layout, std::map>& tabWidgets); DockableTabWidget* findFirstTabWidget(); void collapseEmptyTabs(); public: SplitTabWidget(DockableTabCollection* collection); void addTab(QWidget* widget, const QString& title); bool removeTab(QWidget* widget); void setCanCloseTab(QWidget* widget, bool canClose); void enumerateTabs(const std::function& func); void selectWidget(QWidget* widget); bool isWidgetVisible(QWidget* widget); bool closeTab(QWidget* widget); void setTabSizes(const QList& sizes); void setTabStyle(DockableTabStyle* style); void setCornerWidget(QWidget* widget, Qt::Corner corner = Qt::TopRightCorner, bool expanding = false); QString savedLayoutString() const; void restoreLayoutString(const QString& layout); bool isEmpty() const; Q_SIGNALS: void tabClosed(QWidget* widget); void currentChanged(QWidget* widget); void layoutChanged(); void splitSizeChanged(); private Q_SLOTS: void tabCloseRequested(int idx); void currentTabChanged(int idx); void splitTab(int idx, Qt::Edge edge); void tabRemovedForReparent(int oldIdx, QWidget* widget, DockableTabWidget* target, int newIdx); void childTabClosed(QWidget* widget); void childCurrentChanged(QWidget* widget); void childLayoutChanged(); };