├── .gitignore ├── LICENSE ├── README.md ├── SlidingStackedWidget ├── SlidingStackedWidget.pri ├── slidingstackedwidget.cpp └── slidingstackedwidget.h ├── demo.cpp ├── demo.h ├── demo.pro ├── demo.ui ├── main.cpp └── screenshots ├── demo.gif └── slow_demo.gif /.gitignore: -------------------------------------------------------------------------------- 1 | *.pro.user 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tim Schneeberger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SlidingStackedWidget 2 | 3 | This is an extended version of QStackedWidget for Qt. Check `slidingstackedwidget.h` for a simple API reference. This code is based on an older [wiki post](https://qt.shoutwiki.com/wiki/Extending_QStackedWidget_for_sliding_page_animations_in_Qt) for Symbian devices. 4 | 5 | Check my other Qt widgets/add-ons out: 6 | 7 | ### Usage 8 | 9 | #### Include it in your project 10 | 11 | Copy the `SlidingStackedWidget` subdirectory from this repository into your project folder and add this to your qmake project file: 12 | 13 | ```cmake 14 | include(SlidingStackedWidget/SlidingStackedWidget.pri) 15 | ``` 16 | 17 | ### Screenshots 18 | 19 | Default animation duration (300ms): 20 | 21 | ![screenshot](screenshots/demo.gif) 22 | 23 | Slow-motion (3000ms): 24 | 25 | ![screenshot](screenshots/slow_demo.gif) 26 | 27 | As you can see, the page is slowly fading in/out in addition to the sliding animation. 28 | -------------------------------------------------------------------------------- /SlidingStackedWidget/SlidingStackedWidget.pri: -------------------------------------------------------------------------------- 1 | SOURCES += $$PWD/slidingstackedwidget.cpp 2 | HEADERS += $$PWD/slidingstackedwidget.h 3 | INCLUDEPATH += $$PWD 4 | -------------------------------------------------------------------------------- /SlidingStackedWidget/slidingstackedwidget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | Copyright (c) 2020 Tim Schneeberger (ThePBone) 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | * 20 | */ 21 | #include "slidingstackedwidget.h" 22 | #include 23 | #include 24 | #include 25 | 26 | SlidingStackedWidget::SlidingStackedWidget(QWidget *parent) 27 | 28 | : QStackedWidget(parent) 29 | { 30 | 31 | if (parent!=nullptr) { 32 | m_mainwindow=parent; 33 | } 34 | else { 35 | m_mainwindow=this; 36 | } 37 | m_vertical=false; 38 | m_speed=300; 39 | m_animationtype = QEasingCurve::OutQuart; 40 | m_now=0; 41 | m_next=0; 42 | m_wrap=false; 43 | m_pnow=QPoint(0,0); 44 | m_active=false; 45 | } 46 | 47 | void SlidingStackedWidget::setVerticalMode(bool vertical) { 48 | 49 | m_vertical=vertical; 50 | } 51 | 52 | void SlidingStackedWidget::setSpeed(int speed) { 53 | 54 | m_speed = speed; 55 | } 56 | 57 | void SlidingStackedWidget::setAnimation(enum QEasingCurve::Type animationtype) { 58 | 59 | m_animationtype = animationtype; 60 | } 61 | 62 | void SlidingStackedWidget::setWrap(bool wrap) { 63 | 64 | m_wrap=wrap; 65 | } 66 | 67 | bool SlidingStackedWidget::slideInNext() { 68 | 69 | int now=currentIndex(); 70 | if (m_wrap||(now0)) 80 | slideInIdx(now-1); 81 | else 82 | return false; 83 | return true; 84 | } 85 | 86 | void SlidingStackedWidget::slideInIdx(int idx, enum t_direction direction) { 87 | 88 | if (idx>count()-1) { 89 | direction=m_vertical ? TOP2BOTTOM : RIGHT2LEFT; 90 | idx=(idx)%count(); 91 | } 92 | else if (idx<0) { 93 | direction= m_vertical ? BOTTOM2TOP: LEFT2RIGHT; 94 | idx=(idx+count())%count(); 95 | } 96 | slideInWgt(widget ( idx ),direction); 97 | } 98 | 99 | void SlidingStackedWidget::slideInWgt(QWidget * newwidget, enum t_direction direction) { 100 | 101 | if (m_active) { 102 | return; 103 | } 104 | else m_active=true; 105 | enum t_direction directionhint; 106 | int now = currentIndex(); 107 | int next = indexOf(newwidget); 108 | if (now == next) { 109 | m_active=false; 110 | return; 111 | } 112 | else if (nowsetGeometry ( 0, 0, offsetx, offsety ); 128 | if (direction==BOTTOM2TOP) { 129 | offsetx=0; 130 | offsety=-offsety; 131 | } 132 | else if (direction==TOP2BOTTOM) { 133 | offsetx=0; 134 | } 135 | else if (direction==RIGHT2LEFT) { 136 | offsetx=-offsetx; 137 | offsety=0; 138 | } 139 | else if (direction==LEFT2RIGHT) { 140 | offsety=0; 141 | } 142 | 143 | QPoint pnext=widget(next)->pos(); 144 | QPoint pnow=widget(now)->pos(); 145 | m_pnow=pnow; 146 | widget(next)->move(pnext.x()-offsetx,pnext.y()-offsety); 147 | 148 | widget(next)->show(); 149 | widget(next)->raise(); 150 | 151 | QPropertyAnimation *animnow = new QPropertyAnimation(widget(now), "pos"); 152 | animnow->setDuration(m_speed); 153 | animnow->setEasingCurve(m_animationtype); 154 | animnow->setStartValue(QPoint(pnow.x(), pnow.y())); 155 | animnow->setEndValue(QPoint(offsetx+pnow.x(), offsety+pnow.y())); 156 | 157 | QGraphicsOpacityEffect *animnow_op_eff = new QGraphicsOpacityEffect(); 158 | widget(now)->setGraphicsEffect(animnow_op_eff); 159 | QPropertyAnimation *animnow_op = new QPropertyAnimation(animnow_op_eff, "opacity"); 160 | animnow_op->setDuration(m_speed/2); 161 | animnow_op->setStartValue(1); 162 | animnow_op->setEndValue(0); 163 | connect(animnow_op,&QPropertyAnimation::finished,[=](){ 164 | if(animnow_op_eff != nullptr) 165 | animnow_op_eff->deleteLater(); 166 | }); 167 | 168 | QGraphicsOpacityEffect *animnext_op_eff = new QGraphicsOpacityEffect(); 169 | animnext_op_eff->setOpacity(0); 170 | widget(next)->setGraphicsEffect(animnext_op_eff); 171 | QPropertyAnimation *animnext_op = new QPropertyAnimation(animnext_op_eff, "opacity"); 172 | animnext_op->setDuration(m_speed/2); 173 | animnext_op->setStartValue(0); 174 | animnext_op->setEndValue(1); 175 | connect(animnext_op,&QPropertyAnimation::finished,[=](){ 176 | if(animnext_op_eff != nullptr) 177 | animnext_op_eff->deleteLater(); 178 | }); 179 | 180 | QPropertyAnimation *animnext = new QPropertyAnimation(widget(next), "pos"); 181 | animnext->setDuration(m_speed); 182 | animnext->setEasingCurve(m_animationtype); 183 | animnext->setStartValue(QPoint(-offsetx+pnext.x(), offsety+pnext.y())); 184 | animnext->setEndValue(QPoint(pnext.x(), pnext.y())); 185 | 186 | animgroup = new QParallelAnimationGroup; 187 | animgroup->addAnimation(animnow); 188 | animgroup->addAnimation(animnext); 189 | animgroup->addAnimation(animnow_op); 190 | animgroup->addAnimation(animnext_op); 191 | 192 | QObject::connect(animgroup, SIGNAL(finished()),this,SLOT(animationDoneSlot())); 193 | m_next=next; 194 | m_now=now; 195 | m_active=true; 196 | animgroup->start(QAbstractAnimation::DeleteWhenStopped); 197 | } 198 | 199 | 200 | void SlidingStackedWidget::animationDoneSlot() 201 | { 202 | setCurrentIndex(m_next); 203 | widget(m_now)->hide(); 204 | widget(m_now)->move(m_pnow); 205 | m_active=false; 206 | emit animationFinished(); 207 | } 208 | 209 | 210 | -------------------------------------------------------------------------------- /SlidingStackedWidget/slidingstackedwidget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | Copyright (c) 2020 Tim Schneeberger (ThePBone) 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | * 20 | * Inspired by https://qt.shoutwiki.com/wiki/Extending_QStackedWidget_for_sliding_page_animations_in_Qt 21 | */ 22 | #ifndef SLIDINGSTACKEDWIDGET_H 23 | #define SLIDINGSTACKEDWIDGET_H 24 | #include 25 | #include 26 | #include 27 | 28 | class SlidingStackedWidget : public QStackedWidget { 29 | 30 | Q_OBJECT 31 | public: 32 | //! Animation direction 33 | enum t_direction { 34 | LEFT2RIGHT, 35 | RIGHT2LEFT, 36 | TOP2BOTTOM, 37 | BOTTOM2TOP, 38 | AUTOMATIC 39 | }; 40 | SlidingStackedWidget(QWidget *parent); 41 | 42 | public slots: 43 | //! Set animation speed 44 | void setSpeed(int speed); 45 | //! Set easing curve 46 | void setAnimation(enum QEasingCurve::Type animationtype); 47 | //! Change positioning mode 48 | void setVerticalMode(bool vertical = true); 49 | //! Enables page wrap for \c slideInNext and \c slideInPrev 50 | void setWrap(bool wrap); 51 | //! Slide to next page 52 | bool slideInNext(); 53 | //! Slide to previous page 54 | bool slideInPrev(); 55 | //! Slide to page x 56 | void slideInIdx(int idx, enum t_direction direction=AUTOMATIC); 57 | //! Slide to page with widget 58 | void slideInWgt(QWidget * widget, enum t_direction direction=AUTOMATIC); 59 | signals: 60 | //! Animation is finished 61 | void animationFinished(void); 62 | 63 | protected slots: 64 | void animationDoneSlot(void); 65 | 66 | protected: 67 | QWidget *m_mainwindow; 68 | int m_speed; 69 | enum QEasingCurve::Type m_animationtype; 70 | bool m_vertical; 71 | int m_now; 72 | int m_next; 73 | bool m_wrap; 74 | QPoint m_pnow; 75 | bool m_active; 76 | QList blockedPageList; 77 | QParallelAnimationGroup *animgroup; 78 | }; 79 | #endif // SLIDINGSTACKEDWIDGET_H 80 | -------------------------------------------------------------------------------- /demo.cpp: -------------------------------------------------------------------------------- 1 | #include "demo.h" 2 | #include "ui_demo.h" 3 | 4 | demo::demo(QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::demo) 7 | { 8 | ui->setupUi(this); 9 | ui->stackedWidget->setAnimation(QEasingCurve::Type::OutQuart); 10 | ui->stackedWidget->setSpeed(650); 11 | connect(ui->prev,&QAbstractButton::clicked,[this]{ 12 | if(ui->stackedWidget->slideInPrev()){ 13 | ui->prev->setEnabled(false); 14 | ui->next->setEnabled(false); 15 | } 16 | }); 17 | connect(ui->next,&QAbstractButton::clicked,[this]{ 18 | if(ui->stackedWidget->slideInNext()){ 19 | ui->prev->setEnabled(false); 20 | ui->next->setEnabled(false); 21 | } 22 | }); 23 | connect(ui->stackedWidget,&SlidingStackedWidget::animationFinished,[this]{ 24 | ui->prev->setEnabled(true); 25 | ui->next->setEnabled(true); 26 | }); 27 | } 28 | 29 | demo::~demo() 30 | { 31 | delete ui; 32 | } 33 | -------------------------------------------------------------------------------- /demo.h: -------------------------------------------------------------------------------- 1 | #ifndef DEMO_H 2 | #define DEMO_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class demo; 8 | } 9 | 10 | class demo : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit demo(QWidget *parent = nullptr); 16 | ~demo(); 17 | 18 | private: 19 | Ui::demo *ui; 20 | }; 21 | 22 | #endif // DEMO_H 23 | -------------------------------------------------------------------------------- /demo.pro: -------------------------------------------------------------------------------- 1 | QT += core gui 2 | 3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 | 5 | CONFIG += c++11 6 | 7 | # The following define makes your compiler emit warnings if you use 8 | # any Qt feature that has been marked deprecated (the exact warnings 9 | # depend on your compiler). Please consult the documentation of the 10 | # deprecated API in order to know how to port your code away from it. 11 | DEFINES += QT_DEPRECATED_WARNINGS 12 | 13 | # You can also make your code fail to compile if it uses deprecated APIs. 14 | # In order to do so, uncomment the following line. 15 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 16 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 17 | 18 | include(SlidingStackedWidget/SlidingStackedWidget.pri) 19 | 20 | SOURCES += \ 21 | demo.cpp \ 22 | main.cpp 23 | 24 | HEADERS += \ 25 | demo.h 26 | 27 | # Default rules for deployment. 28 | qnx: target.path = /tmp/$${TARGET}/bin 29 | else: unix:!android: target.path = /opt/$${TARGET}/bin 30 | !isEmpty(target.path): INSTALLS += target 31 | 32 | FORMS += \ 33 | demo.ui 34 | 35 | -------------------------------------------------------------------------------- /demo.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | demo 4 | 5 | 6 | 7 | 0 8 | 0 9 | 480 10 | 286 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | 0 21 | 22 | 23 | 24 | 25 | 0 26 | 27 | 28 | 0 29 | 30 | 31 | 0 32 | 33 | 34 | 0 35 | 36 | 37 | 38 | 39 | 40 | 41 | 10 42 | 10 43 | 171 44 | 61 45 | 46 | 47 | 48 | font-size: 24px; 49 | font-weight: bold; 50 | 51 | 52 | Welcome! 53 | 54 | 55 | 56 | 57 | 58 | 10 59 | 70 60 | 431 61 | 151 62 | 63 | 64 | 65 | font-size: 14px; 66 | 67 | 68 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 69 | 70 | 71 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 72 | 73 | 74 | true 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 0 86 | 0 87 | 445 88 | 230 89 | 90 | 91 | 92 | 93 | 94 | 10 95 | 10 96 | 301 97 | 61 98 | 99 | 100 | 101 | font-size: 24px; 102 | font-weight: bold; 103 | 104 | 105 | 106 | Widgets 107 | 108 | 109 | 110 | 111 | 112 | 10 113 | 70 114 | 431 115 | 131 116 | 117 | 118 | 119 | 120 | Tab 1 121 | 122 | 123 | 124 | 125 | 10 126 | 10 127 | 121 128 | 21 129 | 130 | 131 | 132 | Radio Button 133 | 134 | 135 | true 136 | 137 | 138 | 139 | 140 | 141 | 10 142 | 40 143 | 131 144 | 21 145 | 146 | 147 | 148 | Radio Button 2 149 | 150 | 151 | 152 | 153 | 154 | 10 155 | 70 156 | 141 157 | 23 158 | 159 | 160 | 161 | 162 | Item 1 163 | 164 | 165 | 166 | 167 | Item 2 168 | 169 | 170 | 171 | 172 | Item 3 173 | 174 | 175 | 176 | 177 | 178 | 179 | Tab 2 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 0 190 | 0 191 | 445 192 | 230 193 | 194 | 195 | 196 | 197 | 198 | 10 199 | 10 200 | 271 201 | 61 202 | 203 | 204 | 205 | font-size: 24px; 206 | font-weight: bold; 207 | 208 | 209 | Last page 210 | 211 | 212 | 213 | 214 | 215 | 10 216 | 70 217 | 431 218 | 141 219 | 220 | 221 | 222 | font-size: 14px; 223 | 224 | 225 | Est ridiculus Class consectetuer leo nullam. Convallis porta condimentum dapibus. Suspendisse aliquam ligula elementum blandit. Massa sit. Tempor nonummy commodo odio id, bibendum. 226 | 227 | Consequat, cras suspendisse molestie, cras purus integer tristique quam est lorem. 228 | 229 | 230 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 231 | 232 | 233 | true 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 0 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | Prev 252 | 253 | 254 | 255 | .. 256 | 257 | 258 | Qt::ToolButtonIconOnly 259 | 260 | 261 | 262 | 263 | 264 | 265 | Qt::Horizontal 266 | 267 | 268 | 269 | 40 270 | 20 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | Next 282 | 283 | 284 | 285 | .. 286 | 287 | 288 | Qt::ToolButtonIconOnly 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | SlidingStackedWidget 299 | QStackedWidget 300 |
slidingstackedwidget.h
301 | 1 302 |
303 |
304 | 305 | 306 |
307 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "demo.h" 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | QApplication a(argc, argv); 9 | demo w; 10 | qApp->setPalette(w.style()->standardPalette()); 11 | qApp->setStyle("Fusion"); 12 | qApp->setStyleSheet(""); 13 | w.show(); 14 | return a.exec(); 15 | } 16 | -------------------------------------------------------------------------------- /screenshots/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qt-Widgets/SlidingStackedWidget-1/3fedb180cb5c1bb95cd95bee845eee36f1dc6656/screenshots/demo.gif -------------------------------------------------------------------------------- /screenshots/slow_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qt-Widgets/SlidingStackedWidget-1/3fedb180cb5c1bb95cd95bee845eee36f1dc6656/screenshots/slow_demo.gif --------------------------------------------------------------------------------