├── doc └── 1.gif ├── resource.h ├── tabwidget_demo.rc ├── resource ├── images │ ├── close.png │ ├── close_hover.png │ ├── x-capture-options.png │ ├── default_100_precent │ │ ├── max.png │ │ ├── min.png │ │ ├── tab.png │ │ ├── close.png │ │ ├── btn_add.png │ │ ├── restore.png │ │ ├── caption_bg.png │ │ ├── max_hover.png │ │ ├── min_hover.png │ │ ├── tab_check.png │ │ ├── tab_close.png │ │ ├── tab_hover.png │ │ ├── btn_add_hover.png │ │ ├── close_hover.png │ │ ├── close_pressed.png │ │ ├── max_pressed.png │ │ ├── min_pressed.png │ │ ├── restore_hover.png │ │ ├── btn_add_pressed.png │ │ ├── restore_pressed.png │ │ ├── tab_close_hover.png │ │ ├── btn_add_disabled.png │ │ ├── left_arrow_normal.png │ │ ├── right_arrow_normal.png │ │ ├── tab_close_pressed.png │ │ ├── left_arrow_disabled.png │ │ └── right_arrow_disabled.png │ └── default_200_precent │ │ ├── max.png │ │ ├── min.png │ │ ├── tab.png │ │ ├── close.png │ │ ├── btn_add.png │ │ ├── restore.png │ │ ├── caption_bg.png │ │ ├── max_hover.png │ │ ├── min_hover.png │ │ ├── tab_check.png │ │ ├── tab_close.png │ │ ├── tab_hover.png │ │ ├── btn_add_hover.png │ │ ├── close_hover.png │ │ ├── close_pressed.png │ │ ├── max_pressed.png │ │ ├── min_pressed.png │ │ ├── restore_hover.png │ │ ├── btn_add_pressed.png │ │ ├── restore_pressed.png │ │ ├── tab_close_hover.png │ │ ├── btn_add_disabled.png │ │ └── tab_close_pressed.png └── resource.qrc ├── script ├── readme.md ├── config.json └── auto_compile.py ├── main.cpp ├── qt_ext_tab_bar_define.h ├── tab_bar_signal.h ├── .gitignore ├── tab_widget_signal.h ├── button_widget.h ├── mainwindow.h ├── base_tab_bar_draw_helper.h ├── tabwidget_demo.sln ├── qt_ext_button.h ├── qt_ext_tabbar_style.h ├── tab_bar_draw_helper.h ├── mainwindow.ui ├── tab_widget_proxy.h ├── button_widget.cpp ├── weak_call_back.hpp ├── common_ui_define.h ├── draw_helper.h ├── common_ui_enums.h ├── qt_ext_button.cpp ├── NcFrameLessHelper.h ├── round_shadow_helper.h ├── qt_ext_tab_widget.h ├── mainwindow.cpp ├── qt_ext_tabbar.h ├── draw_helper.cpp ├── qt_ext_tabbar_style.cpp ├── tab_bar_draw_helper.cpp ├── tab_widget_proxy.cpp ├── button_widget.ui ├── qt_ext_tabbar.cpp ├── QtExtTabWidget.vcxproj.filters ├── round_shadow_helper.cpp ├── README.md ├── qt_ext_tab_widget.cpp ├── NcFrameLessHelper.cpp └── QtExtTabWidget.vcxproj /doc/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/doc/1.gif -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource.h -------------------------------------------------------------------------------- /tabwidget_demo.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/tabwidget_demo.rc -------------------------------------------------------------------------------- /resource/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/close.png -------------------------------------------------------------------------------- /resource/images/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/close_hover.png -------------------------------------------------------------------------------- /resource/images/x-capture-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/x-capture-options.png -------------------------------------------------------------------------------- /script/readme.md: -------------------------------------------------------------------------------- 1 | ## 脚本使用方法: 2 | - python版本3.8以上 3 | - 命令行 python auto_compile.py -p .\config.json -c 4 | -c 表示编译项目 5 | -p 表示指定配置 -------------------------------------------------------------------------------- /resource/images/default_100_precent/max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/max.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/min.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/tab.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/max.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/min.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/tab.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/close.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/close.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/btn_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/btn_add.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/restore.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/btn_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/btn_add.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/restore.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/caption_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/caption_bg.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/max_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/max_hover.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/min_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/min_hover.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/tab_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/tab_check.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/tab_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/tab_close.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/tab_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/tab_hover.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/caption_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/caption_bg.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/max_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/max_hover.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/min_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/min_hover.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/tab_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/tab_check.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/tab_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/tab_close.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/tab_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/tab_hover.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/btn_add_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/btn_add_hover.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/close_hover.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/close_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/close_pressed.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/max_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/max_pressed.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/min_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/min_pressed.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/restore_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/restore_hover.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/btn_add_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/btn_add_hover.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/close_hover.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/close_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/close_pressed.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/max_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/max_pressed.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/min_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/min_pressed.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/restore_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/restore_hover.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/btn_add_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/btn_add_pressed.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/restore_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/restore_pressed.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/tab_close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/tab_close_hover.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/btn_add_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/btn_add_pressed.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/restore_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/restore_pressed.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/tab_close_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/tab_close_hover.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/btn_add_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/btn_add_disabled.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/left_arrow_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/left_arrow_normal.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/right_arrow_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/right_arrow_normal.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/tab_close_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/tab_close_pressed.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/btn_add_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/btn_add_disabled.png -------------------------------------------------------------------------------- /resource/images/default_200_precent/tab_close_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_200_precent/tab_close_pressed.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/left_arrow_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/left_arrow_disabled.png -------------------------------------------------------------------------------- /resource/images/default_100_precent/right_arrow_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MingYueRuYa/QtExtTabWidget/HEAD/resource/images/default_100_precent/right_arrow_disabled.png -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "mainwindow.h" 4 | 5 | int main(int argc, char *argv[]) { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /qt_ext_tab_bar_define.h: -------------------------------------------------------------------------------- 1 | #ifndef QT_EXT_TAB_BAR_ENUM_H 2 | #define QT_EXT_TAB_BAR_ENUM_H 3 | 4 | namespace QtExtTabBarDef { 5 | 6 | enum class TabState { Unknown = 0, normal_, hover_, selected_ }; 7 | 8 | } 9 | 10 | #endif // QT_EXT_TAB_BAR_ENUM_H -------------------------------------------------------------------------------- /script/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "process_name":"QtExtTabWidget.exe", 3 | "update_code_command":"git pull", 4 | "pre_compile_command":"", 5 | "compile_file":"../tabwidget_demo.sln", 6 | "compile_tool_dir":"C:/Program Files (x86)/Microsoft Visual Studio 12.0/Common7/IDE", 7 | "start_process_path":"../win32/debug/QtExtTabWidget.exe", 8 | "start_process_args":[] 9 | } 10 | -------------------------------------------------------------------------------- /tab_bar_signal.h: -------------------------------------------------------------------------------- 1 | #ifndef TAB_BAR_SIGNAL_H 2 | #define TAB_BAR_SIGNAL_H 3 | 4 | #include 5 | 6 | class TabBarSingal : public QObject 7 | { 8 | Q_OBJECT 9 | 10 | public: 11 | TabBarSingal() {} 12 | ~TabBarSingal() {} 13 | 14 | signals: 15 | void addBtnClicked(); 16 | void closeTab(int index); 17 | void signalStartDrag(int index); 18 | void signalEndDrag(); 19 | 20 | }; 21 | 22 | #endif // TAB_BAR_SIGNAL_H -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # vs solution 35 | *.suo 36 | *.sdf 37 | *.user 38 | 39 | -------------------------------------------------------------------------------- /tab_widget_signal.h: -------------------------------------------------------------------------------- 1 | #ifndef TAB_WIDGET_SIGNAL_H 2 | #define TAB_WIDGET_SIGNAL_H 3 | 4 | #include 5 | 6 | class TabWidgetSignal : public QObject { 7 | Q_OBJECT 8 | 9 | public: 10 | TabWidgetSignal() {} 11 | ~TabWidgetSignal() {} 12 | 13 | signals: 14 | void addBtnClicked(); 15 | void tabInserted(int index); 16 | void tabClosed(int index); 17 | void closeWnd(); 18 | void minWnd(); 19 | void maxWnd(); 20 | void restoreWnd(); 21 | void tabBarDoubleClicked(); 22 | }; 23 | 24 | #endif // TAB_WIDGET_SIGNAL_H 25 | -------------------------------------------------------------------------------- /button_widget.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTON_WIDGET_H 2 | #define BUTTON_WIDGET_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class ButtonWidget; 8 | } 9 | 10 | class ButtonWidget : public QWidget 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit ButtonWidget(QWidget *parent = 0); 16 | ~ButtonWidget(); 17 | 18 | void setMaxRestoreBtnVisible(bool max_visible, bool restore_visible); 19 | 20 | Q_SIGNALS: 21 | void closeClicked(); 22 | void minClicked(); 23 | void maxClicked(); 24 | void restoreClicked(); 25 | 26 | protected Q_SLOTS: 27 | void on_btn_close_clicked(); 28 | void on_btn_min_clicked(); 29 | void on_btn_max_clicked(); 30 | void on_btn_restore_clicked(); 31 | 32 | private: 33 | void setupUI(); 34 | 35 | private: 36 | Ui::ButtonWidget *ui; 37 | bool is_maxed_ = false; 38 | }; 39 | 40 | #endif // BUTTON_WIDGET_H 41 | -------------------------------------------------------------------------------- /mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "qt_ext_tab_bar_define.h" 10 | 11 | namespace Ui { 12 | class MainWindow; 13 | } 14 | 15 | class NcFramelessHelper; 16 | using QtExtTabBarDef::TabState; 17 | 18 | class MainWindow : public QMainWindow { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit MainWindow(QWidget *parent = 0); 23 | ~MainWindow(); 24 | 25 | private: 26 | void setupUI(); 27 | bool filterTabBarEvent(QObject *obj, QEvent *event); 28 | 29 | protected slots: 30 | void OnTabInserted(int index); 31 | void OnCloseTab(int index); 32 | void OnAddBtnClicked(); 33 | void OnCloseWnd(); 34 | void OnMinWnd(); 35 | void OnMaxWnd(); 36 | void OnRestoreWnd(); 37 | void OnMaxOrRestore(); 38 | 39 | private: 40 | Ui::MainWindow *ui; 41 | NcFramelessHelper *frame_less_helper_ = nullptr; 42 | }; 43 | 44 | #endif // MAINWINDOW_H 45 | -------------------------------------------------------------------------------- /base_tab_bar_draw_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef base_tab_bar_draw_helper_h 2 | #define base_tab_bar_draw_helper_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "qt_ext_tab_bar_define.h" 9 | 10 | using QtExtTabBarDef::TabState; 11 | 12 | class BaseTabBarDrawHelper { 13 | public: 14 | BaseTabBarDrawHelper() = default; 15 | ~BaseTabBarDrawHelper() = default; 16 | 17 | virtual void paintTabAddBtn(QPainter &painter, const QRect &draw_rect, 18 | TabState tab_state) = 0; 19 | virtual void paintTab(QPainter &painter, const QRect &draw_rect, 20 | TabState tab_state, const QString &text, 21 | const QIcon &icon) = 0; 22 | virtual void paintTabBar(QPainter &painter, const QRect &draw_rect) = 0; 23 | virtual void paintScrollBtn(bool, QPainter &, const QRect &, 24 | const QToolButton *) = 0; 25 | virtual QRect calcIconBtnRect(bool, const QSize &, const QRect &) = 0; 26 | 27 | }; 28 | 29 | #endif // base_tab_bar_draw_helper_h -------------------------------------------------------------------------------- /tabwidget_demo.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QtExtTabWidget", "QtExtTabWidget.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.Build.0 = Debug|Win32 16 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.ActiveCfg = Release|Win32 17 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | Qt5Version = Qt5.6.3 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /qt_ext_button.h: -------------------------------------------------------------------------------- 1 | #ifndef QT_EXT_BUTTON_H 2 | #define QT_EXT_BUTTON_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class QtExtButton : public QPushButton 9 | { 10 | enum class ButtonState 11 | { 12 | Normal, 13 | Hover, 14 | Pressed, 15 | Disabled, 16 | }; 17 | 18 | struct BTN_IMAGE 19 | { 20 | QPixmap normal_pixmap_; 21 | QPixmap hover_pixmap_; 22 | QPixmap pressed_pixmap_; 23 | QPixmap disabled_pixmap_; 24 | }; 25 | 26 | public: 27 | QtExtButton(QWidget *parent = nullptr); 28 | ~QtExtButton(); 29 | // 0 normal, 1 hover, 2 pressed, 3 disabled 30 | void setImagePath(const QStringList &path); 31 | 32 | protected: 33 | virtual void paintEvent(QPaintEvent *ev) override; 34 | virtual void enterEvent(QEvent *event) override; 35 | virtual void leaveEvent(QEvent *event) override; 36 | virtual void mousePressEvent(QMouseEvent *event) override; 37 | virtual void mouseReleaseEvent(QMouseEvent *event) override; 38 | 39 | private: 40 | ButtonState state_ = ButtonState::Normal; 41 | BTN_IMAGE btn_image_; 42 | }; 43 | 44 | #endif // QT_EXT_BUTTON_H -------------------------------------------------------------------------------- /qt_ext_tabbar_style.h: -------------------------------------------------------------------------------- 1 | #ifndef TABBAR_STYLE_H 2 | #define TABBAR_STYLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class QToolButton; 9 | class QtExtTabBar; 10 | 11 | class QExtTabBarStyle : public QProxyStyle { 12 | public: 13 | QExtTabBarStyle(QtExtTabBar *tab_bar, QStyle *parent = Q_NULLPTR); 14 | ~QExtTabBarStyle() = default; 15 | 16 | virtual void drawPrimitive( 17 | PrimitiveElement pe, const QStyleOption *option, QPainter *painter, 18 | const QWidget *widget = Q_NULLPTR) const Q_DECL_OVERRIDE; 19 | virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, 20 | const QWidget *widget) const Q_DECL_OVERRIDE; 21 | 22 | virtual QRect subElementRect(SubElement element, const QStyleOption *option, 23 | const QWidget *widget) const Q_DECL_OVERRIDE; 24 | 25 | private: 26 | QRect calcIconRect(bool left, const QStyleOption *option) const; 27 | void drawArrow(PrimitiveElement pe, const QStyleOption *option, 28 | QPainter *painter, const QWidget *widget) const; 29 | 30 | private: 31 | QtExtTabBar *tab_bar_ = nullptr; 32 | }; 33 | 34 | #endif // TABBAR_STYLE_H -------------------------------------------------------------------------------- /tab_bar_draw_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef TAB_BAR_DRAW_HELPER_H 2 | #define TAB_BAR_DRAW_HELPER_H 3 | 4 | #include 5 | #include "base_tab_bar_draw_helper.h" 6 | 7 | class TabBarDrawHelper: public BaseTabBarDrawHelper 8 | { 9 | public: 10 | TabBarDrawHelper(); 11 | ~TabBarDrawHelper(); 12 | 13 | virtual void paintTabAddBtn(QPainter &painter, const QRect &draw_rect, 14 | TabState tab_state) override; 15 | virtual void paintTab(QPainter &painter, const QRect &draw_rect, 16 | TabState tab_state, const QString &text, 17 | const QIcon &icon) override; 18 | virtual void paintTabBar(QPainter &painter, const QRect &draw_rect) override; 19 | virtual void paintScrollBtn(bool, QPainter &, const QRect &, 20 | const QToolButton *) override; 21 | virtual QRect calcIconBtnRect(bool, const QSize &, const QRect &) override; 22 | 23 | private: 24 | QPixmap tab_bar_bk_image_; 25 | QPixmap tab_bk_normal_image_; 26 | QPixmap tab_bk_hover_image_; 27 | QPixmap tab_bk_selected_image_; 28 | QList left_arrow_pixmap_list_; 29 | QList right_arrow_pixmap_list_; 30 | 31 | Q_DISABLE_COPY(TabBarDrawHelper); 32 | }; 33 | 34 | 35 | #endif // TAB_BAR_DRAW_HELPER_H -------------------------------------------------------------------------------- /resource/resource.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/x-capture-options.png 4 | images/close.png 5 | images/close_hover.png 6 | images/default_100_precent/caption_bg.png 7 | images/default_100_precent/tab.png 8 | images/default_100_precent/tab_hover.png 9 | images/default_100_precent/tab_check.png 10 | images/default_100_precent/tab_close_hover.png 11 | images/default_100_precent/tab_close_pressed.png 12 | images/default_100_precent/min.png 13 | images/default_100_precent/min_hover.png 14 | images/default_100_precent/min_pressed.png 15 | images/default_100_precent/max.png 16 | images/default_100_precent/max_hover.png 17 | images/default_100_precent/max_pressed.png 18 | images/default_100_precent/close.png 19 | images/default_100_precent/close_hover.png 20 | images/default_100_precent/close_pressed.png 21 | images/default_100_precent/restore.png 22 | images/default_100_precent/restore_hover.png 23 | images/default_100_precent/restore_pressed.png 24 | images/default_100_precent/left_arrow_normal.png 25 | images/default_100_precent/left_arrow_disabled.png 26 | images/default_100_precent/right_arrow_normal.png 27 | images/default_100_precent/right_arrow_disabled.png 28 | 29 | 30 | -------------------------------------------------------------------------------- /mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 598 10 | 564 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 0 23 | 24 | 25 | 0 26 | 27 | 28 | 0 29 | 30 | 31 | 0 32 | 33 | 34 | 0 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | TabWidgetProxy 46 | QWidget 47 |
tab_widget_proxy.h
48 | 1 49 |
50 |
51 | 52 | 53 |
54 | -------------------------------------------------------------------------------- /tab_widget_proxy.h: -------------------------------------------------------------------------------- 1 | #ifndef TAB_WIDGET_PROXY_H 2 | #define TAB_WIDGET_PROXY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "qt_ext_tab_bar_define.h" 11 | #include "tab_widget_signal.h" 12 | 13 | class QtExtTabWidget; 14 | class NcFramelessHelper; 15 | class BaseTabBarDrawHelper; 16 | using QtExtTabBarDef::TabState; 17 | 18 | class TabWidgetProxy : public QWidget, 19 | public std::enable_shared_from_this { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit TabWidgetProxy(QWidget *parent = 0); 24 | ~TabWidgetProxy(); 25 | bool filterTabBarEvent(QObject *obj, QEvent *event); 26 | void addTab2(QWidget *widget, const QString &label); 27 | QTabBar *tabBar() const; 28 | const TabWidgetSignal &getSignal() const; 29 | void setTabButton2(int index, QTabBar::ButtonPosition, QWidget *btn); 30 | void setTabIcon(int index, const QIcon &icon); 31 | void updateDrawHelp(BaseTabBarDrawHelper *helper); 32 | 33 | private: 34 | void setupUI(); 35 | 36 | protected slots: 37 | void OnTabInserted(int index); 38 | void OnCloseTab(int index); 39 | void OnAddBtnClicked(); 40 | void OnCloseWnd(); 41 | void OnMinWnd(); 42 | void OnMaxWnd(); 43 | void OnRestoreWnd(); 44 | void OnMaxOrRestore(); 45 | 46 | private: 47 | bool isHasParent() const; 48 | void connectSignal(); 49 | 50 | private: 51 | NcFramelessHelper *frame_less_helper_ = nullptr; 52 | QtExtTabWidget *ext_tab_widget_; 53 | TabWidgetSignal signal_; 54 | }; 55 | 56 | #endif // TAB_WIDGET_PROXY_H 57 | -------------------------------------------------------------------------------- /button_widget.cpp: -------------------------------------------------------------------------------- 1 | #include "button_widget.h" 2 | #include "ui_button_widget.h" 3 | 4 | #include 5 | 6 | ButtonWidget::ButtonWidget(QWidget *parent) : 7 | QWidget(parent), 8 | ui(new Ui::ButtonWidget) 9 | { 10 | ui->setupUi(this); 11 | setupUI(); 12 | } 13 | 14 | ButtonWidget::~ButtonWidget() 15 | { 16 | delete ui; 17 | } 18 | 19 | void ButtonWidget::setMaxRestoreBtnVisible(bool max_visible, bool restore_visible) 20 | { 21 | ui->btn_max->setVisible(max_visible); 22 | ui->btn_restore->setVisible(restore_visible); 23 | } 24 | 25 | void ButtonWidget::setupUI() 26 | { 27 | QStringList image_path; 28 | image_path << ":/images/default_100_precent/restore.png" 29 | <<":/images/default_100_precent/restore_hover.png" 30 | << ":/images/default_100_precent/restore_pressed.png" 31 | << ""; 32 | ui->btn_restore->setImagePath(image_path); 33 | image_path.clear(); 34 | image_path << ":/images/default_100_precent/max.png" 35 | <<":/images/default_100_precent/max_hover.png" 36 | << ":/images/default_100_precent/max_pressed.png" 37 | << ""; 38 | ui->btn_max->setImagePath(image_path); 39 | ui->btn_restore->setVisible(false); 40 | } 41 | 42 | void ButtonWidget::on_btn_close_clicked() 43 | { 44 | emit closeClicked(); 45 | } 46 | 47 | void ButtonWidget::on_btn_min_clicked() 48 | { 49 | emit minClicked(); 50 | } 51 | 52 | void ButtonWidget::on_btn_max_clicked() 53 | { 54 | emit maxClicked(); 55 | } 56 | 57 | void ButtonWidget::on_btn_restore_clicked() 58 | { 59 | emit restoreClicked(); 60 | } 61 | -------------------------------------------------------------------------------- /weak_call_back.hpp: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style license 2 | // that can be found in the License file. 3 | // 4 | // Author: Shuo Chen (chenshuo at chenshuo dot com) 5 | 6 | #ifndef MUDUO_BASE_WEAKCALLBACK_H 7 | #define MUDUO_BASE_WEAKCALLBACK_H 8 | 9 | #include 10 | #include 11 | 12 | namespace muduo { 13 | 14 | // A barely usable WeakCallback 15 | 16 | template 17 | class WeakCallback { 18 | public: 19 | WeakCallback(const std::weak_ptr& object, 20 | const std::function& function) 21 | : object_(object), function_(function) {} 22 | 23 | // Default dtor, copy ctor and assignment are okay 24 | 25 | void operator()(ARGS&&... args) const { 26 | std::shared_ptr ptr(object_.lock()); 27 | if (ptr) { 28 | function_(ptr.get(), std::forward(args)...); 29 | } 30 | // else 31 | // { 32 | // LOG_TRACE << "expired"; 33 | // } 34 | } 35 | 36 | private: 37 | std::weak_ptr object_; 38 | std::function function_; 39 | }; 40 | 41 | template 42 | WeakCallback makeWeakCallback( 43 | const std::shared_ptr& object, void (CLASS::*function)(ARGS...)) { 44 | return WeakCallback(object, function); 45 | } 46 | 47 | template 48 | WeakCallback makeWeakCallback( 49 | const std::shared_ptr& object, 50 | void (CLASS::*function)(ARGS...) const) { 51 | return WeakCallback(object, function); 52 | } 53 | 54 | } // namespace muduo 55 | 56 | #endif // MUDUO_BASE_WEAKCALLBACK_H 57 | -------------------------------------------------------------------------------- /common_ui_define.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // 定义基本类型的属性 6 | #define UI_PROPERTY_B(TYPE, name, write, def, call) \ 7 | Q_PROPERTY(TYPE name READ name WRITE write) \ 8 | private: \ 9 | TYPE name##_ = def; \ 10 | \ 11 | public: \ 12 | TYPE name() const { return name##_; } \ 13 | void write(TYPE value) { \ 14 | if (name##_ != value) { \ 15 | name##_ = value; \ 16 | call; \ 17 | } \ 18 | } 19 | 20 | // 定义枚举类型的属性 21 | #define UI_PROPERTY_E(TYPE, name, write, def, call) \ 22 | Q_ENUMS(TYPE) \ 23 | Q_PROPERTY(TYPE name READ name WRITE write) \ 24 | private: \ 25 | TYPE name##_ = def; \ 26 | \ 27 | public: \ 28 | TYPE name() const { return name##_; } \ 29 | void write(TYPE value) { \ 30 | if (name##_ != value) { \ 31 | name##_ = value; \ 32 | call; \ 33 | } \ 34 | } 35 | 36 | #define HOVER_TAB_CONTAINER QColor(243, 244, 246) 37 | #define NORMAL_TAB_CONTAINER QColor(222, 225, 230) 38 | #define CIRCLE_BG_COLOR QColor(220, 220, 221) 39 | #define X_CHARACTER_COLOR QColor(60, 64, 67) 40 | #define NORMAL_TAB_COLOR QColor(240, 240, 240) 41 | -------------------------------------------------------------------------------- /draw_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef UI_DRAWHELPER_DRAWHELPER_H 2 | #define UI_DRAWHELPER_DRAWHELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "common_ui_define.h" 8 | 9 | class QPainter; 10 | 11 | class DrawCharacter { 12 | public: 13 | DrawCharacter() = default; 14 | ~DrawCharacter() = default; 15 | 16 | static void DrawX(QPainter* painter, 17 | const QRect& target_rect, 18 | const QColor& text_color = X_CHARACTER_COLOR, 19 | const QSize& text_size = QSize(8, 8), 20 | const int pen_width = 2); 21 | 22 | static void DrawPlus(QPainter* painter, 23 | const QRect& target_rect, 24 | const QColor& text_color = X_CHARACTER_COLOR, 25 | const QSize& text_size = QSize(8, 8), 26 | const int pen_width = 2); 27 | }; 28 | 29 | class DrawCircle { 30 | public: 31 | DrawCircle() = default; 32 | ~DrawCircle() = default; 33 | 34 | static void Draw(QPainter* painter, 35 | const QRect& target_rect, 36 | const QColor& bg_color = CIRCLE_BG_COLOR); 37 | }; 38 | 39 | class DrawRoundRect { 40 | public: 41 | DrawRoundRect() = default; 42 | ~DrawRoundRect() = default; 43 | 44 | static void Draw(QPainter* painter, 45 | const QRect& target_rect, 46 | const QColor& bg_color = CIRCLE_BG_COLOR, 47 | const int round_size = 5); 48 | }; 49 | 50 | // 未完成 51 | class DrawCircleArror { 52 | public: 53 | DrawCircleArror() = default; 54 | ~DrawCircleArror() = default; 55 | 56 | static void Draw(QPainter* painter, 57 | const QRect& target_rect, 58 | const QColor& bg_color = CIRCLE_BG_COLOR); 59 | }; 60 | 61 | #endif // UI_DRAWHELPER_DRAWHELPER_H 62 | -------------------------------------------------------------------------------- /common_ui_enums.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_UI_ENUMS_H 2 | #define COMMON_UI_ENUMS_H 3 | 4 | #include 5 | 6 | /*! \brief CommonUIEnums 7 | \author liushixiong (635672377@qq.com) 8 | \version 0.01 9 | \date 2022-1-11 13:39:50 10 | 11 | 专为插件构造的enum 12 | 须以这种方式构造enum否则在designer属性栏中看不到选择项 13 | 14 | */ 15 | class CommonUIEnums : public QObject { 16 | Q_OBJECT 17 | Q_ENUMS(DrawType) 18 | Q_ENUMS(Position9) 19 | Q_ENUMS(WidgetSizePolicy) 20 | Q_ENUMS(ImageSizePolicy) 21 | 22 | public: 23 | CommonUIEnums() = default; 24 | ~CommonUIEnums() = default; 25 | 26 | public: 27 | enum DrawType { DrawNone = 0, Circle, Rectangle }; 28 | 29 | // 九宫格位置 30 | enum Position9 { 31 | PosNone = 0, 32 | TopLeft = 1, 33 | TopMiddle, 34 | TopRight, 35 | 36 | RightMiddle, 37 | RightBottom, 38 | 39 | BottomMiddle, 40 | BottomLeft, 41 | 42 | LeftMiddle, 43 | HVCenter 44 | }; 45 | 46 | enum WidgetSizePolicy { 47 | WidgetSizeNone = 0, // 无动作,让布局自动调整 48 | 49 | // size不可变 50 | FixSize, // m_widgetSize_为准 51 | UseImageSize, // 保持和图片大小一致 52 | FixWidthWithImageRatio, // 宽度用户固定,并以Image ratio为准 53 | FixHeightWithImageRatio, // 高度用户固定,并以Image ratio为准 54 | 55 | // 可变,比例不变 56 | ScaleHeightByLayoutWithRatio, // 由布局器管理大小,width由布局器决定, 57 | // 然后再根据设置的比例计算大小 58 | ScaleWidthByLayoutWithRatio, // 由布局器管理大小,height由布局器决定,然后再根据设置的比例计算大小 59 | 60 | // 可变,随机比例 61 | WidgetSizeDragDrop // 用户在无布局的情况下自行拖拽 62 | }; 63 | 64 | enum ImageSizePolicy { 65 | ImageSizeNone = 0, 66 | // 不可变 67 | FixImageSize, // 图片大小固定,用户可设置以m_imageSize_为准 68 | SelfSize, // 图片大小固定,使用图片大小 69 | 70 | // 可变:确定比例 71 | AdjustifyImageSize, // 保持比例拉伸 72 | 73 | // 可变:不确定比例 74 | Stretch, // 双向拉伸 75 | }; 76 | }; 77 | 78 | using UIPosition9 = CommonUIEnums::Position9; 79 | using UIWidgetSizePolicy = CommonUIEnums::WidgetSizePolicy; 80 | using UIImageSizePolicy = CommonUIEnums::ImageSizePolicy; 81 | using UIDrawType = CommonUIEnums::DrawType; 82 | 83 | #endif // COMMON_UI_ENUMS_H 84 | -------------------------------------------------------------------------------- /qt_ext_button.cpp: -------------------------------------------------------------------------------- 1 | #include "qt_ext_button.h" 2 | #include 3 | 4 | QtExtButton::QtExtButton(QWidget *parent) 5 | : QPushButton(parent) 6 | { 7 | } 8 | 9 | QtExtButton::~QtExtButton() 10 | { 11 | } 12 | 13 | void QtExtButton::setImagePath(const QStringList &path) 14 | { 15 | if (path.size() < 4) 16 | return; 17 | btn_image_.normal_pixmap_.load(path[0]); 18 | btn_image_.hover_pixmap_.load(path[1]); 19 | btn_image_.pressed_pixmap_.load(path[2]); 20 | btn_image_.disabled_pixmap_.load(path[3]); 21 | update(); 22 | } 23 | 24 | void QtExtButton::paintEvent(QPaintEvent *ev) 25 | { 26 | QPainter painter(this); 27 | QPixmap *pixmap = nullptr; 28 | if (!isEnabled()) { // state_ == ButtonState::Disabled 29 | pixmap = &btn_image_.disabled_pixmap_; 30 | } else if (state_ == ButtonState::Normal) { 31 | pixmap = &btn_image_.normal_pixmap_; 32 | } else if (state_ == ButtonState::Hover) { 33 | pixmap = &btn_image_.hover_pixmap_; 34 | } else if (state_ == ButtonState::Pressed) { 35 | pixmap = &btn_image_.pressed_pixmap_; 36 | } else { 37 | return; 38 | } 39 | if (!pixmap->isNull()) 40 | painter.drawPixmap(this->rect(), *pixmap); 41 | } 42 | 43 | void QtExtButton::enterEvent(QEvent *event) 44 | { 45 | if (state_ == ButtonState::Hover) 46 | return; 47 | state_ = ButtonState::Hover; 48 | update(); 49 | QPushButton::enterEvent(event); 50 | } 51 | 52 | void QtExtButton::leaveEvent(QEvent *event) 53 | { 54 | if (state_ == ButtonState::Normal) 55 | return; 56 | state_ = ButtonState::Normal; 57 | update(); 58 | QPushButton::leaveEvent(event); 59 | } 60 | 61 | void QtExtButton::mousePressEvent(QMouseEvent *event) 62 | { 63 | if (state_ == ButtonState::Pressed) 64 | return; 65 | state_ = ButtonState::Pressed; 66 | update(); 67 | QPushButton::mousePressEvent(event); 68 | } 69 | 70 | void QtExtButton::mouseReleaseEvent(QMouseEvent *event) 71 | { 72 | if (state_ == ButtonState::Normal) 73 | return; 74 | state_ = ButtonState::Normal; 75 | update(); 76 | QPushButton::mouseReleaseEvent(event); 77 | } -------------------------------------------------------------------------------- /NcFrameLessHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | NcFramelessHelper, an easy way to support move/resize on 3 | frameless toplevel windows. 4 | Copyright (C) 2011 Nishant Parashar 5 | Email:- nishcode (at) gmail (dot) com 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | //Version 3.0.0 22 | 23 | #ifndef NC_FRAMELESS_HELPER_H 24 | #define NC_FRAMELESS_HELPER_H 25 | 26 | #include 27 | #include 28 | 29 | class NcFramelessHelperImpl; 30 | 31 | class NcFramelessHelper : public QObject 32 | { 33 | public: 34 | using FilterEventCallBack = std::function; 35 | 36 | public: 37 | explicit NcFramelessHelper( QObject* parent = 0 ); 38 | ~NcFramelessHelper(); 39 | 40 | void setFilterEventCallBack(FilterEventCallBack filterFunc); 41 | void activateOn( QWidget* topLevelWidget ); 42 | void activeOnWithChildWidget( QWidget* topLevelWidget , QWidget *tagetWidget); 43 | void removeFrom( QWidget* topLevelWidget ); 44 | 45 | void setWidgetMovable( bool movable ); 46 | bool isWidgetMovable(); 47 | 48 | void setWidgetResizable( bool resizable ); 49 | bool isWidgetResizable(); 50 | 51 | void useRubberBandOnMove( bool use ); 52 | bool isUsingRubberBandOnMove(); 53 | 54 | void useRubberBandOnResize( bool use ); 55 | bool isUsingRubberBandOnResisze(); 56 | 57 | //Make sure to leave the same content margins 58 | //around the widget as the newBorderWidth 59 | //this can be done by 60 | //yourWidget->layout()->setMargin( newBorderWidth ); 61 | //otherwise your widget will not expose the 62 | //area where this class works 63 | void setBorderWidth( int newBorderWidth ); 64 | int borderWidth(); 65 | 66 | protected: 67 | virtual bool eventFilter( QObject* obj, QEvent* event ); 68 | 69 | private: 70 | NcFramelessHelperImpl* d; 71 | // add by Shixiong.Liu 72 | FilterEventCallBack filter_event_call_back_ = nullptr; 73 | }; 74 | 75 | #endif // NC_FRAMELESS_HELPER_H 76 | 77 | -------------------------------------------------------------------------------- /round_shadow_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef ROUND_SHADOW_HELPER_H 2 | #define ROUND_SHADOW_HELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define DEFINE_CLASS_MEMBER_DEFAULT(type, mem_name, fun_name, value) \ 10 | protected: \ 11 | type mem_name##_ = value; \ 12 | \ 13 | public: \ 14 | type Get##fun_name() { return mem_name##_; }; \ 15 | void Set##fun_name(type val) { mem_name##_ = val; }; 16 | 17 | class RoundShadowHelper { 18 | public: 19 | enum RoundDirection { 20 | D_NONE = 0, 21 | D_TOP = 0x1 << 1, 22 | D_BOTTOM = 0x1 << 2, 23 | D_LEFT = 0x1 << 3, 24 | D_RIGHT = 0x1 << 4, 25 | D_ALL = D_TOP | D_BOTTOM | D_LEFT | D_RIGHT 26 | }; 27 | 28 | public: 29 | explicit RoundShadowHelper() = default; 30 | explicit RoundShadowHelper(const int shadow_width, const int radius); 31 | ~RoundShadowHelper() = default; 32 | void RoundShadow(QPainter* painter, 33 | const QRect& rect, 34 | RoundDirection dire = D_ALL); 35 | void FillRoundShadow(QPainter* painter, 36 | const QRect& rect, 37 | const QColor& fill_color, 38 | int radius); 39 | 40 | private: 41 | void draw4BorderRectShadow(QPainter* painter, 42 | int shadow_width, 43 | int shadow_width2X, 44 | const QRect& dest_rect, 45 | RoundDirection dirc); 46 | void draw4BorderArcShadow(QPainter* painter, 47 | int shadow_width, 48 | int shadow_width2X, 49 | const QRect& dest_rect, 50 | RoundDirection dirc); 51 | void drawShadowRect(QPainter* painter, 52 | const QPoint& startPoint, 53 | const QPoint& endPoint, 54 | const QRect& destRect); 55 | void drawShadowArc(QPainter* painter, 56 | const QPoint& startPoint, 57 | const QPoint& endPoint, 58 | const QPainterPath& painterPath); 59 | 60 | void getGradient(const QPoint& start_point, 61 | const int radius, 62 | QRadialGradient& radial_gradient); 63 | void getGradient(const QPoint& start_point, 64 | const QPoint& end_point, 65 | QLinearGradient& linear_gradient); 66 | 67 | private: 68 | void _getGradient(QGradient& gradient); 69 | 70 | DEFINE_CLASS_MEMBER_DEFAULT(int, shadow_width, ShadowWidth, 20) 71 | DEFINE_CLASS_MEMBER_DEFAULT(int, radius, Radius, 5) 72 | 73 | friend class RoundImageHelper; 74 | friend class RoundShadowWidget; 75 | }; 76 | 77 | #endif // !ROUND_SHADOW_HELPER_H 78 | -------------------------------------------------------------------------------- /qt_ext_tab_widget.h: -------------------------------------------------------------------------------- 1 | #ifndef QTEXTTABWIDGET_H 2 | #define QTEXTTABWIDGET_H 3 | 4 | #include 5 | #include 6 | 7 | #include "tab_widget_signal.h" 8 | #include "qt_ext_tabbar.h" 9 | 10 | class ButtonWidget; 11 | 12 | class QtExtTabWidget : public QTabWidget { 13 | Q_OBJECT 14 | 15 | public: 16 | enum class DragState { Unknown = 0, Merged, NoChanged }; 17 | 18 | public: 19 | explicit QtExtTabWidget(QWidget *parent = 0); 20 | ~QtExtTabWidget() = default; 21 | void initProperty(bool useScrollButtons = true, bool isMain = true); 22 | void addTab2(QWidget *widget, const QString &label); 23 | void insertTab2(int index, QWidget *widget, const QString &label); 24 | void setTabButton2(int index, QTabBar::ButtonPosition, QWidget *btn); 25 | void setMaxRestoreBtnVisible(bool max_visible, bool restore_visible); 26 | int pointInTabRectIndex(const QPoint &point); 27 | 28 | QSize tabSize() const; 29 | void setTabSize(const QSize &tab_size); 30 | 31 | bool tabAddButton() const; 32 | void setTabAddButton(bool draw_add); 33 | 34 | int tabAddButtonWidth() const; 35 | void setTabAddButtonWidth(int width); 36 | 37 | QtExtTabBar *tabBar2() const; 38 | const TabWidgetSignal &getSignal() const; 39 | 40 | // call back 41 | void setPaintAddBtnFunc(QtExtTabBar::PaintAddButtonFunc func); 42 | void setPaintTabFunc(QtExtTabBar::PaintTabFunc func); 43 | void setPaintTabBarFunc(QtExtTabBar::PaintTabBarFunc func); 44 | void setPaintScrollBtnFunc(QtExtTabBar::PaintScrollBtnFunc func); 45 | void setCalcIconBtnRectFunc(QtExtTabBar::CalcIconBtnRectFunc func); 46 | 47 | protected: 48 | virtual void paintEvent(QPaintEvent *ev) override; 49 | virtual void tabInserted(int index) override; 50 | virtual void resizeEvent(QResizeEvent *ev) override; 51 | virtual void dragEnterEvent(QDragEnterEvent *event) override; 52 | virtual void dragMoveEvent(QDragMoveEvent *event) override; 53 | virtual void dropEvent(QDropEvent *event) override; 54 | virtual void dragLeaveEvent(QDragLeaveEvent *event) override; 55 | 56 | protected: 57 | void setTabBar2(QTabBar *tab_bar); 58 | 59 | protected Q_SLOTS: 60 | void OnCloseTab(int index); 61 | void OnTabBarDoubleClicked(int index); 62 | void OnStartDrag(int index); 63 | void OnCloseWnd(); 64 | void OnMinWnd(); 65 | void OnMaxWnd(); 66 | void OnRestoreWnd(); 67 | bool OnMergeDroppedLabel(QDropEvent *event); 68 | QtExtTabWidget *createDraggedNewWindow(); 69 | 70 | private: 71 | void setupUI(); 72 | bool isInMain(); 73 | void deleteIfEmptyWindow(); 74 | QtExtTabWidget *newTabWidget(); 75 | 76 | private: 77 | static DragState drag_state_; 78 | static QString dragging_label_; 79 | static QIcon dragging_icon_; 80 | bool is_main_ = true; 81 | QPoint dragging_point_delta_ = QPoint(0, 0); 82 | int dragging_index_ = -1; 83 | QtExtTabBar *tab_bar_ = nullptr; 84 | ButtonWidget *button_widget_ = nullptr; 85 | QWidget *dragging_widget_ = nullptr; 86 | TabWidgetSignal signal_; 87 | }; 88 | 89 | #endif // QtExtTabWidget_H 90 | -------------------------------------------------------------------------------- /mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "NcFrameLessHelper.h" 7 | #include "draw_helper.h" 8 | #include "round_shadow_helper.h" 9 | #include "ui_mainwindow.h" 10 | #include "tab_bar_draw_helper.h" 11 | #include "weak_call_back.hpp" 12 | 13 | MainWindow::MainWindow(QWidget *parent) 14 | : QMainWindow(parent), ui(new Ui::MainWindow) { 15 | ui->setupUi(this); 16 | setupUI(); 17 | } 18 | 19 | MainWindow::~MainWindow() { 20 | delete ui; 21 | delete frame_less_helper_; 22 | } 23 | 24 | void MainWindow::setupUI() { 25 | this->setWindowFlags(Qt::FramelessWindowHint | 26 | Qt::X11BypassWindowManagerHint); 27 | frame_less_helper_ = new NcFramelessHelper(); 28 | frame_less_helper_->activateOn(this); 29 | frame_less_helper_->setFilterEventCallBack( 30 | std::bind(&TabWidgetProxy::filterTabBarEvent, ui->tabWidgetProxy, 31 | std::placeholders::_1, std::placeholders::_2)); 32 | frame_less_helper_->activeOnWithChildWidget(this, 33 | ui->tabWidgetProxy->tabBar()); 34 | 35 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(tabInserted(int)), this, 36 | SLOT(OnTabInserted(int))); 37 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(addBtnClicked()), this, 38 | SLOT(OnAddBtnClicked())); 39 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(tabClosed(int)), this, 40 | SLOT(OnCloseTab(int))); 41 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(closeWnd()), this, 42 | SLOT(OnCloseWnd())); 43 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(minWnd()), this, 44 | SLOT(OnMinWnd())); 45 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(maxWnd()), this, 46 | SLOT(OnMaxWnd())); 47 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(restoreWnd()), this, 48 | SLOT(OnRestoreWnd())); 49 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(tabBarDoubleClicked()), 50 | this, SLOT(OnMaxOrRestore())); 51 | 52 | ui->tabWidgetProxy->updateDrawHelp(new TabBarDrawHelper()); 53 | 54 | #ifdef _DEBUG 55 | QWidget *widget = new QWidget(); 56 | widget->setStyleSheet("background-color:#FF00FF00"); 57 | ui->tabWidgetProxy->addTab2(widget, tr("this is first tab tab tab tab tab")); 58 | #endif //_DEBUG 59 | 60 | } 61 | 62 | void MainWindow::OnTabInserted(int index) { 63 | QPushButton *button = new QPushButton(); 64 | button->setStyleSheet( 65 | "QPushButton{border-image: url(:/images/close.png)}" 66 | "QPushButton:hover{border-image: url(:/images/close_hover.png)}"); 67 | ui->tabWidgetProxy->setTabButton2(index, QTabBar::RightSide, button); 68 | ui->tabWidgetProxy->setTabIcon(index, QIcon(":/images/x-capture-options.png")); 69 | } 70 | 71 | void MainWindow::OnCloseTab(int index) { 72 | // todo something 73 | } 74 | 75 | void MainWindow::OnAddBtnClicked() { 76 | QWidget *widget = new QWidget(); 77 | widget->setStyleSheet("background-color:#FFFFFF00"); 78 | ui->tabWidgetProxy->addTab2(widget, tr("this is first tab")); 79 | } 80 | 81 | void MainWindow::OnCloseWnd() { this->close(); } 82 | 83 | void MainWindow::OnMinWnd() {} 84 | 85 | void MainWindow::OnMaxWnd() {} 86 | 87 | void MainWindow::OnRestoreWnd() {} 88 | 89 | void MainWindow::OnMaxOrRestore() {} 90 | -------------------------------------------------------------------------------- /qt_ext_tabbar.h: -------------------------------------------------------------------------------- 1 | #ifndef QTEXTTABBAR_H 2 | #define QTEXTTABBAR_H 3 | 4 | #include 5 | #include 6 | 7 | #include "common_ui_define.h" 8 | #include "qt_ext_tab_bar_define.h" 9 | #include "tab_bar_signal.h" 10 | #include "weak_call_back.hpp" 11 | 12 | class QtExtTabBar; 13 | class RoundShadowHelper; 14 | 15 | using QtExtTabBarDef::TabState; 16 | 17 | class QtExtTabBar : public QTabBar { 18 | Q_OBJECT 19 | 20 | public: 21 | using PaintAddButtonFunc = std::function; 23 | using PaintTabBarFunc = 24 | std::function; 25 | using PaintTabFunc = std::function; 28 | using PaintScrollBtnFunc = 29 | std::function; 30 | using CalcIconBtnRectFunc = 31 | std::function; 32 | 33 | public: 34 | explicit QtExtTabBar(QWidget *parent = 0, bool tab_add_button = true); 35 | 36 | QSize tabSize() const; 37 | void setTabSize(const QSize &tab_size); 38 | 39 | bool tabAddButton() const; 40 | void setTabAddButton(bool draw_add); 41 | 42 | int tabAddButtonWidth() const; 43 | void setTabAddButtonWidth(int width); 44 | 45 | int scrollBtnWidth() const; 46 | void setScrollBtnWidth(int width); 47 | 48 | void setTabButton2(int index, QTabBar::ButtonPosition btn_pos, QWidget *btn); 49 | int pointInTabRectIndex(const QPoint &point); 50 | void setPaintAddBtnFunc(PaintAddButtonFunc func); 51 | void setPaintTabFunc(PaintTabFunc func); 52 | void setPaintTabBarFunc(PaintTabBarFunc func); 53 | void setPaintScrollBtnFunc(PaintScrollBtnFunc func); 54 | void setCalcIconBtnRectFunc(CalcIconBtnRectFunc func); 55 | void copyOtherTabBar(const QtExtTabBar *bar); 56 | int tabCount() const; 57 | const TabBarSingal &getSignal() const; 58 | 59 | protected: 60 | QSize tabSizeHint(int index) const; 61 | virtual void paintEvent(QPaintEvent *event) override; 62 | virtual void mouseReleaseEvent(QMouseEvent *event) override; 63 | virtual void mousePressEvent(QMouseEvent *event) override; 64 | virtual void mouseMoveEvent(QMouseEvent *event) override; 65 | virtual bool event(QEvent *ev) override; 66 | virtual void focusOutEvent(QFocusEvent *e); 67 | 68 | private: 69 | void setupUI(); 70 | void updateUI(); 71 | void drawTab(QPainter *painter); 72 | void drawPlusBtn(QPainter *painter); 73 | 74 | Q_SIGNALS: 75 | void AddBtnClicked(); 76 | void CloseTab(int index); 77 | void signalStartDrag(int index); 78 | void signalEndDrag(); 79 | 80 | protected slots: 81 | void OnRightWidgetClicked(); 82 | 83 | private: 84 | QSize tab_size_ = QSize(230, 40); 85 | QPoint press_pos_ = QPoint(0, 0); 86 | TabState tab_btn_add_state_ = TabState::normal_; 87 | bool dragging_ = false; 88 | bool tab_add_button_ = true; 89 | int tab_add_button_width_ = 30; 90 | int scroll_btn_width_ = 30; 91 | PaintAddButtonFunc paint_add_btn_func_ = nullptr; 92 | PaintTabFunc paint_tab_func_ = nullptr; 93 | PaintTabBarFunc paint_tab_bar_func_ = nullptr; 94 | PaintScrollBtnFunc paint_scroll_btn_func_ = nullptr; 95 | CalcIconBtnRectFunc calc_icon_btn_rect_func_ = nullptr; 96 | TabBarSingal signal_; 97 | 98 | friend class QExtTabBarStyle; 99 | }; 100 | 101 | #endif // QTEXTTABBAR_H 102 | -------------------------------------------------------------------------------- /draw_helper.cpp: -------------------------------------------------------------------------------- 1 | #include "draw_helper.h" 2 | 3 | #include 4 | 5 | void DrawCharacter::DrawX(QPainter* painter, 6 | const QRect& target_rect, 7 | const QColor& text_color, 8 | const QSize& text_size, 9 | const int pen_width) { 10 | painter->save(); 11 | painter->setRenderHints(QPainter::Antialiasing, true); 12 | QRect x_rect = {0, 0, text_size.width(), text_size.height()}; 13 | x_rect.moveCenter(target_rect.center()); 14 | QPen pen; 15 | pen.setBrush(QBrush(text_color, Qt::SolidPattern)); 16 | pen.setStyle(Qt::SolidLine); 17 | pen.setWidth(pen_width); 18 | painter->setPen(pen); 19 | painter->drawLine(x_rect.topLeft(), x_rect.bottomRight()); 20 | painter->drawLine(x_rect.bottomLeft(), x_rect.topRight()); 21 | painter->restore(); 22 | } 23 | 24 | void DrawCharacter::DrawPlus(QPainter* painter, 25 | const QRect& target_rect, 26 | const QColor& text_color, 27 | const QSize& text_size, 28 | const int pen_width) { 29 | painter->save(); 30 | painter->setRenderHints(QPainter::Antialiasing, true); 31 | QRect rect = {0, 0, text_size.width(), text_size.height()}; 32 | rect.moveCenter(target_rect.center()); 33 | QPen pen; 34 | pen.setBrush(QBrush(text_color, Qt::SolidPattern)); 35 | pen.setStyle(Qt::SolidLine); 36 | pen.setWidth(pen_width); 37 | painter->setPen(pen); 38 | // 画 + 39 | painter->drawLine( 40 | QPoint(rect.left() + rect.width() / 2, rect.top()), 41 | QPoint(rect.left() + rect.width() / 2, rect.top() + rect.height())); 42 | // 画 - 43 | painter->drawLine( 44 | QPoint(rect.left(), rect.top() + rect.height() / 2), 45 | QPoint(rect.left() + rect.width(), rect.top() + rect.height() / 2)); 46 | painter->restore(); 47 | } 48 | 49 | void DrawCircle::Draw(QPainter* painter, 50 | const QRect& target_rect, 51 | const QColor& bg_color) { 52 | painter->save(); 53 | painter->setRenderHints(QPainter::Antialiasing, true); 54 | painter->setPen(Qt::NoPen); 55 | painter->setBrush(QBrush(bg_color)); 56 | painter->drawEllipse(target_rect); 57 | painter->restore(); 58 | } 59 | 60 | void DrawRoundRect::Draw(QPainter* painter, 61 | const QRect& target_rect, 62 | const QColor& bg_color, 63 | const int round_size) { 64 | painter->save(); 65 | painter->setRenderHints(QPainter::Antialiasing, true); 66 | painter->setPen(Qt::NoPen); 67 | painter->setBrush(QBrush(bg_color)); 68 | painter->drawRoundedRect(target_rect, round_size, round_size); 69 | painter->restore(); 70 | } 71 | 72 | void DrawCircleArror::Draw(QPainter* painter, 73 | const QRect& target_rect, 74 | const QColor& bg_color) { 75 | painter->save(); 76 | painter->setRenderHints(QPainter::Antialiasing, true); 77 | QRect rect = {0, 0, 15, 15}; 78 | rect.moveCenter(target_rect.center()); 79 | QPen pen; 80 | pen.setWidth(2); 81 | pen.setBrush(QBrush(QColor(95, 99, 104))); 82 | painter->setPen(pen); 83 | painter->drawArc(rect, 30 * 16, 310 * 16); 84 | painter->setPen(Qt::NoPen); 85 | 86 | painter->setBrush(QColor(Qt::green)); 87 | // static const QPointF points[3] = { 88 | // QPointF(0.0, 0.0), 89 | // QPointF(0.0, 20.0), 90 | // QPointF(20.0, 00.0), 91 | // }; 92 | // painter->drawPolygon(points, 3); 93 | QRect rect2 = {0, 0, 5, 5}; 94 | painter->drawRect(rect2); 95 | painter->restore(); 96 | } 97 | -------------------------------------------------------------------------------- /qt_ext_tabbar_style.cpp: -------------------------------------------------------------------------------- 1 | #include "qt_ext_tabbar_style.h" 2 | 3 | #include 4 | 5 | #include "qt_ext_tabbar.h" 6 | 7 | QExtTabBarStyle::QExtTabBarStyle(QtExtTabBar *tab_bar, QStyle *parent) 8 | : QProxyStyle(parent) { 9 | tab_bar_ = tab_bar; 10 | } 11 | 12 | void QExtTabBarStyle::drawPrimitive(PrimitiveElement pe, 13 | const QStyleOption *option, 14 | QPainter *painter, 15 | const QWidget *widget) const { 16 | if (pe == QStyle::PE_IndicatorArrowLeft || 17 | pe == QStyle::PE_IndicatorArrowRight) { 18 | drawArrow(pe, option, painter, widget); 19 | } else if (pe == QStyle::PE_IndicatorTabClose) { 20 | // tab_bar_->paint_tab_close_btn_func(); 21 | } else { 22 | QProxyStyle::drawPrimitive(pe, option, painter, widget); 23 | } 24 | } 25 | 26 | int QExtTabBarStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, 27 | const QWidget *widget) const { 28 | if (PM_TabBarScrollButtonWidth == metric) { 29 | return tab_bar_->scrollBtnWidth(); 30 | } else { 31 | return QProxyStyle::pixelMetric(metric, option, widget); 32 | } 33 | } 34 | 35 | QRect QExtTabBarStyle::subElementRect(SubElement element, 36 | const QStyleOption *option, 37 | const QWidget *widget) const { 38 | if (SE_TabBarTabLeftButton == element || SE_TabBarTabRightButton == element) { 39 | const QStyleOptionTab *tab_option = 40 | qstyleoption_cast(option); 41 | if (nullptr != tab_bar_->calc_icon_btn_rect_func_) { 42 | return tab_bar_->calc_icon_btn_rect_func_( 43 | SE_TabBarTabLeftButton == element, tab_option->iconSize, 44 | tab_option->rect); 45 | } else { 46 | return calcIconRect(SE_TabBarTabLeftButton == element, tab_option); 47 | } 48 | } else { 49 | return QProxyStyle::subElementRect(element, option, widget); 50 | } 51 | } 52 | 53 | QRect QExtTabBarStyle::calcIconRect(bool left, 54 | const QStyleOption *option) const { 55 | const QStyleOptionTab *tab_option = 56 | qstyleoption_cast(option); 57 | QSize icon_size = tab_option->iconSize; 58 | const QRect tab_rect = tab_option->rect; 59 | QPoint center_pos; 60 | QRect button_rect; 61 | const int icon_padding = 8; 62 | if (left) { 63 | center_pos = 64 | QPoint(icon_padding + icon_size.width() / 2 + tab_rect.x(), 65 | tab_rect.y() + (tab_rect.height() - icon_size.height()) / 2 + 66 | icon_size.height() / 2); 67 | } else { 68 | center_pos = QPoint( 69 | tab_rect.x() + tab_rect.width() - icon_padding - icon_size.width() / 2, 70 | tab_rect.y() + (tab_rect.height() - icon_size.height()) / 2 + 71 | icon_size.height() / 2); 72 | } 73 | button_rect = QRect(QPoint(0, 0), icon_size); 74 | button_rect.moveCenter(center_pos); 75 | return button_rect; 76 | } 77 | 78 | void QExtTabBarStyle::drawArrow(PrimitiveElement pe, const QStyleOption *option, 79 | QPainter *painter, 80 | const QWidget *widget) const { 81 | const QToolButton *tool_btn = static_cast(widget); 82 | bool left = (pe == QStyle::PE_IndicatorArrowLeft); 83 | if (nullptr != tool_btn && !tool_btn->isVisible()) return; 84 | if (nullptr != tab_bar_->paint_scroll_btn_func_) { 85 | tab_bar_->paint_scroll_btn_func_(left, *painter, option->rect, tool_btn); 86 | } else { 87 | QProxyStyle::drawPrimitive(pe, option, painter, widget); 88 | } 89 | } -------------------------------------------------------------------------------- /tab_bar_draw_helper.cpp: -------------------------------------------------------------------------------- 1 | #include "tab_bar_draw_helper.h" 2 | 3 | #include 4 | 5 | #include "draw_helper.h" 6 | #include "round_shadow_helper.h" 7 | 8 | TabBarDrawHelper::TabBarDrawHelper() { 9 | tab_bar_bk_image_.load(":/images/default_100_precent/caption_bg.png"); 10 | tab_bk_normal_image_.load(":/images/default_100_precent/tab.png"); 11 | tab_bk_hover_image_.load(":/images/default_100_precent/tab_hover.png"); 12 | tab_bk_selected_image_.load(":/images/default_100_precent/tab_check.png"); 13 | 14 | left_arrow_pixmap_list_.push_back( 15 | QPixmap(":/images/default_100_precent/left_arrow_normal.png")); 16 | left_arrow_pixmap_list_.push_back( 17 | QPixmap(":/images/default_100_precent/left_arrow_disabled.png")); 18 | 19 | right_arrow_pixmap_list_.push_back( 20 | QPixmap(":/images/default_100_precent/right_arrow_normal.png")); 21 | right_arrow_pixmap_list_.push_back( 22 | QPixmap(":/images/default_100_precent/right_arrow_disabled.png")); 23 | } 24 | 25 | TabBarDrawHelper::~TabBarDrawHelper() {} 26 | 27 | void TabBarDrawHelper::paintTabAddBtn(QPainter &painter, const QRect &rect, 28 | TabState tab_state) { 29 | QColor color = (TabState::hover_ == tab_state) ? QColor(255, 255, 255, 50) 30 | : Qt::transparent; 31 | painter.save(); 32 | QRect draw_rect = QRect(QPoint(0, 0), QSize(20, 20)); 33 | draw_rect.moveCenter(rect.center()); 34 | DrawCircle::Draw(&painter, draw_rect, color); 35 | DrawCharacter::DrawPlus(&painter, draw_rect, Qt::white); 36 | painter.restore(); 37 | } 38 | 39 | void TabBarDrawHelper::paintTab(QPainter &painter, const QRect &draw_rect, 40 | TabState tab_state, const QString &text, 41 | const QIcon &icon) { 42 | painter.save(); 43 | painter.setPen(Qt::NoPen); 44 | painter.setRenderHint(QPainter::SmoothPixmapTransform); 45 | if (TabState::selected_ == tab_state) { 46 | if (!tab_bk_selected_image_.isNull()) 47 | painter.drawPixmap(draw_rect, tab_bk_selected_image_); 48 | } else if (TabState::hover_ == tab_state) { 49 | if (!tab_bk_hover_image_.isNull()) 50 | painter.drawPixmap(draw_rect, tab_bk_hover_image_); 51 | } else if (TabState::normal_ == tab_state) { 52 | if (!tab_bk_normal_image_.isNull()) 53 | painter.drawPixmap(draw_rect, tab_bk_normal_image_); 54 | } 55 | QRect real_rect = draw_rect; 56 | real_rect -= QMargins(35, 0, 30, 0); 57 | painter.setPen(Qt::white); 58 | QString draw_text = painter.fontMetrics().elidedText(text, Qt::ElideRight, 59 | real_rect.width(), 0); 60 | painter.drawText(real_rect, Qt::AlignLeft | Qt::AlignVCenter, draw_text); 61 | painter.restore(); 62 | // draw tab icon 63 | if (!icon.isNull()) { 64 | painter.save(); 65 | painter.setPen(Qt::NoPen); 66 | QList actual_size = icon.availableSizes(); 67 | if (actual_size.empty()) { 68 | QSize icon_size = actual_size[0]; 69 | QPoint icon_point = QPoint(draw_rect.left() + 5, 70 | (draw_rect.height() - icon_size.height()) / 2); 71 | QRect icon_rect(icon_point, icon_size); 72 | painter.drawPixmap(icon_rect, icon.pixmap(icon_size)); 73 | } 74 | painter.restore(); 75 | } 76 | } 77 | 78 | void TabBarDrawHelper::paintTabBar(QPainter &painter, const QRect &rect) { 79 | if (tab_bar_bk_image_.isNull()) return; 80 | painter.save(); 81 | painter.drawPixmap(rect, tab_bar_bk_image_); 82 | painter.restore(); 83 | } 84 | 85 | void TabBarDrawHelper::paintScrollBtn(bool left, QPainter &painter, 86 | const QRect &rect, 87 | const QToolButton *tool_btn) { 88 | QBrush rect_brush = Qt::transparent; 89 | if (tool_btn->underMouse()) { 90 | rect_brush = QColor(214, 214, 214, 100); 91 | } 92 | 93 | QRect draw_rect = QRect(0, 0, 20, 20); 94 | draw_rect.moveCenter(rect.center()); 95 | RoundShadowHelper round_helper; 96 | round_helper.FillRoundShadow(&painter, draw_rect, rect_brush.color(), 4); 97 | 98 | draw_rect = QRect(0, 0, 16, 16); 99 | draw_rect.moveCenter(rect.center()); 100 | painter.save(); 101 | if (!tool_btn->isEnabled()) { 102 | // disabled 103 | painter.drawPixmap(draw_rect, left ? left_arrow_pixmap_list_[1] 104 | : right_arrow_pixmap_list_[1]); 105 | } else { 106 | // normal 107 | painter.drawPixmap(draw_rect, left ? left_arrow_pixmap_list_[0] 108 | : right_arrow_pixmap_list_[0]); 109 | } 110 | 111 | painter.restore(); 112 | } 113 | 114 | QRect TabBarDrawHelper::calcIconBtnRect(bool left_button, 115 | const QSize &icon_size, 116 | const QRect &btn_rect) { 117 | QPoint center_pos = QPoint(0, 0); 118 | QRect button_rect = QRect(0, 0, 0, 0); 119 | const int icon_padding = 8; 120 | if (left_button) { 121 | center_pos = 122 | QPoint(icon_padding + icon_size.width() / 2 + btn_rect.x(), 123 | btn_rect.y() + (btn_rect.height() - icon_size.height()) / 2 + 124 | icon_size.height() / 2); 125 | } else { 126 | center_pos = QPoint( 127 | btn_rect.x() + btn_rect.width() - icon_padding - icon_size.width() / 2, 128 | btn_rect.y() + (btn_rect.height() - icon_size.height()) / 2 + 129 | icon_size.height() / 2); 130 | } 131 | button_rect = QRect(QPoint(0, 0), icon_size); 132 | button_rect.moveCenter(center_pos); 133 | return button_rect; 134 | } 135 | -------------------------------------------------------------------------------- /tab_widget_proxy.cpp: -------------------------------------------------------------------------------- 1 | #include "tab_widget_proxy.h" 2 | 3 | #include 4 | 5 | #include "NcFrameLessHelper.h" 6 | #include "base_tab_bar_draw_helper.h" 7 | #include "draw_helper.h" 8 | #include "qt_ext_tab_widget.h" 9 | #include "qt_ext_tabbar.h" 10 | #include "round_shadow_helper.h" 11 | #include "weak_call_back.hpp" 12 | 13 | TabWidgetProxy::TabWidgetProxy(QWidget *parent) : QWidget(parent) { setupUI(); } 14 | 15 | TabWidgetProxy::~TabWidgetProxy() { delete frame_less_helper_; } 16 | 17 | void TabWidgetProxy::setupUI() { 18 | this->setWindowFlags(Qt::FramelessWindowHint | 19 | Qt::X11BypassWindowManagerHint); 20 | 21 | QHBoxLayout *layout = new QHBoxLayout(this); 22 | layout->setSpacing(0); 23 | layout->setContentsMargins(0, 0, 0, 0); 24 | ext_tab_widget_ = new QtExtTabWidget(this); 25 | layout->addWidget(ext_tab_widget_); 26 | ext_tab_widget_->initProperty(); 27 | connectSignal(); 28 | } 29 | 30 | bool TabWidgetProxy::filterTabBarEvent(QObject *obj, QEvent *event) { 31 | // Only click on the tab bar non-tab area can be dragged 32 | if (event->type() != QEvent::MouseButtonPress) return false; 33 | 34 | QMouseEvent *mouse_event = dynamic_cast(event); 35 | // (只有在拖拽区域不在tab rect区域 || 只有一个tab标签时才可以拖动) 36 | if (isMaximized()) { 37 | return true; 38 | } else if (ext_tab_widget_->tabBar2() == obj && 39 | (ext_tab_widget_->pointInTabRectIndex(mouse_event->pos()) < 0 || 40 | ext_tab_widget_->tabBar2()->tabCount() == 1)) { 41 | return false; 42 | } else { 43 | return true; 44 | } 45 | } 46 | 47 | void TabWidgetProxy::addTab2(QWidget *widget, const QString &label) { 48 | ext_tab_widget_->insertTab2(-1, widget, label); 49 | } 50 | 51 | QTabBar *TabWidgetProxy::tabBar() const { return ext_tab_widget_->tabBar(); } 52 | 53 | const TabWidgetSignal &TabWidgetProxy::getSignal() const { return signal_; } 54 | 55 | void TabWidgetProxy::setTabButton2(int index, QTabBar::ButtonPosition btn_pos, 56 | QWidget *btn) { 57 | ext_tab_widget_->setTabButton2(index, btn_pos, btn); 58 | } 59 | 60 | void TabWidgetProxy::setTabIcon(int index, const QIcon &icon) { 61 | ext_tab_widget_->setTabIcon(index, icon); 62 | } 63 | 64 | void TabWidgetProxy::updateDrawHelp(BaseTabBarDrawHelper *helper) { 65 | ext_tab_widget_->setPaintAddBtnFunc(std::bind( 66 | &BaseTabBarDrawHelper::paintTabAddBtn, helper, std::placeholders::_1, 67 | std::placeholders::_2, std::placeholders::_3)); 68 | ext_tab_widget_->setPaintTabFunc( 69 | std::bind(&BaseTabBarDrawHelper::paintTab, helper, std::placeholders::_1, 70 | std::placeholders::_2, std::placeholders::_3, 71 | std::placeholders::_4, std::placeholders::_5)); 72 | ext_tab_widget_->setPaintTabBarFunc( 73 | std::bind(&BaseTabBarDrawHelper::paintTabBar, helper, 74 | std::placeholders::_1, std::placeholders::_2)); 75 | ext_tab_widget_->setPaintScrollBtnFunc(std::bind( 76 | &BaseTabBarDrawHelper::paintScrollBtn, helper, std::placeholders::_1, 77 | std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); 78 | ext_tab_widget_->setCalcIconBtnRectFunc(std::bind( 79 | &BaseTabBarDrawHelper::calcIconBtnRect, helper, std::placeholders::_1, 80 | std::placeholders::_2, std::placeholders::_3)); 81 | } 82 | 83 | void TabWidgetProxy::OnTabInserted(int index) { 84 | if (isHasParent()) emit signal_.tabInserted(index); 85 | } 86 | 87 | void TabWidgetProxy::OnCloseTab(int index) { 88 | if (isHasParent()) emit signal_.tabClosed(index); 89 | } 90 | 91 | void TabWidgetProxy::OnAddBtnClicked() { emit signal_.addBtnClicked(); } 92 | 93 | void TabWidgetProxy::OnCloseWnd() { 94 | if (isHasParent()) 95 | emit signal_.closeWnd(); 96 | else 97 | this->close(); 98 | } 99 | 100 | void TabWidgetProxy::OnMinWnd() { 101 | if (isHasParent()) { 102 | emit signal_.minWnd(); 103 | QWidget *parent = window(); 104 | parent->showMinimized(); 105 | } 106 | 107 | this->showMinimized(); 108 | } 109 | 110 | void TabWidgetProxy::OnMaxWnd() { 111 | if (isHasParent()) { 112 | emit signal_.maxWnd(); 113 | QWidget *parent = window(); 114 | parent->showMaximized(); 115 | } else { 116 | this->showMaximized(); 117 | ext_tab_widget_->currentWidget()->setFocus(); 118 | } 119 | ext_tab_widget_->setMaxRestoreBtnVisible(false, true); 120 | } 121 | 122 | void TabWidgetProxy::OnRestoreWnd() { 123 | if (isHasParent()) { 124 | emit signal_.restoreWnd(); 125 | QWidget *parent = window(); 126 | parent->showNormal(); 127 | } else { 128 | this->showNormal(); 129 | } 130 | ext_tab_widget_->setMaxRestoreBtnVisible(true, false); 131 | } 132 | 133 | void TabWidgetProxy::OnMaxOrRestore() { 134 | if (isHasParent()) { 135 | QWidget *parent = window(); 136 | if (parent->isMaximized()) { 137 | parent->showNormal(); 138 | ext_tab_widget_->setMaxRestoreBtnVisible(true, false); 139 | } else { 140 | parent->showMaximized(); 141 | ext_tab_widget_->setMaxRestoreBtnVisible(false, true); 142 | } 143 | emit signal_.tabBarDoubleClicked(); 144 | } else { 145 | if (isMaximized()) { 146 | this->showNormal(); 147 | ext_tab_widget_->setMaxRestoreBtnVisible(true, false); 148 | } else { 149 | this->showMaximized(); 150 | ext_tab_widget_->setMaxRestoreBtnVisible(false, true); 151 | } 152 | } 153 | } 154 | 155 | bool TabWidgetProxy::isHasParent() const { return nullptr != parentWidget(); } 156 | 157 | void TabWidgetProxy::connectSignal() { 158 | connect(&(ext_tab_widget_->getSignal()), SIGNAL(tabInserted(int)), this, 159 | SLOT(OnTabInserted(int))); 160 | connect(&(ext_tab_widget_->getSignal()), SIGNAL(addBtnClicked()), this, 161 | SLOT(OnAddBtnClicked())); 162 | connect(&(ext_tab_widget_->getSignal()), SIGNAL(tabClosed(int)), this, 163 | SLOT(OnCloseTab(int))); 164 | connect(&(ext_tab_widget_->getSignal()), SIGNAL(closeWnd()), this, 165 | SLOT(OnCloseWnd())); 166 | connect(&(ext_tab_widget_->getSignal()), SIGNAL(minWnd()), this, 167 | SLOT(OnMinWnd())); 168 | connect(&(ext_tab_widget_->getSignal()), SIGNAL(maxWnd()), this, 169 | SLOT(OnMaxWnd())); 170 | connect(&(ext_tab_widget_->getSignal()), SIGNAL(restoreWnd()), this, 171 | SLOT(OnRestoreWnd())); 172 | connect(&(ext_tab_widget_->getSignal()), SIGNAL(tabBarDoubleClicked()), this, 173 | SLOT(OnMaxOrRestore())); 174 | } -------------------------------------------------------------------------------- /button_widget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ButtonWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 320 10 | 89 11 | 12 | 13 | 14 | 15 | 16 | 17 | background-color: rgb(170, 170, 127); 18 | 19 | 20 | 21 | 0 22 | 23 | 24 | 0 25 | 26 | 27 | 0 28 | 29 | 30 | 0 31 | 32 | 33 | 0 34 | 35 | 36 | 37 | 38 | background-color: rgb(52, 52, 54); 39 | 40 | 41 | 42 | 0 43 | 44 | 45 | 0 46 | 47 | 48 | 0 49 | 50 | 51 | 0 52 | 53 | 54 | 0 55 | 56 | 57 | 58 | 59 | 60 | 0 61 | 0 62 | 63 | 64 | 65 | 66 | 32 67 | 32 68 | 69 | 70 | 71 | false 72 | 73 | 74 | Qt::NoFocus 75 | 76 | 77 | QPushButton{border-image: url(":/images/default_100_precent/min.png");background-color: transparent;}QPushButton:hover{border-image: url(":/images/default_100_precent/min_hover.png");background-color: transparent;}QPushButton:pressed{border-image: url(":/images/default_100_precent/min_pressed.png");background-color: transparent;} 78 | 79 | 80 | 81 | 82 | 83 | buttonGroup 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 0 92 | 0 93 | 94 | 95 | 96 | 97 | 32 98 | 32 99 | 100 | 101 | 102 | false 103 | 104 | 105 | Qt::NoFocus 106 | 107 | 108 | QPushButton{border-image: url(":/images/default_100_precent/restore.png");background-color: transparent;}QPushButton:hover{border-image: url(":/images/default_100_precent/restore_hover.png");background-color: transparent;}QPushButton:pressed{border-image: url(":/images/default_100_precent/restore_pressed.png");background-color: transparent;} 109 | 110 | 111 | 112 | 113 | 114 | buttonGroup 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 0 123 | 0 124 | 125 | 126 | 127 | 128 | 32 129 | 32 130 | 131 | 132 | 133 | false 134 | 135 | 136 | Qt::NoFocus 137 | 138 | 139 | QPushButton{border-image: url(":/images/default_100_precent/max.png");background-color: transparent;} 140 | QPushButton:hover{border-image: url(":/images/default_100_precent/max_hover.png");background-color: transparent;} 141 | QPushButton:pressed{border-image: url(":/images/default_100_precent/max_pressed.png");background-color: transparent;} 142 | 143 | 144 | 145 | 146 | 147 | buttonGroup 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 0 156 | 0 157 | 158 | 159 | 160 | 161 | 32 162 | 32 163 | 164 | 165 | 166 | false 167 | 168 | 169 | Qt::NoFocus 170 | 171 | 172 | QPushButton{border-image: url(":/images/default_100_precent/close.png");background-color: transparent;}QPushButton:hover{border-image: url(":/images/default_100_precent/close_hover.png");background-color: transparent;}QPushButton:pressed{border-image: url(":/images/default_100_precent/close_pressed.png");background-color: transparent;} 173 | 174 | 175 | 176 | 177 | 178 | buttonGroup 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | QtExtButton 190 | QPushButton 191 |
qt_ext_button.h
192 |
193 |
194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 |
202 | -------------------------------------------------------------------------------- /qt_ext_tabbar.cpp: -------------------------------------------------------------------------------- 1 | #include "qt_ext_tabbar.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "draw_helper.h" 10 | #include "qt_ext_tabbar_style.h" 11 | #include "round_shadow_helper.h" 12 | 13 | // #define DEV_DEBUG 14 | 15 | QtExtTabBar::QtExtTabBar(QWidget *parent, bool tab_add_button) 16 | : QTabBar(parent), tab_add_button_(tab_add_button) { 17 | this->setExpanding(false); 18 | this->setMovable(false); 19 | this->setStyle(new QExtTabBarStyle(this)); 20 | if (tab_add_button_) addTab(0); 21 | } 22 | 23 | QSize QtExtTabBar::tabSize() const { return tab_size_; } 24 | 25 | void QtExtTabBar::setTabSize(const QSize &tab_size) { 26 | if (tab_size_ == tab_size) return; 27 | tab_size_ = tab_size; 28 | update(); 29 | } 30 | 31 | bool QtExtTabBar::tabAddButton() const { return tab_add_button_; } 32 | 33 | void QtExtTabBar::setTabAddButton(bool draw_add) { 34 | if (tab_add_button_ == draw_add) return; 35 | if (tab_add_button_ && !draw_add) removeTab(count() - 1); 36 | tab_add_button_ = draw_add; 37 | update(); 38 | } 39 | 40 | int QtExtTabBar::tabAddButtonWidth() const { return tab_add_button_width_; } 41 | 42 | void QtExtTabBar::setTabAddButtonWidth(int width) { 43 | if (tab_add_button_width_ == width) return; 44 | tab_add_button_width_ = width; 45 | update(); 46 | } 47 | 48 | int QtExtTabBar::scrollBtnWidth() const { return scroll_btn_width_; } 49 | 50 | void QtExtTabBar::setScrollBtnWidth(int width) { scroll_btn_width_ = width; } 51 | 52 | void QtExtTabBar::setPaintAddBtnFunc(PaintAddButtonFunc func) { 53 | paint_add_btn_func_ = func; 54 | } 55 | 56 | void QtExtTabBar::setPaintTabFunc(PaintTabFunc func) { paint_tab_func_ = func; } 57 | 58 | void QtExtTabBar::setPaintTabBarFunc(PaintTabBarFunc func) { 59 | paint_tab_bar_func_ = func; 60 | } 61 | 62 | void QtExtTabBar::setPaintScrollBtnFunc(PaintScrollBtnFunc func) { 63 | paint_scroll_btn_func_ = func; 64 | } 65 | 66 | void QtExtTabBar::setCalcIconBtnRectFunc(CalcIconBtnRectFunc func) { 67 | calc_icon_btn_rect_func_ = func; 68 | } 69 | 70 | void QtExtTabBar::copyOtherTabBar(const QtExtTabBar *tab_bar) { 71 | if (nullptr == tab_bar || this == tab_bar) return; 72 | 73 | this->tab_size_ = tab_bar->tab_size_; 74 | this->press_pos_ = tab_bar->press_pos_; 75 | this->tab_btn_add_state_ = tab_bar->tab_btn_add_state_; 76 | this->dragging_ = tab_bar->dragging_; 77 | this->tab_add_button_ = tab_bar->tab_add_button_; 78 | this->tab_add_button_width_ = tab_bar->tab_add_button_width_; 79 | this->scroll_btn_width_ = tab_bar->scroll_btn_width_; 80 | this->paint_add_btn_func_ = tab_bar->paint_add_btn_func_; 81 | this->paint_tab_func_ = tab_bar->paint_tab_func_; 82 | this->paint_tab_bar_func_ = tab_bar->paint_tab_bar_func_; 83 | this->paint_scroll_btn_func_ = tab_bar->paint_scroll_btn_func_; 84 | this->calc_icon_btn_rect_func_ = tab_bar->calc_icon_btn_rect_func_; 85 | } 86 | 87 | int QtExtTabBar::tabCount() const { 88 | return tab_add_button_ ? count() - 1 : count(); 89 | } 90 | 91 | const TabBarSingal &QtExtTabBar::getSignal() const { return signal_; } 92 | 93 | void QtExtTabBar::setTabButton2(int index, QTabBar::ButtonPosition btn_pos, 94 | QWidget *btn) { 95 | btn->setFixedSize(this->iconSize()); 96 | if (QTabBar::RightSide == btn_pos) 97 | connect(btn, SIGNAL(clicked()), this, SLOT(OnRightWidgetClicked())); 98 | this->setTabButton(index, btn_pos, btn); 99 | } 100 | 101 | QSize QtExtTabBar::tabSizeHint(int index) const { 102 | if (index == count() - 1 && tab_add_button_) 103 | return QSize(tab_add_button_width_, tab_size_.height()); 104 | return tab_size_; 105 | } 106 | 107 | void QtExtTabBar::paintEvent(QPaintEvent *event) { 108 | QPainter painter(this); 109 | if (nullptr != paint_tab_bar_func_) 110 | paint_tab_bar_func_(painter, this->rect()); 111 | 112 | if (nullptr != paint_tab_func_) drawTab(&painter); 113 | 114 | if (tab_add_button_ && nullptr != paint_add_btn_func_) drawPlusBtn(&painter); 115 | } 116 | 117 | void QtExtTabBar::mouseReleaseEvent(QMouseEvent *e) { 118 | QTabBar::mouseReleaseEvent(e); 119 | // if (dragging_ && e->button() == Qt::LeftButton && 120 | // !contentsRect().contains(e->pos())) { 121 | if (dragging_ && e->button() == Qt::LeftButton) { 122 | dragging_ = false; 123 | } 124 | } 125 | 126 | void QtExtTabBar::mousePressEvent(QMouseEvent *event) { 127 | QTabBar::mousePressEvent(event); 128 | 129 | int index = pointInTabRectIndex(event->pos()); 130 | if (index == count() - 1 && tab_add_button_) { 131 | emit signal_.addBtnClicked(); 132 | return; 133 | } 134 | 135 | if (event->button() == Qt::LeftButton && tabCount() >= 1) { 136 | dragging_ = true; 137 | press_pos_ = event->pos(); 138 | } 139 | } 140 | 141 | void QtExtTabBar::mouseMoveEvent(QMouseEvent *event) { 142 | QTabBar::mouseMoveEvent(event); 143 | 144 | // 高度超过标签栏高度 或 标签数量=1 时,开启拖拽 145 | if (dragging_ && (event->buttons() & Qt::LeftButton) && 146 | (tabCount() >= 1 || !contentsRect().contains(event->pos()))) { 147 | int index = this->currentIndex(); 148 | if (index == -1 || (index == tabCount())) return; 149 | 150 | bool containts = false; 151 | for (int i = 0; i < count(); ++i) { 152 | if (tabRect(index).contains(event->pos())) { 153 | containts = true; 154 | break; 155 | } 156 | } 157 | 158 | // 拖拽到外面来了 159 | // if (count() == 1 || containts) { 160 | if (tabCount() >= 1 && containts) { 161 | emit signal_.signalStartDrag(index); 162 | } 163 | } 164 | } 165 | 166 | bool QtExtTabBar::event(QEvent *ev) { 167 | if (ev->type() == QEvent::HoverMove || ev->type() == QEvent::HoverEnter) { 168 | if (tab_add_button_) { 169 | QHoverEvent *hover_event = static_cast(ev); 170 | QRect tab_add_rect = tabRect(count() - 1); 171 | if (tab_add_rect.contains(hover_event->pos())) { 172 | tab_btn_add_state_ = TabState::hover_; 173 | update(tab_add_rect); 174 | } else { 175 | tab_btn_add_state_ = TabState::normal_; 176 | update(tab_add_rect); 177 | } 178 | } 179 | } else if (ev->type() == QEvent::HoverLeave) { 180 | QHoverEvent *hover_event = static_cast(ev); 181 | if (tab_add_button_) { 182 | QRect tab_add_rect = tabRect(count() - 1); 183 | tab_btn_add_state_ = TabState::normal_; 184 | update(tab_add_rect); 185 | } 186 | } 187 | 188 | return QTabBar::event(ev); 189 | } 190 | 191 | void QtExtTabBar::focusOutEvent(QFocusEvent *e) { 192 | dragging_ = false; 193 | return QTabBar::focusOutEvent(e); 194 | } 195 | 196 | void QtExtTabBar::setupUI() { this->setMouseTracking(true); } 197 | 198 | void QtExtTabBar::updateUI() {} 199 | 200 | int QtExtTabBar::pointInTabRectIndex(const QPoint &point) { 201 | for (int i = 0; i < count(); ++i) { 202 | QRect rect = tabRect(i); 203 | if (!rect.contains(point)) continue; 204 | return i; 205 | } 206 | return -1; 207 | } 208 | 209 | void QtExtTabBar::drawTab(QPainter *painter) { 210 | int tab_count = tab_add_button_ ? count() - 1 : count(); 211 | QStyleOptionTabV3 option; 212 | TabState tab_state = TabState::normal_; 213 | for (int index = 0; index < tab_count; index++) { 214 | QRect rect = tabRect(index); 215 | initStyleOption(&option, index); 216 | if (QStyle::State_Selected & option.state) 217 | tab_state = TabState::selected_; 218 | else if (QStyle::State_MouseOver & option.state) 219 | tab_state = TabState::hover_; 220 | else 221 | tab_state = TabState::normal_; 222 | paint_tab_func_(*painter, rect, tab_state, option.text, 223 | this->tabIcon(index)); 224 | } 225 | } 226 | 227 | void QtExtTabBar::drawPlusBtn(QPainter *painter) { 228 | int last_index = count() - 1; 229 | QStyleOptionTabV3 option; 230 | initStyleOption(&option, last_index); 231 | painter->save(); 232 | paint_add_btn_func_(*painter, tabRect(last_index), tab_btn_add_state_); 233 | painter->restore(); 234 | } 235 | 236 | void QtExtTabBar::OnRightWidgetClicked() { 237 | QObject *obj = (QObject *)(sender()); 238 | for (int i = 0; i < count(); ++i) { 239 | QWidget *widget = tabButton(i, QTabBar::RightSide); 240 | if (widget != obj) continue; 241 | emit signal_.closeTab(i); 242 | break; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /QtExtTabWidget.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} 14 | qrc;* 15 | false 16 | 17 | 18 | {99349809-55BA-4b9d-BF79-8FDBB0286EB3} 19 | ui 20 | 21 | 22 | {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} 23 | qrc;* 24 | false 25 | 26 | 27 | {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} 28 | moc;h;cpp 29 | False 30 | 31 | 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | Header Files 62 | 63 | 64 | Source Files 65 | 66 | 67 | Source Files 68 | 69 | 70 | 71 | 72 | Source Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | 97 | 98 | Form Files 99 | 100 | 101 | Form Files 102 | 103 | 104 | 105 | 106 | Source Files 107 | 108 | 109 | Header Files 110 | 111 | 112 | Header Files 113 | 114 | 115 | Header Files 116 | 117 | 118 | Header Files 119 | 120 | 121 | Header Files 122 | 123 | 124 | Header Files 125 | 126 | 127 | Header Files 128 | 129 | 130 | Header Files 131 | 132 | 133 | Header Files 134 | 135 | 136 | Header Files 137 | 138 | 139 | 140 | 141 | Source Files 142 | 143 | 144 | 145 | 146 | Resource Files 147 | 148 | 149 | Resource Files 150 | 151 | 152 | Resource Files 153 | 154 | 155 | Resource Files 156 | 157 | 158 | Resource Files 159 | 160 | 161 | Resource Files 162 | 163 | 164 | Resource Files 165 | 166 | 167 | Resource Files 168 | 169 | 170 | Resource Files 171 | 172 | 173 | Resource Files 174 | 175 | 176 | Resource Files 177 | 178 | 179 | Resource Files 180 | 181 | 182 | Resource Files 183 | 184 | 185 | Resource Files 186 | 187 | 188 | Resource Files 189 | 190 | 191 | Resource Files 192 | 193 | 194 | Resource Files 195 | 196 | 197 | Resource Files 198 | 199 | 200 | Resource Files 201 | 202 | 203 | Resource Files 204 | 205 | 206 | Resource Files 207 | 208 | 209 | Resource Files 210 | 211 | 212 | Resource Files 213 | 214 | 215 | Resource Files 216 | 217 | 218 | Resource Files 219 | 220 | 221 | 222 | 223 | Resource Files 224 | 225 | 226 | -------------------------------------------------------------------------------- /round_shadow_helper.cpp: -------------------------------------------------------------------------------- 1 | #include "round_shadow_helper.h" 2 | 3 | #include 4 | #include 5 | 6 | using RoundDirc = RoundShadowHelper::RoundDirection; 7 | 8 | // 1.阴影的距离可以调整 9 | // 2.阴影可以调整 10 | // 3.圆角可以调整 11 | // 4.圆角4个阴影可以调整 12 | RoundShadowHelper::RoundShadowHelper(const int shadow_width, const int radius) 13 | : shadow_width_(shadow_width), radius_(radius) {} 14 | 15 | void RoundShadowHelper::RoundShadow(QPainter* painter, const QRect& source_rect, 16 | RoundDirection dirc) { 17 | //不处理空数据或者错误数据 18 | if (nullptr == painter) return; 19 | 20 | const int kBORDER_DISTANCE = shadow_width_ / 2; 21 | const int kSHADOW_WIDTH2X = shadow_width_ * 2; 22 | 23 | painter->setRenderHints(QPainter::Antialiasing, true); 24 | painter->setRenderHints(QPainter::SmoothPixmapTransform, true); 25 | 26 | draw4BorderRectShadow(painter, shadow_width_, kSHADOW_WIDTH2X, source_rect, 27 | dirc); 28 | draw4BorderArcShadow(painter, shadow_width_, kSHADOW_WIDTH2X, source_rect, 29 | dirc); 30 | // // 将图片裁剪为圆角 31 | // QPainterPath path; 32 | // QRect rect(kBORDER_DISTANCE, kBORDER_DISTANCE, 33 | // image_width - kBORDER_DISTANCE * 2, 34 | // image_height - kBORDER_DISTANCE * 2); 35 | // path.addRoundedRect(rect, radius_, radius_, Qt::AbsoluteSize); 36 | // painter->setClipPath(path); 37 | // // painter->drawPixmap(kBORDER_DISTANCE, kBORDER_DISTANCE, image_width, 38 | // // image_height, new_pixmap); 39 | // painter->setPen(Qt::NoPen); 40 | // painter->setBrush(Qt::white); 41 | // painter->drawPath(path); 42 | } 43 | 44 | void RoundShadowHelper::FillRoundShadow(QPainter* painter, const QRect& rect, 45 | const QColor& fill_color, int radius) { 46 | painter->save(); 47 | QPainterPath path; 48 | path.addRoundedRect(rect, radius, radius, Qt::AbsoluteSize); 49 | painter->setClipPath(path); 50 | painter->setPen(Qt::NoPen); 51 | painter->setBrush(fill_color); 52 | painter->drawPath(path); 53 | painter->restore(); 54 | } 55 | 56 | void RoundShadowHelper::draw4BorderRectShadow(QPainter* painter, 57 | int shadow_width, 58 | int shadow_width2X, 59 | const QRect& dest_rect, 60 | RoundDirection dirc) { 61 | const int rect_width = dest_rect.width(); 62 | const int rect_height = dest_rect.height(); 63 | const int rect_pos_x = dest_rect.x(); 64 | const int rect_pos_y = dest_rect.y(); 65 | const QSize horizontal_size = 66 | QSize(rect_width - shadow_width2X, shadow_width); 67 | const QSize vertical_size = QSize(shadow_width, rect_height - shadow_width2X); 68 | 69 | QPoint start_point = QPoint(0, 0); 70 | QPoint end_point = QPoint(0, 0); 71 | // 上边 72 | if (dirc & RoundDirc::D_TOP) { 73 | start_point = 74 | QPoint(rect_pos_x + rect_width / 2.0, rect_pos_y + shadow_width); 75 | end_point = QPoint(rect_pos_x + rect_width / 2, 0 + rect_pos_y); 76 | drawShadowRect( 77 | painter, start_point, end_point, 78 | QRect(QPoint(shadow_width + rect_pos_x, rect_pos_y), horizontal_size)); 79 | } 80 | // 下边 81 | if (dirc & RoundDirc::D_BOTTOM) { 82 | start_point = QPoint(rect_pos_x + rect_width / 2.0, 83 | rect_pos_y + rect_height - shadow_width); 84 | end_point = QPoint(rect_pos_x + rect_width / 2.0, rect_pos_y + rect_height); 85 | drawShadowRect(painter, start_point, end_point, 86 | QRect(QPoint(shadow_width + rect_pos_x, 87 | rect_height - shadow_width + rect_pos_y), 88 | horizontal_size)); 89 | } 90 | 91 | // 左边 92 | if (dirc & RoundDirc::D_LEFT) { 93 | start_point = 94 | QPoint(rect_pos_x + shadow_width, rect_pos_y + rect_height / 2); 95 | end_point = QPoint(rect_pos_x, rect_pos_y + rect_height / 2); 96 | drawShadowRect( 97 | painter, start_point, end_point, 98 | QRect(QPoint(rect_pos_x, shadow_width + rect_pos_y), vertical_size)); 99 | } 100 | // 右边 101 | if (dirc & RoundDirc::D_RIGHT) { 102 | start_point = QPoint(rect_pos_x + rect_width - shadow_width, 103 | rect_pos_y + rect_height / 2); 104 | end_point = QPoint(rect_pos_x + rect_width, rect_pos_y + rect_height / 2); 105 | drawShadowRect(painter, start_point, end_point, 106 | QRect(QPoint(rect_width - shadow_width + rect_pos_x, 107 | shadow_width + rect_pos_y), 108 | vertical_size)); 109 | } 110 | } 111 | 112 | void RoundShadowHelper::draw4BorderArcShadow(QPainter* painter, 113 | int shadow_width, 114 | int shadow_width2X, 115 | const QRect& dest_rect, 116 | RoundDirection dirc) { 117 | const int rect_height = dest_rect.height(); 118 | const int rect_width = dest_rect.width(); 119 | const QSize rect_size = QSize(shadow_width2X, shadow_width2X); 120 | QPoint top_left_pos = dest_rect.topLeft(); 121 | 122 | QPoint center_pos = QPoint(0, 0); 123 | QPainterPath arc_path; 124 | // 上左边 125 | if (dirc & RoundDirc::D_TOP) { 126 | center_pos = QPoint(shadow_width, shadow_width) + top_left_pos; 127 | arc_path.moveTo(center_pos); 128 | arc_path.arcTo(QRect(top_left_pos, rect_size), 90, 90); 129 | drawShadowArc(painter, center_pos, top_left_pos, arc_path); 130 | } 131 | 132 | // 下左边 133 | if (dirc & RoundDirc::D_LEFT) { 134 | QPoint bottom_left_pos = 135 | QPoint(dest_rect.x(), dest_rect.y() + dest_rect.height()); 136 | center_pos = QPoint(shadow_width, -shadow_width) + bottom_left_pos; 137 | arc_path.moveTo(center_pos); 138 | arc_path.arcTo( 139 | QRect(QPoint(0, -shadow_width2X) + bottom_left_pos, rect_size), 180, 140 | 90); 141 | drawShadowArc(painter, center_pos, bottom_left_pos, arc_path); 142 | } 143 | // 上右边 144 | if (dirc & RoundDirc::D_RIGHT) { 145 | QPoint top_right_pos = QPoint(dest_rect.x() + rect_width, dest_rect.y()); 146 | center_pos = QPoint(-shadow_width, shadow_width) + top_right_pos; 147 | arc_path.moveTo(center_pos); 148 | arc_path.arcTo(QRect(QPoint(-shadow_width2X, 0) + top_right_pos, rect_size), 149 | 0, 90); 150 | drawShadowArc(painter, center_pos, top_right_pos, arc_path); 151 | } 152 | 153 | // 下右边 154 | if (dirc & RoundDirc::D_BOTTOM) { 155 | QPoint bottom_right_pos = 156 | QPoint(dest_rect.x() + rect_width, dest_rect.y() + rect_height); 157 | center_pos = QPoint(-shadow_width, -shadow_width) + bottom_right_pos; 158 | arc_path.moveTo(center_pos); 159 | arc_path.arcTo( 160 | QRect(QPoint(-shadow_width2X, -shadow_width2X) + bottom_right_pos, 161 | rect_size), 162 | 0, -90); 163 | drawShadowArc(painter, center_pos, bottom_right_pos, arc_path); 164 | } 165 | } 166 | 167 | void RoundShadowHelper::drawShadowRect(QPainter* painter, 168 | const QPoint& startPoint, 169 | const QPoint& endPoint, 170 | const QRect& destRect) { 171 | painter->save(); 172 | QLinearGradient linear(startPoint, endPoint); 173 | getGradient(startPoint, endPoint, linear); 174 | painter->setPen(Qt::NoPen); 175 | painter->setBrush(linear); 176 | painter->drawRect(destRect); 177 | painter->restore(); 178 | } 179 | 180 | void RoundShadowHelper::drawShadowArc(QPainter* painter, 181 | const QPoint& startPoint, 182 | const QPoint& endPoint, 183 | const QPainterPath& painterPath) { 184 | painter->save(); 185 | QRadialGradient radial_gardeint; 186 | getGradient(startPoint, shadow_width_, radial_gardeint); 187 | painter->setBrush(radial_gardeint); 188 | painter->setPen(Qt::NoPen); 189 | painter->drawPath(painterPath); 190 | painter->restore(); 191 | } 192 | 193 | void RoundShadowHelper::getGradient(const QPoint& start_point, const int radius, 194 | QRadialGradient& radial_gradient) { 195 | radial_gradient = QRadialGradient(start_point, radius); 196 | _getGradient(radial_gradient); 197 | } 198 | 199 | void RoundShadowHelper::getGradient(const QPoint& start_point, 200 | const QPoint& end_point, 201 | QLinearGradient& linear_gradient) { 202 | linear_gradient = QLinearGradient(start_point, end_point); 203 | _getGradient(linear_gradient); 204 | } 205 | 206 | void RoundShadowHelper::_getGradient(QGradient& gradient) { 207 | QColor color = Qt::gray; 208 | gradient.setColorAt(0, color); 209 | color.setAlpha(50); 210 | gradient.setColorAt(0.5, color); 211 | color.setAlpha(30); 212 | gradient.setColorAt(0.6, color); 213 | color.setAlpha(0); 214 | gradient.setColorAt(1, color); 215 | gradient.setSpread(QGradient::PadSpread); 216 | } 217 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QtExtTabWidget 2 | ### 1.效果展示 3 | 4 | ![1](./doc/1.gif) 5 | 6 | ### 2.用法展示 7 | 8 | 我已经将绘制接口独立出来,如果你想要绘制自己的特色,只需要继承`BaseTabBarDrawHelper`实现里面的接口,再调用`QTabWidgetProxy::updateDrawHelp()`函数,传递自定义的class。 9 | 10 | 这里还存在个问题就是多次调用`QTabWidgetProxy::updateDrawHelp()`有内存泄漏的风险。所以这个bug,已经解决了方案我准备将项目升级到vs2019使用智能指针管理内存。 11 | 12 | ```c++ 13 | MainWindow::MainWindow(QWidget *parent) 14 | : QMainWindow(parent), ui(new Ui::MainWindow) { 15 | ui->setupUi(this); 16 | setupUI(); 17 | } 18 | 19 | MainWindow::~MainWindow() { 20 | delete ui; 21 | delete frame_less_helper_; 22 | } 23 | 24 | void MainWindow::setupUI() { 25 | this->setWindowFlags(Qt::FramelessWindowHint | 26 | Qt::X11BypassWindowManagerHint); 27 | frame_less_helper_ = new NcFramelessHelper(); 28 | frame_less_helper_->activateOn(this); 29 | frame_less_helper_->setFilterEventCallBack( 30 | std::bind(&TabWidgetProxy::filterTabBarEvent, ui->tabWidgetProxy, 31 | std::placeholders::_1, std::placeholders::_2)); 32 | frame_less_helper_->activeOnWithChildWidget(this, 33 | ui->tabWidgetProxy->tabBar()); 34 | 35 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(tabInserted(int)), this, 36 | SLOT(OnTabInserted(int))); 37 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(addBtnClicked()), this, 38 | SLOT(OnAddBtnClicked())); 39 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(tabClosed(int)), this, 40 | SLOT(OnCloseTab(int))); 41 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(closeWnd()), this, 42 | SLOT(OnCloseWnd())); 43 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(minWnd()), this, 44 | SLOT(OnMinWnd())); 45 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(maxWnd()), this, 46 | SLOT(OnMaxWnd())); 47 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(restoreWnd()), this, 48 | SLOT(OnRestoreWnd())); 49 | connect(&(ui->tabWidgetProxy->getSignal()), SIGNAL(tabBarDoubleClicked()), 50 | this, SLOT(OnMaxOrRestore())); 51 | 52 | ui->tabWidgetProxy->updateDrawHelp(new TabBarDrawHelper()); 53 | 54 | #ifdef _DEBUG 55 | QWidget *widget = new QWidget(); 56 | widget->setStyleSheet("background-color:#FF00FF00"); 57 | ui->tabWidgetProxy->addTab2(widget, tr("this is first tab tab tab tab tab")); 58 | #endif //_DEBUG 59 | 60 | } 61 | 62 | void MainWindow::OnTabInserted(int index) { 63 | QPushButton *button = new QPushButton(); 64 | button->setStyleSheet( 65 | "QPushButton{border-image: url(:/images/close.png)}" 66 | "QPushButton:hover{border-image: url(:/images/close_hover.png)}"); 67 | ui->tabWidgetProxy->setTabButton2(index, QTabBar::RightSide, button); 68 | ui->tabWidgetProxy->setTabIcon(index, QIcon(":/images/x-capture-options.png")); 69 | } 70 | 71 | void MainWindow::OnCloseTab(int index) { 72 | // todo something 73 | } 74 | 75 | void MainWindow::OnAddBtnClicked() { 76 | QWidget *widget = new QWidget(); 77 | widget->setStyleSheet("background-color:#FFFFFF00"); 78 | ui->tabWidgetProxy->addTab2(widget, tr("this is first tab")); 79 | } 80 | 81 | void MainWindow::OnCloseWnd() { this->close(); } 82 | 83 | void MainWindow::OnMinWnd() {} 84 | 85 | void MainWindow::OnMaxWnd() {} 86 | 87 | void MainWindow::OnRestoreWnd() {} 88 | 89 | void MainWindow::OnMaxOrRestore() {} 90 | 91 | // 下面是具体的绘制过程 92 | #include 93 | #include "draw_helper.h" 94 | #include "round_shadow_helper.h" 95 | #include "tab_bar_draw_helper.h" 96 | 97 | TabBarDrawHelper::TabBarDrawHelper() { 98 | tab_bar_bk_image_.load(":/images/default_100_precent/caption_bg.png"); 99 | tab_bk_normal_image_.load(":/images/default_100_precent/tab.png"); 100 | tab_bk_hover_image_.load(":/images/default_100_precent/tab_hover.png"); 101 | tab_bk_selected_image_.load(":/images/default_100_precent/tab_check.png"); 102 | 103 | left_arrow_pixmap_list_.push_back( 104 | QPixmap(":/images/default_100_precent/left_arrow_normal.png")); 105 | left_arrow_pixmap_list_.push_back( 106 | QPixmap(":/images/default_100_precent/left_arrow_disabled.png")); 107 | 108 | right_arrow_pixmap_list_.push_back( 109 | QPixmap(":/images/default_100_precent/right_arrow_normal.png")); 110 | right_arrow_pixmap_list_.push_back( 111 | QPixmap(":/images/default_100_precent/right_arrow_disabled.png")); 112 | } 113 | 114 | TabBarDrawHelper::~TabBarDrawHelper() {} 115 | 116 | void TabBarDrawHelper::paintTabAddBtn(QPainter &painter, const QRect &rect, 117 | TabState tab_state) { 118 | QColor color = (TabState::hover_ == tab_state) ? QColor(255, 255, 255, 50) 119 | : Qt::transparent; 120 | painter.save(); 121 | QRect draw_rect = QRect(QPoint(0, 0), QSize(20, 20)); 122 | draw_rect.moveCenter(rect.center()); 123 | DrawCircle::Draw(&painter, draw_rect, color); 124 | DrawCharacter::DrawPlus(&painter, draw_rect, Qt::white); 125 | painter.restore(); 126 | } 127 | 128 | void TabBarDrawHelper::paintTab(QPainter &painter, const QRect &draw_rect, 129 | TabState tab_state, const QString &text, 130 | const QIcon &icon) { 131 | painter.save(); 132 | painter.setPen(Qt::NoPen); 133 | painter.setRenderHint(QPainter::SmoothPixmapTransform); 134 | if (TabState::selected_ == tab_state) { 135 | if (!tab_bk_selected_image_.isNull()) 136 | painter.drawPixmap(draw_rect, tab_bk_selected_image_); 137 | } else if (TabState::hover_ == tab_state) { 138 | if (!tab_bk_hover_image_.isNull()) 139 | painter.drawPixmap(draw_rect, tab_bk_hover_image_); 140 | } else if (TabState::normal_ == tab_state) { 141 | if (!tab_bk_normal_image_.isNull()) 142 | painter.drawPixmap(draw_rect, tab_bk_normal_image_); 143 | } 144 | QRect real_rect = draw_rect; 145 | real_rect -= QMargins(35, 0, 30, 0); 146 | painter.setPen(Qt::white); 147 | QString draw_text = 148 | painter.fontMetrics().elidedText(text, Qt::ElideRight, real_rect.width(), 0); 149 | painter.drawText(real_rect, Qt::AlignLeft | Qt::AlignVCenter, draw_text); 150 | painter.restore(); 151 | // draw tab icon 152 | if (!icon.isNull()) { 153 | painter.save(); 154 | painter.setPen(Qt::NoPen); 155 | QList actual_size = icon.availableSizes(); 156 | QSize icon_size = actual_size[0]; 157 | QPoint icon_point = QPoint(draw_rect.left() + 5, 158 | (draw_rect.height() - icon_size.height()) / 2); 159 | QRect icon_rect(icon_point, icon_size); 160 | painter.drawPixmap(icon_rect, icon.pixmap(icon_size)); 161 | painter.restore(); 162 | } 163 | } 164 | 165 | void TabBarDrawHelper::paintTabBar(QPainter &painter, const QRect &rect) { 166 | if (tab_bar_bk_image_.isNull()) return; 167 | painter.save(); 168 | painter.drawPixmap(rect, tab_bar_bk_image_); 169 | painter.restore(); 170 | } 171 | 172 | void TabBarDrawHelper::paintScrollBtn(bool left, QPainter &painter, 173 | const QRect &rect, 174 | const QToolButton *tool_btn) { 175 | QBrush rect_brush = Qt::transparent; 176 | if (tool_btn->underMouse()) { 177 | rect_brush = QColor(214, 214, 214, 100); 178 | } 179 | 180 | QRect draw_rect = QRect(0, 0, 20, 20); 181 | draw_rect.moveCenter(rect.center()); 182 | RoundShadowHelper round_helper; 183 | round_helper.FillRoundShadow(&painter, draw_rect, rect_brush.color(), 4); 184 | 185 | draw_rect = QRect(0, 0, 16, 16); 186 | draw_rect.moveCenter(rect.center()); 187 | painter.save(); 188 | if (!tool_btn->isEnabled()) { 189 | // disabled 190 | painter.drawPixmap(draw_rect, left ? left_arrow_pixmap_list_[1] 191 | : right_arrow_pixmap_list_[1]); 192 | } else { 193 | // normal 194 | painter.drawPixmap(draw_rect, left ? left_arrow_pixmap_list_[0] 195 | : right_arrow_pixmap_list_[0]); 196 | } 197 | 198 | painter.restore(); 199 | } 200 | 201 | QRect TabBarDrawHelper::calcIconBtnRect(bool left_button, 202 | const QSize &icon_size, 203 | const QRect &btn_rect) { 204 | QPoint center_pos = QPoint(0, 0); 205 | QRect button_rect = QRect(0, 0, 0, 0); 206 | const int icon_padding = 8; 207 | if (left_button) { 208 | center_pos = 209 | QPoint(icon_padding + icon_size.width() / 2 + btn_rect.x(), 210 | btn_rect.y() + (btn_rect.height() - icon_size.height()) / 2 + 211 | icon_size.height() / 2); 212 | } else { 213 | center_pos = QPoint( 214 | btn_rect.x() + btn_rect.width() - icon_padding - icon_size.width() / 2, 215 | btn_rect.y() + (btn_rect.height() - icon_size.height()) / 2 + 216 | icon_size.height() / 2); 217 | } 218 | button_rect = QRect(QPoint(0, 0), icon_size); 219 | button_rect.moveCenter(center_pos); 220 | return button_rect; 221 | } 222 | ``` 223 | 224 | ### 3.特性配置 225 | 226 | - 增加无边框窗口功能(缩放,移动) 227 | - 增加tab贴图功能 228 | - 双击关闭tab 229 | - 可以设置是否需要绘制“+”按钮。 230 | - 支持tab拖拽,合并 231 | - 将绘制接口导出来,方便用户自定义绘制和贴图 232 | 233 | ### 4.总结 234 | 235 | 没写之前觉得是个小功能,真正写的过程中,也是遇到了很多奇奇怪怪的问题,也是阅读了Qt源码才解决了问题。还是自己动手才知道其中的难点。 236 | 237 | 虽然很早就写得差不多了,一直在重构。但是总觉得还有很多地方没有处理好。 238 | 239 | 比如: 240 | 241 | - 上面的提到的TabBarDrawHelper内存管理一直在裸奔。 242 | - 整体结构并没有画出来,准备后续的文档在整理了。 243 | - 真正用到项目中估计还有其他问题,还需要在整理。 244 | - ... 245 | 246 | 这大概就是有点完美强迫症吧,但是这复工没多久实在是太忙了,所以就先这样吧,实在是干不动了,^-^。 247 | 248 | 最好还是要谢谢这位同学,项目里面用到的拖拽和合并的功能完全参考这位同学的。地址:https://github.com/iwxyi/Qt-DragableTabWidget 249 | 250 | 251 | 252 | 地址:https://github.com/MingYueRuYa/QtExtTabWidget 253 | 254 | -------------------------------------------------------------------------------- /script/auto_compile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Date : 2021-11-28 17:45:33 4 | # @Author : shixiong.Liu (liushixiong@corp.netease.com) 5 | 6 | import os 7 | import psutil 8 | import subprocess 9 | import sys 10 | import argparse 11 | import colorama 12 | import json 13 | import asyncio 14 | 15 | ''' 16 | - √ 配置json数据 配置文件可以指定路径再加载 17 | - √ 编译处理,Debug|Release 18 | - √ 能够杀掉进程,考虑多进程的问题 19 | - √ 资源编译 20 | - √ 增加编译错误检测 21 | - √ 增加编译警告检测 22 | ''' 23 | 24 | error_list = [] 25 | warning_list = [] 26 | windows_console_encode = 'gb2312' 27 | devenv_process_id = 0 28 | 29 | 30 | async def _read_stream(stream, cb): 31 | while True: 32 | line = await stream.readline() 33 | if line: 34 | content = str(line, encoding=windows_console_encode).splitlines(False) 35 | if content[0].find(' error ') != -1: 36 | error_list.append(content) 37 | elif content[0].find(' warning ') != -1: 38 | warning_list.append(content) 39 | cb(content[0]) 40 | else: 41 | break 42 | 43 | 44 | async def _stream_subprocess(cmd, stdout_cb, stderr_cb): 45 | process_info = await asyncio.create_subprocess_shell(*cmd, 46 | stdout=asyncio.subprocess.PIPE, 47 | stderr=asyncio.subprocess.PIPE) 48 | global devenv_process_id 49 | devenv_process_id = process_info.pid 50 | await asyncio.wait([ 51 | _read_stream(process_info.stdout, stdout_cb), 52 | _read_stream(process_info.stderr, stderr_cb) 53 | ]) 54 | return await process_info.wait() 55 | 56 | 57 | def execute(cmd, stdout_cb, stderr_cb): 58 | loop = asyncio.get_event_loop() 59 | rc = loop.run_until_complete(_stream_subprocess( 60 | cmd, 61 | stdout_cb, 62 | stderr_cb)) 63 | loop.close() 64 | return rc 65 | 66 | 67 | class App: 68 | def __init__(self): 69 | self._clear() 70 | 71 | def _clear(self): 72 | self.pre_compile_command = "" 73 | self.start_process_args = "" 74 | self.process_name = "" 75 | self.start_process_path = "" 76 | self.update_code_command = "" 77 | self.compile_args = "" 78 | self.compile_file = "" 79 | self.compile_tool_dir = "" 80 | 81 | self.update = False 82 | self.resource = False 83 | self.is_kill = False 84 | self.is_start = False 85 | self.is_compile = False 86 | self.compile_args = "" 87 | self.config_path = "" 88 | 89 | def _search_process(self): 90 | list_process_id = [] 91 | for proc in psutil.process_iter(['name', 'pid']): 92 | if proc.info['name'] == self.process_name: 93 | find_flag = 1 94 | list_process_id.append(proc.info['pid']) 95 | break 96 | if len(list_process_id) == 0: 97 | print(colorama.Fore.GREEN + "Not find target process") 98 | return list_process_id 99 | 100 | def _kill_process(self, list_process_id): 101 | for process_id in list_process_id: 102 | target_process = psutil.Process(process_id) 103 | try: 104 | target_process.kill() 105 | print(colorama.Fore.GREEN + "Kill target process successful.") 106 | except psutil.ZombieProcess: 107 | print(colorama.Fore.RED + "Error: this is zombie process") 108 | return 1 109 | except psutil.AccessDenied: 110 | print(colorama.Fore.RED + "Error: Access denied") 111 | return 1 112 | except psutil.TimeoutExpired: 113 | print(colorama.Fore.RED + "Error: Time out expired") 114 | return 1 115 | return 0 116 | 117 | def _pre_compile_code(self): 118 | if len(self.pre_compile_command) == 0: 119 | print(colorama.Fore.RED + "Error: not any previous compile command.") 120 | return 121 | if os.path.isabs(self.pre_compile_command): 122 | os.system(self.pre_compile_command) 123 | else: 124 | os.system(os.getcwd() + '/' + self.pre_compile_command) 125 | print(colorama.Fore.GREEN + "Execute previous compile command finished.") 126 | return 127 | 128 | def _compile_code(self): 129 | compile_tool = 'devenv' 130 | if self.rebuild: 131 | compile_parameter = '/rebuild "' + self.compile_args + '"' 132 | else: 133 | compile_parameter = '/build "' + self.compile_args + '"' 134 | if os.path.isabs(self.compile_file): 135 | execute(['devenv ' + self.compile_file + ' ' + compile_parameter], 136 | lambda x: print("%s" % x), 137 | lambda x: print("%s" % x)) 138 | else: 139 | solution_file = os.getcwd() +'/'+ self.compile_file 140 | execute(['devenv ' + solution_file + ' ' + compile_parameter], 141 | lambda x: print("%s" % x), 142 | lambda x: print("%s" % x)) 143 | return len(error_list) 144 | 145 | def _pre_env(self): 146 | strewn = os.getenv("path") 147 | os.putenv("path", self.compile_tool_dir + ";" + strewn) 148 | return 149 | 150 | def _start_process(self): 151 | try: 152 | if os.path.isabs(self.start_process_path): 153 | list_process_args = [self.start_process_path] 154 | else: 155 | process_path = os.getcwd() + "/" + self.start_process_path 156 | list_process_args = [process_path] 157 | for args in self.start_process_args: 158 | list_process_args.append(args) 159 | sub_process = subprocess.Popen(list_process_args, shell=False) 160 | print(colorama.Fore.GREEN + "Start target process successful, pid=" + str(sub_process.pid)) 161 | except Exception as error: 162 | print(colorama.Fore.GREEN + str(error)) 163 | else: 164 | pass 165 | return 166 | 167 | def _update_code(self): 168 | if len(self.update_code_command) == 0: 169 | print(colorama.Fore.RED + "Error: Not find any update code command.") 170 | else: 171 | os.system(self.update_code_command) 172 | return 173 | 174 | def _parse_args(self, args): 175 | parser = argparse.ArgumentParser(description='Tools for auto update code and compile project') 176 | parser.add_argument('-u', '--update', action='store_true', help='update code by svn or git') 177 | parser.add_argument('-pre', '--precompileaction', action='store_true', help='build code previous action,for ' 178 | 'example Qt qrc file.') 179 | parser.add_argument('-k', '--kill', action='store_true', help='kill target process') 180 | parser.add_argument('-s', '--start', action='store_true', help='start target process') 181 | parser.add_argument('-c', '--compile', action='store_true', help='compile project') 182 | parser.add_argument('-a', '--compileargs', default='Debug|Win32', type=str, 183 | help='compile project with args, for example:"Debug|Win32" or "Release|Win32". default ' 184 | 'value: "Debug|Win32"') 185 | parser.add_argument('-p', '--configpath', default='./config.json', type=str, help='load config json path') 186 | parser.add_argument('-r', '--rebuild', action='store_true', help='rebuild solution') 187 | args = parser.parse_args(args) 188 | self.update = args.update 189 | self.pre_compileaction = args.precompileaction 190 | self.is_kill = args.kill 191 | self.is_start = args.start 192 | self.is_compile = args.compile 193 | self.compile_args = args.compileargs 194 | self.rebuild = args.rebuild 195 | self.config_path = args.configpath 196 | if not os.path.exists(self.config_path): 197 | self.config_path = "./private_init_env/" + os.getenv("username") + "/config.json" 198 | 199 | def _parse_config(self, config_path): 200 | with open(config_path) as f: 201 | data = json.load(f) 202 | self.process_name = data['process_name'] 203 | self.update_code_command = data['update_code_command'] 204 | self.pre_compile_command = data['pre_compile_command'] 205 | self.compile_file = data['compile_file'] 206 | self.compile_tool_dir = data['compile_tool_dir'] 207 | self.start_process_path = data['start_process_path'] 208 | self.start_process_args = data['start_process_args'] 209 | 210 | def run(self, args): 211 | self._parse_args(args) 212 | self._parse_config(self.config_path) 213 | 214 | if self.update: 215 | print('------------------------start update code----------------------------') 216 | self._update_code() 217 | print('------------------------end update code------------------------------') 218 | if self.pre_compileaction: 219 | print('-------------------start previous action--------------------------') 220 | self._pre_compile_code() 221 | print('--------------------end previous action---------------------------') 222 | 223 | kill_flag = 0 224 | if self.is_kill: 225 | print('--------------------start search process--------------------------') 226 | list_process_id = self._search_process() 227 | if len(list_process_id) != 0: 228 | kill_flag = self._kill_process(list_process_id) 229 | print('---------------------end search process---------------------------') 230 | if kill_flag != 0: 231 | print('--------------------stop compile project--------------------------') 232 | else: 233 | if self.is_compile: 234 | print('--------------------start compile project--------------------------') 235 | 236 | print('--------------start init previous environment--------------------') 237 | self._pre_env() 238 | print('---------------end init previous environment---------------------') 239 | 240 | result_code = self._compile_code() 241 | if result_code == 0: 242 | print(colorama.Fore.GREEN + 'compile project finished') 243 | else: 244 | print(colorama.Fore.RED + 'compile project errored') 245 | if len(error_list) > 0: 246 | for index in error_list: 247 | print(colorama.Fore.RED + index[0]) 248 | if len(warning_list) > 0: 249 | for index in warning_list: 250 | print(colorama.Fore.YELLOW + index[0]) 251 | print('--------------------end compile project----------------------------') 252 | if result_code > 0: 253 | return 254 | 255 | if self.is_start: 256 | print('--------------------start call application-------------------------') 257 | self._start_process() 258 | print('--------------------end call application---------------------------') 259 | 260 | import getpass 261 | 262 | def kill_process_tree(): 263 | if devenv_process_id == 0: 264 | return 265 | print(colorama.Fore.RED + 'Got Ctrl+C signal') 266 | print(colorama.Fore.RED + '--------------------start kill process tree-------------------------') 267 | parent = psutil.Process(devenv_process_id) 268 | try: 269 | for child in parent.children(recursive=True): # or parent.children() for recursive=False 270 | child.kill() 271 | parent.kill() 272 | except: 273 | pass 274 | print(colorama.Fore.RED + '--------------------end kill process tree-------------------------') 275 | 276 | if __name__ == '__main__': 277 | colorama.init(autoreset=True) 278 | try: 279 | app = App() 280 | if not app.run(sys.argv[1:]): 281 | sys.exit(-1) 282 | except KeyboardInterrupt: 283 | kill_process_tree() 284 | sys.exit(0) 285 | -------------------------------------------------------------------------------- /qt_ext_tab_widget.cpp: -------------------------------------------------------------------------------- 1 | #include "qt_ext_tab_widget.h" 2 | 3 | #include 4 | 5 | #include "NcFrameLessHelper.h" 6 | #include "button_widget.h" 7 | 8 | #define DRAGABLE_TAB_WINDOW_MIME_KEY "DRAGABLE_TAB_WINDOW_MIME_KEY" 9 | #define DRAGABLE_TAB_WIDGET_MIME_KEY "DRAGABLE_TAB_WIGET_MIME_KEY" 10 | #define DRAGABLE_TAB_LABEL_MIME_KEY "DRAGABLE_TAB_LABEL_MIME_KEY" 11 | #define DRAGABLE_TAB_ICON_MIME_KEY "DRAGABLE_TAB_ICON_MIME_KEY" 12 | #define WIN_FRAME_LEFE_OFFSET 8 13 | 14 | const int kBUTTON_CONTAINER_WIDTH = 100; 15 | QtExtTabWidget::DragState QtExtTabWidget::drag_state_ = DragState::Unknown; 16 | QString QtExtTabWidget::dragging_label_ = ""; 17 | QIcon QtExtTabWidget::dragging_icon_ = QIcon(); 18 | 19 | QtExtTabWidget::QtExtTabWidget(QWidget *parent) { setupUI(); } 20 | 21 | void QtExtTabWidget::initProperty(bool useScrollButtons, bool isMain) { 22 | this->setMovable(false); 23 | this->setMouseTracking(true); 24 | this->setAcceptDrops(true); 25 | this->setDocumentMode(true); 26 | // notice: this is for custom style 27 | this->setStyleSheet("background-color:transparent;"); 28 | this->setUsesScrollButtons(useScrollButtons); 29 | this->is_main_ = isMain; 30 | } 31 | 32 | void QtExtTabWidget::addTab2(QWidget *widget, const QString &label) { 33 | insertTab2(-1, widget, label); 34 | } 35 | 36 | void QtExtTabWidget::insertTab2(int index, QWidget *widget, 37 | const QString &label) { 38 | int tab_index = this->insertTab(index, widget, label); 39 | this->setCurrentIndex(tab_index); 40 | } 41 | 42 | void QtExtTabWidget::setTabButton2(int index, QTabBar::ButtonPosition btn_pos, 43 | QWidget *btn) { 44 | tab_bar_->setTabButton2(index, btn_pos, btn); 45 | } 46 | 47 | void QtExtTabWidget::setMaxRestoreBtnVisible(bool max_visible, 48 | bool restore_visible) { 49 | button_widget_->setMaxRestoreBtnVisible(max_visible, restore_visible); 50 | } 51 | 52 | int QtExtTabWidget::pointInTabRectIndex(const QPoint &point) { 53 | return tab_bar_->pointInTabRectIndex(point); 54 | } 55 | 56 | void QtExtTabWidget::setPaintAddBtnFunc(QtExtTabBar::PaintAddButtonFunc func) { 57 | if (nullptr != tab_bar_) tab_bar_->setPaintAddBtnFunc(func); 58 | } 59 | 60 | void QtExtTabWidget::setPaintTabFunc(QtExtTabBar::PaintTabFunc func) { 61 | if (nullptr != tab_bar_) tab_bar_->setPaintTabFunc(func); 62 | } 63 | 64 | void QtExtTabWidget::setPaintTabBarFunc(QtExtTabBar::PaintTabBarFunc func) { 65 | if (nullptr != tab_bar_) tab_bar_->setPaintTabBarFunc(func); 66 | } 67 | 68 | void QtExtTabWidget::setPaintScrollBtnFunc( 69 | QtExtTabBar::PaintScrollBtnFunc func) { 70 | if (nullptr != tab_bar_) tab_bar_->setPaintScrollBtnFunc(func); 71 | } 72 | 73 | void QtExtTabWidget::setCalcIconBtnRectFunc( 74 | QtExtTabBar::CalcIconBtnRectFunc func) { 75 | if (nullptr != tab_bar_) tab_bar_->setCalcIconBtnRectFunc(func); 76 | } 77 | 78 | QSize QtExtTabWidget::tabSize() const { 79 | if (nullptr != tab_bar_) 80 | return tab_bar_->tabSize(); 81 | else 82 | return QSize(-1, -1); 83 | } 84 | 85 | void QtExtTabWidget::setTabSize(const QSize &tab_size) { 86 | if (nullptr == tab_bar_) return; 87 | if (tab_bar_->tabSize() == tab_size) return; 88 | tab_bar_->setTabSize(tab_size); 89 | } 90 | 91 | bool QtExtTabWidget::tabAddButton() const { 92 | if (nullptr == tab_bar_) 93 | return false; 94 | else 95 | return tab_bar_->tabAddButton(); 96 | } 97 | 98 | void QtExtTabWidget::setTabAddButton(bool draw_add) { 99 | if (nullptr == tab_bar_) return; 100 | if (tab_bar_->tabAddButton() == draw_add) return; 101 | tab_bar_->setTabAddButton(draw_add); 102 | } 103 | 104 | int QtExtTabWidget::tabAddButtonWidth() const { 105 | if (nullptr == tab_bar_) 106 | return -1; 107 | else 108 | return tab_bar_->tabAddButtonWidth(); 109 | } 110 | 111 | void QtExtTabWidget::setTabAddButtonWidth(int width) { 112 | if (nullptr == tab_bar_) return; 113 | if (tab_bar_->tabAddButtonWidth() == width) return; 114 | tab_bar_->setTabAddButtonWidth(width); 115 | } 116 | 117 | QtExtTabBar *QtExtTabWidget::tabBar2() const { return tab_bar_; } 118 | 119 | const TabWidgetSignal &QtExtTabWidget::getSignal() const { return signal_; } 120 | 121 | void QtExtTabWidget::paintEvent(QPaintEvent *ev) { QTabWidget::paintEvent(ev); } 122 | 123 | void QtExtTabWidget::tabInserted(int index) { emit signal_.tabInserted(index); } 124 | 125 | void QtExtTabWidget::resizeEvent(QResizeEvent *ev) { 126 | button_widget_->setFixedSize( 127 | QSize(kBUTTON_CONTAINER_WIDTH, tab_bar_->tabSize().height())); 128 | int tab_bar_width = this->width() - button_widget_->width(); 129 | button_widget_->move(QPoint(tab_bar_width, 0)); 130 | tab_bar_->setFixedWidth(tab_bar_width); 131 | QTabWidget::resizeEvent(ev); 132 | } 133 | 134 | void QtExtTabWidget::dragEnterEvent(QDragEnterEvent *event) { 135 | const QMimeData *mime = event->mimeData(); 136 | // Tab拖拽 137 | QPoint pos = event->pos(); 138 | if (mime->hasFormat(DRAGABLE_TAB_WIDGET_MIME_KEY)) { 139 | event->accept(); 140 | } else { 141 | event->ignore(); 142 | } 143 | } 144 | 145 | void QtExtTabWidget::dragMoveEvent(QDragMoveEvent *event) { 146 | const QMimeData *mime = event->mimeData(); 147 | QPoint pos = event->pos(); 148 | 149 | if (!is_main_) { 150 | event->ignore(); 151 | } else if (count() == 0) { 152 | event->accept(); 153 | } else if (mime->hasFormat(DRAGABLE_TAB_WIDGET_MIME_KEY) && 154 | tab_bar_->geometry().contains(pos)) { // 整行拖拽 155 | if (tab_bar_->geometry().contains(pos)) { 156 | int index = tab_bar_->pointInTabRectIndex(pos); 157 | event->accept(); 158 | QHoverEvent *hover_event = 159 | new QHoverEvent(QEvent::HoverEnter, pos, QPointF(0, 0)); 160 | QApplication::postEvent(tab_bar_, hover_event); 161 | } 162 | } else { 163 | event->ignore(); 164 | QHoverEvent *event = 165 | new QHoverEvent(QEvent::HoverLeave, QPoint(-1, -1), pos); 166 | QApplication::postEvent(tab_bar_, event); 167 | } 168 | } 169 | 170 | void QtExtTabWidget::dropEvent(QDropEvent *event) { 171 | if (OnMergeDroppedLabel(event)) event->accept(); 172 | 173 | QHoverEvent *hover_event = 174 | new QHoverEvent(QEvent::HoverLeave, QPoint(-1, -1), event->pos()); 175 | QApplication::postEvent(tab_bar_, hover_event); 176 | this->show(); 177 | 178 | return QTabWidget::dropEvent(event); 179 | } 180 | 181 | void QtExtTabWidget::dragLeaveEvent(QDragLeaveEvent *event) { 182 | QHoverEvent *hover_event = 183 | new QHoverEvent(QEvent::HoverLeave, QPoint(-1, -1), QPoint(0, 0)); 184 | QApplication::postEvent(tab_bar_, hover_event); 185 | this->show(); 186 | QTabWidget::dragLeaveEvent(event); 187 | } 188 | 189 | void QtExtTabWidget::setTabBar2(QTabBar *tab_bar) { 190 | tab_bar_ = dynamic_cast(tab_bar); 191 | this->setTabBar(tab_bar_); 192 | } 193 | 194 | void QtExtTabWidget::OnCloseTab(int index) { 195 | this->removeTab(index); 196 | emit signal_.tabClosed(index); 197 | if (this->currentIndex() == index && index - 1 > 0) 198 | this->setCurrentIndex(index - 1); 199 | deleteIfEmptyWindow(); 200 | } 201 | 202 | void QtExtTabWidget::OnTabBarDoubleClicked(int index) { 203 | if (-1 != index) { 204 | OnCloseTab(index); 205 | } else { 206 | if (nullptr == parentWidget()) { 207 | isMaximized() ? OnRestoreWnd() : OnMaxWnd(); 208 | } else { 209 | emit signal_.tabBarDoubleClicked(); 210 | } 211 | } 212 | } 213 | 214 | void QtExtTabWidget::OnStartDrag(int index) { 215 | bool is_main_one = (tab_bar_->tabCount() == 1 && isInMain()); 216 | if (is_main_one) return; 217 | 218 | drag_state_ = DragState::Unknown; 219 | dragging_index_ = index; 220 | dragging_widget_ = this->widget(index); 221 | dragging_point_delta_ = 222 | QCursor::pos() - (parent() == nullptr ? this->mapToGlobal(this->pos()) 223 | : mapToGlobal(QPoint(0, 0))); 224 | dragging_point_delta_ = QPoint(0, 0); 225 | 226 | QPixmap pixmap(tab_bar_->size()); 227 | pixmap.fill(Qt::transparent); 228 | tab_bar_->render(&pixmap); 229 | QRect pixmap_rect = tab_bar_->tabRect(index); 230 | pixmap = pixmap.copy(pixmap_rect); 231 | 232 | QMimeData *mime = new QMimeData; 233 | mime->setData(DRAGABLE_TAB_WINDOW_MIME_KEY, 234 | QString::number(reinterpret_cast(this)).toUtf8()); 235 | mime->setData( 236 | DRAGABLE_TAB_WIDGET_MIME_KEY, 237 | QString::number(reinterpret_cast(dragging_widget_)).toUtf8()); 238 | mime->setData(DRAGABLE_TAB_LABEL_MIME_KEY, 239 | tab_bar_->tabText(index).toLocal8Bit()); 240 | dragging_label_ = tab_bar_->tabText(index); 241 | dragging_icon_ = tab_bar_->tabIcon(index); 242 | QDrag *drag = new QDrag(this); 243 | drag->setMimeData(mime); 244 | drag->setPixmap(pixmap); 245 | drag->setHotSpot(dragging_point_delta_); 246 | 247 | if (!isInMain()) this->hide(); 248 | 249 | connect(drag, &QDrag::destroyed, this, [=](QObject *) { 250 | // 判断有没有被合并到窗口 251 | if (drag_state_ == DragState::NoChanged || drag_state_ == DragState::Merged) 252 | return; 253 | 254 | // 没有合并到其他窗口 255 | if (tab_bar_->tabCount() == 1 && !isInMain()) { 256 | } else { 257 | // 多个标签拖出,创建新窗口 258 | createDraggedNewWindow(); 259 | } 260 | this->move(QCursor::pos()); 261 | this->show(); 262 | }); 263 | 264 | // exec 操作会一直阻塞后面的代码,除非使用多线程或者信号槽 265 | drag->exec(); 266 | } 267 | 268 | void QtExtTabWidget::OnCloseWnd() { 269 | if (nullptr == this->parentWidget()) 270 | close(); 271 | else 272 | emit signal_.closeWnd(); 273 | } 274 | 275 | void QtExtTabWidget::OnMinWnd() { 276 | if (nullptr == this->parentWidget()) 277 | this->showMinimized(); 278 | else 279 | emit signal_.minWnd(); 280 | } 281 | 282 | void QtExtTabWidget::OnMaxWnd() { 283 | if (nullptr == this->parentWidget()) { 284 | this->showMaximized(); 285 | this->setMaxRestoreBtnVisible(false, true); 286 | this->currentWidget()->setFocus(); 287 | } else 288 | emit signal_.maxWnd(); 289 | } 290 | 291 | void QtExtTabWidget::OnRestoreWnd() { 292 | if (nullptr == this->parentWidget()) { 293 | this->showNormal(); 294 | this->setMaxRestoreBtnVisible(true, false); 295 | } else { 296 | emit signal_.restoreWnd(); 297 | } 298 | } 299 | 300 | bool QtExtTabWidget::OnMergeDroppedLabel(QDropEvent *event) { 301 | int insert_index = -1; 302 | for (int i = tab_bar_->count() - 1; i >= 0; i--) { 303 | if (tab_bar_->tabRect(i).contains(event->pos())) { 304 | insert_index = i; 305 | break; 306 | } 307 | } 308 | 309 | const QMimeData *mime = event->mimeData(); 310 | QtExtTabWidget *window = reinterpret_cast( 311 | mime->data(DRAGABLE_TAB_WINDOW_MIME_KEY).toInt()); 312 | QWidget *widget = reinterpret_cast( 313 | mime->data(DRAGABLE_TAB_WIDGET_MIME_KEY).toInt()); 314 | QString label = 315 | QString::fromLocal8Bit(mime->data(DRAGABLE_TAB_LABEL_MIME_KEY)); 316 | QIcon icon = dragging_icon_; 317 | if (window == this) { 318 | drag_state_ = DragState::NoChanged; 319 | if (insert_index == currentIndex()) return false; 320 | removeTab(currentIndex()); 321 | insertTab(insert_index, widget, label); 322 | setCurrentIndex(insert_index); 323 | return false; 324 | } 325 | 326 | drag_state_ = DragState::Merged; 327 | // 移除旧的 328 | window->removeTab(window->currentIndex()); 329 | 330 | // 插入新的 331 | if (icon.isNull()) { 332 | if (insert_index >= count()) 333 | addTab(widget, label); 334 | else 335 | insertTab(insert_index, widget, label); 336 | } else { 337 | if (insert_index >= count()) 338 | addTab(widget, icon, label); 339 | else 340 | insertTab(insert_index, widget, icon, label); 341 | } 342 | 343 | setCurrentIndex(insert_index); 344 | this->setFocus(); 345 | QTimer::singleShot(0, this, [=] { widget->setFocus(); }); 346 | return true; 347 | } 348 | 349 | void QtExtTabWidget::setupUI() { 350 | if (nullptr == (tab_bar_ = dynamic_cast(tabBar()))) { 351 | tab_bar_ = new QtExtTabBar(this); 352 | setTabBar(tab_bar_); 353 | } 354 | connect(&(tab_bar_->getSignal()), SIGNAL(addBtnClicked()), &signal_, 355 | SIGNAL(addBtnClicked())); 356 | connect(&(tab_bar_->getSignal()), SIGNAL(closeTab(int)), this, 357 | SLOT(OnCloseTab(int))); 358 | 359 | button_widget_ = new ButtonWidget(this); 360 | connect(button_widget_, SIGNAL(closeClicked()), this, SLOT(OnCloseWnd())); 361 | connect(button_widget_, SIGNAL(minClicked()), this, SLOT(OnMinWnd())); 362 | connect(button_widget_, SIGNAL(maxClicked()), this, SLOT(OnMaxWnd())); 363 | connect(button_widget_, SIGNAL(restoreClicked()), this, SLOT(OnRestoreWnd())); 364 | 365 | connect(this, SIGNAL(tabBarDoubleClicked(int)), this, 366 | SLOT(OnTabBarDoubleClicked(int))); 367 | connect(&(tab_bar_->getSignal()), SIGNAL(signalStartDrag(int)), this, 368 | SLOT(OnStartDrag(int))); 369 | connect(&(tab_bar_->getSignal()), SIGNAL(signalEndDrag()), this, 370 | SLOT(createDraggedNewWindow())); 371 | } 372 | 373 | bool QtExtTabWidget::isInMain() { return parentWidget() != nullptr; } 374 | 375 | QtExtTabWidget *QtExtTabWidget::createDraggedNewWindow() { 376 | int titlebar_height = style()->pixelMetric(QStyle::PM_TitleBarHeight); 377 | 378 | QtExtTabWidget *window = newTabWidget(); 379 | window->setAttribute(Qt::WA_DeleteOnClose, true); 380 | window->resize(QSize(800, 600)); 381 | window->move(QCursor::pos() - dragging_point_delta_ - 382 | QPoint(WIN_FRAME_LEFE_OFFSET, titlebar_height)); 383 | window->show(); 384 | QString label = tab_bar_->tabText(dragging_index_); 385 | QIcon icon = tab_bar_->tabIcon(dragging_index_); 386 | QWidget *button = tab_bar_->tabButton(dragging_index_, QTabBar::RightSide); 387 | tab_bar_->setTabButton(dragging_index_, QTabBar::RightSide, nullptr); 388 | removeTab(dragging_index_); 389 | if (!icon.isNull()) 390 | window->addTab(dragging_widget_, icon, label); 391 | else 392 | window->addTab(dragging_widget_, label); 393 | if (button != nullptr) { 394 | window->setTabButton2(0, QTabBar::RightSide, button); 395 | } 396 | if (!is_main_ && count() == 0) 397 | deleteLater(); 398 | window->raise(); 399 | window->setFocus(); 400 | dragging_widget_->setFocus(); 401 | QTimer::singleShot(0, dragging_widget_, [=] { 402 | window->setFocus(); 403 | }); 404 | return window; 405 | } 406 | 407 | void QtExtTabWidget::deleteIfEmptyWindow() { 408 | if (!is_main_ && count() == 0) deleteLater(); 409 | } 410 | 411 | QtExtTabWidget *QtExtTabWidget::newTabWidget() { 412 | QtExtTabWidget *widget = new QtExtTabWidget(); 413 | NcFramelessHelper *frame_less_helper_ = new NcFramelessHelper(); 414 | widget->setDocumentMode(true); 415 | widget->setWindowFlags(widget->windowFlags() | Qt::FramelessWindowHint); 416 | QtExtTabBar *new_tab_bar = dynamic_cast(widget->tabBar()); 417 | new_tab_bar->copyOtherTabBar(tab_bar_); 418 | new_tab_bar->setTabAddButton(false); 419 | frame_less_helper_->activeOnWithChildWidget(widget, new_tab_bar); 420 | widget->initProperty(true, false); 421 | return widget; 422 | } -------------------------------------------------------------------------------- /NcFrameLessHelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | FramelessHelper, an easy way to support move/resize on 3 | frameless toplevel windows. 4 | Copyright (C) 2011 Nishant Parashar 5 | Email:- nishcode (at) gmail (dot) com 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "NcFramelessHelper.h" 26 | 27 | class NcCursorPosCalculator 28 | { 29 | 30 | public: 31 | NcCursorPosCalculator(); 32 | void reset(); 33 | void recalculate( const QPoint& globalMousePos, const QRect& frameRect ); 34 | 35 | public: 36 | bool onEdges; 37 | bool onLeftEdge; 38 | bool onRightEdge; 39 | bool onTopEdge; 40 | bool onBottomEdge; 41 | bool onTopLeftEdge; 42 | bool onBottomLeftEdge; 43 | bool onTopRightEdge; 44 | bool onBottomRightEdge; 45 | 46 | static int mBorderWidth; 47 | }; 48 | //TODO: Should not be static. 49 | int NcCursorPosCalculator::mBorderWidth = 8; 50 | 51 | class NcWidgetData 52 | { 53 | public: 54 | NcWidgetData( NcFramelessHelperImpl* _d, QWidget* topLevelWidget ); 55 | ~NcWidgetData(); 56 | 57 | //void setWidget( QWidget* topLevelWidget ); 58 | QWidget* widget(); 59 | void handleWidgetEvent( QEvent* event ); 60 | void updateRubberBandStatus(); 61 | bool cursorShapeChanged() const; 62 | 63 | private: 64 | void updateCursorShape( const QPoint& globalMousePos ); 65 | void resizeWidget( const QPoint& globalMousePos ); 66 | void moveWidget( const QPoint& globalMousePos ); 67 | 68 | void handleMousePressEvent( QMouseEvent* event ); 69 | void handleMouseReleaseEvent( QMouseEvent* event ); 70 | void handleMouseMoveEvent( QMouseEvent* event ); 71 | void handleLeaveEvent( QEvent* event ); 72 | void handleHoverMoveEvent( QHoverEvent* event ); 73 | 74 | private: 75 | NcFramelessHelperImpl* d; 76 | QRubberBand* mRubberBand; 77 | bool mLeftButtonPressed; 78 | QWidget* mWidget; 79 | QPoint mDragPos; 80 | NcCursorPosCalculator mPressedMousePos; 81 | NcCursorPosCalculator mMoveMousePos; 82 | bool mCursorShapeChanged; 83 | Qt::WindowFlags mWindowFlags; 84 | }; 85 | 86 | 87 | class NcFramelessHelperImpl 88 | { 89 | public: 90 | QHash< QWidget*, NcWidgetData* > mHashWidgetData; 91 | bool mWidgetMovable; 92 | bool mWidgetResizable; 93 | bool mUseRubberBandOnResize; 94 | bool mUseRubberBandOnMove; 95 | }; 96 | 97 | NcCursorPosCalculator::NcCursorPosCalculator() 98 | { 99 | reset(); 100 | } 101 | 102 | void NcCursorPosCalculator::reset() 103 | { 104 | onEdges = false; 105 | onLeftEdge = false; 106 | onRightEdge = false; 107 | onTopEdge = false; 108 | onBottomEdge = false; 109 | onTopLeftEdge = false; 110 | onBottomLeftEdge = false; 111 | onTopRightEdge = false; 112 | onBottomRightEdge = false; 113 | } 114 | 115 | void NcCursorPosCalculator::recalculate( const QPoint& globalMousePos, const QRect& frameRect ) 116 | { 117 | int globalMouseX = globalMousePos.x(); 118 | int globalMouseY = globalMousePos.y(); 119 | 120 | int frameX = frameRect.x(); 121 | int frameY = frameRect.y(); 122 | 123 | int frameWidth = frameRect.width(); 124 | int frameHeight = frameRect.height(); 125 | 126 | onLeftEdge = globalMouseX >= frameX && 127 | globalMouseX <= frameX + mBorderWidth; 128 | 129 | 130 | onRightEdge = globalMouseX >= frameX + frameWidth - mBorderWidth && 131 | globalMouseX <= frameX + frameWidth; 132 | 133 | onTopEdge = globalMouseY >= frameY && 134 | globalMouseY <= frameY + mBorderWidth; 135 | 136 | 137 | onBottomEdge = globalMouseY >= frameY + frameHeight - mBorderWidth && 138 | globalMouseY <= frameY + frameHeight; 139 | 140 | onTopLeftEdge = onTopEdge && onLeftEdge; 141 | onBottomLeftEdge = onBottomEdge && onLeftEdge; 142 | onTopRightEdge = onTopEdge && onRightEdge; 143 | onBottomRightEdge = onBottomEdge && onRightEdge; 144 | 145 | //only these checks would be enough 146 | onEdges = onLeftEdge || onRightEdge || 147 | onTopEdge || onBottomEdge; 148 | 149 | } 150 | 151 | 152 | NcWidgetData::NcWidgetData( NcFramelessHelperImpl* _d, QWidget* topLevelWidget ) 153 | { 154 | d = _d; 155 | mWidget = topLevelWidget; 156 | mLeftButtonPressed = false; 157 | mRubberBand = 0; 158 | mCursorShapeChanged = false; 159 | 160 | mWindowFlags = mWidget->windowFlags(); 161 | 162 | //---from Qt docs of setWindowFlags()---- 163 | //Note: This function calls setParent() when 164 | //changing the flags for a window, causing the 165 | //widget to be hidden. You must call show() 166 | //to make the widget visible again.. 167 | 168 | // bool visible = mWidget->isVisible(); 169 | 170 | mWidget->setMouseTracking( true ); 171 | // mWidget->setWindowFlags( Qt::CustomizeWindowHint|Qt::FramelessWindowHint ); 172 | //Bug fix, mouse move events does not propagate from child widgets. 173 | //so need the hover events. 174 | mWidget->setAttribute( Qt::WA_Hover ); 175 | 176 | updateRubberBandStatus(); 177 | 178 | // mWidget->setVisible( visible ); 179 | } 180 | 181 | NcWidgetData::~NcWidgetData() 182 | { 183 | //---from Qt docs of setWindowFlags()---- 184 | //Note: This function calls setParent() when 185 | //changing the flags for a window, causing the 186 | //widget to be hidden. You must call show() 187 | //to make the widget visible again.. 188 | 189 | // bool visible = mWidget->isVisible(); 190 | 191 | mWidget->setMouseTracking( false ); 192 | // mWidget->setWindowFlags( mWindowFlags );//^ Qt::CustomizeWindowHint ^ Qt::FramelessWindowHint ); 193 | mWidget->setAttribute( Qt::WA_Hover, false ); 194 | 195 | // mWidget->setVisible( visible ); 196 | 197 | delete mRubberBand; 198 | } 199 | 200 | void NcWidgetData::updateRubberBandStatus() 201 | { 202 | if ( d->mUseRubberBandOnMove || d->mUseRubberBandOnResize ) 203 | { 204 | if ( !mRubberBand ) 205 | mRubberBand = new QRubberBand( QRubberBand::Line/*QRubberBand::Rectangle*/ ); 206 | } 207 | else 208 | { 209 | delete mRubberBand; 210 | mRubberBand = 0; 211 | } 212 | } 213 | 214 | bool NcWidgetData::cursorShapeChanged() const 215 | { 216 | return mCursorShapeChanged; 217 | } 218 | 219 | QWidget* NcWidgetData::widget() 220 | { 221 | return mWidget; 222 | } 223 | 224 | void NcWidgetData::handleWidgetEvent( QEvent* event ) 225 | { 226 | switch ( event->type() ) 227 | { 228 | default: //qDebug() << "Event = " << event; 229 | break; 230 | case QEvent::MouseButtonPress: 231 | handleMousePressEvent( static_cast( event ) ); 232 | break; 233 | 234 | case QEvent::MouseButtonRelease: 235 | handleMouseReleaseEvent( static_cast( event ) ); 236 | break; 237 | 238 | case QEvent::MouseMove: 239 | handleMouseMoveEvent( static_cast( event ) ); 240 | break; 241 | 242 | case QEvent::Leave: 243 | handleLeaveEvent( event ); 244 | break; 245 | 246 | //Bug fix, hover event is necessary coz child widget does not 247 | //propagate mousemove events. so the cursor remains in edge shape 248 | //even in middle of widget. 249 | case QEvent::HoverMove: 250 | handleHoverMoveEvent( static_cast( event ) ); 251 | break; 252 | //case QEvent::Enter: 253 | //qDebug() << "Enter event";//d->handleEnterEvent( event ); 254 | //break; 255 | } 256 | } 257 | 258 | void NcWidgetData::updateCursorShape( const QPoint& globalMousePos ) 259 | { 260 | if ( mWidget->isFullScreen() || mWidget->isMaximized() ) 261 | { 262 | if ( mCursorShapeChanged ) 263 | mWidget->unsetCursor(); 264 | 265 | return; 266 | } 267 | 268 | mMoveMousePos.recalculate( globalMousePos, mWidget->frameGeometry() ); 269 | 270 | if( mMoveMousePos.onTopLeftEdge || mMoveMousePos.onBottomRightEdge ) 271 | { 272 | mWidget->setCursor( Qt::SizeFDiagCursor ); 273 | mCursorShapeChanged = true; 274 | } 275 | else if( mMoveMousePos.onTopRightEdge || mMoveMousePos.onBottomLeftEdge ) 276 | { 277 | mWidget->setCursor( Qt::SizeBDiagCursor ); 278 | mCursorShapeChanged = true; 279 | } 280 | else if( mMoveMousePos.onLeftEdge || mMoveMousePos.onRightEdge ) 281 | { 282 | mWidget->setCursor( Qt::SizeHorCursor ); 283 | mCursorShapeChanged = true; 284 | } 285 | else if( mMoveMousePos.onTopEdge || mMoveMousePos.onBottomEdge ) 286 | { 287 | mWidget->setCursor( Qt::SizeVerCursor ); 288 | mCursorShapeChanged = true; 289 | } 290 | else 291 | { 292 | if ( mCursorShapeChanged ) 293 | { 294 | mWidget->unsetCursor(); 295 | mCursorShapeChanged = false; 296 | } 297 | } 298 | } 299 | 300 | void NcWidgetData::resizeWidget( const QPoint& globalMousePos ) 301 | { 302 | QRect origRect; 303 | 304 | if ( d->mUseRubberBandOnResize ) 305 | origRect = mRubberBand->frameGeometry(); 306 | else 307 | origRect = mWidget->frameGeometry(); 308 | 309 | 310 | int left = origRect.left(); 311 | int top = origRect.top(); 312 | int right = origRect.right(); 313 | int bottom = origRect.bottom(); 314 | origRect.getCoords( &left, &top, &right, &bottom ); 315 | 316 | int minWidth = mWidget->minimumWidth(); 317 | int minHeight = mWidget->minimumHeight(); 318 | 319 | if ( mPressedMousePos.onTopLeftEdge ) 320 | { 321 | left = globalMousePos.x(); 322 | top = globalMousePos.y(); 323 | } 324 | else if ( mPressedMousePos.onBottomLeftEdge ) 325 | { 326 | left = globalMousePos.x(); 327 | bottom = globalMousePos.y(); 328 | } 329 | else if ( mPressedMousePos.onTopRightEdge ) 330 | { 331 | right = globalMousePos.x(); 332 | top = globalMousePos.y(); 333 | } 334 | else if ( mPressedMousePos.onBottomRightEdge ) 335 | { 336 | right = globalMousePos.x(); 337 | bottom = globalMousePos.y(); 338 | } 339 | else if ( mPressedMousePos.onLeftEdge ) 340 | { 341 | left = globalMousePos.x(); 342 | } 343 | else if ( mPressedMousePos.onRightEdge ) 344 | { 345 | right = globalMousePos.x(); 346 | } 347 | else if ( mPressedMousePos.onTopEdge ) 348 | { 349 | top = globalMousePos.y(); 350 | } 351 | else if ( mPressedMousePos.onBottomEdge ) 352 | { 353 | bottom = globalMousePos.y(); 354 | } 355 | 356 | QRect newRect( QPoint(left, top), QPoint(right, bottom) ); 357 | 358 | if ( newRect.isValid() ) 359 | { 360 | if ( minWidth > newRect.width() ) 361 | { 362 | //determine what has caused the width change. 363 | if( left != origRect.left() ) 364 | newRect.setLeft( origRect.left() ); 365 | else 366 | newRect.setRight( origRect.right() ); 367 | } 368 | if ( minHeight > newRect.height() ) 369 | { 370 | //determine what has caused the height change. 371 | if ( top != origRect.top() ) 372 | newRect.setTop( origRect.top() ); 373 | else 374 | newRect.setBottom( origRect.bottom() ); 375 | } 376 | 377 | if ( d->mUseRubberBandOnResize ) 378 | { 379 | mRubberBand->setGeometry( newRect ); 380 | } 381 | else 382 | { 383 | mWidget->setGeometry( newRect ); 384 | } 385 | } 386 | else 387 | { 388 | //qDebug() << "Calculated Rect is not valid" << newRect; 389 | } 390 | 391 | } 392 | 393 | void NcWidgetData::moveWidget( const QPoint& globalMousePos ) 394 | { 395 | if ( d->mUseRubberBandOnMove ) 396 | { 397 | mRubberBand->move( globalMousePos - mDragPos ); 398 | } 399 | else 400 | { 401 | mWidget->move( globalMousePos - mDragPos ); 402 | } 403 | } 404 | 405 | void NcWidgetData::handleMousePressEvent( QMouseEvent* event ) 406 | { 407 | if ( event->button() == Qt::LeftButton ) 408 | { 409 | mLeftButtonPressed = true; 410 | 411 | QRect frameRect = mWidget->frameGeometry(); 412 | mPressedMousePos.recalculate( event->globalPos(), frameRect ); 413 | 414 | mDragPos = event->globalPos() - frameRect.topLeft(); 415 | 416 | if ( mPressedMousePos.onEdges ) 417 | { 418 | if ( d->mUseRubberBandOnResize ) 419 | { 420 | mRubberBand->setGeometry( frameRect ); 421 | mRubberBand->show(); 422 | } 423 | } 424 | else if ( d->mUseRubberBandOnMove ) 425 | { 426 | mRubberBand->setGeometry( frameRect ); 427 | mRubberBand->show(); 428 | } 429 | 430 | } 431 | } 432 | 433 | void NcWidgetData::handleMouseReleaseEvent( QMouseEvent* event ) 434 | { 435 | if ( event->button() == Qt::LeftButton ) 436 | { 437 | mLeftButtonPressed = false; 438 | mPressedMousePos.reset(); 439 | if ( mRubberBand && mRubberBand->isVisible() ) 440 | { 441 | mRubberBand->hide(); 442 | mWidget->setGeometry( mRubberBand->geometry() ); 443 | } 444 | } 445 | } 446 | 447 | void NcWidgetData::handleMouseMoveEvent( QMouseEvent* event ) 448 | { 449 | if ( mLeftButtonPressed ) 450 | { 451 | if ( d->mWidgetResizable && mPressedMousePos.onEdges ) 452 | { 453 | resizeWidget( event->globalPos() ); 454 | } 455 | else if ( d->mWidgetMovable ) 456 | { 457 | moveWidget( event->globalPos() ); 458 | } 459 | } 460 | else if ( d->mWidgetResizable ) 461 | { 462 | updateCursorShape( event->globalPos() ); 463 | } 464 | 465 | } 466 | 467 | void NcWidgetData::handleLeaveEvent( QEvent* /*event*/ ) 468 | { 469 | if ( !mLeftButtonPressed ) 470 | mWidget->unsetCursor(); 471 | mLeftButtonPressed = false; //add by dragon 472 | } 473 | 474 | void NcWidgetData::handleHoverMoveEvent( QHoverEvent* event ) 475 | { 476 | if ( d->mWidgetResizable ) 477 | updateCursorShape( mWidget->mapToGlobal( event->pos() ) ); 478 | } 479 | 480 | NcFramelessHelper::NcFramelessHelper( QObject* parent ) 481 | : QObject( parent ), 482 | d( new NcFramelessHelperImpl ) 483 | { 484 | d->mWidgetMovable = true; 485 | d->mWidgetResizable = true; 486 | d->mUseRubberBandOnResize = false; 487 | d->mUseRubberBandOnMove = false; 488 | } 489 | 490 | NcFramelessHelper::~NcFramelessHelper() 491 | { 492 | QList keys = d->mHashWidgetData.keys(); 493 | int size = keys.size(); 494 | for ( int i = 0; i < size; ++i ) 495 | { 496 | delete d->mHashWidgetData.take( keys[i] ); 497 | } 498 | 499 | delete d; 500 | } 501 | 502 | void NcFramelessHelper::setFilterEventCallBack(FilterEventCallBack filterFunc) 503 | { 504 | filter_event_call_back_ = filterFunc; 505 | } 506 | 507 | bool NcFramelessHelper::eventFilter( QObject *obj, QEvent *event ) 508 | { 509 | if (d->mHashWidgetData.empty()) { 510 | return false; 511 | } 512 | 513 | QEvent::Type type = event->type(); 514 | if ( type == QEvent::MouseMove || 515 | type == QEvent::HoverMove || 516 | type == QEvent::MouseButtonPress || 517 | type == QEvent::MouseButtonRelease || 518 | type == QEvent::Leave 519 | ) 520 | { 521 | // 临时解决方案,始终取第一 522 | // NcWidgetData* data = d->mHashWidgetData.value( static_cast(obj) ); 523 | NcWidgetData* data = d->mHashWidgetData.begin().value(); 524 | if (nullptr == data ) 525 | return false; 526 | 527 | if (nullptr != filter_event_call_back_ && 528 | filter_event_call_back_(obj, event) && 529 | !data->cursorShapeChanged()) { 530 | return false; 531 | } 532 | 533 | if ( data ) 534 | data->handleWidgetEvent( event ); 535 | } 536 | 537 | return false; 538 | } 539 | 540 | void NcFramelessHelper::activateOn( QWidget* topLevelWidget ) 541 | { 542 | if ( d->mHashWidgetData.contains( topLevelWidget ) ) 543 | return; 544 | 545 | NcWidgetData* data = new NcWidgetData( d, topLevelWidget ); 546 | d->mHashWidgetData.insert( topLevelWidget, data ); 547 | 548 | topLevelWidget->installEventFilter( this ); 549 | 550 | } 551 | 552 | void NcFramelessHelper::activeOnWithChildWidget( QWidget* topLevelWidget , QWidget *tagetWidget) 553 | { 554 | activateOn(topLevelWidget); 555 | tagetWidget->installEventFilter(this); 556 | } 557 | 558 | void NcFramelessHelper::removeFrom( QWidget* topLevelWidget ) 559 | { 560 | NcWidgetData* data = d->mHashWidgetData.take( topLevelWidget ); 561 | if ( data ) 562 | { 563 | topLevelWidget->removeEventFilter( this ); 564 | delete data; 565 | } 566 | } 567 | 568 | void NcFramelessHelper::setWidgetMovable( bool movable ) 569 | { 570 | d->mWidgetMovable = movable; 571 | } 572 | 573 | bool NcFramelessHelper::isWidgetMovable() 574 | { 575 | return d->mWidgetMovable; 576 | } 577 | 578 | void NcFramelessHelper::setWidgetResizable( bool resizable ) 579 | { 580 | d->mWidgetResizable = resizable; 581 | } 582 | 583 | bool NcFramelessHelper::isWidgetResizable() 584 | { 585 | return d->mWidgetResizable; 586 | } 587 | 588 | void NcFramelessHelper::useRubberBandOnMove( bool use ) 589 | { 590 | d->mUseRubberBandOnMove = use; 591 | QList list = d->mHashWidgetData.values(); 592 | int size = list.size(); 593 | for ( int i = 0; i < size; ++i ) 594 | list[i]->updateRubberBandStatus(); 595 | } 596 | 597 | bool NcFramelessHelper::isUsingRubberBandOnMove() 598 | { 599 | return d->mUseRubberBandOnMove; 600 | } 601 | 602 | void NcFramelessHelper::useRubberBandOnResize( bool use ) 603 | { 604 | d->mUseRubberBandOnResize = use; 605 | QList list = d->mHashWidgetData.values(); 606 | int size = list.size(); 607 | for ( int i = 0; i < size; ++i ) 608 | list[i]->updateRubberBandStatus(); 609 | } 610 | 611 | bool NcFramelessHelper::isUsingRubberBandOnResisze() 612 | { 613 | return d->mUseRubberBandOnResize; 614 | } 615 | 616 | void NcFramelessHelper::setBorderWidth( int newBorderWidth ) 617 | { 618 | //TODO: Make it non-static. 619 | if ( newBorderWidth >= 0 ) 620 | NcCursorPosCalculator::mBorderWidth = newBorderWidth; 621 | } 622 | 623 | int NcFramelessHelper::borderWidth() 624 | { 625 | return NcCursorPosCalculator::mBorderWidth; 626 | } 627 | -------------------------------------------------------------------------------- /QtExtTabWidget.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | true 63 | true 64 | 65 | 66 | true 67 | true 68 | 69 | 70 | true 71 | true 72 | 73 | 74 | true 75 | true 76 | 77 | 78 | true 79 | true 80 | 81 | 82 | true 83 | true 84 | 85 | 86 | true 87 | true 88 | 89 | 90 | true 91 | true 92 | 93 | 94 | true 95 | true 96 | 97 | 98 | true 99 | true 100 | 101 | 102 | true 103 | true 104 | 105 | 106 | true 107 | true 108 | 109 | 110 | true 111 | true 112 | 113 | 114 | true 115 | true 116 | 117 | 118 | true 119 | true 120 | 121 | 122 | true 123 | true 124 | 125 | 126 | true 127 | true 128 | 129 | 130 | true 131 | true 132 | 133 | 134 | true 135 | true 136 | 137 | 138 | true 139 | true 140 | 141 | 142 | true 143 | true 144 | 145 | 146 | true 147 | true 148 | 149 | 150 | true 151 | true 152 | 153 | 154 | true 155 | true 156 | 157 | 158 | true 159 | true 160 | 161 | 162 | 163 | {B12702AD-ABFB-343A-A199-8E24837244A3} 164 | Qt4VSv1.0 165 | 166 | 167 | 168 | Application 169 | v120 170 | 171 | 172 | Application 173 | v120 174 | 175 | 176 | 177 | $(MSBuildProjectDirectory)\QtMsBuild 178 | 179 | 180 | $(SolutionDir)$(Platform)\$(Configuration)\ 181 | 182 | 183 | $(SolutionDir)$(Platform)\$(Configuration)\ 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | true 203 | UNICODE;WIN32;WIN64;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) 204 | .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;%(AdditionalIncludeDirectories) 205 | Disabled 206 | ProgramDatabase 207 | MultiThreadedDebugDLL 208 | true 209 | 210 | 211 | Console 212 | $(OutDir)\$(ProjectName).exe 213 | $(QTDIR)\lib;%(AdditionalLibraryDirectories) 214 | true 215 | qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;%(AdditionalDependencies) 216 | 217 | 218 | .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp 219 | Moc'ing %(Identity)... 220 | .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName)\.;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets 221 | UNICODE;WIN32;WIN64;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) 222 | 223 | 224 | Uic'ing %(Identity)... 225 | .\GeneratedFiles\ui_%(Filename).h 226 | 227 | 228 | Rcc'ing %(Identity)... 229 | .\GeneratedFiles\qrc_%(Filename).cpp 230 | 231 | 232 | 233 | 234 | true 235 | UNICODE;WIN32;WIN64;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) 236 | .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets;%(AdditionalIncludeDirectories) 237 | 238 | MultiThreadedDLL 239 | true 240 | 241 | 242 | Windows 243 | $(OutDir)\$(ProjectName).exe 244 | $(QTDIR)\lib;%(AdditionalLibraryDirectories) 245 | true 246 | qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies) 247 | 248 | 249 | .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp 250 | Moc'ing %(Identity)... 251 | .\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName)\.;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtWidgets 252 | UNICODE;WIN32;WIN64;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions) 253 | 254 | 255 | Uic'ing %(Identity)... 256 | .\GeneratedFiles\ui_%(Filename).h 257 | 258 | 259 | Rcc'ing %(Identity)... 260 | .\GeneratedFiles\qrc_%(Filename).cpp 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | --------------------------------------------------------------------------------