├── .gitignore ├── QMaterialTree ├── QMaterialTree.pro ├── default.txt ├── editabletreemodel.qrc ├── main.cpp ├── mainwindow.ui ├── qmaterialitemdelegate.cpp ├── qmaterialitemdelegate.h ├── qmaterialview.cpp ├── qmaterialview.h ├── qtmaterialripple.cpp ├── qtmaterialripple.h ├── qtmaterialrippleoverlay.cpp ├── qtmaterialrippleoverlay.h ├── treeitem.cpp ├── treeitem.h ├── treemodel.cpp └── treemodel.h ├── README.md └── example.gif /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | 3 | -------------------------------------------------------------------------------- /QMaterialTree/QMaterialTree.pro: -------------------------------------------------------------------------------- 1 | QT += widgets 2 | 3 | FORMS = mainwindow.ui 4 | HEADERS = \ 5 | treeitem.h \ 6 | treemodel.h \ 7 | qmaterialitemdelegate.h \ 8 | qmaterialview.h \ 9 | qtmaterialripple.h \ 10 | qtmaterialrippleoverlay.h 11 | RESOURCES = editabletreemodel.qrc 12 | SOURCES = \ 13 | treeitem.cpp \ 14 | treemodel.cpp \ 15 | main.cpp \ 16 | qmaterialitemdelegate.cpp \ 17 | qmaterialview.cpp \ 18 | qtmaterialripple.cpp \ 19 | qtmaterialrippleoverlay.cpp 20 | 21 | # install 22 | target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/editabletreemodel 23 | INSTALLS += target 24 | -------------------------------------------------------------------------------- /QMaterialTree/default.txt: -------------------------------------------------------------------------------- 1 | Getting Started 2 | Launching Designer 3 | The User Interface 4 | 5 | Designing a Component 6 | Creating a Dialog 7 | Composing the Dialog 8 | Creating a Layout 9 | Signal and Slot Connections 10 | 11 | Using a Component in Your Application 12 | The Direct Approach 13 | The Single Inheritance Approach 14 | The Multiple Inheritance Approach 15 | Automatic Connections 16 | A Dialog Without Auto-Connect 17 | A Dialog With Auto-Connect 18 | 19 | Form Editing Mode 20 | Managing Forms 21 | Editing a Form 22 | The Property Editor 23 | The Object Inspector 24 | Layouts 25 | Applying and Breaking Layouts 26 | Horizontal and Vertical Layouts 27 | The Grid Layout 28 | Previewing Forms 29 | 30 | Using Containers 31 | General Features 32 | Frames 33 | Group Boxes 34 | Stacked Widgets 35 | Tab Widgets 36 | Toolbox Widgets 37 | 38 | Connection Editing Mode 39 | Connecting Objects 40 | Editing Connections 41 | -------------------------------------------------------------------------------- /QMaterialTree/editabletreemodel.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | default.txt 4 | 5 | 6 | -------------------------------------------------------------------------------- /QMaterialTree/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "qmaterialview.h" 3 | int main(int argc, char *argv[]) 4 | { 5 | Q_INIT_RESOURCE(editabletreemodel); 6 | 7 | QApplication app(argc, argv); 8 | QMaterialView window; 9 | window.show(); 10 | return app.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /QMaterialTree/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | MainWindow 3 | 4 | 5 | 6 | 0 7 | 0 8 | 573 9 | 468 10 | 11 | 12 | 13 | Editable Tree Model 14 | 15 | 16 | 17 | 18 | 0 19 | 20 | 21 | 0 22 | 23 | 24 | 25 | 26 | true 27 | 28 | 29 | QAbstractItemView::SelectItems 30 | 31 | 32 | QAbstractItemView::ScrollPerPixel 33 | 34 | 35 | false 36 | 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 0 48 | 0 49 | 573 50 | 31 51 | 52 | 53 | 54 | 55 | &File 56 | 57 | 58 | 59 | 60 | 61 | &Actions 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | E&xit 78 | 79 | 80 | Ctrl+Q 81 | 82 | 83 | 84 | 85 | Insert Row 86 | 87 | 88 | Ctrl+I, R 89 | 90 | 91 | 92 | 93 | Remove Row 94 | 95 | 96 | Ctrl+R, R 97 | 98 | 99 | 100 | 101 | Insert Column 102 | 103 | 104 | Ctrl+I, C 105 | 106 | 107 | 108 | 109 | Remove Column 110 | 111 | 112 | Ctrl+R, C 113 | 114 | 115 | 116 | 117 | Insert Child 118 | 119 | 120 | Ctrl+N 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /QMaterialTree/qmaterialitemdelegate.cpp: -------------------------------------------------------------------------------- 1 | #include "qmaterialitemdelegate.h" 2 | #include "qpainter.h" 3 | #include "qdebug.h" 4 | #include "treeitem.h" 5 | #include "qmaterialview.h" 6 | #include "qevent.h" 7 | QMaterialItemDelegate::QMaterialItemDelegate(QObject *parent) : QStyledItemDelegate(parent) 8 | { 9 | 10 | bgNormalColor = QColor(255, 255, 255); 11 | bgSelectedColor = QColor(242, 242, 242); 12 | bgHoverColor = QColor(242, 242, 242); 13 | textNormalColor = QColor(0, 0, 0); 14 | textSelectedColor = QColor(64,154,233); 15 | textHoverColor = QColor(0, 0, 0); 16 | 17 | 18 | } 19 | 20 | void QMaterialItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 21 | { 22 | 23 | 24 | painter->setRenderHint(QPainter::Antialiasing); 25 | 26 | QRect optionRect = option.rect; 27 | int x = optionRect.x(); 28 | int y = optionRect.y(); 29 | int width = optionRect.width(); 30 | int height = optionRect.height(); 31 | 32 | QColor bgColor, textColor, tipBgColor, tipTextColor, iconColor; 33 | if (option.state & QStyle::State_Selected) 34 | { 35 | bgColor = this->bgSelectedColor; 36 | textColor = this->textSelectedColor ; 37 | tipBgColor = this->textSelectedColor ; 38 | tipTextColor = this->bgSelectedColor; 39 | iconColor = this->textSelectedColor ; 40 | 41 | } else if (option.state & QStyle::State_MouseOver) 42 | { 43 | bgColor = this->bgHoverColor ; 44 | textColor = this->textHoverColor ; 45 | tipBgColor = this->textSelectedColor ; 46 | tipTextColor = this->bgSelectedColor ; 47 | iconColor = this->textHoverColor; 48 | } else 49 | { 50 | bgColor = this->bgNormalColor ; 51 | textColor = this->textNormalColor ; 52 | tipBgColor = this->bgSelectedColor; 53 | tipTextColor = this->textSelectedColor ; 54 | iconColor = this->textNormalColor ; 55 | } 56 | 57 | 58 | //draw bg 59 | painter->fillRect(optionRect, bgColor); 60 | 61 | //draw text 62 | QString text = index.data(Qt::DisplayRole).toString(); 63 | if (!text.isEmpty()) 64 | { 65 | 66 | TreeItem *item = getItem(index); 67 | int delta = 0; 68 | bool hasChildren = false; 69 | if(item) 70 | { 71 | delta = item->level(); 72 | if(item->childCount()>0) 73 | hasChildren = true; 74 | } 75 | 76 | QRect textRect = optionRect; 77 | textRect.setWidth(width); 78 | textRect.setX(x+delta*25); 79 | 80 | QFont font; 81 | font.setPixelSize(16); 82 | if(hasChildren) 83 | font.setBold(true); 84 | 85 | painter->setFont(font); 86 | painter->setPen(textColor); 87 | painter->drawText(textRect, Qt::AlignLeft|Qt::AlignVCenter, text); 88 | } 89 | 90 | } 91 | 92 | QSize QMaterialItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 93 | { 94 | return QSize(60,40); 95 | } 96 | 97 | TreeItem *QMaterialItemDelegate::getItem(const QModelIndex &index) const 98 | { 99 | if (index.isValid()) { 100 | TreeItem *item = static_cast(index.internalPointer()); 101 | if (item) 102 | return item; 103 | } 104 | return nullptr; 105 | } 106 | -------------------------------------------------------------------------------- /QMaterialTree/qmaterialitemdelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef QMATERIALITEMDELEGATE_H 2 | #define QMATERIALITEMDELEGATE_H 3 | 4 | #include 5 | #include"QStyledItemDelegate" 6 | class TreeItem; 7 | class QMaterialItemDelegate : public QStyledItemDelegate 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit QMaterialItemDelegate(QObject *parent = 0); 12 | 13 | void paint(QPainter *painter, const QStyleOptionViewItem &option, 14 | const QModelIndex &index) const Q_DECL_OVERRIDE; 15 | QSize sizeHint(const QStyleOptionViewItem &option, 16 | const QModelIndex &index) const Q_DECL_OVERRIDE; 17 | private: 18 | TreeItem *getItem(const QModelIndex &index) const; 19 | 20 | private: 21 | QColor bgNormalColor; 22 | QColor bgSelectedColor; 23 | QColor bgHoverColor; 24 | QColor textNormalColor; 25 | QColor textSelectedColor; 26 | QColor textHoverColor; 27 | 28 | signals: 29 | 30 | public slots: 31 | }; 32 | 33 | #endif // QMATERIALITEMDELEGATE_H 34 | -------------------------------------------------------------------------------- /QMaterialTree/qmaterialview.cpp: -------------------------------------------------------------------------------- 1 | #include "qmaterialview.h" 2 | #include "qfile.h" 3 | #include "treemodel.h" 4 | #include "qmaterialitemdelegate.h" 5 | #include "QMouseEvent" 6 | #include "qtmaterialripple.h" 7 | #include "qtmaterialrippleoverlay.h" 8 | #include "qpainterpath.h" 9 | QMaterialView::QMaterialView(QWidget *parent) : QTreeView(parent),\ 10 | m_overlay(new QtMaterialRippleOverlay(this)) 11 | { 12 | init(); 13 | 14 | setExpandsOnDoubleClick(false); 15 | connect(this,SIGNAL(pressed(QModelIndex)),this,SLOT(expandItem(QModelIndex))); 16 | 17 | this->resize(330,500); 18 | } 19 | 20 | void QMaterialView::mousePressEvent(QMouseEvent *event) 21 | { 22 | 23 | m_pos = event->pos(); 24 | QTreeView::mousePressEvent(event); 25 | 26 | } 27 | 28 | void QMaterialView::setRect(QRect rect) 29 | { 30 | m_rect = rect; 31 | QPainterPath path; 32 | path.addRect(rect); 33 | m_path=path; 34 | } 35 | 36 | void QMaterialView::init() 37 | { 38 | QStringList headers; 39 | headers << tr("Title"); 40 | 41 | QFile file(":/default.txt"); 42 | file.open(QIODevice::ReadOnly); 43 | TreeModel *model = new TreeModel(headers, file.readAll()); 44 | file.close(); 45 | 46 | this->setModel(model); 47 | for (int column = 0; column < model->columnCount(); ++column) 48 | this->resizeColumnToContents(column); 49 | 50 | this->setEditTriggers(QAbstractItemView::NoEditTriggers); 51 | this->setAnimated(true); 52 | this->setHeaderHidden(true); 53 | 54 | QMaterialItemDelegate *delegate = new QMaterialItemDelegate(this); 55 | this->setItemDelegate(delegate); 56 | this->setRootIsDecorated(false); 57 | this->setIndentation(0); 58 | 59 | this->setStyleSheet("QTreeView::branch:has-children:!has-siblings:closed,\ 60 | QTreeView::branch:closed:has-children:has-siblings{border-image: none; image: none;}\ 61 | QTreeView::branch:open:has-children:!has-siblings,\ 62 | QTreeView::branch:open:has-children:has-siblings{border-image: none; image: none;}"); 63 | 64 | } 65 | 66 | void QMaterialView::expandItem(const QModelIndex &index) 67 | { 68 | 69 | QRect rect = this->visualRect(index); 70 | 71 | if(rect.contains(m_pos)) 72 | { 73 | m_overlay->setGeometry(rect); 74 | 75 | //can use mapToGlobal and mapFromGlobal in not parent-child widgets 76 | QPoint p =m_overlay->mapFrom(this,m_pos); 77 | 78 | QtMaterialRipple *ripple = new QtMaterialRipple(p); 79 | 80 | ripple->setRadiusEndValue(rect.width()); 81 | ripple->setOpacityStartValue(0.35); 82 | ripple->setColor(QColor(0,0.7*255,0.8*255,255)); 83 | ripple->radiusAnimation()->setDuration(800); 84 | ripple->opacityAnimation()->setDuration(1300); 85 | 86 | m_overlay->addRipple(ripple); 87 | 88 | } 89 | 90 | if(index.child(0,0).isValid()) 91 | this->isExpanded(index)? this->collapse(index) : this->expand(index); 92 | } 93 | -------------------------------------------------------------------------------- /QMaterialTree/qmaterialview.h: -------------------------------------------------------------------------------- 1 | #ifndef QMATERIALVIEW_H 2 | #define QMATERIALVIEW_H 3 | 4 | #include 5 | class QtMaterialRippleOverlay; 6 | class QMaterialView : public QTreeView 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit QMaterialView(QWidget *parent = 0); 11 | 12 | void mousePressEvent(QMouseEvent *event); 13 | 14 | void setRect(QRect rect); 15 | private: 16 | void init(); 17 | 18 | private slots: 19 | void expandItem(const QModelIndex &index); 20 | private: 21 | QRect m_rect; 22 | 23 | QPainterPath m_path; 24 | 25 | QPoint m_pos; 26 | 27 | QtMaterialRippleOverlay *const m_overlay; 28 | }; 29 | 30 | #endif // QMATERIALVIEW_H 31 | -------------------------------------------------------------------------------- /QMaterialTree/qtmaterialripple.cpp: -------------------------------------------------------------------------------- 1 | #include "qtmaterialripple.h" 2 | #include "qtmaterialrippleoverlay.h" 3 | 4 | /*! 5 | * \class QtMaterialRipple 6 | * \internal 7 | */ 8 | 9 | QtMaterialRipple::QtMaterialRipple(const QPoint ¢er, QObject *parent) 10 | : QParallelAnimationGroup(parent), 11 | m_overlay(0), 12 | m_radiusAnimation(animate("radius")), 13 | m_opacityAnimation(animate("opacity")), 14 | m_radius(0), 15 | m_opacity(0), 16 | m_center(center) 17 | { 18 | init(); 19 | } 20 | 21 | QtMaterialRipple::QtMaterialRipple(const QPoint ¢er, 22 | QtMaterialRippleOverlay *overlay, 23 | QObject *parent) 24 | : QParallelAnimationGroup(parent), 25 | m_overlay(overlay), 26 | m_radiusAnimation(animate("radius")), 27 | m_opacityAnimation(animate("opacity")), 28 | m_radius(0), 29 | m_opacity(0), 30 | m_center(center) 31 | { 32 | init(); 33 | } 34 | 35 | QtMaterialRipple::~QtMaterialRipple() 36 | { 37 | } 38 | 39 | void QtMaterialRipple::setRadius(qreal radius) 40 | { 41 | Q_ASSERT(m_overlay); 42 | 43 | if (m_radius == radius) { 44 | return; 45 | } 46 | m_radius = radius; 47 | m_overlay->update(); 48 | } 49 | 50 | void QtMaterialRipple::setOpacity(qreal opacity) 51 | { 52 | Q_ASSERT(m_overlay); 53 | 54 | if (m_opacity == opacity) { 55 | return; 56 | } 57 | m_opacity = opacity; 58 | m_overlay->update(); 59 | } 60 | 61 | void QtMaterialRipple::setColor(const QColor &color) 62 | { 63 | if (m_brush.color() == color) { 64 | return; 65 | } 66 | m_brush.setColor(color); 67 | 68 | if (m_overlay) { 69 | m_overlay->update(); 70 | } 71 | } 72 | 73 | void QtMaterialRipple::setBrush(const QBrush &brush) 74 | { 75 | m_brush = brush; 76 | 77 | if (m_overlay) { 78 | m_overlay->update(); 79 | } 80 | } 81 | 82 | void QtMaterialRipple::destroy() 83 | { 84 | Q_ASSERT(m_overlay); 85 | 86 | m_overlay->removeRipple(this); 87 | } 88 | 89 | /*! 90 | * \internal 91 | */ 92 | QPropertyAnimation *QtMaterialRipple::animate(const QByteArray &property, 93 | const QEasingCurve &easing, 94 | int duration) 95 | { 96 | QPropertyAnimation *animation = new QPropertyAnimation; 97 | animation->setTargetObject(this); 98 | animation->setPropertyName(property); 99 | animation->setEasingCurve(easing); 100 | animation->setDuration(duration); 101 | addAnimation(animation); 102 | return animation; 103 | } 104 | 105 | /*! 106 | * \internal 107 | */ 108 | void QtMaterialRipple::init() 109 | { 110 | setOpacityStartValue(0.5); 111 | setOpacityEndValue(0); 112 | setRadiusStartValue(0); 113 | setRadiusEndValue(300); 114 | 115 | m_brush.setColor(Qt::black); 116 | m_brush.setStyle(Qt::SolidPattern); 117 | 118 | connect(this, SIGNAL(finished()), this, SLOT(destroy())); 119 | } 120 | -------------------------------------------------------------------------------- /QMaterialTree/qtmaterialripple.h: -------------------------------------------------------------------------------- 1 | #ifndef QTMATERIALRIPPLE_H 2 | #define QTMATERIALRIPPLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class QtMaterialRippleOverlay; 11 | 12 | class QtMaterialRipple : public QParallelAnimationGroup 13 | { 14 | Q_OBJECT 15 | 16 | Q_PROPERTY(qreal radius WRITE setRadius READ radius) 17 | Q_PROPERTY(qreal opacity WRITE setOpacity READ opacity) 18 | 19 | public: 20 | explicit QtMaterialRipple(const QPoint ¢er, QObject *parent = 0); 21 | QtMaterialRipple(const QPoint ¢er, QtMaterialRippleOverlay *overlay, QObject *parent = 0); 22 | ~QtMaterialRipple(); 23 | 24 | inline void setOverlay(QtMaterialRippleOverlay *overlay); 25 | 26 | void setRadius(qreal radius); 27 | inline qreal radius() const; 28 | 29 | void setOpacity(qreal opacity); 30 | inline qreal opacity() const; 31 | 32 | void setColor(const QColor &color); 33 | inline QColor color() const; 34 | 35 | void setBrush(const QBrush &brush); 36 | inline QBrush brush() const; 37 | 38 | inline QPoint center() const; 39 | 40 | inline QPropertyAnimation *radiusAnimation() const; 41 | inline QPropertyAnimation *opacityAnimation() const; 42 | 43 | inline void setOpacityStartValue(qreal value); 44 | inline void setOpacityEndValue(qreal value); 45 | inline void setRadiusStartValue(qreal value); 46 | inline void setRadiusEndValue(qreal value); 47 | inline void setDuration(int msecs); 48 | 49 | protected slots: 50 | void destroy(); 51 | 52 | private: 53 | Q_DISABLE_COPY(QtMaterialRipple) 54 | 55 | QPropertyAnimation *animate(const QByteArray &property, 56 | const QEasingCurve &easing = QEasingCurve::OutQuad, 57 | int duration = 800); 58 | 59 | void init(); 60 | 61 | QtMaterialRippleOverlay *m_overlay; 62 | QPropertyAnimation *const m_radiusAnimation; 63 | QPropertyAnimation *const m_opacityAnimation; 64 | qreal m_radius; 65 | qreal m_opacity; 66 | QPoint m_center; 67 | QBrush m_brush; 68 | }; 69 | 70 | inline void QtMaterialRipple::setOverlay(QtMaterialRippleOverlay *overlay) 71 | { 72 | m_overlay = overlay; 73 | } 74 | 75 | inline qreal QtMaterialRipple::radius() const 76 | { 77 | return m_radius; 78 | } 79 | 80 | inline qreal QtMaterialRipple::opacity() const 81 | { 82 | return m_opacity; 83 | } 84 | 85 | inline QColor QtMaterialRipple::color() const 86 | { 87 | return m_brush.color(); 88 | } 89 | 90 | inline QBrush QtMaterialRipple::brush() const 91 | { 92 | return m_brush; 93 | } 94 | 95 | inline QPoint QtMaterialRipple::center() const 96 | { 97 | return m_center; 98 | } 99 | 100 | inline QPropertyAnimation *QtMaterialRipple::radiusAnimation() const 101 | { 102 | return m_radiusAnimation; 103 | } 104 | 105 | inline QPropertyAnimation *QtMaterialRipple::opacityAnimation() const 106 | { 107 | return m_opacityAnimation; 108 | } 109 | 110 | inline void QtMaterialRipple::setOpacityStartValue(qreal value) 111 | { 112 | m_opacityAnimation->setStartValue(value); 113 | } 114 | 115 | inline void QtMaterialRipple::setOpacityEndValue(qreal value) 116 | { 117 | m_opacityAnimation->setEndValue(value); 118 | } 119 | 120 | inline void QtMaterialRipple::setRadiusStartValue(qreal value) 121 | { 122 | m_radiusAnimation->setStartValue(value); 123 | } 124 | 125 | inline void QtMaterialRipple::setRadiusEndValue(qreal value) 126 | { 127 | m_radiusAnimation->setEndValue(value); 128 | } 129 | 130 | inline void QtMaterialRipple::setDuration(int msecs) 131 | { 132 | m_radiusAnimation->setDuration(msecs); 133 | m_opacityAnimation->setDuration(msecs); 134 | } 135 | 136 | #endif // QTMATERIALRIPPLE_H 137 | -------------------------------------------------------------------------------- /QMaterialTree/qtmaterialrippleoverlay.cpp: -------------------------------------------------------------------------------- 1 | #include "qtmaterialrippleoverlay.h" 2 | #include 3 | #include "qtmaterialripple.h" 4 | #include "qtmaterialripple.h" 5 | #include "QMouseEvent" 6 | 7 | /*! 8 | * \class QtMaterialRippleOverlay 9 | * \internal 10 | */ 11 | 12 | QtMaterialRippleOverlay::QtMaterialRippleOverlay(QWidget *parent) 13 | : QWidget(parent), 14 | m_useClip(false) 15 | { 16 | //set mousevnets pass through this to its below widgets 17 | setAttribute(Qt::WA_TransparentForMouseEvents); 18 | 19 | setAttribute(Qt::WA_NoSystemBackground); 20 | } 21 | 22 | QtMaterialRippleOverlay::~QtMaterialRippleOverlay() 23 | { 24 | } 25 | 26 | void QtMaterialRippleOverlay::addRipple(QtMaterialRipple *ripple) 27 | { 28 | ripple->setOverlay(this); 29 | m_ripples.push_back(ripple); 30 | ripple->start(); 31 | 32 | connect(this, SIGNAL(destroyed(QObject*)), ripple, SLOT(stop())); 33 | connect(this, SIGNAL(destroyed(QObject*)), ripple, SLOT(deleteLater())); 34 | } 35 | 36 | void QtMaterialRippleOverlay::addRipple(const QPoint &position, qreal radius) 37 | { 38 | QtMaterialRipple *ripple = new QtMaterialRipple(position); 39 | ripple->setRadiusEndValue(radius); 40 | addRipple(ripple); 41 | } 42 | 43 | void QtMaterialRippleOverlay::removeRipple(QtMaterialRipple *ripple) 44 | { 45 | if (m_ripples.removeOne(ripple)) { 46 | delete ripple; 47 | update(); 48 | } 49 | } 50 | 51 | /*! 52 | * \reimp 53 | */ 54 | void QtMaterialRippleOverlay::paintEvent(QPaintEvent *event) 55 | { 56 | Q_UNUSED(event) 57 | 58 | QPainter painter(this); 59 | painter.setRenderHint(QPainter::Antialiasing); 60 | painter.setPen(Qt::NoPen); 61 | 62 | if (m_useClip) { 63 | painter.setClipPath(m_clipPath); 64 | } 65 | 66 | QList::const_iterator i; 67 | for (i = m_ripples.begin(); i != m_ripples.end(); ++i) { 68 | paintRipple(&painter, *i); 69 | } 70 | } 71 | 72 | void QtMaterialRippleOverlay::paintRipple(QPainter *painter, QtMaterialRipple *ripple) 73 | { 74 | const qreal radius = ripple->radius(); 75 | const QPointF center = ripple->center(); 76 | painter->setOpacity(ripple->opacity()); 77 | painter->setBrush(ripple->brush()); 78 | painter->drawEllipse(center, radius, radius); 79 | 80 | } 81 | -------------------------------------------------------------------------------- /QMaterialTree/qtmaterialrippleoverlay.h: -------------------------------------------------------------------------------- 1 | #ifndef QTMATERIALRIPPLEOVERLAY_H 2 | #define QTMATERIALRIPPLEOVERLAY_H 3 | 4 | #include 5 | #include "qwidget.h" 6 | class QtMaterialRipple; 7 | 8 | class QtMaterialRippleOverlay : public QWidget 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | explicit QtMaterialRippleOverlay(QWidget *parent = 0); 14 | ~QtMaterialRippleOverlay(); 15 | 16 | void addRipple(QtMaterialRipple *ripple); 17 | void addRipple(const QPoint &position, qreal radius = 300); 18 | 19 | void removeRipple(QtMaterialRipple *ripple); 20 | 21 | inline void setClipping(bool enable); 22 | inline bool hasClipping() const; 23 | 24 | inline void setClipPath(const QPainterPath &path); 25 | 26 | protected: 27 | void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; 28 | 29 | inline QList ripples() const; 30 | 31 | private: 32 | Q_DISABLE_COPY(QtMaterialRippleOverlay) 33 | 34 | void paintRipple(QPainter *painter, QtMaterialRipple *ripple); 35 | 36 | QList m_ripples; 37 | QPainterPath m_clipPath; 38 | bool m_useClip; 39 | }; 40 | 41 | inline void QtMaterialRippleOverlay::setClipping(bool enable) 42 | { 43 | m_useClip = enable; 44 | update(); 45 | } 46 | 47 | inline bool QtMaterialRippleOverlay::hasClipping() const 48 | { 49 | return m_useClip; 50 | } 51 | 52 | inline void QtMaterialRippleOverlay::setClipPath(const QPainterPath &path) 53 | { 54 | m_clipPath = path; 55 | update(); 56 | } 57 | 58 | inline QList QtMaterialRippleOverlay::ripples() const 59 | { 60 | return m_ripples; 61 | } 62 | 63 | #endif // QTMATERIALRIPPLEOVERLAY_H 64 | -------------------------------------------------------------------------------- /QMaterialTree/treeitem.cpp: -------------------------------------------------------------------------------- 1 | #include "treeitem.h" 2 | #include 3 | 4 | TreeItem::TreeItem(const QVector &data, TreeItem *parent) 5 | { 6 | parentItem = parent; 7 | itemData = data; 8 | } 9 | 10 | TreeItem::~TreeItem() 11 | { 12 | qDeleteAll(childItems); 13 | } 14 | 15 | TreeItem *TreeItem::child(int number) 16 | { 17 | return childItems.value(number); 18 | } 19 | 20 | int TreeItem::childCount() const 21 | { 22 | return childItems.count(); 23 | } 24 | 25 | int TreeItem::childNumber() const 26 | { 27 | if (parentItem) 28 | return parentItem->childItems.indexOf(const_cast(this)); 29 | 30 | return 0; 31 | } 32 | 33 | int TreeItem::columnCount() const 34 | { 35 | return itemData.count(); 36 | } 37 | 38 | QVariant TreeItem::data(int column) const 39 | { 40 | return itemData.value(column); 41 | } 42 | 43 | bool TreeItem::insertChildren(int position, int count, int columns) 44 | { 45 | if (position < 0 || position > childItems.size()) 46 | return false; 47 | 48 | for (int row = 0; row < count; ++row) { 49 | QVector data(columns); 50 | TreeItem *item = new TreeItem(data, this); 51 | childItems.insert(position, item); 52 | } 53 | 54 | return true; 55 | } 56 | 57 | bool TreeItem::insertColumns(int position, int columns) 58 | { 59 | if (position < 0 || position > itemData.size()) 60 | return false; 61 | 62 | for (int column = 0; column < columns; ++column) 63 | itemData.insert(position, QVariant()); 64 | 65 | foreach (TreeItem *child, childItems) 66 | child->insertColumns(position, columns); 67 | 68 | return true; 69 | } 70 | 71 | TreeItem *TreeItem::parent() 72 | { 73 | return parentItem; 74 | } 75 | 76 | bool TreeItem::removeChildren(int position, int count) 77 | { 78 | if (position < 0 || position + count > childItems.size()) 79 | return false; 80 | 81 | for (int row = 0; row < count; ++row) 82 | delete childItems.takeAt(position); 83 | 84 | return true; 85 | } 86 | 87 | bool TreeItem::removeColumns(int position, int columns) 88 | { 89 | if (position < 0 || position + columns > itemData.size()) 90 | return false; 91 | 92 | for (int column = 0; column < columns; ++column) 93 | itemData.remove(position); 94 | 95 | foreach (TreeItem *child, childItems) 96 | child->removeColumns(position, columns); 97 | 98 | return true; 99 | } 100 | 101 | bool TreeItem::setData(int column, const QVariant &value) 102 | { 103 | if (column < 0 || column >= itemData.size()) 104 | return false; 105 | 106 | itemData[column] = value; 107 | return true; 108 | } 109 | 110 | int TreeItem::level() 111 | { 112 | return m_level; 113 | } 114 | 115 | void TreeItem::setLevel(int level) 116 | { 117 | this->m_level = level; 118 | } 119 | -------------------------------------------------------------------------------- /QMaterialTree/treeitem.h: -------------------------------------------------------------------------------- 1 | #ifndef TREEITEM_H 2 | #define TREEITEM_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class TreeItem 9 | { 10 | public: 11 | explicit TreeItem(const QVector &data, TreeItem *parent = 0); 12 | ~TreeItem(); 13 | 14 | TreeItem *child(int number); 15 | int childCount() const; 16 | int columnCount() const; 17 | QVariant data(int column) const; 18 | bool insertChildren(int position, int count, int columns); 19 | bool insertColumns(int position, int columns); 20 | TreeItem *parent(); 21 | bool removeChildren(int position, int count); 22 | bool removeColumns(int position, int columns); 23 | int childNumber() const; 24 | bool setData(int column, const QVariant &value); 25 | 26 | int level(); 27 | void setLevel(int level); 28 | 29 | private: 30 | QList childItems; 31 | QVector itemData; 32 | TreeItem *parentItem; 33 | 34 | int m_level; 35 | }; 36 | 37 | #endif // TREEITEM_H 38 | -------------------------------------------------------------------------------- /QMaterialTree/treemodel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "treeitem.h" 3 | #include "treemodel.h" 4 | 5 | TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent) 6 | : QAbstractItemModel(parent) 7 | { 8 | QVector rootData; 9 | foreach (QString header, headers) 10 | rootData << header; 11 | 12 | rootItem = new TreeItem(rootData); 13 | setupModelData(data.split(QString("\n")), rootItem); 14 | levelOrder(rootItem); 15 | } 16 | 17 | TreeModel::~TreeModel() 18 | { 19 | delete rootItem; 20 | } 21 | 22 | int TreeModel::rowCount(const QModelIndex &parent) const 23 | { 24 | TreeItem *parentItem = getItem(parent); 25 | 26 | return parentItem->childCount(); 27 | } 28 | 29 | int TreeModel::columnCount(const QModelIndex & /* parent */) const 30 | { 31 | return rootItem->columnCount(); 32 | } 33 | 34 | QVariant TreeModel::data(const QModelIndex &index, int role) const 35 | { 36 | if (!index.isValid()) 37 | return QVariant(); 38 | 39 | if (role != Qt::DisplayRole && role != Qt::EditRole) 40 | return QVariant(); 41 | 42 | TreeItem *item = getItem(index); 43 | 44 | return item->data(index.column()); 45 | } 46 | 47 | TreeItem *TreeModel::getItem(const QModelIndex &index) const 48 | { 49 | if (index.isValid()) { 50 | TreeItem *item = static_cast(index.internalPointer()); 51 | if (item) 52 | return item; 53 | } 54 | return rootItem; 55 | } 56 | 57 | QVariant TreeModel::headerData(int section, Qt::Orientation orientation, 58 | int role) const 59 | { 60 | if (orientation == Qt::Horizontal && role == Qt::DisplayRole) 61 | return rootItem->data(section); 62 | 63 | return QVariant(); 64 | } 65 | 66 | 67 | bool TreeModel::setHeaderData(int section, Qt::Orientation orientation, 68 | const QVariant &value, int role) 69 | { 70 | if (role != Qt::EditRole || orientation != Qt::Horizontal) 71 | return false; 72 | 73 | bool result = rootItem->setData(section, value); 74 | 75 | if (result) 76 | emit headerDataChanged(orientation, section, section); 77 | 78 | return result; 79 | } 80 | 81 | QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const 82 | { 83 | if (parent.isValid() && parent.column() != 0) 84 | return QModelIndex(); 85 | 86 | TreeItem *parentItem = getItem(parent); 87 | 88 | TreeItem *childItem = parentItem->child(row); 89 | if (childItem) 90 | return createIndex(row, column, childItem); 91 | else 92 | return QModelIndex(); 93 | } 94 | 95 | QModelIndex TreeModel::parent(const QModelIndex &index) const 96 | { 97 | if (!index.isValid()) 98 | return QModelIndex(); 99 | 100 | TreeItem *childItem = getItem(index); 101 | TreeItem *parentItem = childItem->parent(); 102 | 103 | if (parentItem == rootItem) 104 | return QModelIndex(); 105 | 106 | return createIndex(parentItem->childNumber(), 0, parentItem); 107 | } 108 | 109 | void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent) 110 | { 111 | QList parents; 112 | QList indentations; 113 | parents << parent; 114 | indentations << 0; 115 | 116 | int number = 0; 117 | 118 | while (number < lines.count()) 119 | { 120 | 121 | int position = 0; 122 | while (position < lines[number].length()) { 123 | if (lines[number].at(position) != ' ') 124 | break; 125 | ++position; 126 | } 127 | 128 | QString lineData = lines[number].mid(position).trimmed(); 129 | 130 | if (!lineData.isEmpty()) 131 | { 132 | // Read the column data from the rest of the line. 133 | QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts); 134 | QVector columnData; 135 | for (int column = 0; column < columnStrings.count(); ++column) 136 | columnData << columnStrings[column]; 137 | 138 | if (position > indentations.last()) 139 | { 140 | // The last child of the current parent is now the new parent 141 | // unless the current parent has no children. 142 | if (parents.last()->childCount() > 0) { 143 | parents << parents.last()->child(parents.last()->childCount()-1); 144 | indentations << position; 145 | } 146 | } 147 | else 148 | { 149 | while (position < indentations.last() && parents.count() > 0) { 150 | parents.pop_back(); 151 | indentations.pop_back(); 152 | } 153 | } 154 | 155 | // Append a new item to the current parent's list of children. 156 | TreeItem *parent = parents.last(); 157 | parent->insertChildren(parent->childCount(), 1, rootItem->columnCount()); 158 | for (int column = 0; column < columnData.size(); ++column) 159 | { 160 | parent->child(parent->childCount() - 1)->setData(column, columnData[column]); 161 | 162 | } 163 | } 164 | 165 | ++number; 166 | } 167 | } 168 | 169 | void TreeModel::levelOrder( TreeItem *root) 170 | { 171 | if(root == NULL) 172 | return ; 173 | 174 | QQueue< TreeItem *> que; 175 | que.push_back(root); 176 | 177 | int level = -2; 178 | while(!que.empty()) 179 | { 180 | level++; 181 | int size =que.size(); 182 | 183 | //pop an item and push its children 184 | 185 | for(int i=0;isetLevel(level); 189 | que.pop_front(); 190 | 191 | int childCount = parentItem->childCount(); 192 | for(int j=0;jchild(j) ; 195 | que.push_back(child); 196 | } 197 | } 198 | 199 | 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /QMaterialTree/treemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef TREEMODEL_H 2 | #define TREEMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class TreeItem; 9 | 10 | class TreeModel : public QAbstractItemModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | TreeModel(const QStringList &headers, const QString &data, 16 | QObject *parent = 0); 17 | ~TreeModel(); 18 | 19 | QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; 20 | QVariant headerData(int section, Qt::Orientation orientation, 21 | int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; 22 | bool setHeaderData(int section, Qt::Orientation orientation, 23 | const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE; 24 | 25 | QModelIndex index(int row, int column, 26 | const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; 27 | QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; 28 | 29 | int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; 30 | int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; 31 | 32 | private: 33 | void setupModelData(const QStringList &lines, TreeItem *parent); 34 | 35 | void levelOrder( TreeItem *root); 36 | 37 | TreeItem *getItem(const QModelIndex &index) const; 38 | 39 | TreeItem *rootItem; 40 | }; 41 | 42 | #endif // TREEMODEL_H 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | a simple Material TreeView with ripple effect based on Qt. 2 | inspired by https://github.com/laserpants/qt-material-widgets and https://doc.qt.io/qt-5/qtwidgets-itemviews-editabletreemodel-example.html
3 | -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mangohl/QMaterial-TreeView/36c8638419fc5d42dab361afa0ec2ff265af76f0/example.gif --------------------------------------------------------------------------------