├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── dmsnavigation.pro ├── resources └── DMSNavigation.gif └── src ├── DMSNavigation.cpp ├── DMSNavigation.h └── main.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 thelostcode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DMS Navigation 2 | ========= 3 | 4 | The DMS Navigation widget implements a beautiful tab widget alternative for Qt. 5 | The widget is implemented as Qt Widget and **not** QML. 6 | 7 | I use this widget in a private project and decided to publish it beacuase it looks beautiful. 8 | 9 | ![center](resources/DMSNavigation.gif) 10 | 11 | API 12 | ---- 13 | The API implements a set of methods you know from QTabWidget. 14 | 15 | ```cpp 16 | int addTab(QWidget* widget, const QString& text); 17 | int addTab(QWidget* widget, const QIcon& icon, const QString& text); 18 | 19 | int insertTab(int index, QWidget* widget, const QString& text); 20 | int insertTab(int index, QWidget* widget, const QIcon& icon, const QString& text); 21 | 22 | void removeTab(int index); 23 | 24 | QString tabText(int index) const; 25 | void setTabText(int index, const QString& text); 26 | 27 | QIcon tabIcon(int index) const; 28 | void setTabIcon(int index, const QIcon& icon); 29 | 30 | enum HorizontalAlignment { AlignLeft, AlignCenter, AlignRight }; 31 | 32 | HorizontalAlignment horizontalAlignment() const; 33 | void setHorizontalAlignment(HorizontalAlignment alignment); 34 | 35 | int count() const; 36 | int currentIndex() const; 37 | 38 | public slots: 39 | void setCurrentIndex(int index); 40 | ``` 41 | 42 | Usage 43 | ---- 44 | Using the widget is pretty simple. This is the example of the GIF you see above. 45 | 46 | ```cpp 47 | int main(int argc, char** argv) 48 | { 49 | QApplication app(argc, argv); 50 | 51 | DMSNavigation navi; 52 | navi.resize(600, 50); 53 | navi.show(); 54 | 55 | navi.addTab(new QTextEdit("The first."), "Text 1"); 56 | navi.addTab(new QTextEdit("The second."), "Text 2"); 57 | navi.addTab(new QTextEdit("The third."), "Text 3"); 58 | 59 | return app.exec(); 60 | } 61 | ``` 62 | 63 | Version 64 | ---- 65 | 66 | #### 1.0 67 | Initial realease. 68 | 69 | 70 | Thank you thibaut. 71 | ----------- 72 | A special thank goes to **thibaut** who designed and inspired me. The widget was implemented as HTML and CSS control and I found it on the following site: http://ui-cloud.com/dark-navigation/. 73 | 74 | License 75 | ---- 76 | MIT 77 | 78 | **Free Software, Hell Yeah!** -------------------------------------------------------------------------------- /dmsnavigation.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (2.01a) Mo 17. Feb 13:11:24 2014 3 | ###################################################################### 4 | QT += core gui 5 | 6 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 7 | 8 | TEMPLATE = app 9 | TARGET = dmsnavigation 10 | DEPENDPATH += . src 11 | INCLUDEPATH += . 12 | 13 | # Input 14 | HEADERS += src/DMSNavigation.h 15 | SOURCES += src/DMSNavigation.cpp src/main.cpp 16 | -------------------------------------------------------------------------------- /resources/DMSNavigation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qt-Widgets/dmsnavigation/e6135cbc8f1a50ba38e34bee690e25a7a5203335/resources/DMSNavigation.gif -------------------------------------------------------------------------------- /src/DMSNavigation.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2014 thelostcode 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | #include "DMSNavigation.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | const int CONTENT_MARGIN_LEFT = 16; 35 | const int CONTENT_MARGIN_RIGHT = 16; 36 | const int CONTENT_MARGIN = CONTENT_MARGIN_LEFT + CONTENT_MARGIN_RIGHT; 37 | const int DMS_ITEM_MINIMUM_SIZE = 40 + CONTENT_MARGIN; 38 | const int DMS_ITEM_MAXIMUM_SIZE = 120 + CONTENT_MARGIN; 39 | const int DMS_ITEM_ICON_MARGIN = 8; 40 | 41 | namespace PNG 42 | { 43 | const unsigned char Body[215] = 44 | { 45 | 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00, 46 | 0x0D,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x01,0x00,0x00, 47 | 0x00,0x26,0x08,0x02,0x00,0x00,0x00,0x8A,0xDE,0x66,0x50, 48 | 0x00,0x00,0x00,0x01,0x73,0x52,0x47,0x42,0x00,0xAE,0xCE, 49 | 0x1C,0xE9,0x00,0x00,0x00,0x04,0x67,0x41,0x4D,0x41,0x00, 50 | 0x00,0xB1,0x8F,0x0B,0xFC,0x61,0x05,0x00,0x00,0x00,0x09, 51 | 0x70,0x48,0x59,0x73,0x00,0x00,0x0E,0xC3,0x00,0x00,0x0E, 52 | 0xC3,0x01,0xC7,0x6F,0xA8,0x64,0x00,0x00,0x00,0x1A,0x74, 53 | 0x45,0x58,0x74,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65, 54 | 0x00,0x50,0x61,0x69,0x6E,0x74,0x2E,0x4E,0x45,0x54,0x20, 55 | 0x76,0x33,0x2E,0x35,0x2E,0x31,0x31,0x47,0xF3,0x42,0x37, 56 | 0x00,0x00,0x00,0x46,0x49,0x44,0x41,0x54,0x18,0x57,0x63, 57 | 0x30,0xB5,0xB4,0x64,0x08,0x0C,0x0B,0x67,0x70,0xF3,0xF6, 58 | 0x61,0x70,0xF3,0xF2,0x66,0x70,0x45,0xC7,0x9E,0x5E,0x0C, 59 | 0x2E,0xC8,0xD8,0xC3,0x13,0x8C,0x9D,0x91,0xB1,0xBB,0x07, 60 | 0x18,0x3B,0xC1,0xB0,0x9B,0x3B,0x1C,0x3B,0xC2,0xB0,0xAB, 61 | 0x1B,0x1C,0x3B,0x80,0xB0,0x8B,0x2B,0x0A,0xB6,0x07,0x61, 62 | 0x67,0x17,0x30,0xD6,0x33,0x34,0x04,0x00,0x5C,0x76,0x1E, 63 | 0xCF,0x7D,0xBC,0xF8,0x0F,0x00,0x00,0x00,0x00,0x49,0x45, 64 | 0x4E,0x44,0xAE,0x42,0x60,0x82 65 | }; 66 | 67 | const unsigned char BorderLeft[215] = 68 | { 69 | 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00, 70 | 0x0D,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x01,0x00,0x00, 71 | 0x00,0x26,0x08,0x02,0x00,0x00,0x00,0x8A,0xDE,0x66,0x50, 72 | 0x00,0x00,0x00,0x01,0x73,0x52,0x47,0x42,0x00,0xAE,0xCE, 73 | 0x1C,0xE9,0x00,0x00,0x00,0x04,0x67,0x41,0x4D,0x41,0x00, 74 | 0x00,0xB1,0x8F,0x0B,0xFC,0x61,0x05,0x00,0x00,0x00,0x09, 75 | 0x70,0x48,0x59,0x73,0x00,0x00,0x0E,0xC3,0x00,0x00,0x0E, 76 | 0xC3,0x01,0xC7,0x6F,0xA8,0x64,0x00,0x00,0x00,0x1A,0x74, 77 | 0x45,0x58,0x74,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65, 78 | 0x00,0x50,0x61,0x69,0x6E,0x74,0x2E,0x4E,0x45,0x54,0x20, 79 | 0x76,0x33,0x2E,0x35,0x2E,0x31,0x31,0x47,0xF3,0x42,0x37, 80 | 0x00,0x00,0x00,0x46,0x49,0x44,0x41,0x54,0x18,0x57,0x63, 81 | 0x30,0xB5,0xB4,0x64,0x88,0x4E,0x48,0x64,0x08,0x0C,0x0B, 82 | 0x67,0x08,0x0C,0x0D,0x63,0x08,0x40,0xC7,0x21,0xA1,0x0C, 83 | 0xFE,0xC8,0x38,0x38,0x04,0x8C,0xFD,0x90,0x71,0x50,0x30, 84 | 0x18,0xFB,0xC2,0x70,0x60,0x10,0x1C,0xFB,0xC0,0x70,0x40, 85 | 0x20,0x1C,0x7B,0x83,0xB0,0x7F,0x00,0x0A,0xF6,0x02,0x61, 86 | 0x3F,0x7F,0x30,0xD6,0x33,0x34,0x04,0x00,0xBB,0x7F,0x23, 87 | 0x70,0x3D,0x75,0xDA,0x5F,0x00,0x00,0x00,0x00,0x49,0x45, 88 | 0x4E,0x44,0xAE,0x42,0x60,0x82 89 | }; 90 | 91 | const unsigned char BorderRight[208] = 92 | { 93 | 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00, 94 | 0x0D,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x01,0x00,0x00, 95 | 0x00,0x26,0x08,0x02,0x00,0x00,0x00,0x8A,0xDE,0x66,0x50, 96 | 0x00,0x00,0x00,0x01,0x73,0x52,0x47,0x42,0x00,0xAE,0xCE, 97 | 0x1C,0xE9,0x00,0x00,0x00,0x04,0x67,0x41,0x4D,0x41,0x00, 98 | 0x00,0xB1,0x8F,0x0B,0xFC,0x61,0x05,0x00,0x00,0x00,0x09, 99 | 0x70,0x48,0x59,0x73,0x00,0x00,0x0E,0xC3,0x00,0x00,0x0E, 100 | 0xC3,0x01,0xC7,0x6F,0xA8,0x64,0x00,0x00,0x00,0x1A,0x74, 101 | 0x45,0x58,0x74,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65, 102 | 0x00,0x50,0x61,0x69,0x6E,0x74,0x2E,0x4E,0x45,0x54,0x20, 103 | 0x76,0x33,0x2E,0x35,0x2E,0x31,0x31,0x47,0xF3,0x42,0x37, 104 | 0x00,0x00,0x00,0x3F,0x49,0x44,0x41,0x54,0x18,0x57,0x65, 105 | 0xC3,0xC9,0x09,0xC0,0x20,0x14,0x05,0xC0,0x57,0x91,0xE0, 106 | 0xF6,0x17,0x97,0x83,0x87,0xA4,0xFF,0x92,0x44,0x45,0xC1, 107 | 0x64,0x60,0x10,0x55,0xD1,0x9E,0x17,0x52,0x2A,0x24,0x17, 108 | 0xF0,0x77,0xCA,0xA0,0x6B,0xFA,0xD7,0x35,0xEE,0xA2,0x67, 109 | 0xD8,0x59,0x4E,0x3F,0x12,0x5F,0xDD,0x4C,0xB3,0xB1,0xB6, 110 | 0x03,0x90,0x17,0x18,0xDF,0x51,0xCA,0x88,0xA5,0x00,0x00, 111 | 0x00,0x00,0x49,0x45,0x4E,0x44,0xAE,0x42,0x60,0x82 112 | }; 113 | 114 | const unsigned char BodyHover[226] = 115 | { 116 | 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00, 117 | 0x0D,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x01,0x00,0x00, 118 | 0x00,0x26,0x08,0x02,0x00,0x00,0x00,0x8A,0xDE,0x66,0x50, 119 | 0x00,0x00,0x00,0x01,0x73,0x52,0x47,0x42,0x00,0xAE,0xCE, 120 | 0x1C,0xE9,0x00,0x00,0x00,0x04,0x67,0x41,0x4D,0x41,0x00, 121 | 0x00,0xB1,0x8F,0x0B,0xFC,0x61,0x05,0x00,0x00,0x00,0x09, 122 | 0x70,0x48,0x59,0x73,0x00,0x00,0x0E,0xC3,0x00,0x00,0x0E, 123 | 0xC3,0x01,0xC7,0x6F,0xA8,0x64,0x00,0x00,0x00,0x1A,0x74, 124 | 0x45,0x58,0x74,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65, 125 | 0x00,0x50,0x61,0x69,0x6E,0x74,0x2E,0x4E,0x45,0x54,0x20, 126 | 0x76,0x33,0x2E,0x35,0x2E,0x31,0x31,0x47,0xF3,0x42,0x37, 127 | 0x00,0x00,0x00,0x51,0x49,0x44,0x41,0x54,0x18,0x57,0x4D, 128 | 0xC6,0xB9,0x0D,0xC0,0x30,0x08,0x40,0x51,0x26,0x8A,0xE4, 129 | 0x22,0x85,0x27,0xF0,0x01,0xF8,0x8C,0x94,0x26,0xD9,0x7F, 130 | 0x8D,0x00,0x05,0x4A,0xF1,0xF5,0x3E,0x9C,0x31,0xC2,0xFD, 131 | 0xBC,0xC0,0x6B,0x43,0xDB,0x97,0x69,0xCD,0x65,0x92,0x2A, 132 | 0xD1,0x98,0xF6,0xF8,0x57,0xC2,0x3E,0xEC,0xAB,0x2A,0xD5, 133 | 0xD6,0xED,0x8B,0x2A,0x15,0x6E,0xAE,0x96,0x89,0xDD,0x2C, 134 | 0x26,0x55,0x4A,0x48,0xEE,0x11,0xC2,0x07,0x57,0x24,0x22, 135 | 0x24,0xCE,0xB6,0x33,0xC8,0x00,0x00,0x00,0x00,0x49,0x45, 136 | 0x4E,0x44,0xAE,0x42,0x60,0x82 137 | }; 138 | 139 | unsigned char BodyClicked[237] = 140 | { 141 | 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00, 142 | 0x0D,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x01,0x00,0x00, 143 | 0x00,0x26,0x08,0x02,0x00,0x00,0x00,0x8A,0xDE,0x66,0x50, 144 | 0x00,0x00,0x00,0x01,0x73,0x52,0x47,0x42,0x00,0xAE,0xCE, 145 | 0x1C,0xE9,0x00,0x00,0x00,0x04,0x67,0x41,0x4D,0x41,0x00, 146 | 0x00,0xB1,0x8F,0x0B,0xFC,0x61,0x05,0x00,0x00,0x00,0x09, 147 | 0x70,0x48,0x59,0x73,0x00,0x00,0x0E,0xC3,0x00,0x00,0x0E, 148 | 0xC3,0x01,0xC7,0x6F,0xA8,0x64,0x00,0x00,0x00,0x1A,0x74, 149 | 0x45,0x58,0x74,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65, 150 | 0x00,0x50,0x61,0x69,0x6E,0x74,0x2E,0x4E,0x45,0x54,0x20, 151 | 0x76,0x33,0x2E,0x35,0x2E,0x31,0x31,0x47,0xF3,0x42,0x37, 152 | 0x00,0x00,0x00,0x5C,0x49,0x44,0x41,0x54,0x18,0x57,0x3D, 153 | 0xC9,0xD1,0x0D,0x40,0x30,0x00,0x05,0xC0,0xB7,0x84,0x98, 154 | 0x82,0x28,0x8A,0x6A,0x29,0x5A,0x54,0x4D,0x6A,0x0B,0xA3, 155 | 0x91,0x97,0xF0,0x71,0x5F,0x07,0xA9,0x14,0x5C,0x38,0xC8, 156 | 0x1F,0x11,0xEE,0xE5,0xE3,0xF9,0xFB,0x6E,0xDE,0x03,0x4D, 157 | 0xEB,0x46,0xD6,0xAF,0x34,0x2C,0x8E,0xCC,0xBC,0x90,0xB6, 158 | 0x13,0xA9,0xD1,0x52,0x67,0x06,0x6A,0xB5,0x41,0xD3,0x6B, 159 | 0x54,0x6D,0x47,0x42,0x36,0x28,0x6A,0x89,0xAC,0x2C,0x91, 160 | 0xA4,0x29,0xC2,0x75,0xFF,0x72,0x21,0x1E,0xD2,0x08,0x1E, 161 | 0x83,0x94,0x8C,0x15,0x5A,0x00,0x00,0x00,0x00,0x49,0x45, 162 | 0x4E,0x44,0xAE,0x42,0x60,0x82 163 | }; 164 | 165 | const unsigned int BodySize = 215; 166 | const unsigned int BorderLeftSize = 215; 167 | const unsigned int BorderRightSize = 208; 168 | const unsigned int BodyHoverSize = 226; 169 | const unsigned int BodyClickedSize = 237; 170 | } 171 | 172 | //----------------------------------------------------------------------------- 173 | // DMSItem 174 | //----------------------------------------------------------------------------- 175 | class DMSItem 176 | { 177 | public: 178 | DMSItem() 179 | : isClicked(false) 180 | { 181 | } 182 | 183 | QString title; 184 | bool isClicked; 185 | QRect lastRect; 186 | QPixmap icon; 187 | 188 | public: 189 | inline int width(const QFontMetrics& fontMetrics) 190 | { return fontMetrics.width(title); } 191 | }; 192 | 193 | //----------------------------------------------------------------------------- 194 | // DMSNavigationBar 195 | //----------------------------------------------------------------------------- 196 | class DMSNavigationBar : public QWidget 197 | { 198 | public: 199 | explicit DMSNavigationBar(DMSNavigation* q, QWidget* parent = NULL, Qt::WindowFlags flags = 0); 200 | ~DMSNavigationBar(); 201 | 202 | int addTab(const QString& text); 203 | int addTab(const QIcon& icon, const QString& text); 204 | 205 | int insertTab(int index, const QString& text); 206 | int insertTab(int index, const QIcon& icon, const QString& text); 207 | 208 | void removeTab(int index); 209 | 210 | QString tabText(int index) const; 211 | void setTabText(int index, const QString& text); 212 | 213 | QIcon tabIcon(int index) const; 214 | void setTabIcon(int index, const QIcon& icon); 215 | 216 | DMSNavigation::HorizontalAlignment horizontalAlignment() const; 217 | void setHorizontalAlignment(DMSNavigation::HorizontalAlignment alignment); 218 | 219 | int count() const; 220 | int currentIndex() const; 221 | 222 | public slots: 223 | void setCurrentIndex(int index); 224 | 225 | protected: 226 | virtual void enterEvent(QEvent* event); 227 | virtual void leaveEvent(QEvent* event); 228 | virtual void paintEvent(QPaintEvent* event); 229 | virtual void mouseMoveEvent(QMouseEvent* event); 230 | virtual void mousePressEvent(QMouseEvent* event); 231 | 232 | private: 233 | void paintItem(const QRectF& rect, QPainter* painter, int index, int reserved); 234 | void paintContent(const QRectF& rect, QPainter* painter, int index, int reserved); 235 | void paintLeftBorder(const QRectF& rect, QPainter* painter); 236 | void paintRightBorder(const QRectF& rect, QPainter* painter); 237 | 238 | int indexByPosition(const QPoint& position); 239 | int navigationWidth(); 240 | int navigationItemWidth(int index, int reserved); 241 | 242 | protected: 243 | DMSNavigation* q_ptr; 244 | 245 | private: 246 | int m_nCurrentIndex; 247 | QList m_itemList; 248 | DMSNavigation::HorizontalAlignment m_alignment; 249 | }; 250 | 251 | //----------------------------------------------------------------------------- 252 | // DMSNavigation 253 | //----------------------------------------------------------------------------- 254 | DMSNavigation::DMSNavigation(QWidget* parent, Qt::WindowFlags flags) 255 | : QWidget(parent, flags) 256 | , navigation(new DMSNavigationBar(this, 0)) 257 | { 258 | QVBoxLayout *vl = new QVBoxLayout(); 259 | vl->setMargin(0); 260 | vl->setSpacing(0); 261 | vl->addWidget(navigation, 0, Qt::AlignTop); 262 | vl->addWidget(&stacked, 1); 263 | setLayout(vl); 264 | } 265 | 266 | DMSNavigation::~DMSNavigation() 267 | { 268 | delete navigation; 269 | } 270 | 271 | int DMSNavigation::addTab(QWidget* widget, const QString& text) 272 | { 273 | stacked.addWidget(widget); 274 | int r = navigation->addTab(text); 275 | if (count() == 1) setCurrentIndex(0); 276 | return r; 277 | } 278 | 279 | int DMSNavigation::addTab(QWidget* widget, const QIcon& icon, const QString& text) 280 | { 281 | stacked.addWidget(widget); 282 | int r = navigation->addTab(icon, text); 283 | if (count() == 1) setCurrentIndex(0); 284 | return r; 285 | } 286 | 287 | int DMSNavigation::insertTab(int index, QWidget* widget, const QString& text) 288 | { 289 | stacked.insertWidget(index, widget); 290 | return navigation->insertTab(index, text); 291 | } 292 | 293 | int DMSNavigation::insertTab(int index, QWidget* widget, const QIcon& icon, const QString& text) 294 | { 295 | stacked.insertWidget(index, widget); 296 | return navigation->insertTab(index, icon, text); 297 | } 298 | 299 | void DMSNavigation::removeTab(int index) 300 | { 301 | navigation->removeTab(index); 302 | stacked.removeWidget(stacked.widget(index)); 303 | } 304 | 305 | QString DMSNavigation::tabText(int index) const 306 | { 307 | return navigation->tabText(index); 308 | } 309 | 310 | void DMSNavigation::setTabText(int index, const QString& text) 311 | { 312 | navigation->setTabText(index, text); 313 | } 314 | 315 | QIcon DMSNavigation::tabIcon(int index) const 316 | { 317 | return navigation->tabIcon(index); 318 | } 319 | 320 | void DMSNavigation::setTabIcon(int index, const QIcon& icon) 321 | { 322 | navigation->setTabIcon(index, icon); 323 | } 324 | 325 | DMSNavigation::HorizontalAlignment DMSNavigation::horizontalAlignment() const 326 | { 327 | return navigation->horizontalAlignment(); 328 | } 329 | 330 | void DMSNavigation::setHorizontalAlignment(DMSNavigation::HorizontalAlignment alignment) 331 | { 332 | navigation->setHorizontalAlignment(alignment); 333 | } 334 | 335 | int DMSNavigation::count() const 336 | { 337 | return navigation->count(); 338 | } 339 | 340 | int DMSNavigation::currentIndex() const 341 | { 342 | return navigation->currentIndex(); 343 | } 344 | 345 | void DMSNavigation::setCurrentIndex(int index) 346 | { 347 | int current = navigation->currentIndex(); 348 | navigation->setCurrentIndex(index); 349 | if (current != navigation->currentIndex()) 350 | { 351 | stacked.setCurrentIndex(index); 352 | emit currentChanged(index); 353 | } 354 | } 355 | 356 | //----------------------------------------------------------------------------- 357 | // DMSNavigationBar 358 | //----------------------------------------------------------------------------- 359 | DMSNavigationBar::DMSNavigationBar(DMSNavigation* q, QWidget* parent, Qt::WindowFlags flags) 360 | : QWidget(parent, flags) 361 | , m_nCurrentIndex(-1) 362 | , m_itemList(QList()) 363 | , m_alignment(DMSNavigation::AlignCenter) 364 | , q_ptr(q) 365 | { 366 | setMouseTracking(true); 367 | setFixedHeight(38); 368 | } 369 | 370 | DMSNavigationBar::~DMSNavigationBar() 371 | { 372 | qDeleteAll(m_itemList); 373 | } 374 | 375 | int DMSNavigationBar::addTab(const QString& text) 376 | { 377 | return addTab(QIcon(), text); 378 | } 379 | 380 | int DMSNavigationBar::addTab(const QIcon& icon, const QString& text) 381 | { 382 | DMSItem* item = new DMSItem(); 383 | item->icon = icon.pixmap(QSize(16, 16)); 384 | item->title = text; 385 | m_itemList.append(item); 386 | update(); 387 | return count() - 1; 388 | } 389 | 390 | int DMSNavigationBar::insertTab(int index, const QString& text) 391 | { 392 | return insertTab(index, QIcon(), text); 393 | } 394 | 395 | int DMSNavigationBar::insertTab(int index, const QIcon& icon, const QString& text) 396 | { 397 | DMSItem* item = new DMSItem(); 398 | item->icon = icon.pixmap(QSize(16, 16)); 399 | item->title = text; 400 | m_itemList.insert(index, item); 401 | update(); 402 | return index; 403 | } 404 | 405 | void DMSNavigationBar::removeTab(int index) 406 | { 407 | delete m_itemList.takeAt(index); 408 | update(); 409 | } 410 | 411 | QString DMSNavigationBar::tabText(int index) const 412 | { 413 | return m_itemList[index]->title; 414 | } 415 | 416 | void DMSNavigationBar::setTabText(int index, const QString& text) 417 | { 418 | m_itemList[index]->title = text; 419 | update(); 420 | } 421 | 422 | QIcon DMSNavigationBar::tabIcon(int index) const 423 | { 424 | return m_itemList[index]->icon; 425 | } 426 | 427 | void DMSNavigationBar::setTabIcon(int index, const QIcon& icon) 428 | { 429 | m_itemList[index]->icon = icon.pixmap(QSize(16, 16)); 430 | update(); 431 | } 432 | 433 | DMSNavigation::HorizontalAlignment DMSNavigationBar::horizontalAlignment() const 434 | { 435 | return m_alignment; 436 | } 437 | 438 | void DMSNavigationBar::setHorizontalAlignment(DMSNavigation::HorizontalAlignment alignment) 439 | { 440 | m_alignment = alignment; 441 | update(); 442 | } 443 | 444 | int DMSNavigationBar::count() const 445 | { 446 | return m_itemList.count(); 447 | } 448 | 449 | int DMSNavigationBar::currentIndex() const 450 | { 451 | return m_nCurrentIndex; 452 | } 453 | 454 | void DMSNavigationBar::setCurrentIndex(int index) 455 | { 456 | if (index != -1 && index != m_nCurrentIndex) 457 | { 458 | if (m_nCurrentIndex != -1) 459 | m_itemList[m_nCurrentIndex]->isClicked = false; 460 | 461 | m_itemList[index]->isClicked = true; 462 | m_nCurrentIndex = index; 463 | update(); 464 | } 465 | } 466 | 467 | void DMSNavigationBar::enterEvent(QEvent* event) 468 | { 469 | QWidget::enterEvent(event); 470 | update(); 471 | } 472 | 473 | void DMSNavigationBar::leaveEvent(QEvent* event) 474 | { 475 | QWidget::enterEvent(event); 476 | update(); 477 | } 478 | 479 | void DMSNavigationBar::paintEvent(QPaintEvent*) 480 | { 481 | QPainter p(this); 482 | 483 | // background 484 | static QPixmap pixContent; 485 | if (pixContent.isNull()) 486 | pixContent.loadFromData(PNG::Body, PNG::BodySize); 487 | p.drawPixmap(rect(), pixContent); 488 | 489 | int navWidth = navigationWidth(); 490 | const int navHeight = rect().height(); 491 | 492 | int margin = 0; 493 | switch (m_alignment) 494 | { 495 | case DMSNavigation::AlignLeft: 496 | margin = 0; 497 | break; 498 | case DMSNavigation::AlignRight: 499 | margin = rect().width() - navWidth; 500 | default: 501 | margin = rect().width() / 2 - navWidth / 2; 502 | break; 503 | } 504 | 505 | double x = margin; 506 | 507 | // outer - inner look & feel 508 | if (count()) 509 | paintRightBorder(QRectF(x - 1, 0, 1, navHeight), &p); 510 | 511 | // content 512 | for (int i = 0; i < m_itemList.count(); i++) 513 | { 514 | int itemWidth = navigationItemWidth(i, x); 515 | paintItem(QRectF(x, 0, itemWidth, navHeight), &p, i, x); 516 | x += itemWidth; 517 | } 518 | 519 | // outer - inner look & feel 520 | if (count()) 521 | paintLeftBorder(QRectF(x, 0, 1, navHeight), &p); 522 | } 523 | 524 | void DMSNavigationBar::mouseMoveEvent(QMouseEvent* event) 525 | { 526 | QWidget::mouseMoveEvent(event); 527 | update(); 528 | } 529 | 530 | void DMSNavigationBar::mousePressEvent(QMouseEvent* event) 531 | { 532 | QWidget::mousePressEvent(event); 533 | if (event->button() != Qt::LeftButton) 534 | return; 535 | 536 | int index = indexByPosition(event->pos()); 537 | q_ptr->setCurrentIndex(index); 538 | } 539 | 540 | void DMSNavigationBar::paintItem(const QRectF& rect, QPainter* painter, int index, int reserved) 541 | { 542 | paintLeftBorder(QRectF(rect.x(), rect.top(), 1, rect.height()), painter); 543 | paintContent(QRectF(rect.x() + 1, rect.top(), rect.width() - 2, rect.height()), painter, index, reserved); 544 | paintRightBorder(QRectF(rect.x() + rect.width() - 1, rect.top(), 1, rect.height()), painter); 545 | m_itemList[index]->lastRect = rect.toRect(); 546 | } 547 | 548 | void DMSNavigationBar::paintLeftBorder(const QRectF& rect, QPainter* painter) 549 | { 550 | static QPixmap pixLeft; 551 | if (pixLeft.isNull()) 552 | pixLeft.loadFromData(PNG::BorderLeft, PNG::BorderLeftSize); 553 | painter->drawPixmap(rect.toRect(), pixLeft); 554 | } 555 | 556 | void DMSNavigationBar::paintContent(const QRectF& rect, QPainter* painter, int index, int reserved) 557 | { 558 | bool isClicked = m_itemList[index]->isClicked; 559 | 560 | if (isClicked || !rect.contains(mapFromGlobal(QCursor::pos()))) 561 | { 562 | if (isClicked) 563 | { 564 | static QPixmap pixContent; 565 | if (pixContent.isNull()) 566 | pixContent.loadFromData(PNG::BodyClicked, PNG::BodyClickedSize); 567 | painter->drawPixmap(rect.toRect(), pixContent); 568 | } 569 | else 570 | { 571 | static QPixmap pixContent; 572 | if (pixContent.isNull()) 573 | pixContent.loadFromData(PNG::Body, PNG::BodySize); 574 | painter->drawPixmap(rect.toRect(), pixContent); 575 | } 576 | } 577 | else 578 | { 579 | static QPixmap pixContent; 580 | if (pixContent.isNull()) 581 | pixContent.loadFromData(PNG::BodyHover, PNG::BodyHoverSize); 582 | painter->drawPixmap(rect.toRect(), pixContent); 583 | } 584 | 585 | 586 | QString strText = m_itemList[index]->title; 587 | 588 | QRectF textTargetRect = rect; 589 | QRectF iconTargetRect = rect; 590 | if (!m_itemList[index]->icon.isNull()) 591 | { 592 | iconTargetRect.setX(iconTargetRect.x() + DMS_ITEM_ICON_MARGIN / 2 + m_itemList[index]->icon.width() / 2); 593 | iconTargetRect.setY(iconTargetRect.y() + rect.height() / 2 - m_itemList[index]->icon.height() / 2); 594 | 595 | iconTargetRect.setWidth(m_itemList[index]->icon.width()); 596 | iconTargetRect.setHeight(m_itemList[index]->icon.height()); 597 | 598 | textTargetRect.setX(iconTargetRect.right() - DMS_ITEM_ICON_MARGIN); 599 | 600 | painter->drawPixmap(iconTargetRect.toRect(), m_itemList[index]->icon); 601 | } 602 | 603 | if (textTargetRect.width() < navigationItemWidth(index, reserved)) 604 | strText = fontMetrics().elidedText(strText, Qt::TextElideMode::ElideRight, textTargetRect.width() - CONTENT_MARGIN); 605 | 606 | painter->setPen(Qt::white); 607 | painter->drawText(textTargetRect, strText, QTextOption(Qt::AlignCenter)); 608 | } 609 | 610 | void DMSNavigationBar::paintRightBorder(const QRectF& rect, QPainter* painter) 611 | { 612 | static QPixmap pixRight; 613 | if (pixRight.isNull()) 614 | pixRight.loadFromData(PNG::BorderRight, PNG::BorderRightSize); 615 | painter->drawPixmap(rect.toRect(), pixRight); 616 | } 617 | 618 | int DMSNavigationBar::indexByPosition(const QPoint& position) 619 | { 620 | for (int i = 0; i < m_itemList.count(); i++) 621 | { 622 | if (m_itemList[i]->lastRect.contains(position)) 623 | return i; 624 | } 625 | return -1; 626 | } 627 | 628 | int DMSNavigationBar::navigationWidth() 629 | { 630 | int result = 0; 631 | for (int i = 0; i < m_itemList.count(); i++) 632 | { 633 | result += navigationItemWidth(i, result); 634 | } 635 | 636 | return result; 637 | } 638 | 639 | int DMSNavigationBar::navigationItemWidth(int index, int reserved) 640 | { 641 | DMSItem *item = m_itemList[index]; 642 | int result = item->width(fontMetrics()); 643 | 644 | int available = rect().width() - reserved; 645 | 646 | int iconWidth = 0; 647 | if (!item->icon.isNull()) 648 | iconWidth = item->icon.width() + DMS_ITEM_ICON_MARGIN; 649 | 650 | result = result + CONTENT_MARGIN_LEFT + CONTENT_MARGIN_RIGHT + iconWidth; 651 | result = qMax(DMS_ITEM_MINIMUM_SIZE, result); 652 | result = qMin(DMS_ITEM_MAXIMUM_SIZE, result); 653 | result = qMin(result, available); 654 | 655 | return result; 656 | } -------------------------------------------------------------------------------- /src/DMSNavigation.h: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2014 thelostcode 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | #ifndef DMS_NAVIGATION_H 26 | #define DMS_NAVIGATION_H 27 | 28 | #include 29 | #include 30 | 31 | class QEvent; 32 | class QPaintEvent; 33 | class QMouseEvent; 34 | 35 | class DMSNavigationBar; 36 | class DMSNavigation : public QWidget 37 | { 38 | Q_OBJECT 39 | Q_ENUMS(HorizontalAlignment) 40 | Q_PROPERTY(HorizontalAlignment horizonalAlignment READ horizontalAlignment WRITE setHorizontalAlignment) 41 | Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex) 42 | Q_PROPERTY(int cound READ count) 43 | 44 | public: 45 | explicit DMSNavigation(QWidget* parent = NULL, Qt::WindowFlags flags = 0); 46 | ~DMSNavigation(); 47 | 48 | int addTab(QWidget* widget, const QString& text); 49 | int addTab(QWidget* widget, const QIcon& icon, const QString& text); 50 | 51 | int insertTab(int index, QWidget* widget, const QString& text); 52 | int insertTab(int index, QWidget* widget, const QIcon& icon, const QString& text); 53 | 54 | void removeTab(int index); 55 | 56 | QString tabText(int index) const; 57 | void setTabText(int index, const QString& text); 58 | 59 | QIcon tabIcon(int index) const; 60 | void setTabIcon(int index, const QIcon& icon); 61 | 62 | enum HorizontalAlignment { AlignLeft, AlignCenter, AlignRight }; 63 | 64 | HorizontalAlignment horizontalAlignment() const; 65 | void setHorizontalAlignment(HorizontalAlignment alignment); 66 | 67 | int count() const; 68 | int currentIndex() const; 69 | 70 | public slots: 71 | void setCurrentIndex(int index); 72 | 73 | signals: 74 | void currentChanged(int index); 75 | 76 | private: 77 | Q_DISABLE_COPY(DMSNavigation) 78 | 79 | private: 80 | QStackedWidget stacked; 81 | DMSNavigationBar* navigation; 82 | 83 | }; 84 | 85 | #endif -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2014 thelostcode 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include "DMSNavigation.h" 31 | 32 | int main(int argc, char** argv) 33 | { 34 | QApplication app(argc, argv); 35 | 36 | DMSNavigation navi; 37 | navi.resize(600, 50); 38 | navi.show(); 39 | 40 | navi.addTab(new QTextEdit("The first."), "Text 1"); 41 | navi.addTab(new QTextEdit("The second."), "Text 2"); 42 | navi.addTab(new QTextEdit("The third."), "Text 3"); 43 | 44 | return app.exec(); 45 | } 46 | --------------------------------------------------------------------------------