├── .gitignore ├── LICENSE ├── README.md ├── aboutdialog ├── aboutdialog.pri ├── caboutdialog.cpp ├── caboutdialog.h └── caboutdialog.ui ├── dialogs ├── csimpleprogressdialog.cpp ├── csimpleprogressdialog.h ├── csimpleprogressdialog.ui └── dialogs.pri ├── historylist ├── chistorylist.h └── historylist.pri ├── imageprocessing ├── imageprocessing.pri └── resize │ ├── cimageresizer.cpp │ └── cimageresizer.h ├── logger ├── cloggerinmemory.cpp ├── cloggerinmemory.h ├── cloggerinterface.h └── logger.pri ├── mouseclickdetector ├── cmouseclickdetector.cpp ├── cmouseclickdetector.h └── mouseclickdetector.pri ├── qtcore_helpers ├── qdatetime_helpers.hpp ├── qdebug_helpers.hpp ├── qstring_helpers.hpp └── qtcore_helpers.pri ├── qtutils.pro ├── qtutils_project_suppressions.cfg ├── settings ├── csettings.cpp ├── csettings.h └── settings.pri ├── settingsui ├── csettingsdialog.cpp ├── csettingsdialog.h ├── csettingsdialog.ui ├── csettingspage.h └── settingsui.pri ├── std_helpers ├── qt_container_helpers.hpp └── std_helpers.pri ├── string ├── string.pri ├── stringutils.cpp └── stringutils.h ├── taskbarprogress ├── cprogressbartaskbar.cpp ├── cprogressbartaskbar.h ├── taskbarprogress.pri └── taskbarprogress │ ├── ctaskbarprogress.h │ ├── ctaskbarprogress_freebsd.cpp │ ├── ctaskbarprogress_linux.cpp │ ├── ctaskbarprogress_mac.cpp │ ├── ctaskbarprogress_win.cpp │ ├── freebsd │ ├── qxtglobal.h │ ├── qxttooltip.cpp │ ├── qxttooltip.h │ └── qxttooltip_p.h │ └── linux │ ├── qxtglobal.h │ ├── qxttooltip.cpp │ ├── qxttooltip.h │ └── qxttooltip_p.h ├── ui ├── ui-inspector │ ├── cuiinspector.cpp │ └── cuiinspector.h └── ui.pri ├── utils ├── naturalsorting │ ├── cnaturalsorterqcollator.cpp │ ├── cnaturalsorterqcollator.h │ ├── cnaturalsorting.cpp │ ├── cnaturalsorting.h │ ├── naturalsorting_qt.cpp │ ├── naturalsorting_qt.h │ └── sortingoptions.h ├── resources.cpp ├── resources.h └── utils.pri ├── widgets ├── cclickablelabel.cpp ├── cclickablelabel.h ├── chistorycombobox.cpp ├── chistorycombobox.h ├── circularprogressindicator │ ├── ccircularprogressindicator.cpp │ └── ccircularprogressindicator.h ├── clineedit.cpp ├── clineedit.h ├── cpersistentwindow.cpp ├── cpersistentwindow.h ├── ctexteditwithlinenumbers.cpp ├── ctexteditwithlinenumbers.h ├── layouts │ ├── coverlaylayout.cpp │ └── coverlaylayout.h ├── widgets.pri ├── widgetutils.cpp └── widgetutils.h └── windows ├── windows.pri ├── windowsutils.cpp └── windowsutils.h /.gitignore: -------------------------------------------------------------------------------- 1 | .hgignore 2 | .hg/ 3 | 4 | *.pdb 5 | *.Debug 6 | *.Release 7 | *.vc*proj 8 | *.filters 9 | *.user 10 | *.orig 11 | *.orig 12 | *.sln 13 | *.sdf 14 | *.suo 15 | *.opensdf 16 | Makefile -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | qtutils 2 | ======= 3 | 4 | A collection of various Qt-based classes and code snippets that I decided to extract in a standalone library for use across my Qt applications. 5 | 6 | Taskbar progress is probably the only module that might be of interest to the community. It allows using Windows Vista / 7 / 8 taskbar button progress indicator feature in a Qt-compatible way. There's also a class for easy reflection of an existing QProgressBar state in the taskbar. 7 | 8 | Currently there's only implementation for Windows, Mac OS implementation is planned. 9 | -------------------------------------------------------------------------------- /aboutdialog/aboutdialog.pri: -------------------------------------------------------------------------------- 1 | FORMS += \ 2 | $$PWD/caboutdialog.ui 3 | 4 | HEADERS += \ 5 | $$PWD/caboutdialog.h 6 | 7 | SOURCES += \ 8 | $$PWD/caboutdialog.cpp 9 | -------------------------------------------------------------------------------- /aboutdialog/caboutdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "caboutdialog.h" 2 | 3 | DISABLE_COMPILER_WARNINGS 4 | #include "ui_caboutdialog.h" 5 | 6 | #include 7 | #include 8 | RESTORE_COMPILER_WARNINGS 9 | 10 | CAboutDialog::CAboutDialog(QWidget *parent /*= 0*/) : CAboutDialog(QString(), parent) 11 | { 12 | } 13 | 14 | CAboutDialog::CAboutDialog(const QString& versionString, QWidget *parent, const QString& inceptionYear, const QString& copyrightOwner) : 15 | QDialog(parent), 16 | ui(new Ui::CAboutDialog) 17 | { 18 | ui->setupUi(this); 19 | 20 | ui->lblProgramName->setText(QApplication::applicationDisplayName()); 21 | setWindowTitle("About " + QApplication::applicationDisplayName()); 22 | 23 | if (!versionString.isEmpty()) 24 | ui->lblVersion->setText(tr("Version %1 (%2 %3)\nUsing Qt version %4").arg(versionString, __DATE__, __TIME__, QT_VERSION_STR)); 25 | else 26 | ui->lblVersion->setText(tr("Built on %1 at %2\nUsing Qt version %3").arg(__DATE__, __TIME__, QT_VERSION_STR)); 27 | 28 | ui->lblCopyright->setText("©" % inceptionYear % ' ' % copyrightOwner); 29 | } 30 | 31 | CAboutDialog::~CAboutDialog() 32 | { 33 | delete ui; 34 | } 35 | -------------------------------------------------------------------------------- /aboutdialog/caboutdialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "compiler/compiler_warnings_control.h" 4 | 5 | DISABLE_COMPILER_WARNINGS 6 | #include 7 | RESTORE_COMPILER_WARNINGS 8 | 9 | namespace Ui { 10 | class CAboutDialog; 11 | } 12 | 13 | class CAboutDialog final : public QDialog 14 | { 15 | public: 16 | explicit CAboutDialog(QWidget *parent); 17 | explicit CAboutDialog(const QString& versionString, QWidget *parent, const QString& inceptionYear = QLatin1String(__DATE__).right(4), const QString& copyrightOwner = "Violet Giraffe"); 18 | ~CAboutDialog() override; 19 | 20 | private: 21 | Ui::CAboutDialog *ui; 22 | }; 23 | -------------------------------------------------------------------------------- /aboutdialog/caboutdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CAboutDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 328 10 | 179 11 | 12 | 13 | 14 | About 15 | 16 | 17 | 18 | 19 | 20 | 21 | 16 22 | 50 23 | false 24 | 25 | 26 | 27 | Application Name 28 | 29 | 30 | Qt::AlignCenter 31 | 32 | 33 | 34 | 35 | 36 | 37 | Qt::Vertical 38 | 39 | 40 | QSizePolicy::Fixed 41 | 42 | 43 | 44 | 20 45 | 20 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Version 0.9.1 (22.01.2016 14:37), using Qt version 5.5 54 | 55 | 56 | Qt::AlignCenter 57 | 58 | 59 | 60 | 61 | 62 | 63 | Qt::Vertical 64 | 65 | 66 | 67 | 20 68 | 33 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 7 78 | 79 | 80 | 81 | © 2016 Violet Giraffe 82 | 83 | 84 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 85 | 86 | 87 | 88 | 89 | 90 | 91 | Qt::Horizontal 92 | 93 | 94 | QDialogButtonBox::Close 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | buttonBox 104 | accepted() 105 | CAboutDialog 106 | accept() 107 | 108 | 109 | 248 110 | 254 111 | 112 | 113 | 157 114 | 274 115 | 116 | 117 | 118 | 119 | buttonBox 120 | rejected() 121 | CAboutDialog 122 | reject() 123 | 124 | 125 | 316 126 | 260 127 | 128 | 129 | 286 130 | 274 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /dialogs/csimpleprogressdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "csimpleprogressdialog.h" 2 | 3 | DISABLE_COMPILER_WARNINGS 4 | #include "ui_csimpleprogressdialog.h" 5 | RESTORE_COMPILER_WARNINGS 6 | 7 | CSimpleProgressDialog::CSimpleProgressDialog(QWidget *parent) noexcept : 8 | QDialog(parent), 9 | ui(new Ui::CSimpleProgressDialog) 10 | { 11 | ui->setupUi(this); 12 | 13 | connect(ui->_cancelButton, &QPushButton::clicked, this, &CSimpleProgressDialog::reject); 14 | } 15 | 16 | CSimpleProgressDialog::~CSimpleProgressDialog() noexcept 17 | { 18 | delete ui; 19 | } 20 | 21 | void CSimpleProgressDialog::setLabelText(const QString &text) 22 | { 23 | ui->_label->setText(text); 24 | } 25 | 26 | void CSimpleProgressDialog::setValue(int value) 27 | { 28 | ui->_progressBar->setValue(value); 29 | showOrHideAsNecessary(); 30 | } 31 | 32 | void CSimpleProgressDialog::setMinValue(int value) 33 | { 34 | ui->_progressBar->setMinimum(value); 35 | showOrHideAsNecessary(); 36 | } 37 | 38 | void CSimpleProgressDialog::setMaxValue(int value) 39 | { 40 | ui->_progressBar->setMaximum(value); 41 | showOrHideAsNecessary(); 42 | } 43 | 44 | void CSimpleProgressDialog::setCancellable(bool visible) 45 | { 46 | ui->_cancelButton->setVisible(visible); 47 | setWindowFlag(Qt::WindowCloseButtonHint, visible); 48 | } 49 | 50 | void CSimpleProgressDialog::setCancelButtonText(const QString& text) 51 | { 52 | ui->_cancelButton->setText(text); 53 | } 54 | 55 | void CSimpleProgressDialog::setAutoShow(bool autoShow) 56 | { 57 | _autoShow = autoShow; 58 | showOrHideAsNecessary(); 59 | } 60 | 61 | void CSimpleProgressDialog::setAutoClose(bool autoClose) 62 | { 63 | _autoClose = autoClose; 64 | showOrHideAsNecessary(); 65 | } 66 | 67 | void CSimpleProgressDialog::showOrHideAsNecessary() 68 | { 69 | if (_autoClose && ui->_progressBar->value() >= ui->_progressBar->maximum()) 70 | close(); 71 | else if (_autoShow && ui->_progressBar->value() > ui->_progressBar->minimum()) 72 | show(); 73 | } 74 | -------------------------------------------------------------------------------- /dialogs/csimpleprogressdialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "compiler/compiler_warnings_control.h" 3 | 4 | DISABLE_COMPILER_WARNINGS 5 | #include 6 | RESTORE_COMPILER_WARNINGS 7 | 8 | namespace Ui { 9 | class CSimpleProgressDialog; 10 | } 11 | 12 | class CSimpleProgressDialog : public QDialog 13 | { 14 | public: 15 | explicit CSimpleProgressDialog(QWidget *parent = nullptr) noexcept; 16 | ~CSimpleProgressDialog() noexcept override; 17 | 18 | void setLabelText(const QString& text); 19 | 20 | void setValue(int value); 21 | void setMinValue(int value); 22 | void setMaxValue(int value); 23 | 24 | void setCancellable(bool visible); 25 | void setCancelButtonText(const QString& text); 26 | 27 | void setAutoShow(bool autoShow); 28 | void setAutoClose(bool autoClose); 29 | 30 | private: 31 | void showOrHideAsNecessary(); 32 | 33 | private: 34 | Ui::CSimpleProgressDialog *ui; 35 | bool _autoShow = false; 36 | bool _autoClose = false; 37 | }; 38 | -------------------------------------------------------------------------------- /dialogs/csimpleprogressdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CSimpleProgressDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 307 10 | 92 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | Making everything better, please wait... 21 | 22 | 23 | 24 | 25 | 26 | 27 | 0 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Qt::Horizontal 37 | 38 | 39 | 40 | 40 41 | 20 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | &Cancel 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Qt::Vertical 59 | 60 | 61 | 62 | 20 63 | 0 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /dialogs/dialogs.pri: -------------------------------------------------------------------------------- 1 | FORMS += \ 2 | $$PWD/csimpleprogressdialog.ui 3 | 4 | HEADERS += \ 5 | $$PWD/csimpleprogressdialog.h 6 | 7 | SOURCES += \ 8 | $$PWD/csimpleprogressdialog.cpp 9 | -------------------------------------------------------------------------------- /historylist/chistorylist.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "assert/advanced_assert.h" 3 | #include "container/set_operations.hpp" 4 | #include "lang/type_traits_fast.hpp" 5 | 6 | #include 7 | #include 8 | 9 | template 10 | class CHistoryList 11 | { 12 | public: 13 | using container_type = std::deque; 14 | explicit CHistoryList(size_t maxSize = 2000) noexcept : 15 | _currentIndex{ size_t_max }, 16 | _maxSize{ maxSize } 17 | {} 18 | 19 | // Access and status 20 | [[nodiscard]] inline bool empty() const { return _list.empty(); } 21 | [[nodiscard]] inline size_t size() const { return _list.size(); } 22 | inline void clear() { _list.clear(); } 23 | 24 | [[nodiscard]] inline typename container_type::const_iterator begin() const { return _list.begin(); } 25 | [[nodiscard]] inline typename container_type::const_iterator end() const { return _list.end(); } 26 | [[nodiscard]] inline typename container_type::const_reverse_iterator rbegin() const { return _list.rbegin(); } 27 | [[nodiscard]] inline typename container_type::const_reverse_iterator rend() const { return _list.rend(); } 28 | 29 | [[nodiscard]] inline const T& front() const { return _list.front(); } 30 | [[nodiscard]] inline const T& back() const { return _list.back(); } 31 | 32 | [[nodiscard]] inline const T& operator[](size_t index) const { return _list[index]; } 33 | 34 | [[nodiscard]] inline bool historyLocationSet() const { return _currentIndex <= _list.size(); } 35 | 36 | [[nodiscard]] inline const container_type& list() const { return _list; } 37 | 38 | [[nodiscard]] size_t currentIndex() const; 39 | [[nodiscard]] const T& currentItem() const; 40 | 41 | [[nodiscard]] bool isAtEnd() const; 42 | [[nodiscard]] bool isAtBeginning() const; 43 | 44 | // Actions 45 | void addLatest(const T& item); 46 | void addLatest(const std::vector& items); 47 | const T& navigateBack(); 48 | const T& navigateForward(); 49 | 50 | private: 51 | container_type _list; 52 | size_t _currentIndex; 53 | const size_t _maxSize; 54 | }; 55 | 56 | template 57 | const T& CHistoryList::navigateForward() 58 | { 59 | if (_currentIndex < size() - 1) 60 | _currentIndex = currentIndex() + 1; 61 | return currentItem(); 62 | } 63 | 64 | template 65 | const T& CHistoryList::navigateBack() 66 | { 67 | if (_currentIndex > 0) 68 | _currentIndex = currentIndex() - 1; 69 | return currentItem(); 70 | } 71 | 72 | template 73 | const T& CHistoryList::currentItem() const 74 | { 75 | if (!empty()) 76 | return _list[currentIndex()]; 77 | else 78 | { 79 | static const T blank; 80 | return blank; 81 | } 82 | } 83 | 84 | template 85 | bool CHistoryList::isAtEnd() const 86 | { 87 | assert_r(empty() || _currentIndex < size()); 88 | return !empty() && _currentIndex == size() - 1; 89 | } 90 | 91 | template 92 | bool CHistoryList::isAtBeginning() const 93 | { 94 | assert_r(empty() || _currentIndex < size()); 95 | return !empty() && _currentIndex == 0; 96 | } 97 | 98 | template 99 | size_t CHistoryList::currentIndex() const 100 | { 101 | if (empty()) 102 | return size_t_max; 103 | else if (_currentIndex < size()) 104 | return _currentIndex; 105 | else 106 | return size() - 1; 107 | } 108 | 109 | template 110 | void CHistoryList::addLatest(const T& item) 111 | { 112 | if (currentItem() == item) 113 | return; 114 | 115 | const auto currentPosition = std::find(_list.begin(), _list.end(), item); 116 | if (currentPosition == _list.end()) 117 | _list.push_back(item); 118 | else if (size() > 1 && currentPosition != _list.end() - 1) 119 | std::rotate(currentPosition, currentPosition + 1, _list.end()); 120 | 121 | assert_r(!empty()); 122 | if (size() > 1 && _currentIndex <= size() - 2) // If we already were at the end of the list, just move the index to the newest element 123 | { 124 | // If we were not in the end, move and insert [0; _currentIndex] just before end 125 | decltype(_list) newList; 126 | for (size_t i = _currentIndex + 1; i < size() - 1; ++i) 127 | newList.push_back(std::move(_list[i])); 128 | for (size_t i = 0; i <= _currentIndex; ++i) 129 | newList.push_back(std::move(_list[i])); 130 | _list = newList; 131 | _list.emplace_back(item); 132 | } 133 | 134 | if (_list.size() > _maxSize) 135 | _list.pop_front(); 136 | 137 | _currentIndex = _list.size() - 1; 138 | } 139 | 140 | template 141 | void CHistoryList::addLatest(const std::vector& items) 142 | { 143 | _list = container_type(items.begin(), items.end()); 144 | _list = SetOperations::uniqueElements(_list); 145 | // Trim to size - erase old (first) items 146 | if (const auto size = _list.size(); size > _maxSize) 147 | _list.erase(_list.begin(), _list.begin() + size - _maxSize); 148 | 149 | _currentIndex = _list.size() - 1; 150 | } 151 | -------------------------------------------------------------------------------- /historylist/historylist.pri: -------------------------------------------------------------------------------- 1 | HEADERS += \ 2 | historylist/chistorylist.h 3 | -------------------------------------------------------------------------------- /imageprocessing/imageprocessing.pri: -------------------------------------------------------------------------------- 1 | HEADERS += \ 2 | imageprocessing/resize/cimageresizer.h 3 | 4 | SOURCES += \ 5 | imageprocessing/resize/cimageresizer.cpp 6 | -------------------------------------------------------------------------------- /imageprocessing/resize/cimageresizer.cpp: -------------------------------------------------------------------------------- 1 | #if __has_include("resize/cimageinterpolationkernel.h") 2 | 3 | #include "cimageresizer.h" 4 | #include "resize/cimageinterpolationkernel.h" 5 | 6 | #include "assert/advanced_assert.h" 7 | #include "math/math.hpp" 8 | #include "compiler/compiler_warnings_control.h" 9 | 10 | DISABLE_COMPILER_WARNINGS 11 | #include 12 | #include 13 | #include 14 | RESTORE_COMPILER_WARNINGS 15 | 16 | #include 17 | #include 18 | 19 | QSize scaled(const QSize& source, const QSize& dest) 20 | { 21 | const float xScaleFactor = (float)source.width() / (float)dest.width(); 22 | const float yScaleFactor = (float)source.height() / (float)dest.height(); 23 | 24 | const float actualFactor = (xScaleFactor > 1.0f && yScaleFactor > 1.0f) ? std::max(xScaleFactor, yScaleFactor) : std::min(xScaleFactor, yScaleFactor); 25 | 26 | return source / actualFactor; 27 | } 28 | 29 | inline QRgb applyKernel(const CImageInterpolationKernelBase& kernel, const QImage& source, uint32_t x, uint32_t y) 30 | { 31 | assert_r(x >= 0 && y >= 0); // Tested - doesn't affect performance within 1 ms resolution 32 | 33 | float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f; 34 | 35 | const uint32_t srcHeight = (uint32_t)source.height(), srcWidth = (uint32_t)source.width(); 36 | const uint32_t kernelSize = kernel.size(); 37 | 38 | using FloatType = decltype(kernel.coeff(0, 0)); 39 | 40 | if (source.depth() == 32) 41 | { 42 | for (uint32_t k = y, k_kernel = 0; k < y + kernelSize && k < srcHeight; ++k, ++ k_kernel) 43 | { 44 | // TODO: strict aliasong violation! 45 | const QRgb * line = reinterpret_cast(source.constScanLine((int)k)); 46 | for (uint32_t i = x, i_kernel = 0; i < x + kernelSize && i < srcWidth; ++i, ++i_kernel) 47 | { 48 | const auto coeff = kernel.coeff(i_kernel, k_kernel); 49 | r += static_cast(qRed(line[i])) * coeff; 50 | g += static_cast(qGreen(line[i])) * coeff; 51 | b += static_cast(qBlue(line[i])) * coeff; 52 | a += static_cast(qAlpha(line[i])) * coeff; 53 | } 54 | } 55 | } 56 | else 57 | { 58 | for (uint32_t i = x, i_kernel = 0; i < x + kernelSize && i < srcWidth; ++i, ++i_kernel) 59 | for (uint32_t k = y, k_kernel = 0; k < y + kernelSize && k < srcHeight; ++k, ++ k_kernel) 60 | { 61 | const QColor pixel(source.pixel(i, k)); 62 | const auto coeff = kernel.coeff(i_kernel, k_kernel); 63 | 64 | r += static_cast(pixel.red()) * coeff; 65 | g += static_cast(pixel.green()) * coeff; 66 | b += static_cast(pixel.blue()) * coeff; 67 | a += static_cast(pixel.alpha()) * coeff; 68 | } 69 | } 70 | 71 | r = Math::clamp(FloatType{0.0f}, r, FloatType{255.0f}); 72 | g = Math::clamp(FloatType{0.0f}, g, FloatType{255.0f}); 73 | b = Math::clamp(FloatType{0.0f}, b, FloatType{255.0f}); 74 | a = Math::clamp(FloatType{0.0f}, a, FloatType{255.0f}); 75 | 76 | return qRgba(Math::round(r), 77 | Math::round(g), 78 | Math::round(b), 79 | Math::round(a) 80 | ); 81 | } 82 | 83 | inline float ratio(const QSize& size) 84 | { 85 | return (float)size.width() / (float)size.height(); 86 | } 87 | 88 | inline QSize operator*(const QSize& origSize, int k) 89 | { 90 | return QSize(origSize.width() * k, origSize.height() * k); 91 | } 92 | 93 | QImage bicubicInterpolation(const QImage& source, const QSize& targetSize, ImageResizing::AspectRatio aspectRatio) 94 | { 95 | using namespace ImageResizing; 96 | 97 | if (targetSize.width() == source.width() && targetSize.height() == source.height()) 98 | return source; 99 | else if (targetSize.width() >= source.width() || targetSize.height() >= source.height()) 100 | return source.scaled(targetSize, aspectRatio == KeepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 101 | 102 | QSize actualTargetSize = targetSize; 103 | if (::fabs(ratio(targetSize) - ratio(source.size())) < 0.01f) 104 | actualTargetSize = source.size().scaled(targetSize, aspectRatio == KeepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio); 105 | 106 | if (actualTargetSize.isEmpty()) 107 | return QImage(); 108 | 109 | const float scaleFactor = source.width() > actualTargetSize.width() ? (float)source.width() / (float)actualTargetSize.width() : (float)actualTargetSize.width() / (float)source.width(); 110 | 111 | const int intScaleFactor = (int)ceilf(scaleFactor); 112 | const QSize upscaledSourceSize = ::fabs(scaleFactor - 1.0f) < 0.001f ? scaled(source.size(), actualTargetSize * intScaleFactor) : source.size(); 113 | 114 | QImage dest(actualTargetSize, source.format()); 115 | QImage upscaledSource(upscaledSourceSize == source.size() ? source : source.scaled(upscaledSourceSize, Qt::IgnoreAspectRatio, source.depth() == 32 ? Qt::SmoothTransformation : Qt::FastTransformation)); 116 | 117 | // TODO: refactor this. There's no need to create all the kernels, we're only going to need one. 118 | const CLanczosKernel lanczosKernel((uint32_t)upscaledSource.width() / (uint32_t)actualTargetSize.width(), 3); 119 | const CBicubicKernel bicubicKernel((uint32_t)upscaledSource.width() / (uint32_t)actualTargetSize.width(), 0.5f); 120 | 121 | const CImageInterpolationKernelBase& kernel = upscaledSourceSize.width() / actualTargetSize.width() >= 30 ? 122 | (CImageInterpolationKernelBase&)bicubicKernel : 123 | (CImageInterpolationKernelBase&)bicubicKernel; 124 | 125 | const uint32_t kernelSize = kernel.size(); 126 | const uint32_t targetWidth = (uint32_t)actualTargetSize.width(), targetHeight = (uint32_t)actualTargetSize.height(); 127 | if (upscaledSource.depth() == 32) 128 | { 129 | for (uint32_t y = 0; y < targetHeight; ++y) 130 | { 131 | auto* scanLine = dest.scanLine(y); 132 | for (uint32_t x = 0; x < targetWidth; ++x) 133 | { 134 | const auto rgb = applyKernel(kernel, upscaledSource, x*kernelSize, y*kernelSize); 135 | auto* pixelAddress = scanLine + sizeof(QRgb) * x; 136 | memcpy(pixelAddress, &rgb, sizeof(rgb)); 137 | } 138 | } 139 | } 140 | else 141 | { 142 | for (uint32_t x = 0; x < targetWidth; ++x) 143 | { 144 | for (uint32_t y = 0; y < targetHeight; ++y) 145 | { 146 | dest.setPixel(x, y, applyKernel(kernel, upscaledSource, x*kernelSize, y*kernelSize)); 147 | } 148 | } 149 | } 150 | return dest; 151 | } 152 | 153 | QImage ImageResizing::resize(const QImage& source, const QSize& targetSize, ResizeMethod method, AspectRatio aspectRatio) 154 | { 155 | // CTimeElapsed timer(true); 156 | // EXEC_ON_SCOPE_EXIT([&]{ 157 | // qInfo() << "Image resized from" << source.size() << "to" << targetSize << "in" << timer.elapsed() << "ms"; 158 | // }); 159 | 160 | switch(method) 161 | { 162 | case DefaultQimageFast: 163 | return source.scaled(targetSize, aspectRatio == KeepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio, Qt::FastTransformation); 164 | case DefaultQimageSmooth: 165 | return source.scaled(targetSize, aspectRatio == KeepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 166 | case Smart: 167 | { 168 | auto actualTargetSize = source.size().scaled(targetSize, aspectRatio == KeepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio); 169 | if (source.size().width() < actualTargetSize.width()) 170 | return source.scaled(targetSize, aspectRatio == KeepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio, Qt::FastTransformation); 171 | else 172 | return source.scaled(targetSize, aspectRatio == KeepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 173 | } 174 | default: 175 | return {}; 176 | } 177 | } 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /imageprocessing/resize/cimageresizer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QImage; 4 | class QSize; 5 | 6 | namespace ImageResizing 7 | { 8 | 9 | enum ResizeMethod { 10 | DefaultQimageFast, 11 | DefaultQimageSmooth, 12 | Smart 13 | }; 14 | 15 | enum AspectRatio { 16 | KeepAspectRatio, 17 | IgnoreAspectRatio 18 | }; 19 | 20 | QImage resize(const QImage& source, const QSize& targetSize, ResizeMethod method, AspectRatio aspectRatio = KeepAspectRatio); 21 | 22 | } // namespace ImageResizing 23 | -------------------------------------------------------------------------------- /logger/cloggerinmemory.cpp: -------------------------------------------------------------------------------- 1 | #include "cloggerinmemory.h" 2 | 3 | void CLoggerInMemory::log(const QString& msg) 4 | { 5 | _entries.push_back(msg); 6 | } 7 | 8 | QStringList CLoggerInMemory::contents() const 9 | { 10 | return _entries; 11 | } 12 | -------------------------------------------------------------------------------- /logger/cloggerinmemory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cloggerinterface.h" 4 | 5 | class CLoggerInMemory : public CLoggerInterface 6 | { 7 | public: 8 | void log(const QString& msg) override; 9 | [[nodiscard]] QStringList contents() const override; 10 | 11 | private: 12 | QStringList _entries; 13 | }; 14 | -------------------------------------------------------------------------------- /logger/cloggerinterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "compiler/compiler_warnings_control.h" 4 | 5 | DISABLE_COMPILER_WARNINGS 6 | #include 7 | RESTORE_COMPILER_WARNINGS 8 | 9 | class CLoggerInterface 10 | { 11 | public: 12 | virtual ~CLoggerInterface() = default; 13 | 14 | virtual void log(const QString& message) = 0; 15 | [[nodiscard]] virtual QStringList contents() const = 0; 16 | }; 17 | 18 | template 19 | CLoggerInterface& loggerInstance() 20 | { 21 | static LoggerType instance; 22 | return instance; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /logger/logger.pri: -------------------------------------------------------------------------------- 1 | HEADERS += \ 2 | $$PWD/cloggerinterface.h \ 3 | $$PWD/cloggerinmemory.h 4 | 5 | SOURCES += \ 6 | $$PWD/cloggerinmemory.cpp 7 | -------------------------------------------------------------------------------- /mouseclickdetector/cmouseclickdetector.cpp: -------------------------------------------------------------------------------- 1 | #include "cmouseclickdetector.h" 2 | #include "assert/advanced_assert.h" 3 | 4 | DISABLE_COMPILER_WARNINGS 5 | #include 6 | #include 7 | #include 8 | RESTORE_COMPILER_WARNINGS 9 | 10 | CMouseClickDetector *CMouseClickDetector::globalInstance() 11 | { 12 | static CMouseClickDetector inst; 13 | return &inst; 14 | } 15 | 16 | void CMouseClickDetector::notify(QObject * object, QEvent * event) 17 | { 18 | eventFilter(object, event); 19 | } 20 | 21 | bool CMouseClickDetector::eventFilter(QObject * object, QEvent * event) 22 | { 23 | if (event->type() == QEvent::MouseButtonRelease) 24 | { 25 | QMouseEvent * mouseEvent = dynamic_cast(event); 26 | assert_and_return_r(mouseEvent, false); 27 | if (mouseEvent->buttons() == Qt::LeftButton) 28 | { 29 | const QPoint pos = mouseEvent->pos(); 30 | 31 | if (!_lastClickTimestampForObject.contains(object)) 32 | _lastClickTimestampForObject[object] = 0; 33 | 34 | if (mouseEvent->timestamp() - _lastClickTimestampForObject[object] <= (unsigned int)QApplication::doubleClickInterval()) 35 | { 36 | _lastClickTimestampForObject[object] = 0; 37 | emit doubleLeftClickDetected(object, pos); 38 | } 39 | else 40 | { 41 | _lastClickTimestampForObject[object] = (qint64)mouseEvent->timestamp(); 42 | QTimer::singleShot(QApplication::doubleClickInterval() + 1, this, [this, object, pos](){ 43 | if (_lastClickTimestampForObject[object] != 0) 44 | { 45 | _lastClickTimestampForObject[object] = 0; 46 | emit singleLeftClickDetected(object, pos); 47 | } 48 | }); 49 | } 50 | } 51 | } 52 | 53 | return QObject::eventFilter(object, event); 54 | } 55 | -------------------------------------------------------------------------------- /mouseclickdetector/cmouseclickdetector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "compiler/compiler_warnings_control.h" 4 | 5 | DISABLE_COMPILER_WARNINGS 6 | #include 7 | #include 8 | RESTORE_COMPILER_WARNINGS 9 | 10 | #include 11 | 12 | class CMouseClickDetector final : public QObject 13 | { 14 | Q_OBJECT 15 | public: 16 | using QObject::QObject; 17 | 18 | [[nodiscard]] static CMouseClickDetector * globalInstance(); 19 | 20 | void notify(QObject* object, QEvent* event); 21 | 22 | signals: 23 | void singleLeftClickDetected(QObject* object, QPoint pos); 24 | void doubleLeftClickDetected(QObject* object, QPoint pos); 25 | 26 | protected: 27 | bool eventFilter(QObject* object, QEvent* event) override; 28 | 29 | private: 30 | std::unordered_map _lastClickTimestampForObject; 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /mouseclickdetector/mouseclickdetector.pri: -------------------------------------------------------------------------------- 1 | HEADERS += \ 2 | mouseclickdetector/cmouseclickdetector.h 3 | 4 | SOURCES += \ 5 | mouseclickdetector/cmouseclickdetector.cpp 6 | -------------------------------------------------------------------------------- /qtcore_helpers/qdatetime_helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "compiler/compiler_warnings_control.h" 3 | 4 | DISABLE_COMPILER_WARNINGS 5 | #include 6 | RESTORE_COMPILER_WARNINGS 7 | 8 | [[nodiscard]] inline time_t toTime_t(const QDateTime& date) noexcept 9 | { 10 | return static_cast(date.toMSecsSinceEpoch() / 1000LL); 11 | } 12 | 13 | [[nodiscard]]inline QDateTime fromTime_t(const time_t time) noexcept 14 | { 15 | QDateTime date; 16 | date.setMSecsSinceEpoch(static_cast(time) * 1000LL); 17 | return date; 18 | } 19 | -------------------------------------------------------------------------------- /qtcore_helpers/qdebug_helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "compiler/compiler_warnings_control.h" 3 | 4 | DISABLE_COMPILER_WARNINGS 5 | #include 6 | #include 7 | RESTORE_COMPILER_WARNINGS 8 | 9 | inline QDebug& operator<<(QDebug& debug, const std::string& std_str) 10 | { 11 | return debug << QString::fromStdString(std_str); 12 | } 13 | 14 | inline QDebug operator<<(QDebug&& debug, const std::string& std_str) 15 | { 16 | return debug << QString::fromStdString(std_str); 17 | } 18 | 19 | template 20 | QDebug operator<<(QDebug&& debug, const std::array& ar) 21 | { 22 | return debug << QByteArray::fromRawData(ar.data(), ar.size()).toHex(); 23 | } 24 | 25 | template 26 | QDebug& operator<<(QDebug& debug, const std::array& ar) 27 | { 28 | return debug << QByteArray::fromRawData((const char*)ar.data(), (int)ar.size() * sizeof(T)).toHex(); 29 | } -------------------------------------------------------------------------------- /qtcore_helpers/qstring_helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define QSL QStringLiteral 6 | #define QL1 QLatin1String 7 | -------------------------------------------------------------------------------- /qtcore_helpers/qtcore_helpers.pri: -------------------------------------------------------------------------------- 1 | HEADERS += \ 2 | $$PWD/qdatetime_helpers.hpp \ 3 | $$PWD/qdebug_helpers.hpp \ 4 | $$PWD/qstring_helpers.hpp 5 | -------------------------------------------------------------------------------- /qtutils.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | TARGET = qtutils 3 | CONFIG += staticlib 4 | 5 | CONFIG += strict_c++ c++latest 6 | 7 | mac* | linux* | freebsd { 8 | CONFIG(release, debug|release):CONFIG *= Release optimize_full 9 | CONFIG(debug, debug|release):CONFIG *= Debug 10 | } 11 | 12 | Release:OUTPUT_DIR=release/$${ARCHITECTURE} 13 | Debug:OUTPUT_DIR=debug/$${ARCHITECTURE} 14 | 15 | DESTDIR = ../bin/$${OUTPUT_DIR} 16 | OBJECTS_DIR = ../build/$${OUTPUT_DIR}/$${TARGET} 17 | MOC_DIR = ../build/$${OUTPUT_DIR}/$${TARGET} 18 | UI_DIR = ../build/$${OUTPUT_DIR}/$${TARGET} 19 | RCC_DIR = ../build/$${OUTPUT_DIR}/$${TARGET} 20 | 21 | QT = core gui widgets 22 | 23 | # Required for qInfo() to log function name, file and line in release build 24 | DEFINES += QT_MESSAGELOGCONTEXT 25 | 26 | INCLUDEPATH += \ 27 | ../image-processing \ 28 | ../cpputils \ 29 | ../cpp-template-utils \ 30 | 31 | win*{ 32 | QMAKE_CXXFLAGS += /MP /Zi /wd4251 /JMC 33 | QMAKE_CXXFLAGS += /std:c++latest /permissive- /Zc:__cplusplus 34 | DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX 35 | QMAKE_CXXFLAGS_WARN_ON = /W4 36 | 37 | !*msvc2013*:QMAKE_LFLAGS += /DEBUG:FASTLINK 38 | 39 | Debug:QMAKE_LFLAGS += /INCREMENTAL 40 | Release:QMAKE_LFLAGS += /OPT:REF /OPT:ICF 41 | *msvc*{ 42 | QMAKE_CXXFLAGS += /FS 43 | } 44 | } 45 | 46 | linux*|mac*|freebsd{ 47 | QMAKE_CXXFLAGS += -pedantic-errors 48 | QMAKE_CFLAGS += -pedantic-errors 49 | QMAKE_CXXFLAGS_WARN_ON = -Wall 50 | 51 | Release:DEFINES += NDEBUG=1 52 | Debug:DEFINES += _DEBUG 53 | 54 | PRE_TARGETDEPS += $${DESTDIR}/libcpputils.a 55 | } 56 | 57 | include(imageprocessing/imageprocessing.pri) 58 | include(logger/logger.pri) 59 | include(aboutdialog/aboutdialog.pri) 60 | include(settings/settings.pri) 61 | include(taskbarprogress/taskbarprogress.pri) 62 | include(utils/utils.pri) 63 | include(settingsui/settingsui.pri) 64 | include(string/string.pri) 65 | include(mouseclickdetector/mouseclickdetector.pri) 66 | include(historylist/historylist.pri) 67 | include(widgets/widgets.pri) 68 | include(dialogs/dialogs.pri) 69 | include(ui/ui.pri) 70 | include(std_helpers/std_helpers.pri) 71 | include(qtcore_helpers/qtcore_helpers.pri) 72 | win*:include(windows/windows.pri) 73 | -------------------------------------------------------------------------------- /qtutils_project_suppressions.cfg: -------------------------------------------------------------------------------- 1 | [cppcheck] 2 | unusedPrivateFunction:*ctaskbarprogress.h:30 3 | [cppcheck_files] 4 | [cppcheck_includes] 5 | -------------------------------------------------------------------------------- /settings/csettings.cpp: -------------------------------------------------------------------------------- 1 | #include "csettings.h" 2 | 3 | DISABLE_COMPILER_WARNINGS 4 | #include 5 | RESTORE_COMPILER_WARNINGS 6 | 7 | QString CSettings::_applicationName; 8 | QString CSettings::_organizationName; 9 | QSettings::Format CSettings::_settingsFormat = QSettings::NativeFormat; 10 | 11 | CSettings::CSettings() noexcept : 12 | _impl{ 13 | _settingsFormat, QSettings::UserScope, 14 | _organizationName.isEmpty() ? qApp->organizationName() : _organizationName, 15 | _applicationName.isEmpty() ? qApp->applicationName() : _applicationName 16 | } 17 | { 18 | } 19 | 20 | void CSettings::setApplicationName(const QString &name) 21 | { 22 | _applicationName = name; 23 | } 24 | 25 | void CSettings::setOrganizationName(const QString &name) 26 | { 27 | _organizationName = name; 28 | } 29 | 30 | void CSettings::setFormat(QSettings::Format format) 31 | { 32 | _settingsFormat = format; 33 | } 34 | 35 | void CSettings::setValue(const QString & key, const QVariant & value) 36 | { 37 | _impl.setValue(key, value); 38 | } 39 | 40 | QVariant CSettings::value(const QString & key, const QVariant & defaultValue) const 41 | { 42 | return _impl.value(key, defaultValue); 43 | } 44 | 45 | QStringList CSettings::allKeys() const 46 | { 47 | return _impl.allKeys(); 48 | } 49 | 50 | void CSettings::clear() 51 | { 52 | _impl.clear(); 53 | } 54 | -------------------------------------------------------------------------------- /settings/csettings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "compiler/compiler_warnings_control.h" 4 | 5 | DISABLE_COMPILER_WARNINGS 6 | #include 7 | RESTORE_COMPILER_WARNINGS 8 | 9 | class CSettings 10 | { 11 | public: 12 | CSettings() noexcept; 13 | 14 | static void setApplicationName(const QString& name); 15 | static void setOrganizationName(const QString& name); 16 | static void setFormat(QSettings::Format format); 17 | 18 | void setValue(const QString& key, const QVariant& value); 19 | [[nodiscard]] QVariant value(const QString& key, const QVariant& defaultValue = QVariant()) const; 20 | 21 | [[nodiscard]] QStringList allKeys() const; 22 | 23 | void clear(); 24 | 25 | private: 26 | QSettings _impl; 27 | 28 | static QString _applicationName; 29 | static QString _organizationName; 30 | static QSettings::Format _settingsFormat; 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /settings/settings.pri: -------------------------------------------------------------------------------- 1 | HEADERS += \ 2 | settings/csettings.h 3 | 4 | SOURCES += \ 5 | settings/csettings.cpp 6 | -------------------------------------------------------------------------------- /settingsui/csettingsdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "csettingsdialog.h" 2 | #include "csettingspage.h" 3 | #include "ui_csettingsdialog.h" 4 | #include "../settings/csettings.h" 5 | #include "assert/advanced_assert.h" 6 | 7 | DISABLE_COMPILER_WARNINGS 8 | #include 9 | #include 10 | RESTORE_COMPILER_WARNINGS 11 | 12 | CSettingsDialog::CSettingsDialog(QWidget *parent) noexcept : 13 | QDialog(parent), 14 | ui(new Ui::CSettingsDialog) 15 | { 16 | ui->setupUi(this); 17 | 18 | // The list doesn't expand, the settings pane does 19 | ui->splitter->setStretchFactor(0, 0); 20 | ui->splitter->setStretchFactor(1, 1); 21 | 22 | connect(ui->pageList, &QListWidget::currentItemChanged, this, &CSettingsDialog::pageChanged); 23 | ui->pageList->setResizeMode(QListWidget::Adjust); 24 | 25 | new QShortcut(QKeySequence("Ctrl+Shift+W"), this, this, &CSettingsDialog::wipeSettings); 26 | 27 | ui->pageList->setFocus(); 28 | setTabOrder(ui->pageList, nullptr); 29 | } 30 | 31 | CSettingsDialog::~CSettingsDialog() 32 | { 33 | delete ui; 34 | } 35 | 36 | CSettingsDialog& CSettingsDialog::addSettingsPage(CSettingsPage* page, const QString &pageName) 37 | { 38 | page->setParent(this); 39 | ui->pages->addWidget(page); 40 | 41 | QListWidgetItem * item = new QListWidgetItem(pageName.isEmpty() ? page->windowTitle() : pageName); 42 | item->setData(Qt::UserRole, ui->pages->count()-1); 43 | ui->pageList->addItem(item); 44 | 45 | if (ui->pages->count() == 1) 46 | { 47 | ui->pageList->setCurrentRow(0); 48 | setTabOrder(ui->pageList, page); 49 | } 50 | else 51 | setTabOrder(ui->pages->widget(ui->pages->count() - 1), page); 52 | 53 | ui->pageList->adjustSize(); 54 | 55 | return *this; 56 | } 57 | 58 | void CSettingsDialog::pageChanged(QListWidgetItem * item) 59 | { 60 | const int pageIndex = item->data(Qt::UserRole).toInt(); 61 | ui->pages->setCurrentIndex(pageIndex); 62 | } 63 | 64 | void CSettingsDialog::wipeSettings() 65 | { 66 | if (QMessageBox::question(this, tr("Wipe settings"), tr("Wipe all settings?")) == QMessageBox::Yes) 67 | { 68 | CSettings{}.clear(); 69 | exit(0); // Exiting immediately so that the current application state cannot be re-saved upon next normal exit 70 | } 71 | } 72 | 73 | void CSettingsDialog::accept() 74 | { 75 | for (int i = 0; i < ui->pages->count(); ++i) 76 | { 77 | CSettingsPage * page = dynamic_cast(ui->pages->widget(i)); 78 | assert_r(page); 79 | page->acceptSettings(); 80 | } 81 | 82 | emit settingsChanged(); 83 | 84 | QDialog::accept(); 85 | } 86 | -------------------------------------------------------------------------------- /settingsui/csettingsdialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "compiler/compiler_warnings_control.h" 4 | 5 | DISABLE_COMPILER_WARNINGS 6 | #include 7 | RESTORE_COMPILER_WARNINGS 8 | 9 | namespace Ui { 10 | class CSettingsDialog; 11 | } 12 | 13 | class CSettingsPage; 14 | 15 | class QListWidgetItem; 16 | 17 | class CSettingsDialog final : public QDialog 18 | { 19 | Q_OBJECT 20 | public: 21 | explicit CSettingsDialog(QWidget *parent = nullptr) noexcept; 22 | ~CSettingsDialog() override; 23 | 24 | CSettingsDialog& addSettingsPage(CSettingsPage * page, const QString& pageName = QString()); 25 | 26 | signals: 27 | void settingsChanged(); 28 | 29 | private: 30 | void pageChanged(QListWidgetItem *item); 31 | void wipeSettings(); 32 | 33 | private slots: 34 | void accept() override; 35 | 36 | private: 37 | Ui::CSettingsDialog *ui; 38 | }; 39 | -------------------------------------------------------------------------------- /settingsui/csettingsdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CSettingsDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 622 10 | 437 11 | 12 | 13 | 14 | Settings 15 | 16 | 17 | 18 | 19 | 20 | Qt::Horizontal 21 | 22 | 23 | false 24 | 25 | 26 | false 27 | 28 | 29 | 30 | 31 | 32 | 50 33 | 0 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Qt::TabFocus 43 | 44 | 45 | Qt::Horizontal 46 | 47 | 48 | QDialogButtonBox::Cancel|QDialogButtonBox::Save 49 | 50 | 51 | 52 | 53 | 54 | 55 | pageList 56 | 57 | 58 | 59 | 60 | buttonBox 61 | accepted() 62 | CSettingsDialog 63 | accept() 64 | 65 | 66 | 248 67 | 254 68 | 69 | 70 | 157 71 | 274 72 | 73 | 74 | 75 | 76 | buttonBox 77 | rejected() 78 | CSettingsDialog 79 | reject() 80 | 81 | 82 | 316 83 | 260 84 | 85 | 86 | 286 87 | 274 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /settingsui/csettingspage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "compiler/compiler_warnings_control.h" 4 | 5 | DISABLE_COMPILER_WARNINGS 6 | #include 7 | RESTORE_COMPILER_WARNINGS 8 | 9 | class CSettingsPage : public QWidget 10 | { 11 | public: 12 | using QWidget::QWidget; 13 | 14 | virtual void acceptSettings() = 0; 15 | }; 16 | -------------------------------------------------------------------------------- /settingsui/settingsui.pri: -------------------------------------------------------------------------------- 1 | FORMS += \ 2 | settingsui/csettingsdialog.ui 3 | 4 | HEADERS += \ 5 | settingsui/csettingsdialog.h \ 6 | settingsui/csettingspage.h 7 | 8 | SOURCES += \ 9 | settingsui/csettingsdialog.cpp 10 | -------------------------------------------------------------------------------- /std_helpers/qt_container_helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | std::vector to_vector(const Container& qtContainer) 11 | { 12 | using T = typename Container::value_type; 13 | std::vector v; 14 | v.reserve((size_t)qtContainer.size()); 15 | 16 | std::copy(std::cbegin(qtContainer), std::cend(qtContainer), std::back_inserter(v)); 17 | return v; 18 | } 19 | 20 | template 21 | std::vector to_vector(Container&& qtContainer) 22 | { 23 | static_assert (std::is_rvalue_reference_v); 24 | using T = typename Container::value_type; 25 | std::vector v; 26 | const size_t size = static_cast(qtContainer.size()); 27 | v.reserve(size); 28 | for (int i = 0; i < static_cast(size); ++i) 29 | v.emplace_back(std::move(qtContainer[i])); 30 | 31 | return v; 32 | } 33 | 34 | template 35 | std::vector to_vector(const Container& qtContainer) 36 | { 37 | std::vector v; 38 | v.reserve((size_t)qtContainer.size()); 39 | 40 | std::copy(std::cbegin(qtContainer), std::cend(qtContainer), std::back_inserter(v)); 41 | return v; 42 | } 43 | 44 | template