├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── Screenshots ├── LibreOffice Writer Properties.png ├── designer_widgetbox.png ├── designer_widgetbox_150x150.png ├── widgetbox_in_qtdesigner.png ├── widgetbox_sample1.png └── widgetbox_sample2.png ├── Sources ├── CategoryWidgets.cpp ├── CategoryWidgets.h ├── PageEventFilter.cpp ├── PageEventFilter.h ├── WidgetBox.pro ├── collapsed.png ├── expanded.png ├── icons.qrc ├── plugin_export.h ├── widgetbox.cpp ├── widgetbox.h ├── widgetbox.png ├── widgetboxdesignercontainerextension.cpp ├── widgetboxdesignercontainerextension.h ├── widgetboxextensionfactory.cpp ├── widgetboxextensionfactory.h ├── widgetboxplugin.cpp └── widgetboxplugin.h └── TODO /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.c text 7 | *.h text 8 | 9 | # Declare files that will always have CRLF line endings on checkout. 10 | *.sln text eol=crlf 11 | 12 | # Denote all files that are truly binary and should not be modified. 13 | *.png binary 14 | *.jpg binary 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Qt-es 2 | /.qmake.cache 3 | /.qmake.stash 4 | *.pro.user 5 | *.pro.user.* 6 | *.moc 7 | moc_*.cpp 8 | qrc_*.cpp 9 | ui_*.h 10 | Makefile* 11 | *-build-* 12 | 13 | # QtCreator 14 | *.autosave 15 | 16 | *.user 17 | build-* 18 | release 19 | *.ver 20 | *.o 21 | 22 | # Compiled Object files 23 | *.slo 24 | *.lo 25 | *.o 26 | *.obj 27 | 28 | # Precompiled Headers 29 | *.gch 30 | *.pch 31 | 32 | # Compiled Dynamic libraries 33 | *.so 34 | *.dylib 35 | *.dll 36 | 37 | # Fortran module files 38 | *.mod 39 | 40 | # Compiled Static libraries 41 | *.lai 42 | *.la 43 | *.a 44 | *.lib 45 | 46 | # Executables 47 | *.exe 48 | *.out 49 | *.app 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WidgetBox 2 | Widget Box is a Qt widget which contains a list of widgets (pages) separated by categories 3 | 4 | Widget similar to the Widget Box in the Qt Designer. 5 | 6 | ![alt tag](Screenshots/designer_widgetbox_150x150.png "Widget Box in Qt Designer") 7 | 8 | It contains a list of widgets (pages) separated by categories. Each category button can be clicked in order to expand and collapse the list below the button. Sample screenshots on moment of uploading to GitHub: 9 | 10 |         ![alt tag](Screenshots/widgetbox_sample1.png "Widget Box Sample 1")        ![alt tag](Screenshots/widgetbox_sample2.png "Widget Box Sample 2") 11 | -------------------------------------------------------------------------------- /Screenshots/LibreOffice Writer Properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Screenshots/LibreOffice Writer Properties.png -------------------------------------------------------------------------------- /Screenshots/designer_widgetbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Screenshots/designer_widgetbox.png -------------------------------------------------------------------------------- /Screenshots/designer_widgetbox_150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Screenshots/designer_widgetbox_150x150.png -------------------------------------------------------------------------------- /Screenshots/widgetbox_in_qtdesigner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Screenshots/widgetbox_in_qtdesigner.png -------------------------------------------------------------------------------- /Screenshots/widgetbox_sample1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Screenshots/widgetbox_sample1.png -------------------------------------------------------------------------------- /Screenshots/widgetbox_sample2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Screenshots/widgetbox_sample2.png -------------------------------------------------------------------------------- /Sources/CategoryWidgets.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "widgetbox.h" 4 | #include "CategoryWidgets.h" 5 | #include "PageEventFilter.h" 6 | 7 | /** 8 | * @class AbstractCategoryButton is abstract ancestor class for all categories 9 | * widgets types 10 | * @brief AbstractCategoryButton::AbstractCategoryButton 11 | */ 12 | 13 | AbstractCategory::AbstractCategory(const QString &, QTreeWidget *parent, 14 | QTreeWidgetItem *item) 15 | : QWidget(parent) 16 | , mItem(item) 17 | , mEventFilter(new PageEventFilter(this, item)) 18 | { 19 | mItem->setExpanded(true); 20 | connect(mEventFilter, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(onButtonPress())); 21 | } 22 | 23 | void AbstractCategory::onButtonPress() 24 | { 25 | setExpanded(!mItem->isExpanded()); 26 | } 27 | 28 | void AbstractCategory::onPageExpand(bool expanded) 29 | { 30 | mItem->setExpanded(expanded); 31 | int index = mItem->treeWidget()->indexOfTopLevelItem(mItem); 32 | ((WidgetBox *)mItem->treeWidget()->parent())->setCurrentIndex(index); 33 | 34 | emit pageExpanded(isExpanded()); 35 | } 36 | 37 | int AbstractCategory::itemIndex() const 38 | { 39 | return mItem->treeWidget()->indexOfTopLevelItem(mItem); 40 | } 41 | 42 | /** 43 | * @class PageButton 44 | * @brief The PageButton class: page (category) button for widget box 45 | */ 46 | ButtonCategory::ButtonCategory(const QString &text, QTreeWidget *parent, 47 | QTreeWidgetItem *item) 48 | : AbstractCategory(text, parent, item) 49 | 50 | { 51 | mButton = new QPushButton(text, parent); 52 | mButton->installEventFilter(eventFilter()); // Send mouse events to tree widget 53 | // Prefix __qt__passive_ enables mouse events for widget in Qt Designer 54 | mButton->setObjectName(QString("__qt__passive_ButtonCategory%1").arg(itemIndex())); 55 | 56 | QHBoxLayout *horizontalLayout = new QHBoxLayout(this); 57 | horizontalLayout->setSpacing(0); 58 | horizontalLayout->setContentsMargins(0, 0, 0, 0); 59 | horizontalLayout->addWidget(mButton); // Add button to layout 60 | 61 | mButton->setIcon(QIcon(":/plugins/widgetbox/expanded.png")); 62 | // setFlat(true); 63 | 64 | mButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); 65 | QFontMetrics fm(font()); 66 | mButton->resize(size().width(), fm.height()); 67 | } 68 | 69 | void ButtonCategory::setTitle(QString const &title) 70 | { 71 | mButton->setText(title); 72 | } 73 | 74 | void ButtonCategory::onButtonPress() 75 | { 76 | AbstractCategory::onButtonPress(); 77 | 78 | if(isExpanded()) { 79 | mButton->setIcon(QIcon(":/plugins/widgetbox/expanded.png")); 80 | } else { 81 | mButton->setIcon(QIcon(":/plugins/widgetbox/collapsed.png")); 82 | } 83 | } 84 | 85 | LineCategory::LineCategory(const QString &text, QTreeWidget *parent, QTreeWidgetItem *item) 86 | : AbstractCategory(text, parent, item) 87 | { 88 | QHBoxLayout *horizontalLayout = new QHBoxLayout(this); 89 | horizontalLayout->setSpacing(4); 90 | horizontalLayout->setContentsMargins(0, 0, 6, 0); 91 | 92 | mCheckBox = new QCheckBox(this); 93 | mCheckBox->setChecked(true); 94 | mCheckBox->installEventFilter(eventFilter()); // Send mouse events to tree widget 95 | horizontalLayout->addWidget(mCheckBox); // Add checkbox to layout 96 | // Prefix __qt__passive_ enables mouse events for widget in Qt Designer 97 | mCheckBox->setObjectName(QString("__qt__passive_CheckBox%1").arg(itemIndex())); 98 | 99 | QFrame *line1 = new QFrame(this); 100 | line1->installEventFilter(eventFilter()); // Send mouse events to tree widget 101 | // Prefix __qt__passive_ enables mouse events for widget in Qt Designer 102 | line1->setObjectName(QString("__qt__passive_line1_%1").arg(itemIndex())); 103 | QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 104 | sizePolicy.setHorizontalStretch(0); 105 | sizePolicy.setVerticalStretch(0); 106 | sizePolicy.setHeightForWidth(line1->sizePolicy().hasHeightForWidth()); 107 | line1->setSizePolicy(sizePolicy); 108 | line1->setFrameShadow(QFrame::Plain); 109 | line1->setLineWidth(1); 110 | line1->setMidLineWidth(1); 111 | line1->setFrameShape(QFrame::HLine); 112 | horizontalLayout->addWidget(line1); // Add line1 to layout 113 | 114 | mLabel = new QLabel(text, this); 115 | mLabel->installEventFilter(eventFilter()); // Send mouse events to tree widget 116 | // Prefix __qt__passive_ enables mouse events for widget in Qt Designer 117 | mLabel->setObjectName(QString("__qt__passive_Label%1").arg(itemIndex())); 118 | QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred); 119 | sizePolicy1.setHorizontalStretch(0); 120 | sizePolicy1.setVerticalStretch(0); 121 | sizePolicy1.setHeightForWidth(mLabel->sizePolicy().hasHeightForWidth()); 122 | mLabel->setSizePolicy(sizePolicy1); 123 | horizontalLayout->addWidget(mLabel); // Add label to layout 124 | 125 | QFrame *line2 = new QFrame(this); 126 | line2->installEventFilter(eventFilter()); // Send mouse events to tree widget 127 | // Prefix __qt__passive_ enables mouse events for widget in Qt Designer 128 | line2->setObjectName(QString("__qt__passive_line2_%1").arg(itemIndex())); 129 | sizePolicy.setHeightForWidth(line2->sizePolicy().hasHeightForWidth()); 130 | line2->setSizePolicy(sizePolicy); 131 | line2->setFrameShadow(QFrame::Plain); 132 | line2->setLineWidth(1); 133 | line2->setMidLineWidth(1); 134 | line2->setFrameShape(QFrame::HLine); 135 | horizontalLayout->addWidget(line2); // Add line2 to layout 136 | } 137 | 138 | void LineCategory::setExpanded(bool expanded) 139 | { 140 | AbstractCategory::setExpanded(expanded); 141 | mCheckBox->setChecked(expanded); 142 | } 143 | 144 | void LineCategory::setTitle(QString const &title) 145 | { 146 | mLabel->setText(title); 147 | } 148 | -------------------------------------------------------------------------------- /Sources/CategoryWidgets.h: -------------------------------------------------------------------------------- 1 | #ifndef CATEGORYWIDGETS_H 2 | #define CATEGORYWIDGETS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class PageEventFilter; 11 | 12 | class AbstractCategory : public QWidget 13 | { 14 | Q_OBJECT 15 | public: 16 | AbstractCategory(const QString &text, QTreeWidget *parent, QTreeWidgetItem *item); 17 | bool isExpanded() const { return mItem->isExpanded(); } 18 | virtual QString title() const = 0; 19 | 20 | public slots: 21 | virtual void setTitle(QString const &title) = 0; 22 | virtual void setExpanded(bool expanded) { onPageExpand(expanded); } 23 | 24 | protected: 25 | QTreeWidgetItem *item() const { return mItem; } 26 | PageEventFilter *eventFilter() const { return mEventFilter; } 27 | int itemIndex() const; 28 | 29 | signals: 30 | void pageExpanded(bool expanded); 31 | 32 | protected slots: 33 | virtual void onButtonPress(); 34 | void onPageExpand(bool expanded = false); 35 | 36 | private: 37 | QTreeWidgetItem* mItem; 38 | PageEventFilter *mEventFilter; 39 | }; 40 | 41 | class ButtonCategory : public AbstractCategory 42 | { 43 | Q_OBJECT 44 | public: 45 | ButtonCategory(const QString &text, QTreeWidget *parent, QTreeWidgetItem *item); 46 | QString title() const override { return mButton->text(); } 47 | 48 | public slots: 49 | void setTitle(QString const &title) override; 50 | 51 | private slots: 52 | void onButtonPress() override; 53 | 54 | private: 55 | QPushButton *mButton; 56 | }; 57 | 58 | class LineCategory : public AbstractCategory 59 | { 60 | Q_OBJECT 61 | public: 62 | LineCategory(const QString &text, QTreeWidget *parent, QTreeWidgetItem *item); 63 | QString title() const override { return mLabel->text(); } 64 | 65 | public slots: 66 | void setTitle(QString const &title) override; 67 | void setExpanded(bool expanded) override; 68 | 69 | private: 70 | QLabel *mLabel; 71 | QCheckBox *mCheckBox; 72 | }; 73 | 74 | #endif // CATEGORYWIDGETS_H 75 | -------------------------------------------------------------------------------- /Sources/PageEventFilter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "PageEventFilter.h" 5 | 6 | 7 | /*! 8 | * \class propagates mouse press event filter to parent QTreeWidget 9 | */ 10 | 11 | PageEventFilter::PageEventFilter(QObject *parent, QTreeWidgetItem *item) 12 | : QObject(parent) 13 | , mItem(item) 14 | { 15 | connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), 16 | mItem->treeWidget(), SIGNAL(itemClicked(QTreeWidgetItem*,int))); 17 | } 18 | 19 | bool PageEventFilter::eventFilter(QObject *obj, QEvent *event) 20 | { 21 | if (event->type() == QEvent::MouseButtonPress) 22 | { 23 | // Resend signal to QTreeWidget 24 | emit itemClicked(mItem, 0); 25 | return true; // Filter event to the object 26 | } 27 | else 28 | { 29 | // standard event processing 30 | return QObject::eventFilter(obj, event); 31 | } 32 | } 33 | 34 | /*! 35 | * \class PageResizeFilter provides event filter to change item size hint in 36 | * Qt Designer on page widget resize as seems QTreeWidgetItem knows nothing 37 | * about its widget in design time and does not automatically adjust item 38 | * size like it does in run-time 39 | */ 40 | 41 | PageResizeFilter::PageResizeFilter(QObject *parent, QTreeWidgetItem *item) 42 | : QObject(parent) 43 | , mItem(item) 44 | { 45 | } 46 | 47 | /*! 48 | * \brief PageResizeFilter::eventFilter changes item size hint in Qt Designer 49 | * on page widget resize as seems QTreeWidgetItem knows nothing about its 50 | * widget in design time and does not automatically adjust item size 51 | * like it does in run-time 52 | * \param obj page widget to filter QEvent::Resize 53 | * \param event 54 | * \return false to continue event processing by page widget 55 | */ 56 | bool PageResizeFilter::eventFilter(QObject *obj, QEvent *event) 57 | { 58 | if (event->type() == QEvent::Resize) 59 | { 60 | QWidget *page = ((QWidget *)obj); 61 | mItem->setSizeHint(0, page->geometry().size()); 62 | return false; // Sent event to the object (do not filter it) 63 | } 64 | else 65 | { 66 | // standard event processing 67 | return QObject::eventFilter(obj, event); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/PageEventFilter.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGEEVENTFILTER_H 2 | #define PAGEEVENTFILTER_H 3 | 4 | #include 5 | 6 | class QTreeWidgetItem; 7 | 8 | class PageEventFilter : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | PageEventFilter(QObject *parent, QTreeWidgetItem *item); 13 | 14 | protected: 15 | bool eventFilter(QObject *obj, QEvent *event); 16 | 17 | signals: 18 | void itemClicked(QTreeWidgetItem *item, int column); 19 | 20 | private: 21 | QTreeWidgetItem *mItem; 22 | }; 23 | 24 | 25 | class PageResizeFilter : public QObject 26 | { 27 | Q_OBJECT 28 | public: 29 | PageResizeFilter(QObject *parent, QTreeWidgetItem *item); 30 | 31 | protected: 32 | bool eventFilter(QObject *obj, QEvent *event); 33 | 34 | private: 35 | QTreeWidgetItem *mItem; 36 | }; 37 | 38 | #endif // PAGEEVENTFILTER_H 39 | -------------------------------------------------------------------------------- /Sources/WidgetBox.pro: -------------------------------------------------------------------------------- 1 | CONFIG += plugin debug_and_release 2 | TARGET = $$qtLibraryTarget(WidgetBoxPlugin) 3 | TEMPLATE = lib 4 | 5 | HEADERS = widgetboxplugin.h widgetbox.h \ 6 | widgetboxextensionfactory.h \ 7 | widgetboxdesignercontainerextension.h \ 8 | CategoryWidgets.h \ 9 | plugin_export.h \ 10 | PageEventFilter.h 11 | 12 | SOURCES = widgetboxplugin.cpp widgetbox.cpp \ 13 | widgetboxextensionfactory.cpp \ 14 | widgetboxdesignercontainerextension.cpp \ 15 | CategoryWidgets.cpp \ 16 | PageEventFilter.cpp 17 | 18 | RESOURCES = icons.qrc 19 | LIBS += -L. 20 | 21 | greaterThan(QT_MAJOR_VERSION, 4) { 22 | QT += designer 23 | } else { 24 | CONFIG += designer 25 | } 26 | 27 | target.path = $$[QT_INSTALL_PLUGINS]/designer 28 | 29 | creator_target.name = Copying the target dll to Qt Creator plugins directory as well 30 | creator_target.input = $qtLbraryTarget(WidgetBoxPlugin) 31 | creator_target.path = $$(QTCREATOR_BIN_PATH)/plugins/designer 32 | creator_target.CONFIG += no_check_exist 33 | creator_target.output = WidgetBoxPlugin.dll 34 | win32:creator_target.files = $$OUT_PWD/release/WidgetBoxPlugin.dll 35 | else:unix:creator_target.files = $$OUT_PWD/release/libWidgetBoxPlugin.so 36 | QMAKE_EXTRA_COMPILERS += creator_target 37 | 38 | INSTALLS += target creator_target 39 | -------------------------------------------------------------------------------- /Sources/collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Sources/collapsed.png -------------------------------------------------------------------------------- /Sources/expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Sources/expanded.png -------------------------------------------------------------------------------- /Sources/icons.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | collapsed.png 6 | expanded.png 7 | widgetbox.png 8 | 9 | 10 | -------------------------------------------------------------------------------- /Sources/plugin_export.h: -------------------------------------------------------------------------------- 1 | #ifndef PLUGIN_EXPORT_H 2 | #define PLUGIN_EXPORT_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef QT_PLUGIN 12 | #define PLUGIN_EXPORT QDESIGNER_WIDGET_EXPORT 13 | #else 14 | #define PLUGIN_EXPORT 15 | #endif // QT_PLUGIN 16 | 17 | #endif // PLUGIN_EXPORT_H 18 | -------------------------------------------------------------------------------- /Sources/widgetbox.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "widgetbox.h" 7 | #include "CategoryWidgets.h" 8 | #include "PageEventFilter.h" 9 | 10 | /*! 11 | * \class WidgetBox 12 | * \brief The WidgetBox class: Widget similar to the Widget Box in the Qt Designer. 13 | * It contains a list of widgets (pages) separated by categories. Each category 14 | * can be clicked in order to expand and collapse the list below the category. 15 | * 16 | */ 17 | 18 | WidgetBox::WidgetBox(QWidget *parent) : QWidget(parent) 19 | { 20 | // Set default tree widget settings 21 | mTreeWidget = new QTreeWidget(this); 22 | mTreeWidget->setFrameStyle(QFrame::NoFrame); 23 | mTreeWidget->setAnimated(true); 24 | mTreeWidget->setVerticalScrollMode(QTreeWidget::ScrollPerPixel); 25 | mTreeWidget->header()->hide(); 26 | mTreeWidget->setRootIsDecorated(false); 27 | mTreeWidget->setIndentation(0); 28 | mTreeWidget->setUniformRowHeights(false); 29 | mTreeWidget->setSelectionMode(QAbstractItemView::NoSelection); 30 | mTreeWidget->setExpandsOnDoubleClick(false); 31 | 32 | // Make tree widget same color as normal widget 33 | QPalette pal = mTreeWidget->palette(); 34 | pal.setColor(QPalette::Base, palette().background().color()); 35 | mTreeWidget->setPalette(pal); 36 | 37 | // Create WidgetBox layout 38 | QBoxLayout* layout = new QVBoxLayout(this); 39 | layout->setContentsMargins(0, 0, 0, 0); 40 | layout->addWidget(mTreeWidget); 41 | 42 | connect(mTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*, int)), 43 | SLOT(onItemClicked(QTreeWidgetItem*, int))); 44 | connect(mTreeWidget, SIGNAL(itemActivated(QTreeWidgetItem*, int)), 45 | SLOT(onItemClicked(QTreeWidgetItem*, int))); 46 | } 47 | WidgetBox::~WidgetBox() 48 | { 49 | delete mSheet; 50 | } 51 | 52 | 53 | #if defined(QT_PLUGIN) 54 | QSize WidgetBox::sizeHint() const 55 | { 56 | return QSize(130, 210); 57 | } 58 | #endif 59 | 60 | /*! 61 | * \brief WidgetBox::createCategory create page category widget 62 | * \param page page item 63 | * \param pageName page name to display in category 64 | */ 65 | void WidgetBox::createCategory(QTreeWidgetItem* page, QString pageName) 66 | { 67 | AbstractCategory *category; 68 | switch (mCategoryType) 69 | { 70 | case Button: 71 | category = new ButtonCategory(pageName, mTreeWidget, page); 72 | break; 73 | case Line: 74 | category = new LineCategory(pageName, mTreeWidget, page); 75 | break; 76 | default: 77 | category = new ButtonCategory(pageName, mTreeWidget, page); 78 | break; 79 | } 80 | // Set new item widget: Qt removes old widget automatically, no need to delete old one 81 | mTreeWidget->setItemWidget(page, 0, category); 82 | // Change property in Designer on page expand/unexpand 83 | connect(category, SIGNAL(pageExpanded(bool)), SLOT(setPageExpandedProperty())); 84 | } 85 | 86 | /*! 87 | * \brief WidgetBox::recreateCategories recreates categories widgets if 88 | * categoryType was changed 89 | */ 90 | void WidgetBox::recreateCategories() 91 | { 92 | for(int i = 0; i < count(); i++) 93 | { 94 | QString pageName = pageTitle(i); 95 | bool expanded = isPageExpanded(i); 96 | // Qt removes old widget automatically, no need to delete old one 97 | createCategory(mTreeWidget->topLevelItem(i), pageName); 98 | category(i)->setExpanded(expanded); 99 | } 100 | } 101 | 102 | void WidgetBox::setCategoryType(const CategoryType type) 103 | { 104 | mCategoryType = type; 105 | recreateCategories(); 106 | } 107 | 108 | 109 | QTreeWidgetItem * WidgetBox::addCategory(QString pageName) 110 | { 111 | return insertCategory(count(), pageName); 112 | } 113 | 114 | /** 115 | * @brief WidgetBox::setupWidget set default widget properties 116 | * @param widget 117 | */ 118 | void WidgetBox::setupWidget(QWidget *widget) 119 | { 120 | // The given widget's autoFillBackground property must be set to true, 121 | // otherwise the widget's background will be transparent, 122 | // showing both the model data and the tree widget item. 123 | widget->setAutoFillBackground(true); 124 | widget->setPalette(palette()); 125 | widget->setBackgroundRole(backgroundRole()); 126 | widget->setStyle(style()); // Set parent widget style 127 | } 128 | 129 | void WidgetBox::createContainerWidget(QTreeWidgetItem* page, QWidget *widget) 130 | { 131 | QTreeWidgetItem* container = new QTreeWidgetItem(); 132 | container->setDisabled(true); 133 | page->addChild(container); 134 | mTreeWidget->setItemWidget(container, 0, widget); 135 | setupWidget(widget); 136 | 137 | // Send mouse events from page widget to tree widget 138 | PageEventFilter *eventFilter = new PageEventFilter(this, container); 139 | widget->installEventFilter(eventFilter); 140 | widget->setObjectName(QString("__qt__passive_Page%1").arg(count() + 1)); 141 | 142 | #if defined(QT_PLUGIN) 143 | QSize size(mTreeWidget->width(), mTreeWidget->width() / 1.618); 144 | container->setSizeHint(0, size); 145 | widget->resize(size); 146 | // Change item size hint in Qt Designer on page widget resize 147 | // as seems QTreeWidgetItem knows nothing about its widget in design time 148 | // and does not automatically adjust item size like it does in run-time 149 | PageResizeFilter *filter = new PageResizeFilter(this, container); 150 | widget->installEventFilter(filter); 151 | #endif 152 | } 153 | 154 | void WidgetBox::addPage(QWidget *widget) 155 | { 156 | insertPage(count(), widget); 157 | } 158 | 159 | QTreeWidgetItem * WidgetBox::insertCategory(int index, QString pageName) 160 | { 161 | QTreeWidgetItem* page = new QTreeWidgetItem(); 162 | mTreeWidget->insertTopLevelItem(index, page); 163 | createCategory(page, pageName); 164 | 165 | return page; 166 | } 167 | 168 | void WidgetBox::insertPage(int index, QWidget *widget) 169 | { 170 | if (index < 0 || index > count()) 171 | return; 172 | 173 | // Set default page name 174 | QString pageName = widget->windowTitle(); 175 | if (pageName.isEmpty()) 176 | { 177 | pageName = tr("Page %1").arg(count() + 1); 178 | widget->setWindowTitle(pageName); 179 | } 180 | 181 | QTreeWidgetItem* page = insertCategory(index, pageName); 182 | createContainerWidget(page, widget); 183 | 184 | connect(widget, SIGNAL(windowTitleChanged(QString)), 185 | category(index), SLOT(setTitle(QString))); 186 | } 187 | 188 | void WidgetBox::removePage(int index) 189 | { 190 | QTreeWidgetItem *topLevelItem = mTreeWidget->topLevelItem(index); 191 | if (!topLevelItem) 192 | return; 193 | 194 | if (topLevelItem->childCount() > 0) 195 | mTreeWidget->removeItemWidget(topLevelItem->child(0), 0); 196 | mTreeWidget->removeItemWidget(topLevelItem, 0); 197 | 198 | delete topLevelItem; 199 | } 200 | 201 | int WidgetBox::currentIndex() const 202 | { 203 | if(count() > 0) { 204 | return mTreeWidget->currentIndex().row(); 205 | } else { 206 | return -1; 207 | } 208 | } 209 | 210 | void WidgetBox::setCurrentIndex(int index) 211 | { 212 | if (index != currentIndex() && checkIndex(index)) 213 | { 214 | mTreeWidget->setCurrentItem(mTreeWidget->topLevelItem(index)); 215 | emit currentIndexChanged(index); 216 | } 217 | } 218 | 219 | bool WidgetBox::checkIndex(int index) const 220 | { 221 | return index >= 0 && index < count(); 222 | } 223 | 224 | QWidget *WidgetBox::page(int index) const 225 | { 226 | if(checkIndex(index)) 227 | { 228 | QTreeWidgetItem* page = mTreeWidget->topLevelItem(index); 229 | return mTreeWidget->itemWidget(page->child(0), 0); 230 | } 231 | else 232 | { 233 | return 0; 234 | } 235 | } 236 | 237 | QWidget* WidgetBox::widget(int index) const 238 | { 239 | return page(index); 240 | } 241 | 242 | void WidgetBox::setPageTitle(QString const &newTitle) 243 | { 244 | if (checkIndex(currentIndex())) 245 | { 246 | category(currentIndex())->setTitle(newTitle); 247 | // Qt doc: use QWidget::windowTitle property to store the page title. 248 | // Note that currently there is no way of adding a custom property 249 | // (e.g., a page title) to the pages without using a predefined property as placeholder. 250 | page(currentIndex())->setWindowTitle(newTitle); 251 | emit pageTitleChanged(newTitle); 252 | } 253 | } 254 | 255 | /*! 256 | * \brief WidgetBox::pageTitle Current page title 257 | * \return 258 | */ 259 | QString WidgetBox::pageTitle() const 260 | { 261 | return pageTitle(currentIndex()); 262 | } 263 | 264 | /*! 265 | * \brief WidgetBox::pageTitle page title 266 | * \param index page index 267 | * \return 268 | */ 269 | QString WidgetBox::pageTitle(int index) const 270 | { 271 | if (checkIndex(index)) 272 | { 273 | return category(index)->title(); 274 | } 275 | else 276 | { 277 | return QString(); 278 | } 279 | } 280 | 281 | AbstractCategory *WidgetBox::category(int index) const 282 | { 283 | return (AbstractCategory *)mTreeWidget->itemWidget(mTreeWidget->topLevelItem(index), 0); 284 | } 285 | 286 | 287 | /*! 288 | * \brief WidgetBox::isPageExpanded Checks current page is expanded 289 | * \return 290 | */ 291 | bool WidgetBox::isPageExpanded() const 292 | { 293 | return isPageExpanded(currentIndex()); 294 | } 295 | 296 | /*! 297 | * \brief WidgetBox::isPageExpanded Checks page with given index is expanded 298 | * \param index page index 299 | * \return true if specified page expanded 300 | */ 301 | bool WidgetBox::isPageExpanded(int index) const 302 | { 303 | if (checkIndex(index)) 304 | { 305 | return category(index)->isExpanded(); 306 | } 307 | else 308 | { 309 | return false; 310 | } 311 | } 312 | 313 | void WidgetBox::setPageExpandedProperty() 314 | { 315 | changeQtDesignerProperty("isPageExpanded", isPageExpanded(currentIndex()), true); 316 | } 317 | 318 | void WidgetBox::setPageExpanded(bool expanded) 319 | { 320 | if(checkIndex(currentIndex())) 321 | { 322 | setPageExpanded(currentIndex(), expanded); 323 | setPageExpandedProperty(); 324 | } 325 | } 326 | 327 | void WidgetBox::setPageExpanded(int index, bool expanded) 328 | { 329 | if(checkIndex(index)) category(index)->setExpanded(expanded); 330 | } 331 | 332 | int WidgetBox::getPageIndex(QTreeWidgetItem *item) 333 | { 334 | if (!item) return -1; 335 | 336 | QTreeWidgetItem *parent = item->parent(); 337 | if(parent) // Parent is top level item 338 | { 339 | return mTreeWidget->indexOfTopLevelItem(parent); 340 | } 341 | else // Current item is top level 342 | { 343 | return item->treeWidget()->indexOfTopLevelItem(item); 344 | } 345 | } 346 | 347 | void WidgetBox::onItemClicked(QTreeWidgetItem *item, int ) 348 | { 349 | int index = getPageIndex(item); 350 | setCurrentIndex(index); 351 | 352 | changeQtDesignerProperty("currentIndex", index); 353 | } 354 | 355 | void WidgetBox::changeQtDesignerProperty(QString propertyName, QVariant value, 356 | bool markChangedOnly) 357 | { 358 | #if defined(QT_PLUGIN) 359 | QDesignerFormWindowInterface *form = 360 | QDesignerFormWindowInterface::findFormWindow(this); 361 | 362 | if(form) 363 | { 364 | if(!mSheet) // Need to create sheet only once 365 | { 366 | QDesignerFormEditorInterface *editor = form->core(); 367 | QExtensionManager *manager = editor->extensionManager(); 368 | mSheet = qt_extension(manager, this); 369 | } 370 | // Set property in Qt Designer 371 | int propertyIndex = mSheet->indexOf(propertyName); 372 | if(!markChangedOnly) mSheet->setProperty(propertyIndex, value); 373 | mSheet->setChanged(propertyIndex, true); 374 | } 375 | #else 376 | Q_UNUSED(propertyName); 377 | Q_UNUSED(value); 378 | Q_UNUSED(markChangedOnly); 379 | #endif 380 | } 381 | -------------------------------------------------------------------------------- /Sources/widgetbox.h: -------------------------------------------------------------------------------- 1 | #ifndef WIDGETBOX_H 2 | #define WIDGETBOX_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "plugin_export.h" 8 | 9 | class AbstractCategory; 10 | 11 | class PLUGIN_EXPORT WidgetBox : public QWidget 12 | { 13 | Q_OBJECT 14 | Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex) 15 | Q_PROPERTY(QString pageTitle READ pageTitle WRITE setPageTitle STORED true) 16 | Q_PROPERTY(bool isPageExpanded READ isPageExpanded WRITE setPageExpanded) 17 | Q_PROPERTY(CategoryType categoryType READ categoryType WRITE setCategoryType) 18 | Q_ENUMS(CategoryType) 19 | 20 | public: 21 | enum CategoryType { 22 | Button, 23 | Line, 24 | Frame // TODO 25 | }; 26 | 27 | WidgetBox(QWidget *parent = 0); 28 | ~WidgetBox(); 29 | 30 | CategoryType categoryType() const { return mCategoryType; } 31 | void setCategoryType(const CategoryType type); 32 | 33 | #if defined(QT_PLUGIN) 34 | QSize sizeHint() const Q_DECL_OVERRIDE; 35 | #endif 36 | 37 | int count() const { return mTreeWidget->topLevelItemCount(); } 38 | QWidget *page(int index) const; 39 | QWidget *widget(int index) const; 40 | QString pageTitle() const; 41 | QString pageTitle(int index) const; 42 | bool isPageExpanded() const; 43 | bool isPageExpanded(int index) const; 44 | int currentIndex() const; 45 | 46 | public slots: 47 | void setCurrentIndex(int index); 48 | 49 | void addPage(QWidget *widget); 50 | void insertPage(int index, QWidget *widget); 51 | void removePage(int index); 52 | 53 | void setPageTitle(QString const &newTitle); 54 | void setPageExpanded(bool expanded); 55 | void setPageExpanded(int index, bool expanded); 56 | 57 | protected: 58 | QTreeWidgetItem * addCategory(QString pageName); 59 | QTreeWidgetItem * insertCategory(int index, QString pageName); 60 | 61 | AbstractCategory *category(int index) const; 62 | 63 | void createContainerWidget(QTreeWidgetItem* page, QWidget *widget); 64 | void createCategory(QTreeWidgetItem* page, QString pageName); 65 | 66 | void changeQtDesignerProperty(QString propertyName, QVariant value, 67 | bool markChangedOnly = false); 68 | 69 | protected slots: 70 | void onItemClicked(QTreeWidgetItem *item, int); 71 | void setPageExpandedProperty(); 72 | 73 | signals: 74 | void currentIndexChanged(int index); 75 | void pageTitleChanged(const QString &title); 76 | 77 | private: 78 | bool checkIndex(int index) const; 79 | void setupWidget(QWidget *widget); 80 | int getPageIndex(QTreeWidgetItem *item); 81 | void recreateCategories(); 82 | 83 | QTreeWidget *mTreeWidget; 84 | CategoryType mCategoryType = Button; 85 | // Necessary only in Qt Designer 86 | QDesignerPropertySheetExtension *mSheet = 0; 87 | }; 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /Sources/widgetbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akontsevich/WidgetBox/408fcb38270c1696ecbd66ad8a3bb512202f00be/Sources/widgetbox.png -------------------------------------------------------------------------------- /Sources/widgetboxdesignercontainerextension.cpp: -------------------------------------------------------------------------------- 1 | #include "widgetbox.h" 2 | #include "widgetboxdesignercontainerextension.h" 3 | 4 | 5 | WidgetBoxDesignerContainerExtension::WidgetBoxDesignerContainerExtension(WidgetBox *widget, 6 | QObject *parent) 7 | : QObject(parent) 8 | , mWidgetBox(widget) 9 | { 10 | } 11 | 12 | void WidgetBoxDesignerContainerExtension::addWidget(QWidget *widget) 13 | { 14 | mWidgetBox->addPage(widget); 15 | } 16 | 17 | int WidgetBoxDesignerContainerExtension::count() const 18 | { 19 | return mWidgetBox->count(); 20 | } 21 | 22 | int WidgetBoxDesignerContainerExtension::currentIndex() const 23 | { 24 | return mWidgetBox->currentIndex(); 25 | } 26 | 27 | void WidgetBoxDesignerContainerExtension::insertWidget(int index, QWidget *widget) 28 | { 29 | mWidgetBox->insertPage(index, widget); 30 | } 31 | 32 | void WidgetBoxDesignerContainerExtension::remove(int index) 33 | { 34 | mWidgetBox->removePage(index); 35 | } 36 | 37 | void WidgetBoxDesignerContainerExtension::setCurrentIndex(int index) 38 | { 39 | mWidgetBox->setCurrentIndex(index); 40 | } 41 | 42 | QWidget* WidgetBoxDesignerContainerExtension::widget(int index) const 43 | { 44 | return mWidgetBox->widget(index); 45 | } 46 | -------------------------------------------------------------------------------- /Sources/widgetboxdesignercontainerextension.h: -------------------------------------------------------------------------------- 1 | #ifndef WIDGETBOXDESIGNERCONTAINEREXTENSION_H 2 | #define WIDGETBOXDESIGNERCONTAINEREXTENSION_H 3 | 4 | #include 5 | 6 | QT_BEGIN_NAMESPACE 7 | class QExtensionManager; 8 | QT_END_NAMESPACE 9 | class WidgetBox; 10 | 11 | class WidgetBoxDesignerContainerExtension : public QObject, 12 | public QDesignerContainerExtension 13 | { 14 | Q_OBJECT 15 | Q_INTERFACES(QDesignerContainerExtension) 16 | 17 | public: 18 | explicit WidgetBoxDesignerContainerExtension(WidgetBox *widget, QObject *parent); 19 | 20 | void addWidget(QWidget *widget) Q_DECL_OVERRIDE; 21 | int count() const Q_DECL_OVERRIDE; 22 | int currentIndex() const Q_DECL_OVERRIDE; 23 | void insertWidget(int index, QWidget *widget) Q_DECL_OVERRIDE; 24 | void remove(int index) Q_DECL_OVERRIDE; 25 | void setCurrentIndex(int index) Q_DECL_OVERRIDE; 26 | QWidget *widget(int index) const Q_DECL_OVERRIDE; 27 | 28 | private: 29 | WidgetBox *mWidgetBox; 30 | }; 31 | 32 | #endif // WIDGETBOXDESIGNERCONTAINEREXTENSION_H 33 | -------------------------------------------------------------------------------- /Sources/widgetboxextensionfactory.cpp: -------------------------------------------------------------------------------- 1 | #include "widgetbox.h" 2 | #include "widgetboxextensionfactory.h" 3 | #include "widgetboxdesignercontainerextension.h" 4 | 5 | 6 | WidgetBoxExtensionFactory::WidgetBoxExtensionFactory(QExtensionManager *parent) 7 | : QExtensionFactory(parent) 8 | { 9 | 10 | } 11 | 12 | QObject *WidgetBoxExtensionFactory::createExtension(QObject *object, 13 | const QString &iid, 14 | QObject *parent) const 15 | { 16 | WidgetBox *widget = qobject_cast(object); 17 | 18 | if (widget && (iid == Q_TYPEID(QDesignerContainerExtension))) 19 | { 20 | return new WidgetBoxDesignerContainerExtension(widget, parent); 21 | } 22 | else 23 | { 24 | return 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/widgetboxextensionfactory.h: -------------------------------------------------------------------------------- 1 | #ifndef WIDGETBOXEXTENSIONFACTORY_H 2 | #define WIDGETBOXEXTENSIONFACTORY_H 3 | 4 | #include 5 | 6 | class WidgetBoxExtensionFactory : public QExtensionFactory 7 | { 8 | Q_OBJECT 9 | 10 | public: 11 | WidgetBoxExtensionFactory(QExtensionManager *parent = 0); 12 | 13 | protected: 14 | QObject *createExtension(QObject *object, const QString &iid, QObject *parent) const Q_DECL_OVERRIDE; 15 | }; 16 | 17 | #endif // WIDGETBOXEXTENSIONFACTORY_H 18 | -------------------------------------------------------------------------------- /Sources/widgetboxplugin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "widgetbox.h" 11 | #include "widgetboxplugin.h" 12 | #include "widgetboxextensionfactory.h" 13 | 14 | 15 | WidgetBoxPlugin::WidgetBoxPlugin(QObject *parent) 16 | : QObject(parent) 17 | { 18 | mInitialized = false; 19 | } 20 | 21 | void WidgetBoxPlugin::initialize(QDesignerFormEditorInterface *formEditor) 22 | { 23 | if (mInitialized) 24 | return; 25 | 26 | // Add extension registrations, etc. here 27 | QExtensionManager *manager = formEditor->extensionManager(); 28 | QExtensionFactory *factory = new WidgetBoxExtensionFactory(manager); 29 | 30 | Q_ASSERT(manager != 0); 31 | manager->registerExtensions(factory, Q_TYPEID(QDesignerContainerExtension)); 32 | 33 | mInitialized = true; 34 | } 35 | 36 | bool WidgetBoxPlugin::isInitialized() const 37 | { 38 | return mInitialized; 39 | } 40 | 41 | QWidget *WidgetBoxPlugin::createWidget(QWidget *parent) 42 | { 43 | WidgetBox *widget = new WidgetBox(parent); 44 | 45 | connect(widget, &WidgetBox::currentIndexChanged, 46 | this, &WidgetBoxPlugin::currentIndexChanged); 47 | connect(widget, &WidgetBox::pageTitleChanged, 48 | this, &WidgetBoxPlugin::pageTitleChanged); 49 | 50 | return widget; 51 | } 52 | 53 | QString WidgetBoxPlugin::name() const 54 | { 55 | return QLatin1String("WidgetBox"); 56 | } 57 | 58 | QString WidgetBoxPlugin::group() const 59 | { 60 | return QLatin1String("Containers"); 61 | } 62 | 63 | QIcon WidgetBoxPlugin::icon() const 64 | { 65 | return QIcon(":/plugins/widgetbox/widgetbox.png"); 66 | } 67 | 68 | QString WidgetBoxPlugin::toolTip() const 69 | { 70 | return QLatin1String("Widget Box is a Qt widget which contains a list of " 71 | "widgets (pages) separated by categories"); 72 | } 73 | 74 | QString WidgetBoxPlugin::whatsThis() const 75 | { 76 | return QLatin1String("Widget similar to the Widget Box in the Qt Designer. " 77 | "It contains a list of widgets (pages) separated by " 78 | "categories. Each category button can be clicked in " 79 | "order to expand and collapse the list below the button."); 80 | } 81 | 82 | bool WidgetBoxPlugin::isContainer() const 83 | { 84 | return true; 85 | } 86 | 87 | QString WidgetBoxPlugin::domXml() const 88 | { 89 | return QLatin1String("\ 90 | \ 91 | \ 92 | \ 93 | \ 94 | 0\ 95 | 0\ 96 | 130\ 97 | 210\ 98 | \ 99 | \ 100 | \ 101 | \ 102 | \ 103 | WidgetBox\ 104 | QWidget\ 105 | addPage\ 106 | \ 107 | \ 108 | "); 109 | } 110 | 111 | QString WidgetBoxPlugin::includeFile() const 112 | { 113 | return QLatin1String("widgetbox.h"); 114 | } 115 | 116 | void WidgetBoxPlugin::currentIndexChanged(int index) 117 | { 118 | Q_UNUSED(index); 119 | WidgetBox *widget = qobject_cast(sender()); 120 | 121 | if(widget) 122 | { 123 | QDesignerFormWindowInterface *form = QDesignerFormWindowInterface::findFormWindow(widget); 124 | if(form) 125 | form->emitSelectionChanged(); 126 | } 127 | } 128 | 129 | void WidgetBoxPlugin::pageTitleChanged(const QString &title) 130 | { 131 | Q_UNUSED(title); 132 | WidgetBox *widget = qobject_cast(sender()); 133 | 134 | if(widget) 135 | { 136 | QWidget *page = widget->widget(widget->currentIndex()); 137 | QDesignerFormWindowInterface *form; 138 | form = QDesignerFormWindowInterface::findFormWindow(widget); 139 | 140 | if(form) 141 | { 142 | QDesignerFormEditorInterface *editor = form->core(); 143 | QExtensionManager *manager = editor->extensionManager(); 144 | 145 | QDesignerPropertySheetExtension *sheet; 146 | sheet = qt_extension(manager, page); 147 | const int propertyIndex = sheet->indexOf(QLatin1String("windowTitle")); 148 | sheet->setChanged(propertyIndex, true); 149 | } 150 | } 151 | } 152 | 153 | #if QT_VERSION < 0x050000 154 | Q_EXPORT_PLUGIN2(WidgetBoxPlugin, WidgetBoxPlugin) 155 | #endif // QT_VERSION < 0x050000 156 | -------------------------------------------------------------------------------- /Sources/widgetboxplugin.h: -------------------------------------------------------------------------------- 1 | #ifndef WIDGETBOXPLUGIN_H 2 | #define WIDGETBOXPLUGIN_H 3 | 4 | #include 5 | 6 | class WidgetBoxPlugin : public QObject, public QDesignerCustomWidgetInterface 7 | { 8 | Q_OBJECT 9 | Q_INTERFACES(QDesignerCustomWidgetInterface) 10 | #if QT_VERSION >= 0x050000 11 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface") 12 | #endif // QT_VERSION >= 0x050000 13 | 14 | public: 15 | WidgetBoxPlugin(QObject *parent = 0); 16 | 17 | bool isContainer() const; 18 | bool isInitialized() const; 19 | QIcon icon() const; 20 | QString domXml() const; 21 | QString group() const; 22 | QString includeFile() const; 23 | QString name() const; 24 | QString toolTip() const; 25 | QString whatsThis() const; 26 | QWidget *createWidget(QWidget *parent); 27 | void initialize(QDesignerFormEditorInterface *formEditor); 28 | 29 | private slots: 30 | void currentIndexChanged(int index); 31 | void pageTitleChanged(const QString &title); 32 | 33 | private: 34 | bool mInitialized; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * Fix isPageExpanded propery change in Qt Designer 2 | 3 | * Style category button (or panel) closer to Qt Creator (Designer) look 4 | or like to LibreOffice Writer Properties Tool Box on the right side 5 | 6 | Hope for community participation on these improvements. 7 | --------------------------------------------------------------------------------