diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ce8dd5..c7adf80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ option (QACCORDION_BUILD_TESTER OFF) option (QACCORDION_EXTERNAL OFF) set (qAccordion_VERSION_MAJOR 0) -set (qAccordion_VERSION_MINOR 1) +set (qAccordion_VERSION_MINOR 2) set (qAccordion_VERSION_PATCH 0) add_subdirectory(test) diff --git a/README.md b/README.md index 4cb6d91..a357097 100644 --- a/README.md +++ b/README.md @@ -112,8 +112,8 @@ If you find a Bug or have a feature request head over to github and open a new [ ## ToDo ## * Drag and Drop support. The API already supports moving Content Panes but only programmatically. * User defined Icons and Icon position. -* Changable Animation Type -* Maybe much, maybe nothing. So far it covers all my use cases ;) +* Definable animation type. +* Trigger open / close not only on single mouse click (e.g. double click, mouse over). ## FAQ ## @@ -137,4 +137,4 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -````` \ No newline at end of file +````` diff --git a/doc/main_page.dox b/doc/main_page.dox index b04a5d7..8e4fd13 100644 --- a/doc/main_page.dox +++ b/doc/main_page.dox @@ -23,6 +23,7 @@ * used in the demo application located in the test folder. * * ## License + * ``` * Copyright (C) 2015 Christian Rapp * * This program is free software: you can redistribute it and/or modify @@ -37,5 +38,5 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * + * ``` */ diff --git a/include/qAccordion/contentpane.h b/include/qAccordion/contentpane.h index 95edf4a..1a1721c 100644 --- a/include/qAccordion/contentpane.h +++ b/include/qAccordion/contentpane.h @@ -29,6 +29,7 @@ #include #include "clickableframe.h" +#include "qaccordion.h" /** * @brief Content Pane class @@ -78,10 +79,10 @@ class ContentPane : public QWidget explicit ContentPane(QString header, QFrame *content, QWidget *parent = 0); /** - * @brief Check if this Content pane is open - * @return boolean True if open + * @brief Check if this Content pane is active + * @return boolean True if active */ - bool getOpen(); + bool getActive(); /** * @brief Get the content frame of the content pane @@ -233,6 +234,7 @@ class ContentPane : public QWidget uint getAnimationDuration(); signals: + /** * @brief Clicked signal is emitted when the header is clicked */ @@ -240,34 +242,21 @@ class ContentPane : public QWidget /** * @brief Signal will be emitted after the open animation finished */ - void contentPaneOpened(); + void isActive(); /** * @brief Signal will be emitted after the close animation finished */ - void contentPaneClosed(); + void isInactive(); public slots: - /** - * @brief Open the content pane - * - * @details - * This will open the content pane if it is currently closed. - * @warning - * Currently there is no inbuild mechanism to close an already open - * Content Pane when you open another one programmatically. Meaning you have - * to take care of this yourself. - */ - void openContentPane(); - /** - * @brief Close the content pane - * - * @details - * This will close the content pane if it is currently open. - */ - void closeContentPane(); + void headerClicked(QPoint pos); private: + // yeah we are friends. this is important to keep openContentPane and + // closeContentPane private + friend class QAccordion; + ClickableFrame *header; QFrame *container; QFrame *content; @@ -276,7 +265,7 @@ public slots: int contentPaneFrameStyle; int containerAnimationMaxHeight; - bool open; + bool active; std::unique_ptr openAnimation; std::unique_ptr closeAnimation; @@ -287,7 +276,25 @@ public slots: void initAnimations(); private slots: - void clickableFrameClicked(QPoint pos); + + /** + * @brief Open the content pane + * + * @details + * This will open the content pane if it is currently closed. + * @warning + * Currently there is no inbuild mechanism to close an already open + * Content Pane when you open another one programmatically. Meaning you have + * to take care of this yourself. + */ + void openContentPane(); + /** + * @brief Close the content pane + * + * @details + * This will close the content pane if it is currently open. + */ + void closeContentPane(); protected: /** diff --git a/include/qAccordion/qaccordion.h b/include/qAccordion/qaccordion.h index 37ed55e..4766ea5 100644 --- a/include/qAccordion/qaccordion.h +++ b/include/qAccordion/qaccordion.h @@ -35,6 +35,8 @@ #include "config.h" #include "contentpane.h" +class ContentPane; + /** * @brief QAccordion base class * @@ -365,6 +367,7 @@ public slots: bool checkIndexError(uint index, bool sizeIndexAllowed, const QString &errMessage); + void handleClickedSignal(ContentPane *cpane); private slots: void numberOfPanesChanged(int number); diff --git a/src/contentpane.cpp b/src/contentpane.cpp index 0157842..ab08948 100644 --- a/src/contentpane.cpp +++ b/src/contentpane.cpp @@ -31,7 +31,7 @@ ContentPane::ContentPane(QString header, QFrame *content, QWidget *parent) this->initDefaults(std::move(header)); } -bool ContentPane::getOpen() { return this->open; } +bool ContentPane::getActive() { return this->active; } QFrame *ContentPane::getContentFrame() { return this->content; } @@ -51,7 +51,7 @@ void ContentPane::setMaximumHeight(int maxHeight) { this->containerAnimationMaxHeight = maxHeight; - if (this->open) + if (this->getActive()) this->container->setMaximumHeight(this->containerAnimationMaxHeight); this->openAnimation->setEndValue(this->containerAnimationMaxHeight); this->closeAnimation->setStartValue(this->containerAnimationMaxHeight); @@ -110,25 +110,25 @@ int ContentPane::getContainerFrameStyle() void ContentPane::openContentPane() { - if (this->open) + if (this->getActive()) return; this->openAnimation->start(); this->header->setCaretPixmap(clickcon::CARRET_ICON_OPENED); - this->open = true; + this->active = true; } void ContentPane::closeContentPane() { - if (!this->open) + if (!this->getActive()) return; this->closeAnimation->start(); this->header->setCaretPixmap(clickcon::CARRET_ICON_CLOSED); - this->open = false; + this->active = false; } void ContentPane::initDefaults(QString header) { - this->open = false; + this->active = false; this->headerFrameStyle = QFrame::Shape::StyledPanel | QFrame::Shadow::Raised; this->contentPaneFrameStyle = @@ -155,7 +155,7 @@ void ContentPane::initHeaderFrame(QString header) this->layout()->addWidget(this->header); QObject::connect(this->header, &ClickableFrame::singleClick, this, - &ContentPane::clickableFrameClicked); + &ContentPane::headerClicked); } void ContentPane::initContainerContentFrame() @@ -201,10 +201,12 @@ void ContentPane::initAnimations() this->openAnimation->setEasingCurve( QEasingCurve(QEasingCurve::Type::Linear)); this->closeAnimation->setEasingCurve( - QEasingCurve(QEasingCurve::Type::Linear)); + QEasingCurve(QEasingCurve::Type::Linear)); } -void ContentPane::clickableFrameClicked(__attribute__((unused)) QPoint pos) { emit this->clicked(); } +void ContentPane::headerClicked(__attribute__((unused)) QPoint pos) { + emit this->clicked(); +} void ContentPane::paintEvent(__attribute__((unused)) QPaintEvent *event) { diff --git a/src/qaccordion.cpp b/src/qaccordion.cpp index b012e05..52d0372 100644 --- a/src/qaccordion.cpp +++ b/src/qaccordion.cpp @@ -184,7 +184,7 @@ void QAccordion::getActiveContentPaneIndex(std::vector &indexVector) indexVector.clear(); std::for_each(this->contentPanes.begin(), this->contentPanes.end(), [&indexVector, this](ContentPane *pane) { - if (pane->getOpen()) { + if (pane->getActive()) { indexVector.push_back( this->findContentPaneIndex("", nullptr, pane)); } @@ -223,27 +223,8 @@ int QAccordion::internalAddContentPane(QString header, QFrame *cframe, this->contentPanes.push_back(cpane); // manage the clicked signal in a lambda expression - QObject::connect(cpane, &ContentPane::clicked, [this, cpane]() { - // if the clicked content pane is open we simply close it and return - if (cpane->getOpen()) { - cpane->closeContentPane(); - return; - } - // if it is not open we will open it and search our vector for other - // panes that are already open. - // TODO: Is it really necessary to search for more than one open cpane? - if (!cpane->getOpen()) { - // check if multiActive is allowed - if (!this->getMultiActive()) { - std::for_each(this->contentPanes.begin(), - this->contentPanes.end(), [](ContentPane *pane) { - if (pane->getOpen()) - pane->closeContentPane(); - }); - } - cpane->openContentPane(); - } - }); + QObject::connect(cpane, &ContentPane::clicked, + [this, cpane]() { this->handleClickedSignal(cpane); }); emit numberOfContentPanesChanged(this->contentPanes.size()); @@ -276,29 +257,9 @@ bool QAccordion::internalInsertContentPane(uint index, QString header, this->contentPanes.insert(this->contentPanes.begin() + index, cpane); - // TODO: This code has to be merged with internalAddContentPane. // manage the clicked signal in a lambda expression - QObject::connect(cpane, &ContentPane::clicked, [this, cpane]() { - // if the clicked content pane is open we simply close it and return - if (cpane->getOpen()) { - cpane->closeContentPane(); - return; - } - // if it is not open we will open it and search our vector for other - // panes that are already open. - // TODO: Is it really necessary to search for more than one open cpane? - if (!cpane->getOpen()) { - // check if multiActive is allowed - if (!this->getMultiActive()) { - std::for_each(this->contentPanes.begin(), - this->contentPanes.end(), [](ContentPane *pane) { - if (pane->getOpen()) - pane->closeContentPane(); - }); - } - cpane->openContentPane(); - } - }); + QObject::connect(cpane, &ContentPane::clicked, + [this, cpane]() { this->handleClickedSignal(cpane); }); emit numberOfContentPanesChanged(this->contentPanes.size()); @@ -404,6 +365,42 @@ bool QAccordion::checkIndexError(uint index, bool sizeIndexAllowed, return false; } +void QAccordion::handleClickedSignal(ContentPane *cpane) +{ + // if the clicked content pane is open we simply close it and return + if (cpane->getActive()) { + // if collapsible and multiActive are false we are not allowed to close + // this pane + if (!this->collapsible && !this->multiActive) { + return; + } + // when multiActive is true we have to check if there is any other open + // cpane. if so we can close this one + std::vector activePanes; + if (!this->collapsible) { + this->getActiveContentPaneIndex(activePanes); + if (activePanes.size() == 1) + return; // only one active --> good bye :) + } + cpane->closeContentPane(); + return; + } + // if it is not open we will open it and search our vector for other + // panes that are already open. + // TODO: Is it really necessary to search for more than one open cpane? + if (!cpane->getActive()) { + // check if multiActive is allowed + if (!this->getMultiActive()) { + std::for_each(this->contentPanes.begin(), this->contentPanes.end(), + [](ContentPane *pane) { + if (pane->getActive()) + pane->closeContentPane(); + }); + } + cpane->openContentPane(); + } +} + void QAccordion::numberOfPanesChanged(int number) { // automatically open contentpane if we have only one and collapsible is diff --git a/test/mainwindow.cpp b/test/mainwindow.cpp index c25e49b..c8cfa72 100644 --- a/test/mainwindow.cpp +++ b/test/mainwindow.cpp @@ -72,6 +72,9 @@ void MainWindow::networkRequestFinished(QNetworkReply *reply) void MainWindow::contentPaneAdd(QAccordion *topAccordion) { + ui->widgetControlAccordion->setCollapsible(false); + // good pratice is to check the return value of addContentPane. see the API + // Reference for more details int indexAddPane = ui->widgetControlAccordion->addContentPane("Add Pane"); // Get the content frame @@ -187,7 +190,8 @@ void MainWindow::contentPaneRemove(QAccordion *topAccordion) topAccordion, this]() { if (headerName->text() != "") { - bool status = topAccordion->removeContentPane(true, headerName->text()); + bool status = + topAccordion->removeContentPane(true, headerName->text()); if (status) { this->statusBar()->showMessage( "Content Pane \"" + headerName->text() + "\" removed", 3000);