├── doc
├── .gitignore
├── logo_64.png
├── logo_96.png
├── Doxyfile.in
├── customdoxygen.css
├── CMakeLists.txt
├── qttagsfix.py
├── qt-tagfiles.py
├── DoxygenLayout.xml
└── Doxyfile
├── src
├── widgets
│ ├── TimerEdit
│ ├── TreeComboBox
│ ├── ExportableTableView
│ ├── OverlayStackLayout
│ ├── ScrollableMessageBox
│ ├── widgets.pro
│ ├── maxLibQtWidgets.pri
│ ├── CMakeLists.txt
│ ├── ActionPushButton.h
│ ├── ActionPushButton.cpp
│ ├── TimerEdit.h
│ ├── ScrollableMessageBox.h
│ ├── ScrollableMessageBox.cpp
│ ├── BuddyLabel.h
│ ├── OverlayStackLayout.cpp
│ ├── ExportableTableView.h
│ ├── RoundedMessageBox.h
│ ├── TimerEdit.cpp
│ ├── OverlayStackLayout.h
│ ├── ExportableTableView.cpp
│ └── CollapsingToolBar.h
├── core
│ ├── AppDebugMessageHandler
│ ├── maxLibQtCore.pri
│ ├── core.pro
│ ├── CMakeLists.txt
│ ├── AppDebugMessageHandler.cpp
│ └── AppDebugMessageHandler.h
├── itemmodels
│ ├── GroupedItemsProxyModel
│ ├── maxLibQtItemModels.pri
│ ├── itemmodels.pro
│ ├── CMakeLists.txt
│ └── GroupedItemsProxyModel.h
├── quick
│ ├── quick.pro
│ └── maxLibQt
│ │ ├── maxLibQt.dox
│ │ └── controls
│ │ ├── qmldir
│ │ ├── controls.pri
│ │ ├── controls.pro
│ │ ├── MLHexSpinBox.qml
│ │ └── tests.qml
├── src.pro
└── CMakeLists.txt
├── maxLibQt.pro
├── examples
└── imageviewer
│ ├── doc
│ ├── imageviewer-grid_alt.jpg
│ ├── imageviewer-image_alt.jpg
│ ├── imageviewer-grid_loaded.jpg
│ ├── imageviewer-image_info.jpg
│ ├── imageviewer-grid_infobox.jpg
│ └── imageviewer-image_tbleft.jpg
│ ├── imageviewer.qrc
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── main.cpp
│ ├── GraphicsImageView.h
│ ├── GraphicsImageView.cpp
│ ├── ImageGrid.h
│ ├── imageviewer.css
│ ├── ImageViewer.h
│ └── ImageGrid.cpp
├── .gitignore
├── maxLibQt.pri
├── CMakeLists.txt
├── LICENSE.txt
└── README.md
/doc/.gitignore:
--------------------------------------------------------------------------------
1 | html*/
2 |
--------------------------------------------------------------------------------
/src/widgets/TimerEdit:
--------------------------------------------------------------------------------
1 | #include "TimerEdit.h"
2 |
--------------------------------------------------------------------------------
/src/widgets/TreeComboBox:
--------------------------------------------------------------------------------
1 | #include "TreeComboBox.h"
2 |
--------------------------------------------------------------------------------
/src/widgets/ExportableTableView:
--------------------------------------------------------------------------------
1 | #include "ExportableTableView.h"
2 |
--------------------------------------------------------------------------------
/src/widgets/OverlayStackLayout:
--------------------------------------------------------------------------------
1 | #include "OverlayStackLayout.h"
2 |
--------------------------------------------------------------------------------
/src/core/AppDebugMessageHandler:
--------------------------------------------------------------------------------
1 | #include "AppDebugMessageHandler.h"
2 |
--------------------------------------------------------------------------------
/src/widgets/ScrollableMessageBox:
--------------------------------------------------------------------------------
1 | #include "ScrollableMessageBox.h"
2 |
--------------------------------------------------------------------------------
/src/itemmodels/GroupedItemsProxyModel:
--------------------------------------------------------------------------------
1 | #include "GroupedItemsProxyModel.h"
2 |
--------------------------------------------------------------------------------
/doc/logo_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpaperno/maxLibQt/HEAD/doc/logo_64.png
--------------------------------------------------------------------------------
/doc/logo_96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpaperno/maxLibQt/HEAD/doc/logo_96.png
--------------------------------------------------------------------------------
/src/quick/quick.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += maxLibQt/controls
4 |
--------------------------------------------------------------------------------
/maxLibQt.pro:
--------------------------------------------------------------------------------
1 | TARGET = maxLibQt
2 | TEMPLATE = subdirs
3 |
4 | SUBDIRS += src
5 |
6 | include(maxLibQt.pri)
7 |
--------------------------------------------------------------------------------
/src/quick/maxLibQt/maxLibQt.dox:
--------------------------------------------------------------------------------
1 | /*!
2 | \namespace maxLibQt
3 | \brief QML controls and components.
4 | */
5 |
--------------------------------------------------------------------------------
/src/src.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += core
4 | SUBDIRS += itemmodels
5 | SUBDIRS += quick
6 | SUBDIRS += widgets
7 |
--------------------------------------------------------------------------------
/examples/imageviewer/doc/imageviewer-grid_alt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpaperno/maxLibQt/HEAD/examples/imageviewer/doc/imageviewer-grid_alt.jpg
--------------------------------------------------------------------------------
/examples/imageviewer/doc/imageviewer-image_alt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpaperno/maxLibQt/HEAD/examples/imageviewer/doc/imageviewer-image_alt.jpg
--------------------------------------------------------------------------------
/examples/imageviewer/imageviewer.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | imageviewer.css
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/imageviewer/doc/imageviewer-grid_loaded.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpaperno/maxLibQt/HEAD/examples/imageviewer/doc/imageviewer-grid_loaded.jpg
--------------------------------------------------------------------------------
/examples/imageviewer/doc/imageviewer-image_info.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpaperno/maxLibQt/HEAD/examples/imageviewer/doc/imageviewer-image_info.jpg
--------------------------------------------------------------------------------
/examples/imageviewer/doc/imageviewer-grid_infobox.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpaperno/maxLibQt/HEAD/examples/imageviewer/doc/imageviewer-grid_infobox.jpg
--------------------------------------------------------------------------------
/examples/imageviewer/doc/imageviewer-image_tbleft.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpaperno/maxLibQt/HEAD/examples/imageviewer/doc/imageviewer-image_tbleft.jpg
--------------------------------------------------------------------------------
/src/quick/maxLibQt/controls/qmldir:
--------------------------------------------------------------------------------
1 | module maxLibQt.controls
2 |
3 | MLDoubleSpinBox 1.0 MLDoubleSpinBox.qml
4 | MLHexSpinBox 1.0 MLHexSpinBox.qml
5 |
--------------------------------------------------------------------------------
/src/quick/maxLibQt/controls/controls.pri:
--------------------------------------------------------------------------------
1 |
2 | QML_SRC += $$PWD/MLDoubleSpinBox.qml
3 | QML_SRC += $$PWD/MLHexSpinBox.qml
4 |
5 | !qtquickcompiler: QML_FILES += $$QML_SRC
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.user
2 | *.vcproj
3 | *.vcxproj*
4 | *.sln
5 | .*project
6 | .settings
7 | .idea
8 | build*/
9 | cmake-build-*/
10 | *.qmlc
11 | docs/
12 | *.tmp
13 | *.bak
14 |
--------------------------------------------------------------------------------
/src/itemmodels/maxLibQtItemModels.pri:
--------------------------------------------------------------------------------
1 |
2 | INCLUDEPATH += $$PWD
3 |
4 | HEADERS += \
5 | $$PWD/GroupedItemsProxyModel.h
6 |
7 | SOURCES += \
8 | $$PWD/GroupedItemsProxyModel.cpp
9 |
--------------------------------------------------------------------------------
/maxLibQt.pri:
--------------------------------------------------------------------------------
1 |
2 | DISTFILES += \
3 | $$PWD/LICENSE.txt \
4 | $$PWD/LICENSE.GPL.txt \
5 | $$PWD/README.md
6 |
7 | OTHER_FILES += \
8 | $$DISTFILES \
9 | $$PWD/CMakeLists.txt
10 |
--------------------------------------------------------------------------------
/src/core/maxLibQtCore.pri:
--------------------------------------------------------------------------------
1 | ## maxLibQtCore
2 |
3 | INCLUDEPATH += $$PWD
4 |
5 | HEADERS += \
6 | $$PWD/AppDebugMessageHandler.h
7 |
8 | SOURCES += \
9 | $$PWD/AppDebugMessageHandler.cpp
10 |
--------------------------------------------------------------------------------
/src/core/core.pro:
--------------------------------------------------------------------------------
1 | TARGET = maxLibQtCore
2 | TEMPLATE = lib
3 |
4 | QT += core
5 |
6 | DESTDIR = $${OUT_PWD}/bin
7 | DEFINES += QT_USE_QSTRINGBUILDER
8 |
9 | include(maxLibQtCore.pri)
10 |
11 | OTHER_FILES += $$PWD/CMakeLists.txt
12 |
--------------------------------------------------------------------------------
/src/quick/maxLibQt/controls/controls.pro:
--------------------------------------------------------------------------------
1 | TARGET = maxLibQtQuickControls
2 | TEMPLATE = aux
3 |
4 | include(controls.pri)
5 |
6 | QT += quick qml
7 |
8 | OTHER_FILES += qmldir
9 | OTHER_FILES += $$QML_SRC
10 | OTHER_FILES += tests.qml
11 |
--------------------------------------------------------------------------------
/src/widgets/widgets.pro:
--------------------------------------------------------------------------------
1 | TARGET = maxLibQtWidgets
2 | TEMPLATE = lib
3 |
4 | QT += core widgets
5 |
6 | DESTDIR = $${OUT_PWD}/bin
7 | DEFINES += QT_USE_QSTRINGBUILDER
8 |
9 | include(maxLibQtWidgets.pri)
10 |
11 | OTHER_FILES += $$PWD/CMakeLists.txt
12 |
--------------------------------------------------------------------------------
/src/itemmodels/itemmodels.pro:
--------------------------------------------------------------------------------
1 | TARGET = maxLibQtItemModels
2 | TEMPLATE = lib
3 |
4 | QT += core
5 |
6 | DESTDIR = $${OUT_PWD}/bin
7 | DEFINES += QT_USE_QSTRINGBUILDER
8 |
9 | include(maxLibQtItemModels.pri)
10 |
11 | OTHER_FILES += $$PWD/CMakeLists.txt
12 |
--------------------------------------------------------------------------------
/doc/Doxyfile.in:
--------------------------------------------------------------------------------
1 | # Doxyfile 1.8.13
2 |
3 | @INCLUDE = @DOC_DIR@/Doxyfile
4 |
5 | OUTPUT_DIRECTORY = @DOC_OUTPUT_DIR@
6 |
7 | QUIET = YES
8 | WARNINGS = NO
9 | WARN_IF_UNDOCUMENTED = NO
10 | WARN_IF_DOC_ERROR = NO
11 | HTML_TIMESTAMP = NO
12 |
--------------------------------------------------------------------------------
/src/widgets/maxLibQtWidgets.pri:
--------------------------------------------------------------------------------
1 |
2 | QT += widgets
3 |
4 | INCLUDEPATH += $$PWD
5 |
6 | HEADERS += \
7 | $$PWD/ActionPushButton.h \
8 | $$PWD/BuddyLabel.h \
9 | $$PWD/CollapsingToolBar.h \
10 | $$PWD/ExportableTableView.h \
11 | $$PWD/OverlayStackLayout.h \
12 | $$PWD/RoundedMessageBox.h \
13 | $$PWD/ScrollableMessageBox.h \
14 | $$PWD/TimerEdit.h \
15 | $$PWD/TreeComboBox.h
16 |
17 | SOURCES += \
18 | $$PWD/ActionPushButton.cpp \
19 | $$PWD/ExportableTableView.cpp \
20 | $$PWD/OverlayStackLayout.cpp \
21 | $$PWD/ScrollableMessageBox.cpp \
22 | $$PWD/TimerEdit.cpp \
23 | $$PWD/TreeComboBox.cpp
24 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.12)
2 | project(maxLibQt)
3 |
4 | set(SRC_SUBDIRS
5 | core
6 | itemmodels
7 | widgets
8 | )
9 |
10 | set(BUILT_LIBRARIES) # populated by subprojects
11 |
12 | foreach(sub_dir ${SRC_SUBDIRS})
13 | add_subdirectory(${sub_dir})
14 | endforeach(sub_dir)
15 |
16 | # maxLibQt is a "dummy" lib so that other binaries could link
17 | # to this single one instead of the individual libs.
18 | # The maxLibQt target cannot be built specifically.
19 | if (BUILT_LIBRARIES)
20 | add_library(${PROJECT_NAME} INTERFACE)
21 | target_link_libraries(${PROJECT_NAME} INTERFACE ${BUILT_LIBRARIES})
22 | endif()
23 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.12)
2 | project(maxLibQt)
3 |
4 | set(CMAKE_COLOR_MAKEFILE ON)
5 |
6 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
7 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
9 |
10 | set(SRC_DIR "${PROJECT_SOURCE_DIR}/src")
11 | set(DOC_DIR "${PROJECT_SOURCE_DIR}/doc")
12 |
13 | find_package(Qt5Core)
14 | if(Qt5Core_FOUND)
15 | message(STATUS "Qt Version: ${Qt5Core_VERSION}")
16 | else()
17 | message(FATAL_ERROR "Qt not found!")
18 | endif()
19 |
20 | ## Subprojects
21 | add_subdirectory("${SRC_DIR}")
22 |
23 | ## Doxymentation
24 | add_subdirectory("${DOC_DIR}")
25 |
--------------------------------------------------------------------------------
/doc/customdoxygen.css:
--------------------------------------------------------------------------------
1 |
2 | #projectlogo img
3 | {
4 | max-height: 56px
5 | }
6 |
7 |
8 | #projectname
9 | {
10 | /*font: bold 280% Tahoma, Arial,sans-serif;*/
11 | /*margin: 0px;*/
12 | padding: 2px 2px 0px 10px;
13 | font-variant: small-caps;
14 | }
15 |
16 |
17 | a.el {
18 | color: #009b00;
19 | font-weight: normal;
20 | }
21 |
22 | a.el:visited {
23 | color: #007800;
24 | font-weight: normal;
25 | }
26 |
27 | .navpath li.navelem a {
28 | font-weight: bold
29 | }
30 |
31 | .memtitle {
32 | font-size: 1.3em;
33 | }
34 |
35 | div.fragment {
36 | padding: 5px;
37 | }
38 |
39 | div.line {
40 | padding: 2px 0;
41 | text-indent: 0;
42 | }
43 |
44 |
45 | h2.memtitle { display: none; }
46 | div.memproto { border-top-left-radius: 4px; }
47 | table.memname tr { display: inline-table; }
48 | table.memname td { padding: 1px 0; }
49 | td.memname { font-size: larger; }
50 |
--------------------------------------------------------------------------------
/src/core/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.12)
2 | cmake_policy(SET CMP0020 NEW)
3 | set(CMAKE_CXX_STANDARD 11)
4 | if (CMAKE_VERSION VERSION_LESS "3.1" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
5 | set (CMAKE_CXX_FLAGS "--std=gnu++11 ${CMAKE_CXX_FLAGS}")
6 | endif ()
7 |
8 | find_package(Qt5Core)
9 |
10 | project(maxLibQtCore)
11 |
12 | set(SRCS
13 | "${CMAKE_CURRENT_LIST_DIR}/AppDebugMessageHandler.cpp"
14 | )
15 |
16 | set(HDRS
17 | "${CMAKE_CURRENT_LIST_DIR}/AppDebugMessageHandler.h"
18 | )
19 |
20 | qt5_wrap_cpp(SRCS ${HDRS})
21 |
22 | add_library(${PROJECT_NAME} ${SRCS} ${HDRS})
23 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core)
24 | target_compile_definitions(${PROJECT_NAME} PRIVATE QT_USE_QSTRINGBUILDER)
25 | target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
26 |
27 | # need to push this upstream
28 | set(BUILT_LIBRARIES ${BUILT_LIBRARIES} ${PROJECT_NAME} PARENT_SCOPE)
29 |
--------------------------------------------------------------------------------
/src/itemmodels/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.12)
2 | cmake_policy(SET CMP0020 NEW)
3 | set(CMAKE_CXX_STANDARD 11)
4 | if (CMAKE_VERSION VERSION_LESS "3.1" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
5 | set (CMAKE_CXX_FLAGS "--std=gnu++11 ${CMAKE_CXX_FLAGS}")
6 | endif ()
7 |
8 | find_package(Qt5Core)
9 |
10 | project(maxLibQtItemModels)
11 |
12 | set(SRCS
13 | "${CMAKE_CURRENT_LIST_DIR}/GroupedItemsProxyModel.cpp"
14 | )
15 |
16 | set(HDRS
17 | "${CMAKE_CURRENT_LIST_DIR}/GroupedItemsProxyModel.h"
18 | )
19 |
20 | qt5_wrap_cpp(SRCS ${HDRS})
21 |
22 | add_library(${PROJECT_NAME} ${SRCS} ${HDRS})
23 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core)
24 | target_compile_definitions(${PROJECT_NAME} PRIVATE QT_USE_QSTRINGBUILDER)
25 | target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
26 |
27 | # need to push this upstream
28 | set(BUILT_LIBRARIES ${BUILT_LIBRARIES} ${PROJECT_NAME} PARENT_SCOPE)
29 |
--------------------------------------------------------------------------------
/doc/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | ## Generate Doxygen docs from source
2 |
3 | set(DOC_OUTPUT_DIR "${DOC_DIR}" CACHE STRING "Documentation output path.")
4 |
5 | find_package(Doxygen)
6 |
7 | if(DOXYGEN_FOUND AND DOC_DIR AND DOC_OUTPUT_DIR)
8 | set(DOXYGEN_IN ${DOC_DIR}/Doxyfile.in)
9 | set(DOXYGEN_OUT "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
10 |
11 | configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
12 |
13 | add_custom_target(maxLibQt-doxygen
14 | COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
15 | WORKING_DIRECTORY ${DOC_DIR}
16 | COMMENT "Generating API documentation with Doxygen."
17 | VERBATIM )
18 |
19 | ## clean
20 | file(TO_NATIVE_PATH "html/*.html html/*.js html/*.css html/*.png" DOCFILES)
21 | if(WIN32)
22 | set(RM del /Q)
23 | separate_arguments(DOCFILES WINDOWS_COMMAND "${DOCFILES}")
24 | else()
25 | set(RM rm)
26 | separate_arguments(DOCFILES UNIX_COMMAND "${DOCFILES}")
27 | endif()
28 | add_custom_target(maxLibQt-doxygen-clean
29 | COMMAND ${RM} ${DOCFILES}
30 | COMMAND ${CMAKE_COMMAND} -E remove_directory html/search
31 | WORKING_DIRECTORY ${DOC_OUTPUT_DIR}
32 | COMMENT "Deleting stale documentation files."
33 | VERBATIM )
34 | endif()
35 |
--------------------------------------------------------------------------------
/examples/imageviewer/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.1.0)
2 |
3 | project(ImageViewer)
4 |
5 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
6 | set(CMAKE_AUTOMOC ON)
7 | set(CMAKE_AUTOUIC ON)
8 | set(CMAKE_AUTORCC ON)
9 |
10 | find_package(Qt5Core)
11 | find_package(Qt5Gui)
12 | find_package(Qt5Widgets)
13 | find_package(Qt5Svg)
14 |
15 | set(PWD "${CMAKE_CURRENT_LIST_DIR}")
16 | set(SRC_PATH "../../src")
17 |
18 | include_directories(${SRC_PATH}/widgets)
19 | #add_subdirectory(${MLQT_PATH}/widgets)
20 |
21 | set(HDRS
22 | "${PWD}/GraphicsImageView.h"
23 | "${PWD}/ImageGrid.h"
24 | "${PWD}/ImageViewer.h"
25 | "${SRC_PATH}/widgets/OverlayStackLayout.h"
26 | )
27 |
28 | set(SRCS
29 | "${PWD}/main.cpp"
30 | "${PWD}/GraphicsImageView.cpp"
31 | "${PWD}/ImageGrid.cpp"
32 | "${PWD}/ImageViewer.cpp"
33 | "${SRC_PATH}/widgets/OverlayStackLayout.cpp"
34 | )
35 |
36 | set(RSRCS "${PWD}/imageviewer.qrc")
37 |
38 | add_executable(${PROJECT_NAME} ${HDRS} ${SRCS} ${RSRCS})
39 |
40 | target_compile_definitions(${PROJECT_NAME} PRIVATE QT_USE_QSTRINGBUILDER)
41 | target_compile_definitions(${PROJECT_NAME} PRIVATE SOURCE_DIR="${PWD}")
42 | target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Svg)
43 |
--------------------------------------------------------------------------------
/examples/imageviewer/README.md:
--------------------------------------------------------------------------------
1 | ## Image Viewer Example Application ##
2 |
3 | This is a simple but fully functional image viewer application with a "gallery view" page
4 | and another for viewing a full size image with adjustable scaling, rotation, and zoom factors.
5 | This example is primarily meant to demonstrate usage of the [StackOverlayLayout][1].
6 |
7 | 
8 | 
9 | 
10 | 
11 | 
12 | 
13 |
14 | [1]: https://mpaperno.github.io/maxLibQt/class_overlay_stack_layout.html#details
15 |
16 | -------------
17 | Copyright (c)2019 Maxim Paperno. All rights reserved.
18 | This example is part of the maxLibQt project and is governed by
19 | the same licensing.
20 |
21 | This program is distributed in the hope that it will be useful,
22 | but WITHOUT ANY WARRANTY; without even the implied warranty of
23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*! \page LICENSE.txt LICENSE.txt
2 | \verbatim
3 |
4 | maxLibQt C++ code library for the Qt framework.
5 |
6 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
7 | Contact: http://www.WorldDesign.com/contact
8 |
9 | LICENSE
10 |
11 | Unless specified otherwise in the individual component files,
12 | the components of this library are licensed under a dual-use system.
13 |
14 | Commercial License Usage
15 | Licensees holding valid commercial licenses may use the components in
16 | accordance with the terms contained in a written agreement between
17 | you and the copyright holder.
18 |
19 | GNU General Public License Usage
20 | Alternatively, the components may be used under the terms of the GNU
21 | General Public License as published by the Free Software Foundation,
22 | either version 3 of the License, or (at your option) any later version.
23 |
24 | This program is distributed in the hope that it will be useful,
25 | but WITHOUT ANY WARRANTY; without even the implied warranty of
26 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 | GNU General Public License for more details.
28 |
29 | A copy of the GNU General Public License is available in the file LICENSE.GPL.txt
30 | which should be included in this distribution. It is also available at .
31 |
32 | \endverbatim */
33 |
--------------------------------------------------------------------------------
/src/widgets/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.1.0)
2 |
3 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
4 | set(CMAKE_AUTOMOC ON)
5 |
6 | find_package(Qt5Core)
7 | find_package(Qt5Widgets)
8 |
9 | project(maxLibQtWidgets)
10 |
11 | set(SRCS
12 | "${CMAKE_CURRENT_LIST_DIR}/ActionPushButton.cpp"
13 | "${CMAKE_CURRENT_LIST_DIR}/ExportableTableView.cpp"
14 | "${CMAKE_CURRENT_LIST_DIR}/OverlayStackLayout.cpp"
15 | "${CMAKE_CURRENT_LIST_DIR}/ScrollableMessageBox.cpp"
16 | "${CMAKE_CURRENT_LIST_DIR}/TimerEdit.cpp"
17 | "${CMAKE_CURRENT_LIST_DIR}/TreeComboBox.cpp"
18 | )
19 |
20 | set(HDRS
21 | "${CMAKE_CURRENT_LIST_DIR}/ActionPushButton.h"
22 | "${CMAKE_CURRENT_LIST_DIR}/BuddyLabel.h"
23 | "${CMAKE_CURRENT_LIST_DIR}/CollapsingToolBar.h"
24 | "${CMAKE_CURRENT_LIST_DIR}/ExportableTableView.h"
25 | "${CMAKE_CURRENT_LIST_DIR}/OverlayStackLayout.h"
26 | "${CMAKE_CURRENT_LIST_DIR}/RoundedMessageBox.h"
27 | "${CMAKE_CURRENT_LIST_DIR}/ScrollableMessageBox.h"
28 | "${CMAKE_CURRENT_LIST_DIR}/TimerEdit.h"
29 | "${CMAKE_CURRENT_LIST_DIR}/TreeComboBox.h"
30 | )
31 |
32 | add_library(${PROJECT_NAME} ${SRCS} ${HDRS})
33 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Widgets)
34 | target_compile_definitions(${PROJECT_NAME} PRIVATE QT_USE_QSTRINGBUILDER)
35 |
36 | # need to push this upstream
37 | set(BUILT_LIBRARIES ${BUILT_LIBRARIES} ${PROJECT_NAME} PARENT_SCOPE)
38 |
--------------------------------------------------------------------------------
/src/quick/maxLibQt/controls/MLHexSpinBox.qml:
--------------------------------------------------------------------------------
1 | /*
2 | MLHexSpinBox
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2018 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | import QtQuick 2.10
29 | import QtQuick.Controls 2.3
30 |
31 | /*!
32 | \brief MLHexSpinBox allows editing integers using hexadecimal notation.
33 |
34 | It uses MLDoubleSpinBox as the base class because this allows a wider range of values, including signed integers.
35 | The basic Controls 2 SpinBox is limited to signed int range only.
36 |
37 | Individual property documentation can be found inline.
38 |
39 | \sa MLDoubleSpinBox
40 | */
41 |
42 | MLDoubleSpinBox {
43 | id: control
44 | objectName: "MLHexSpinBox"
45 |
46 | property bool upperCase: true //!< Whether to force upper-case formatting for letters.
47 | property bool zeroPad: true //!< Whether to pad numbers with leading zeros up to \p digits length.
48 | property bool showPrefix: true //!< Whether to show the "0x" prefix.
49 | property int digits: Math.abs(topValue).toString(16).length //!< Number of digits expected, used in validator, input mask, and for zero-padding. Default is based on maximum value.
50 |
51 | from: 0
52 | to: 0xFFFFFFFF
53 | decimals: 0
54 | inputMethodHints: Qt.ImhNoPredictiveText | (upperCase ? Qt.ImhPreferUppercase : 0)
55 | inputMask: (value < 0 ? "-" : "") + (showPrefix ? "\\0\\x" : "") + "H".repeat(digits)
56 |
57 | validator: RegExpValidator {
58 | regExp: new RegExp("-?(0x)?[0-9A-Fa-f]{1," + control.digits + "}")
59 | }
60 |
61 | function textFromValue(value, locale) {
62 | var ret = Math.abs(Number(value)).toString(16);
63 | if (zeroPad && digits > ret.length)
64 | ret = "0".repeat(digits - ret.length) + ret;
65 | if (upperCase)
66 | ret = ret.toUpperCase();
67 | if (showPrefix)
68 | ret = "0x" + ret;
69 |
70 | return ret;
71 | }
72 |
73 | function valueFromText(text, locale) {
74 | return parseInt(text, 16);
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/examples/imageviewer/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Image Viewer example application
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #include "ImageViewer.h"
29 | #include
30 | #include
31 | #include
32 |
33 | #define RESOURCES_PATH ":" // use for embedded CSS resource
34 | //#define RESOURCES_PATH SOURCE_DIR // use for local CSS file, SOURCE_DIR should be set in build script
35 |
36 | /*!
37 | \ingroup examples
38 | \defgroup examples_imageviewer Image Viewer example application
39 | Main application file for the Image Viewer example.
40 |
41 | ```
42 | Usage: ImageViewer [options] [path]
43 |
44 | Options:
45 | -m, --maxcache Maximum size of image previews cache (default: 1000 MB).
46 | -?, -h, --help Displays this help.
47 |
48 | Arguments:
49 | path Start with this image folder or file.
50 | ```
51 |
52 | \note The global \c QPixmapCache is used to store image previews in the \c ImageGrid class. It should be suitably "large."
53 |
54 | */
55 | int main(int argc, char *argv[])
56 | {
57 | QApplication::setStyle("Fusion"); // things style better with Fusion
58 | QApplication a(argc, argv);
59 | QCoreApplication::setOrganizationName("maxLibQt");
60 | QCoreApplication::setApplicationName("maxLibQt Image Viewer Example");
61 |
62 | const int maxCache = QPixmapCache::cacheLimit() * 100 / 1024;
63 |
64 | QCommandLineParser clp;
65 | clp.setApplicationDescription(QCoreApplication::applicationName());
66 | clp.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
67 | clp.addPositionalArgument(QStringLiteral("path"), QStringLiteral("Start with this image folder or file."), QStringLiteral("[path]"));
68 | clp.addOption({ {QStringLiteral("m"), QStringLiteral("maxcache")},
69 | QStringLiteral("Maximum size of image previews cache (default: %1 MB).").arg(maxCache),
70 | QStringLiteral("MBytes"), QString::number(maxCache) });
71 | clp.addHelpOption();
72 | clp.process(a);
73 |
74 | QPixmapCache::setCacheLimit(clp.value(QStringLiteral("m")).toInt() * 1024);
75 | ImageViewer w(RESOURCES_PATH "/imageviewer.css", clp.positionalArguments().value(0));
76 | return a.exec();
77 | }
78 |
--------------------------------------------------------------------------------
/examples/imageviewer/GraphicsImageView.h:
--------------------------------------------------------------------------------
1 | /*
2 | GraphicsImageView
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef GRAPHICSIMAGEVIEW_H
29 | #define GRAPHICSIMAGEVIEW_H
30 |
31 | #include
32 | #include
33 |
34 | /*!
35 | \ingroup examples_imageviewer
36 | A custom \c QGraphicsView for displaying an image scaled to fit into the full available viewport/scene geometry.
37 | It will automatically resize the \c QGraphicsScene and rescale the contained image whenever the size of this widget
38 | changes. It also has a few extra features for "zooming" and rotating the image using \c QGraphicsItem transformations,
39 | and a setting for how the image ratio is preserved while scaling.
40 | */
41 | class GraphicsImageView : public QGraphicsView
42 | {
43 | Q_OBJECT
44 | Q_PROPERTY(QString imageFile READ imageFile WRITE setImageFile NOTIFY imageChanged USER true)
45 | Q_PROPERTY(qreal imageScale READ imageScale WRITE setImageScale NOTIFY imageScaleChanged)
46 | Q_PROPERTY(qreal imageRotation READ imageRotation WRITE setImageRotation NOTIFY imageRotationChanged)
47 | Q_PROPERTY(Qt::AspectRatioMode imageScalingMode READ imageScalingMode WRITE setImageScalingMode NOTIFY imageScalingModeChanged)
48 |
49 | public:
50 | explicit GraphicsImageView(QWidget *p = nullptr);
51 |
52 | QString imageFile() const { return m_imageFile; }
53 | Qt::AspectRatioMode imageScalingMode() const { return m_scaleMode; }
54 | qreal imageScale() const { return m_item ? m_item->scale() : 0.0; }
55 | qreal imageRotation() const { return m_item ? m_item->rotation() : 0.0; }
56 |
57 | public slots:
58 | void setImageFile(const QString &imageFile);
59 | void setImageScalingMode(int mode);
60 |
61 | void setImageScale(qreal scale) const;
62 | void zoomImage(int steps) const { setImageScale(imageScale() + steps * 0.1); }
63 | void zoomIn() const { zoomImage(1); }
64 | void zoomOut() const { zoomImage(-1); }
65 | void zoomReset() const { setImageScale(1.0); }
66 |
67 | void setImageRotation(qreal degrees) const;
68 | void rotateCw() const { setImageRotation(imageRotation() + 90.0); }
69 | void rotateCCw() const { setImageRotation(imageRotation() - 90.0); }
70 | void rotationReset() const { setImageRotation(0); }
71 |
72 | signals:
73 | void imageChanged() const;
74 | void imageScaleChanged(qreal) const;
75 | void imageRotationChanged(qreal) const;
76 | void imageScalingModeChanged(Qt::AspectRatioMode) const;
77 |
78 | protected slots:
79 | void loadImage(const QSize &size);
80 |
81 | protected:
82 | void resizeEvent(QResizeEvent *e) override;
83 |
84 | private:
85 | bool createItem();
86 | void removeItem();
87 |
88 | Qt::AspectRatioMode m_scaleMode = Qt::KeepAspectRatio;
89 | QString m_imageFile;
90 | QGraphicsPixmapItem *m_item = nullptr;
91 |
92 | Q_DISABLE_COPY(GraphicsImageView)
93 | };
94 |
95 | #endif // GRAPHICSIMAGEVIEW_H
96 |
--------------------------------------------------------------------------------
/src/widgets/ActionPushButton.h:
--------------------------------------------------------------------------------
1 | /*
2 | ActionPushButton
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef ACTIONPUSHBUTTON_H
29 | #define ACTIONPUSHBUTTON_H
30 |
31 | #include
32 |
33 | class QAction;
34 | class QEvent;
35 |
36 | /*!
37 | \brief The ActionPushButton class is a \c QPushButton which takes a default \c QAction, just like a \c QToolButton can.
38 | Like \c QToolButton, it will inherit all properties from the default \c QAction, such as text, icon, checkable status & state,
39 | tool tip, and so on. The default action can be set with \c setDefaultAction() and retrieved with \c defaultAction().
40 | The default action can also be set using the dedicated \ref ActionPushButton(QAction *, QWidget *) constructor.
41 |
42 | It also adds a \c triggered(QAction *) signal for all \c QActions added to the button (not just the default one).
43 | */
44 | class ActionPushButton : public QPushButton
45 | {
46 | Q_OBJECT
47 | //! Current default action, if any. Value is `nullptr` if no default action has been set.
48 | Q_PROPERTY(QAction *defaultAction READ defaultAction WRITE setDefaultAction)
49 |
50 | public:
51 | //! Inherits base class constructors.
52 | using QPushButton::QPushButton;
53 | //! Construct using \a defaultAction as the default action.
54 | explicit ActionPushButton(QAction *defaultAction, QWidget *parent = nullptr);
55 |
56 | //! Current default action, if any. Returns `nullptr` if no default action has been set.
57 | inline QAction *defaultAction() const { return m_defaultAction; }
58 |
59 | public slots:
60 | //! Sets the default action to \a action. The action is added to the widget if it hasn't been already.
61 | //! To clear the default action, pass a `nullptr` as the \a action. Clearing the default action in this way
62 | //! does *not* remove it from this widget itself. Use \c QWidget::removeAction() for that instead, which will
63 | //! also clear the default action on this button (if the action being removed is the current default, of course).
64 | void setDefaultAction(QAction *action);
65 |
66 | signals:
67 | //! Signal emitted whenever any \c QAction added to this button (with \c QWidget::addAction() or \c setDefaultAction()) is triggered.
68 | void triggered(QAction *);
69 |
70 | protected:
71 | bool event(QEvent *e) override;
72 | void nextCheckState() override;
73 |
74 | private slots:
75 | void updateFromAction(QAction *action);
76 | void onActionTriggered();
77 |
78 | private:
79 | QAction *m_defaultAction = nullptr;
80 |
81 | Q_DISABLE_COPY(ActionPushButton)
82 |
83 | #ifdef DOXYGEN_SHOULD_INCLUDE_THIS
84 | public:
85 | //! Inherited from \c QPushButton. \{
86 | ActionPushButton(QWidget *parent = nullptr);
87 | ActionPushButton(const QString &text, QWidget *parent = nullptr);
88 | ActionPushButton(const QIcon &icon, const QString &text, QWidget *parent = nullptr);
89 | //! \}
90 | #endif
91 | };
92 |
93 | #endif // ACTIONPUSHBUTTON_H
94 |
--------------------------------------------------------------------------------
/src/widgets/ActionPushButton.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | ActionPushButton
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #include "ActionPushButton.h"
29 |
30 | #include
31 | #include
32 |
33 | ActionPushButton::ActionPushButton(QAction *defaultAction, QWidget *parent) :
34 | QPushButton(parent)
35 | {
36 | setDefaultAction(defaultAction);
37 | }
38 |
39 | bool ActionPushButton::event(QEvent *e)
40 | {
41 | switch (e->type()) {
42 | case QEvent::ActionAdded:
43 | if (QActionEvent *ae = static_cast(e))
44 | connect(ae->action(), &QAction::triggered, this, &ActionPushButton::onActionTriggered);
45 | break;
46 |
47 | case QEvent::ActionRemoved:
48 | if (QActionEvent *ae = static_cast(e)) {
49 | ae->action()->disconnect(this);
50 | if (ae->action() == m_defaultAction)
51 | setDefaultAction(nullptr);
52 | }
53 | break;
54 |
55 | case QEvent::ActionChanged:
56 | if (QActionEvent *ae = static_cast(e))
57 | if (ae->action() == m_defaultAction)
58 | updateFromAction(m_defaultAction);
59 | break;
60 |
61 | default:
62 | break;
63 | }
64 | return QPushButton::event(e);
65 | }
66 |
67 | void ActionPushButton::nextCheckState()
68 | {
69 | if (!!m_defaultAction)
70 | m_defaultAction->trigger();
71 | else
72 | QPushButton::nextCheckState();
73 | }
74 |
75 | void ActionPushButton::updateFromAction(QAction *action)
76 | {
77 | if (!action)
78 | return;
79 | QString buttonText = action->iconText();
80 | // If iconText() is generated from text(), we need to remove any '&'s so they don't turn into shortcuts
81 | if (buttonText == action->text())
82 | buttonText.replace(QLatin1String("&"), QLatin1String(""));
83 | setText(buttonText);
84 | setIcon(action->icon());
85 | setToolTip(action->toolTip());
86 | setStatusTip(action->statusTip());
87 | setWhatsThis(action->whatsThis());
88 | setCheckable(action->isCheckable());
89 | setChecked(action->isChecked());
90 | setEnabled(action->isEnabled());
91 | setVisible(action->isVisible());
92 | setAutoRepeat(action->autoRepeat());
93 | if (!testAttribute(Qt::WA_SetFont)) {
94 | setFont(action->font());
95 | setAttribute(Qt::WA_SetFont, false);
96 | }
97 | }
98 |
99 | void ActionPushButton::setDefaultAction(QAction *action)
100 | {
101 | if (m_defaultAction == action)
102 | return;
103 |
104 | if (!!m_defaultAction && !!m_defaultAction->menu() && m_defaultAction->menu() == menu())
105 | setMenu(nullptr);
106 |
107 | m_defaultAction = action;
108 | if (!action)
109 | return;
110 |
111 | if (!actions().contains(action))
112 | addAction(action);
113 | updateFromAction(action);
114 | if (!!action->menu() && !menu())
115 | setMenu(action->menu());
116 | }
117 |
118 | void ActionPushButton::onActionTriggered()
119 | {
120 | if (QAction *act = qobject_cast(sender()))
121 | emit triggered(act);
122 | }
123 |
--------------------------------------------------------------------------------
/src/widgets/TimerEdit.h:
--------------------------------------------------------------------------------
1 | /*
2 | TimerEdit
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef TIMEREDIT_H
29 | #define TIMEREDIT_H
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | /**
38 | \class TimerEdit
39 | \version 1.0.0
40 |
41 | \brief A time value line editor which accepts negative and large times (> 23:59:59), suitable for a timer, etc.
42 |
43 | Qt's \c QTimeEdit has a limitation in that it can only accept "valid" times, in the range of 0:00:00 to 23:59:59.
44 | The TimerEdit control aims to address that. It allows any amount of time to be entered, as well as negative times.
45 | It provides a mask for data entry, a validator with settable min/max times, and increment/decrement via keyboard
46 | or mouse wheel in configurable steps.
47 |
48 | Data I/O is in seconds only, that is you set the time value in total seconds and also read it back in seconds
49 | (there is no conversion from/to a \e QTime object).
50 |
51 | */
52 | class TimerEdit : public QLineEdit
53 | {
54 | Q_OBJECT
55 | Q_PROPERTY(int minimumTime READ minimumTime WRITE setMinimumTime)
56 | Q_PROPERTY(int maximumTime READ maximumTime WRITE setMaximumTime)
57 | Q_PROPERTY(bool showSeconds READ showSeconds WRITE setShowSeconds)
58 | Q_PROPERTY(unsigned int singleStep READ singleStep WRITE setSingleStep)
59 | Q_PROPERTY(unsigned int pageStep READ pageStep WRITE setPageStep)
60 |
61 |
62 | public:
63 | enum TimeParts {Whole, Polarity, Hours, Minutes, Seconds};
64 |
65 | TimerEdit(QWidget *parent = Q_NULLPTR);
66 |
67 | int timeInSeconds() const;
68 | int timepart(const QString &input, TimeParts part) const;
69 |
70 | bool showSeconds() const { return m_showSeconds; }
71 | int minimumTime() const { return m_minTime; }
72 | int maximumTime() const { return m_maxTime; }
73 | unsigned int singleStep() const { return m_singleStep; }
74 | unsigned int pageStep() const { return m_pageStep; }
75 |
76 | public slots:
77 | void setTime(int seconds);
78 | void incrDecr(int seconds);
79 | void setTimeRange(int minSeconds, int maxSeconds);
80 | void setMinimumTime(int minSeconds);
81 | void setMaximumTime(int maxSeconds);
82 | void setShowSeconds(bool showSeconds);
83 |
84 | void setSingleStep(unsigned int step) { m_singleStep = step; }
85 | void setPageStep(unsigned int pageStep) { m_pageStep = pageStep; }
86 |
87 | protected:
88 | void textEditedHandler();
89 | virtual void setupFormat();
90 | void emitValueChanged();
91 | void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
92 | void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
93 |
94 | bool m_showSeconds;
95 | int m_minTime; // seconds
96 | int m_maxTime; // seconds
97 | unsigned m_singleStep; // seconds
98 | unsigned m_pageStep; // seconds
99 | short m_hourDigits;
100 | QRegularExpressionValidator * m_validator;
101 | };
102 |
103 | #endif // TIMEREDIT_H
104 |
--------------------------------------------------------------------------------
/src/widgets/ScrollableMessageBox.h:
--------------------------------------------------------------------------------
1 | /*
2 | ScrollableMessageBox
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef SCROLLABLEESSAGEBOX_H
29 | #define SCROLLABLEESSAGEBOX_H
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | /**
39 | \class ScrollableMessageBox
40 | \version 1.0.0
41 |
42 | \brief A simple message box with a large scrollable area (a \c QTextEdit ) for detailed text (including HTML formatting).
43 |
44 | This is a basic message box dialog for presenting an amount of text which would not comfortably fit in a regular popup message
45 | dialog. It addresses the deficiencies found in \c QMessageBox with "informative text" option, namely its small and fixed
46 | size and lack of text formatting options (no rich text or even font control).
47 |
48 | Basic usage is like \c QMessageBox. You can set the dialog title, an optional short text message to show at the top, and
49 | the larger text block to show as the "details." There is only one "OK" button to dismiss the dialog.
50 |
51 | When first shown, the dialog will try to esablish a reasonable size based on the contents of the QTextEdit. The dialog is also
52 | fully resizable.
53 |
54 | Developers have direct access to the \c QTextEdit used for displaying the text, the \c QLineEdit used for the short text message,
55 | and the \c QDialogButtonBox with the default "Ok" button. There are a couple convenience functions for setting the font style
56 | and word wrap on the \c QTextEdit. By default a variable-width font is used with word-wrap on.
57 |
58 | */
59 | class ScrollableMessageBox : public QDialog
60 | {
61 | Q_OBJECT
62 |
63 | class TextEdit : public QTextEdit
64 | {
65 | public:
66 | TextEdit(QWidget *parent=0) : QTextEdit(parent) {}
67 | QSize sizeHint() const Q_DECL_OVERRIDE;
68 | };
69 |
70 | public:
71 | explicit ScrollableMessageBox(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::Dialog);
72 | ScrollableMessageBox(const QString &title, const QString &text = QString(), const QString &details = QString(), QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::Dialog);
73 |
74 | //! The QTextEdit which holds the detailed text
75 | QTextEdit * textEdit() const { return m_textEdit; }
76 | //! The QLabel which holds the text
77 | QLabel * textLabel() const { return m_textLabel; }
78 | //! The dialog's button box
79 | QDialogButtonBox * buttonBox() const { return m_btnBox; }
80 |
81 | public slots:
82 | void setText(const QString &text = QString());
83 | void setDetailedText(const QString &details = QString(), Qt::TextFormat format = Qt::AutoText);
84 | void setFontFixedWidth(bool fixed = true);
85 | void setWordWrap(bool wrap = true);
86 |
87 | protected:
88 | void init(const QString &title = QString(), const QString &text = QString(), const QString &details = QString());
89 |
90 | QLabel * m_textLabel;
91 | TextEdit * m_textEdit;
92 | QDialogButtonBox * m_btnBox;
93 | };
94 |
95 | #endif // SCROLLABLEESSAGEBOX_H
96 |
--------------------------------------------------------------------------------
/src/widgets/ScrollableMessageBox.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | ScrollableMessageBox
3 |
4 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
5 | Contact: http://www.WorldDesign.com/contact
6 |
7 | LICENSE:
8 |
9 | Commercial License Usage
10 | Licensees holding valid commercial licenses may use this file in
11 | accordance with the terms contained in a written agreement between
12 | you and the copyright holder.
13 |
14 | GNU General Public License Usage
15 | Alternatively, this file may be used under the terms of the GNU
16 | General Public License as published by the Free Software Foundation,
17 | either version 3 of the License, or (at your option) any later version.
18 |
19 | This program is distributed in the hope that it will be useful,
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 | GNU General Public License for more details.
23 |
24 | A copy of the GNU General Public License is available at .
25 | */
26 |
27 | #include "ScrollableMessageBox.h"
28 |
29 |
30 | ScrollableMessageBox::ScrollableMessageBox(QWidget *parent, Qt::WindowFlags f) :
31 | QDialog(parent, f | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
32 | {
33 | init();
34 | }
35 |
36 | ScrollableMessageBox::ScrollableMessageBox(const QString &title, const QString &text, const QString &details, QWidget *parent, Qt::WindowFlags f) :
37 | QDialog(parent, f | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
38 | {
39 | init(title, text, details);
40 | }
41 |
42 | void ScrollableMessageBox::setText(const QString &text)
43 | {
44 | m_textLabel->setText(text);
45 | }
46 |
47 | void ScrollableMessageBox::setDetailedText(const QString &details, Qt::TextFormat format)
48 | {
49 | if (format == Qt::AutoText)
50 | m_textEdit->setText(details);
51 | else if (format == Qt::RichText)
52 | m_textEdit->setHtml(details);
53 | else
54 | m_textEdit->setPlainText(details);
55 | }
56 |
57 | void ScrollableMessageBox::setFontFixedWidth(bool fixed)
58 | {
59 | QFont newFont(m_textEdit->font());
60 | if (fixed) {
61 | newFont.setFamily("Courier");
62 | newFont.setStyleHint(QFont::TypeWriter);
63 | }
64 | else {
65 | newFont.setFamily("Helvetica");
66 | newFont.setStyleHint(QFont::SansSerif);
67 | }
68 | #ifdef Q_OS_MACOS
69 | newFont.setPointSize(13);
70 | m_textEdit->setAttribute(Qt::WA_MacNormalSize);
71 | #elif defined Q_OS_WIN
72 | newFont.setPointSize(10);
73 | #endif
74 | m_textEdit->setFont(newFont);
75 | }
76 |
77 | void ScrollableMessageBox::setWordWrap(bool wrap)
78 | {
79 | m_textEdit->setWordWrapMode(wrap ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);
80 | }
81 |
82 | void ScrollableMessageBox::init(const QString & title, const QString & text, const QString & details)
83 | {
84 | m_textLabel = new QLabel(this);
85 | m_textLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
86 | m_textLabel->setOpenExternalLinks(true);
87 |
88 | m_textEdit = new TextEdit(this);
89 | m_textEdit->setReadOnly(true);
90 | m_textEdit->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard);
91 |
92 | m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok, this);
93 |
94 | connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
95 | connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
96 |
97 | QVBoxLayout * lo = new QVBoxLayout(this);
98 | lo->setSpacing(8);
99 | lo->addWidget(m_textLabel);
100 | lo->addWidget(m_textEdit);
101 | lo->addWidget(m_btnBox);
102 |
103 | setSizeGripEnabled(true);
104 | setFontFixedWidth(false);
105 | setWordWrap(true);
106 |
107 | if (!title.isEmpty())
108 | setWindowTitle(title);
109 | if (!text.isEmpty())
110 | setText(text);
111 | if (!details.isEmpty())
112 | setDetailedText(details);
113 | }
114 |
115 |
116 | /*
117 | ScrollableMessageBox::TextEdit
118 | */
119 |
120 | QSize ScrollableMessageBox::TextEdit::sizeHint() const
121 | {
122 | // stupid trick to get an idea of necessary size to contain all the text, works for html and plain
123 | QLabel tmp;
124 | tmp.setFont(font());
125 | tmp.setText(toHtml());
126 | return tmp.sizeHint() + QSize(30, 20);
127 | }
128 |
--------------------------------------------------------------------------------
/examples/imageviewer/GraphicsImageView.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | GraphicsImageView
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #include "GraphicsImageView.h"
29 | #include
30 |
31 | GraphicsImageView::GraphicsImageView(QWidget *p) :
32 | QGraphicsView(new QGraphicsScene, p)
33 | {
34 | setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
35 | setBackgroundRole(QPalette::Shadow);
36 | setViewportUpdateMode(FullViewportUpdate);
37 | setOptimizationFlags(DontSavePainterState | DontClipPainter | DontAdjustForAntialiasing);
38 | setAlignment(Qt::AlignCenter);
39 | setFrameStyle(QFrame::NoFrame);
40 | setLineWidth(0);
41 | }
42 |
43 | void GraphicsImageView::setImageFile(const QString &imageFile)
44 | {
45 | if (m_imageFile != imageFile) {
46 | m_imageFile = imageFile;
47 | loadImage(viewport()->contentsRect().size());
48 | }
49 | }
50 |
51 | void GraphicsImageView::setImageScalingMode(int mode)
52 | {
53 | if (m_scaleMode != Qt::AspectRatioMode(mode)) {
54 | m_scaleMode = Qt::AspectRatioMode(mode);
55 | if (m_item) {
56 | rotationReset();
57 | zoomReset();
58 | scene()->setSceneRect(viewport()->contentsRect());
59 | loadImage(viewport()->contentsRect().size());
60 | }
61 | emit imageScalingModeChanged(m_scaleMode);
62 | }
63 | }
64 |
65 | void GraphicsImageView::setImageScale(qreal scale) const
66 | {
67 | if (m_item && scale > 0.0) {
68 | m_item->setScale(scale);
69 | if (qFuzzyCompare(scale, 1.0))
70 | m_item->setPos(0,0);
71 | emit imageScaleChanged(scale);
72 | }
73 | }
74 |
75 | void GraphicsImageView::setImageRotation(qreal degrees) const
76 | {
77 | if (!m_item)
78 | return;
79 | m_item->setRotation(degrees);
80 | emit imageRotationChanged(degrees);
81 | }
82 |
83 | void GraphicsImageView::loadImage(const QSize &size)
84 | {
85 | if (!scene())
86 | return;
87 | if (m_imageFile.isEmpty()) {
88 | // remove existing image, if any
89 | removeItem();
90 | return;
91 | }
92 |
93 | // Load image at original size
94 | QPixmap pm(m_imageFile);
95 | if (pm.isNull()) {
96 | // file not found/other error
97 | removeItem();
98 | return;
99 | }
100 | // Resize the image here.
101 | pm = pm.scaled(size, m_scaleMode, Qt::SmoothTransformation);
102 | if (createItem()) {
103 | m_item->setPixmap(pm);
104 | m_item->setTransformOriginPoint(m_item->boundingRect().center());
105 | }
106 | if (pm.size().width() < size.width() || pm.size().height() < size.height())
107 | scene()->setSceneRect(QRectF(viewport()->contentsRect().topLeft(), QSizeF(pm.size())));
108 | emit imageChanged();
109 | }
110 |
111 | void GraphicsImageView::resizeEvent(QResizeEvent *e)
112 | {
113 | QGraphicsView::resizeEvent(e);
114 | if (!scene())
115 | return;
116 | // Set scene size to fill the available viewport size;
117 | const QRect sceneRect(viewport()->contentsRect());
118 | scene()->setSceneRect(sceneRect);
119 | // Keep the root item sized to fill the viewport and scene;
120 | if (m_item)
121 | loadImage(sceneRect.size());
122 | }
123 |
124 | bool GraphicsImageView::createItem() {
125 | if (m_item)
126 | return true;
127 | if (!m_item && scene()) {
128 | m_item = new QGraphicsPixmapItem();
129 | m_item->setFlag(QGraphicsItem::ItemIsMovable);
130 | m_item->setTransformationMode(Qt::SmoothTransformation);
131 | m_item->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
132 | m_item->setCursor(Qt::ClosedHandCursor);
133 | scene()->addItem(m_item);
134 | return true;
135 | }
136 | return false;
137 | }
138 |
139 | void GraphicsImageView::removeItem()
140 | {
141 | if (m_item) {
142 | if (scene())
143 | scene()->removeItem(m_item);
144 | delete m_item;
145 | m_item = nullptr;
146 | emit imageChanged();
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/widgets/BuddyLabel.h:
--------------------------------------------------------------------------------
1 | /*
2 | BuddyLabel
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef BUDDYLABEL_H
29 | #define BUDDYLABEL_H
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | /*!
39 | \brief The BuddyLabel class is a QLabel with enhanced "buddy" capabilities.
40 |
41 | It overrides the \c QLabel::setBuddy() method and, besides the usual shortcut handling provided by \c QLabel,
42 | it adds mouse click handling and mirroring of the buddy's tool tip text.
43 |
44 | Mouse clicks are connected to the \c QWidget::setFocus slot. For \c QCheckBox it also connects to the \c click() slot so the box can be (un)checked by clicking on the label.
45 | Mouse double-clicks are connected to \c QLineEdit::selectAll() on widgets which either are or have a \c QLineEdit (like \c QAbstractSpinBox and editable \c QComboBox).
46 | Custom connections could be added by connecting to the \c clicked() and/or \c doubleClicked() signals, or inheriting and overriding the \c connectBuddy() virtual method.
47 | */
48 | class BuddyLabel : public QLabel
49 | {
50 | Q_OBJECT
51 | public:
52 | using QLabel::QLabel;
53 |
54 | public slots:
55 | //! Overrides the \c QLabel::setBuddy() method, which isn't virtual. Calls the base class implementation as well, so the shortcut mechanism still works.
56 | void setBuddy(QWidget *buddy)
57 | {
58 | if (this->buddy()) {
59 | this->buddy()->removeEventFilter(this);
60 | disconnect(this->buddy());
61 | disconnectBuddy(this->buddy());
62 | }
63 |
64 | QLabel::setBuddy(buddy);
65 |
66 | if (!buddy)
67 | return;
68 |
69 | setToolTip(buddy->toolTip());
70 | buddy->installEventFilter(this);
71 | connectBuddy(buddy);
72 | }
73 |
74 | signals:
75 | //! Emitted when label is clicked with left mouse button (or something emulating one).
76 | void clicked();
77 | //! Emitted when label is double-clicked with left mouse button (or something emulating one).
78 | void doubleClicked();
79 |
80 | protected:
81 | //! Override this method for custom connections.
82 | virtual void connectBuddy(QWidget *buddy)
83 | {
84 | // Single clicks
85 | connect(this, &BuddyLabel::clicked, buddy, QOverload<>::of(&QWidget::setFocus));
86 | if (QCheckBox *cb = qobject_cast(buddy))
87 | connect(this, &BuddyLabel::clicked, cb, &QCheckBox::click);
88 |
89 | // Double clicks
90 | if (QLineEdit *le = qobject_cast(buddy))
91 | connect(this, &BuddyLabel::doubleClicked, le, &QLineEdit::selectAll);
92 | else if (QAbstractSpinBox *sb = qobject_cast(buddy))
93 | connect(this, &BuddyLabel::doubleClicked, sb, &QAbstractSpinBox::selectAll);
94 | else if (QComboBox *cb = qobject_cast(buddy))
95 | if (cb->isEditable() && cb->lineEdit())
96 | connect(this, &BuddyLabel::doubleClicked, cb->lineEdit(), &QLineEdit::selectAll);
97 | }
98 |
99 | //! Hook for custom disconnections. We already disconnect ourselves from all slots in \a buddy in the main handler.
100 | virtual void disconnectBuddy(QWidget *buddy) { Q_UNUSED(buddy) }
101 |
102 | //! The filter monitors for tool tip changes on the buddy
103 | bool eventFilter(QObject *obj, QEvent *ev)
104 | {
105 | if (ev->type() == QEvent::ToolTipChange && buddy() && obj == buddy())
106 | setToolTip(buddy()->toolTip());
107 | return false;
108 | }
109 |
110 | void mousePressEvent(QMouseEvent *ev)
111 | {
112 | if (ev->button() == Qt::LeftButton) {
113 | m_pressed = true;
114 | ev->accept();
115 | }
116 | QLabel::mousePressEvent(ev);
117 | }
118 |
119 | void mouseReleaseEvent(QMouseEvent *ev)
120 | {
121 | if (m_pressed && rect().contains(ev->pos()))
122 | emit clicked();
123 | m_pressed = false;
124 | QLabel::mouseReleaseEvent(ev);
125 | }
126 |
127 | void mouseDoubleClickEvent(QMouseEvent *ev)
128 | {
129 | if (ev->button() == Qt::LeftButton && rect().contains(ev->pos()))
130 | emit doubleClicked();
131 | QLabel::mouseDoubleClickEvent(ev);
132 | }
133 |
134 | private:
135 | bool m_pressed = false;
136 | Q_DISABLE_COPY(BuddyLabel)
137 | };
138 |
139 | #endif // BUDDYLABEL_H
140 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # maxLibQt - C++ and QML code library for the *Qt*™ framework.
2 |
3 | This is a (growing) collection of somewhat random C++ classes and QtQuick QML modules for use with the [Qt](http://qt.io)™ framework.
4 |
5 | They are free to use in other open source projects under the terms of the GNU Public License (GPL). For use in
6 | commercial or other closed-source software, you need to contact me for a license agreement. See the LICENSE.txt file.
7 |
8 | The original target Qt library version was 5.2+. Newer components, and some that have been updated more recently,
9 | may require a later version, at least 5.9 (if you run into something that could be made more backwards-compatible,
10 | let me know). Most (if not all) of the C++ code requires C++11 at minimum.
11 |
12 | Project home: https://github.com/mpaperno/maxLibQt
13 |
14 | ## C++ Components:
15 |
16 | * Core
17 | * `AppDebugMessageHandler` - Custom debug/message handler class to work in conjunction with *qDebug()* family of functions.
18 | * Item Models
19 | * `GroupedItemsProxyModel` - A proxy model (`QIdentityProxyModel` subclass) which allows a grouped tree-based item presentation of a flat table data model. Typically used for visually grouping items by some shared criteria, like a category or subject. Useful in a `QTreeView` or the `TreeComboBox` from this collection.
20 | * Layouts
21 | * `OverlayStackLayout` - A QStackedLayout with additional features to allow stacks with "floating" overlay widgets, such as toolbars, buttons,
22 | messages, etc., while still allowing interaction with exposed areas of the widget(s) underneath. Includes a functional image viewer example application.
23 | * Widgets
24 | * `ActionPushButton` - A QPushButton which takes a default QAction, just like a QToolButton can.
25 | * `BuddyLabel` - A QLabel with enhanced "buddy" capabilities like click redirect and tooltip inheritance.
26 | * `CollapsingToolBar` - A QToolBar which toggles between _ToolButtonIconOnly_ and _ToolButtonTextBesideIcon_ styles based on available width.
27 | * `ExportableTableView` - A QTableView with features to export data as plain text or HTML.
28 | * `RoundedMessageBox` - A frameless QMessageBox implementation, fully stylable via CSS or QPalette.
29 | * `ScrollableMessageBox` - A simple message box with a large scrollable area (a QTextEdit) for detailed text (including HTML formatting).
30 | * `TimerEdit` - A time value line editor which accepts negative and large times (> 23:59:59), suitable for a timer, etc.
31 | * `TreeComboBox` - A QComboBox control which works with a tree-based data model & view, allowing drill-down selection of items.
32 |
33 | ## QML Components:
34 |
35 | * Controls
36 | * [`MLDoubleSpinBox`](maxLibQt::controls::MLDoubleSpinBox) - A SpinBox control which handles float/double number types to the desired precision, as well as large integers (within ECMAScript limits). Avoids the `int` size type limitation of the current QtQuick Controls (v2.0 - 2.4) SpinBox but still using the current theme styling (Fusion/Material/etc).
37 | * [`MLHexSpinBox`](maxLibQt::controls::MLHexSpinBox) - A SpinBox control which allows editing integers in hexadeciaml format. Allows a wide range of numbers, including unsigned integers. Based on `MLDoubleSpinBox`.
38 |
39 | ### Documentation:
40 |
41 | Some Doxygen-style documentation is embedded in the code and can be generated as either part of the CMake build or manually with the
42 | included Doxyfile (run `doxygen doc/Doxyfile` in root of this project). To generate docs for *QML code* you'd need to use install [doxyqml](https://github.com/agateau/doxyqml) (the Doxyfile is already configured to use it if you have it). Some of the source code is further documented inline (but never enough).
43 |
44 | This documentation is also published at https://mpaperno.github.io/maxLibQt/
45 |
46 | ### Building (C++):
47 |
48 | Both *CMake* and *qmake* project files are provided. They're set up to be built as individual libraries split by base folder (core, item models, etc). With CMake you can also build one library file which contains all the others (haven't figured out how to do that with qmake yet).
49 |
50 | You can also simply include whichever C++ module(s) you need into your own project. They're standalone unless specifically mentioned otherwise in their docs. There are also `.pri` *qmake* files for each source folder which could be used to quickly include all modules in that folder (and add to the `INCLUDEPATH`).
51 |
52 | ### Using (QML)
53 |
54 | Simplest way to use the QtQuick modules would be to copy them into your code tree.
55 |
56 | To use them as external ("library") imports in your code, put the `maxLibQt` folder (from inside the `/src/quick/` folder of this project) into the QML search path for your app. Then, for example to use `MLDoubleSpinBox`, you'd specify `import maxLibQt.controls 1.0` in your QML.
57 |
58 | -------------
59 | ### Author
60 |
61 | Maxim Paperno
62 | https://github.com/mpaperno/
63 | http://www.WorldDesign.com/contact
64 |
65 | Please inquire about custom C++/Qt development work.
66 |
67 | ### Copyright, License, and Disclaimer
68 |
69 | Copyright (c) Maxim Paperno. All rights reserved.
70 |
71 | See LICENSE.txt file for license details.
72 |
73 | This program is distributed in the hope that it will be useful,
74 | but WITHOUT ANY WARRANTY; without even the implied warranty of
75 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
76 |
77 | *Qt* is a trademark of *The Qt Company* with which neither I nor this project have any affiliation.
78 |
--------------------------------------------------------------------------------
/src/widgets/OverlayStackLayout.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | OverlayStackLayout
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #include "OverlayStackLayout.h"
29 | #include
30 | #include
31 |
32 | namespace {
33 | static const char offsetProperty[15] {"positionOffset"};
34 | }
35 |
36 | OverlayStackLayout::OverlayStackLayout(QWidget * parent) :
37 | QStackedLayout(parent)
38 | {
39 | setStackingMode(StackAll);
40 | }
41 |
42 | OverlayStackLayout::OverlayStackLayout(QLayout * parentLayout) :
43 | QStackedLayout(parentLayout)
44 | {
45 | setStackingMode(StackAll);
46 | }
47 |
48 | int OverlayStackLayout::insertWidget(int index, QWidget *widget, Qt::Alignment alignment)
49 | {
50 | const int ret = insertWidget(index, widget);
51 | if (ret > -1)
52 | setAlignment(widget, alignment);
53 | return ret;
54 | }
55 |
56 | int OverlayStackLayout::insertWidget(int index, QWidget *widget, Qt::Alignment alignment, const QPoint &offset)
57 | {
58 | const int ret = insertWidget(index, widget, alignment);
59 | if (ret > -1)
60 | setOffset(widget, offset);
61 | return ret;
62 | }
63 |
64 | void OverlayStackLayout::setOffset(QWidget *widget, const QPoint &offset) const
65 | {
66 | if (widget->property(offsetProperty).isValid() && widget->property(offsetProperty).toPoint() == offset)
67 | return;
68 | widget->setProperty(offsetProperty, offset);
69 | doLayout();
70 | }
71 |
72 | void OverlayStackLayout::setSenderOffset(const QPoint &offset) const
73 | {
74 | if (QWidget *w = qobject_cast(sender()))
75 | setOffset(w, offset);
76 | }
77 |
78 | void OverlayStackLayout::setSenderAlignment(Qt::Alignment align)
79 | {
80 | if (QWidget *w = qobject_cast(sender()))
81 | setAlignment(w, align);
82 | }
83 |
84 | void OverlayStackLayout::setStackingMode(QStackedLayout::StackingMode mode)
85 | {
86 | if (mode == stackingMode())
87 | return;
88 | QStackedLayout::setStackingMode(mode);
89 | // resetting the mode to StackAll messes with our layout
90 | if (mode == StackAll)
91 | doLayout();
92 | }
93 |
94 | void OverlayStackLayout::setGeometry(const QRect & geo)
95 | {
96 | if (geo == geometry())
97 | return;
98 | QLayout::setGeometry(geo);
99 | doLayout();
100 | }
101 |
102 | void OverlayStackLayout::doLayout() const
103 | {
104 | const int n = count();
105 | if (!n)
106 | return;
107 | for (int i=0; i < n; ++i) {
108 | QWidget *w = nullptr;
109 | if (QLayoutItem *item = itemAt(i))
110 | w = item->widget();
111 | if (!w)
112 | continue;
113 |
114 | // available geometry for widgets, use size based on layout attribute
115 | const QRect rect = w->testAttribute(Qt::WA_LayoutOnEntireRect) ? geometry() : contentsRect();
116 | // widget's desired size
117 | const QSize wSize = w->sizeHint().expandedTo(w->minimumSize()).boundedTo(w->maximumSize());
118 | QRect wRect(rect.topLeft(), wSize); // default widget position and size
119 | QPoint offset(0, 0); // position offset
120 | if (w->property(offsetProperty).isValid())
121 | offset = w->property(offsetProperty).toPoint();
122 |
123 | // expand or constrain to full width?
124 | if ((w->sizePolicy().expandingDirections() & Qt::Horizontal) || wRect.width() > rect.width() - offset.x())
125 | wRect.setWidth(rect.width() - offset.x());
126 |
127 | // expand or constrain to full height?
128 | if ((w->sizePolicy().expandingDirections() & Qt::Vertical) || wRect.height() > rect.height() - offset.y())
129 | wRect.setHeight(rect.height() - offset.y());
130 |
131 | // Adjust position for alignment
132 | const Qt::Alignment align = itemAt(i)->alignment();
133 | if (rect.width() > wRect.width() - offset.x()) {
134 | switch (align & Qt::AlignHorizontal_Mask) {
135 | case Qt::AlignHCenter:
136 | wRect.moveLeft(rect.x() + (rect.width() - wRect.width()) / 2);
137 | break;
138 | case Qt::AlignRight:
139 | wRect.moveRight(rect.right());
140 | break;
141 | case Qt::AlignLeft:
142 | default:
143 | wRect.moveLeft(rect.left());
144 | break;
145 | }
146 | }
147 | if (rect.height() > wRect.height() - offset.y()) {
148 | switch (align & Qt::AlignVertical_Mask) {
149 | case Qt::AlignVCenter:
150 | wRect.moveTop(rect.y() + (rect.height() - wRect.height()) / 2);
151 | break;
152 | case Qt::AlignBottom:
153 | wRect.moveBottom(rect.bottom());
154 | break;
155 | case Qt::AlignTop:
156 | default:
157 | wRect.moveTop(rect.top());
158 | break;
159 | }
160 | }
161 | // adjust for user-defined offset
162 | if (!offset.isNull())
163 | wRect.moveTopLeft(wRect.topLeft() + offset);
164 |
165 | // Set position and size of the widget.
166 | w->setGeometry(wRect);
167 | // Honor the stacking attribute
168 | if (w->testAttribute(Qt::WA_AlwaysStackOnTop))
169 | w->raise();
170 | }
171 | }
172 |
173 | #include "moc_OverlayStackLayout.cpp"
174 |
--------------------------------------------------------------------------------
/examples/imageviewer/ImageGrid.h:
--------------------------------------------------------------------------------
1 | /*
2 | ImageGrid
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef IMAGEGRID_H
29 | #define IMAGEGRID_H
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | /*!
41 | \ingroup examples_imageviewer
42 | */
43 | class ImageGridDelegate : public QStyledItemDelegate
44 | {
45 | Q_OBJECT
46 | public:
47 | using QStyledItemDelegate::QStyledItemDelegate;
48 |
49 | bool showTooltips = true; // respond to tooltip help event
50 | qreal frameWidth = 2.0; // selection frame size
51 |
52 | void paint(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &idx) const override;
53 | QPixmap pixmap(const QModelIndex &idx, const QSize &size) const;
54 | bool helpEvent(QHelpEvent *e, QAbstractItemView *v, const QStyleOptionViewItem &, const QModelIndex &idx) override;
55 | QSize sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &) const override { return opt.decorationSize; }
56 |
57 | protected:
58 | // we don't use this, but provide an optimized version in case this gets called internally in superclass
59 | void initStyleOption(QStyleOptionViewItem *o, const QModelIndex &idx) const override { o->index = idx; }
60 | private:
61 | Q_DISABLE_COPY(ImageGridDelegate)
62 | };
63 |
64 |
65 | /*!
66 | \ingroup examples_imageviewer
67 |
68 | \c ImageGrid is a \c QListView using a custom \c ImageGridDelegate to present a grid of "previews" of images in a given folder.
69 | The number of columns used to show the images, or the image preview sizes can set explicitly, or both can auto-adjust based
70 | on the available space (this is the default setting).
71 |
72 | Note that the global \c QPixmapCache is used to store image previews by the \c ImageGridDelegate class. It should be suitably "large."
73 | */
74 | class ImageGrid : public QListView
75 | {
76 | Q_OBJECT
77 | Q_PROPERTY(int count READ count NOTIFY countChanged)
78 | Q_PROPERTY(QString currentPath READ currentPath WRITE setCurrentPath NOTIFY currentPathChanged)
79 | Q_PROPERTY(QString currentImage READ currentImage WRITE setCurrentImage NOTIFY currentImageChanged)
80 | Q_PROPERTY(int currentFileIndex READ currentFileIndex WRITE setCurrentFile NOTIFY currentFileIndexChanged)
81 | Q_PROPERTY(QDir::SortFlags sortFlags READ sortFlags WRITE sortBy NOTIFY sortChanged)
82 | Q_PROPERTY(QSize imageSize READ imageSize WRITE setImageSize NOTIFY imageSizeChanged)
83 | Q_PROPERTY(int columns READ columns WRITE setColumns)
84 |
85 | public:
86 | explicit ImageGrid(QWidget *p = nullptr);
87 |
88 | int count() const;
89 | QString currentPath() const;
90 | QString currentImage() const;
91 | int currentFileIndex() const;
92 | QDir::SortFlags sortFlags() const;
93 | QSize imageSize() const;
94 | int columns() const;
95 |
96 | public slots:
97 | void setCurrentPath(const QString &path);
98 | void setCurrentImage(const QString &imageFile);
99 | void setCurrentFile(int index);
100 | void nextImage() { selectImage(findNextImage(false)); }
101 | void prevImage() { selectImage(findNextImage(true)); }
102 | void sortBy(QDir::SortFlags flags);
103 | void setColumns(uint columns = 0);
104 | void setImageSize(const QSize &size = QSize());
105 |
106 | signals:
107 | void countChanged(int count);
108 | void currentPathChanged(const QString &path) const;
109 | void imageSelected(const QString &filename) const;
110 | void currentImageChanged(const QString &filename) const;
111 | void currentFileIndexChanged(int index) const;
112 | void sortChanged(QDir::SortFlags flags) const;
113 | void imageSizeChanged(const QSize &size) const;
114 |
115 | protected:
116 | void resizeEvent(QResizeEvent *e) override;
117 |
118 | private slots:
119 | void selectImage(const QModelIndex &idx);
120 | void updateLayout(bool force = false);
121 | void resetModel();
122 |
123 | private:
124 | uint columnsForWidth(int w) const;
125 | QModelIndex findNextImage(bool prevoius = false);
126 |
127 | QFileSystemModel *m_model = nullptr;
128 | uint m_columns = 0;
129 | QDir::SortFlags m_sortBy = QDir::Name;
130 | QSize m_icnSize;
131 |
132 | Q_DISABLE_COPY(ImageGrid)
133 | };
134 |
135 | inline
136 | int ImageGrid::count() const { return m_model ? m_model->rowCount(rootIndex()) : 0; }
137 |
138 | inline
139 | QString ImageGrid::currentPath() const { return m_model ? m_model->rootPath() : QString(); }
140 |
141 | inline
142 | QString ImageGrid::currentImage() const {
143 | return currentIndex().isValid() ?
144 | currentIndex().data(QFileSystemModel::FilePathRole).toString() :
145 | QString();
146 | }
147 |
148 | inline
149 | int ImageGrid::currentFileIndex() const { return currentIndex().row(); }
150 |
151 | inline
152 | QDir::SortFlags ImageGrid::sortFlags() const { return m_sortBy; }
153 |
154 | inline
155 | QSize ImageGrid::imageSize() const { return m_icnSize; }
156 |
157 | inline
158 | int ImageGrid::columns() const { return m_columns; }
159 |
160 | inline
161 | void ImageGrid::resizeEvent(QResizeEvent *e)
162 | {
163 | QListView::resizeEvent(e);
164 | updateLayout();
165 | }
166 |
167 | #endif // IMAGEGRID_H
168 |
--------------------------------------------------------------------------------
/src/widgets/ExportableTableView.h:
--------------------------------------------------------------------------------
1 | /*
2 | ExportableTableView
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef EXPORTABLETABLEVIEW_H
29 | #define EXPORTABLETABLEVIEW_H
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 |
42 | /**
43 | \class ExportableTableView
44 | \version 1.0.1
45 |
46 | \brief The ExportableTableView class provides a regular QTableView but with features to export the data
47 | as plain text or HTML.
48 |
49 | Any selection of data can be exported. The horizontal headings, if any, are included in the export.
50 | The export functions are available to the user via a custom context menu or keyboard shortcuts.
51 | 3 field delimiter choices are available for plain-text export (tab, comma, or pipe).
52 | Data can be saved to the clipboard or to a file.
53 |
54 | The export functions can also be accessed programmatically via \c toPlainText(), \c toHtml(),
55 | and \c saveToFile().
56 |
57 | The style sheet for the generated page can be set with \c setHtmlStyle(). The overall HTML
58 | template can be customized with \c setHtmlTemplate(). The data itself is always
59 | formatted as a basic HTML table and then inserted into the template at the \c %2 placeholder.
60 |
61 | HTML version tries to preserve many data role attributes of the model items:
62 |
63 | \li \c Qt::FontRole
64 | \li \c Qt::ForegroundRole
65 | \li \c Qt::BackgroundRole
66 | \li \c Qt::TextAlignmentRole
67 | \li \c Qt::ToolTipRole
68 |
69 | Note that \c Qt::EditRole data is not specifically preserved (unless it already matches \c Qt::DisplayRole ).
70 |
71 | */
72 | class ExportableTableView : public QTableView
73 | {
74 | Q_OBJECT
75 |
76 | public:
77 | /*!
78 | \brief ExportableTableView
79 | \param parent
80 | */
81 | ExportableTableView(QWidget *parent = Q_NULLPTR);
82 |
83 | /*! \brief Get the default style sheet used for HTML export. */
84 | static QString getDefaultHtmlStyle();
85 | /*! \brief Get the default overall template used for HTML export. */
86 | static QString getDefaultHtmlTemplate();
87 | /*!
88 | \brief Set the style sheet for HTML export. This goes between the \c "" tags of the page.
89 | \param value The CSS code.
90 | */
91 | void setHtmlStyle(const QString &value);
92 | /*!
93 | \brief Set the overall template for HTML export. The template must have two placeholders: \c %1 for the style and \c %2 for the table.
94 | \param value The HTML code.
95 | */
96 | void setHtmlTemplate(const QString &value);
97 |
98 | /*!
99 | \brief Saves data from the passed model indices to a text string.
100 | \param indexList A list of indices to export.
101 | \param delim The delimiter to use. Can be multiple characters (eg. \c ", ") Default is a single TAB.
102 | \return \e QString with the formatted text. If the data has column headings, these are on the first line.
103 | */
104 | QString toPlainText(const QModelIndexList &indexList, const QString &delim = "\t") const;
105 |
106 | /*!
107 | \brief Saves data from the passed model indices to an HTML-formatted string.
108 | \param indexList A list of indices to export.
109 | \return \e QString with the formatted HTML. If the data has column headings, these are included as table headings.
110 | */
111 | QString toHtml(const QModelIndexList &indexList) const;
112 |
113 | /*!
114 | Saves data from the passed model indices to a file in text or HTML format. The file extension determines
115 | the format/text delimiter: \e \.html for HTML, \e \.tab for TAB, \e \.csv for CSV, and anything else
116 | is pipe-delimited.
117 |
118 | \param indexList A list of indices to export.
119 | \param fileName An optional file name (with path). If none is passed then a file chooser dialog is presented.
120 | \return true on success, false on failure (no file selected or write error)
121 | */
122 | bool saveToFile(const QModelIndexList &indexList, const QString &fileName = QString());
123 |
124 | /*! \brief Return a list of selected cells. If none are selected then it first selects the whole table. */
125 | QModelIndexList getSelectedOrAll();
126 |
127 | QSize sizeHint() const Q_DECL_OVERRIDE;
128 |
129 | public slots:
130 | /*!
131 | \brief Copies currently selected cell(s) to the clipboard as plain text.
132 | \param delim The delimiter to use. Can be multiple characters (eg. ", ") Default is a single TAB.
133 | */
134 | void copyText(const QString &delim = QString("\t"));
135 | /*! \brief Copies currently selected cell(s) to the clipboard as HTML (with MIME content-type \e "text/html"). */
136 | void copyHtml();
137 | /*! \brief Designed for \e QAction connections, it calls \c copyText() or \c copyHtml() based on delimiter specified in \c sender()->property\("delim"\) (use "html" as \p delim for HTML format). */
138 | void copy();
139 | /*! \brief Saves currently selected cell(s) to a file of the user's choice and format. */
140 | void save();
141 |
142 | protected:
143 | void onCustomContextMenuRequested(const QPoint &pos);
144 |
145 | QString m_htmlTemplate;
146 | QString m_htmlStyle;
147 | };
148 |
149 | #endif // EXPORTABLETABLEVIEW_H
150 |
--------------------------------------------------------------------------------
/doc/qttagsfix.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | __copyright__ = """
3 | COPYRIGHT: (c)2020 Maxim Paperno; All Rights Reserved.
4 |
5 | Released under MIT License:
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy of
8 | this software and associated documentation files (the "Software"), to deal in
9 | the Software without restriction, including without limitation the rights to
10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 | the Software, and to permit persons to whom the Software is furnished to do so,
12 | subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 | """
24 |
25 | DESCRIPTION = """
26 | A utility to fix Qt's Doxygen-compatible tag files which have invalid data
27 | for enum types and values, as per QTBUG-61790. It can be used as a standalone
28 | utility, or imported as a package to call the fixEnums() method directly.
29 |
30 | It reads a given input file, applies the fixes, and writes the fixed XML
31 | back to a file (either the same one as the input, or a new one).
32 |
33 | It looks for tags improperly formatted like this:
34 |
35 |
36 | Orientation
37 | Orientation-enum
38 |
39 |
40 |
41 | Orientation-enum
42 |
43 |
44 |
45 | And converts them to:
46 |
47 |
48 | Orientation
49 | qt.html
50 | Orientation-enum
51 |
52 |
53 | Horizontal
54 | qt.html
55 | Orientation-enum
56 |
57 |
58 | One limitation is that there is no way to detect deprecated enums, so those
59 | will still be linked to the parent's main docs page, instead of the "-obsolete"
60 | page where they actually are listed.
61 | """
62 |
63 | import sys
64 | import argparse
65 | import xml.etree.ElementTree as ET
66 |
67 | # used as parser target for preserving comments in XML
68 | class CommentedTreeBuilder(ET.TreeBuilder):
69 | def comment(self, data):
70 | self.start(ET.Comment, {})
71 | self.data(data)
72 | self.end(ET.Comment)
73 |
74 |
75 | # to strip useless arglist tag
76 | def stripEmptyArglist(parent):
77 | alist = parent.find('arglist')
78 | if alist is not None and not alist.text:
79 | parent.remove(alist)
80 | lastEl = parent.find('anchor')
81 | if lastEl is not None:
82 | parent.find('anchor').tail = parent.tail
83 |
84 |
85 | # Fix enum members in Qt tagfiles (QTBUG-61790).
86 | # `infile` is the full path to the XML tag file to process.
87 | # If `outfile` is `None` (default) then the `infile` file is overwritten.
88 | def fixEnums(infile, outfile=None):
89 | if not outfile:
90 | outfile = infile
91 | tree = ET.parse(infile, ET.XMLParser(target=CommentedTreeBuilder()))
92 | root = tree.getroot()
93 |
94 | # iterate over all compounds with named enum members
95 | for comp in root.iterfind(".//member[@kind='enum']/name/../.."):
96 | if comp is None:
97 | continue
98 | # the file name of the parent compound becomes the for enum links
99 | fn = comp.find('filename')
100 | if fn is None:
101 | continue
102 | # Skip the whole compound if it doesn't have the broken enum syntax anywhere
103 | # (we could probably just bail out entirely here, but who knows...)
104 | if comp.find(".//member[@name]") is None:
105 | continue
106 |
107 | # get line break and indent level for inserting elements
108 | tail = comp.find("./member/name").tail
109 | # element needed for enumeration and enumvalue tags
110 | afileEl = ET.Element('anchorfile')
111 | afileEl.text = fn.text
112 | afileEl.tail = tail
113 |
114 | # fix tags (change to kind="enumeration" and add )
115 | for enum in comp.iterfind(".//member[@kind='enum']"):
116 | if enum.find('anchorfile') is not None:
117 | continue # has anchorfile element, must have been fixed already
118 | enum.set('kind', "enumeration")
119 | enum.insert(1, afileEl)
120 | stripEmptyArglist(enum)
121 |
122 | # fix enum value members which appear as but should
123 | # be add elements
124 | for enumval in comp.iterfind(".//member[@name]"):
125 | ename = enumval.get('name')
126 | if ename is None or enumval.find('name') is not None:
127 | continue # must have been fixed already
128 | enumval.attrib.clear() # remove 'name' attrib
129 | enumval.set('kind', "enumvalue")
130 | nameEl = ET.Element('name')
131 | nameEl.text = ename
132 | nameEl.tail = tail
133 | enumval.insert(0, nameEl)
134 | enumval.insert(1, afileEl)
135 | stripEmptyArglist(enumval)
136 |
137 | # write out the new XML
138 | tree.write(outfile, encoding="UTF-8", xml_declaration=True)
139 |
140 |
141 | def main():
142 | parser = argparse.ArgumentParser(
143 | formatter_class=argparse.RawDescriptionHelpFormatter,
144 | description=DESCRIPTION)
145 |
146 | parser.add_argument("infile",
147 | help="Input XML tagfile to fix.")
148 | parser.add_argument("outfile",
149 | help="Output file for fixed XML. Use 'overwrite' to modify the input file itself.")
150 |
151 | opts = parser.parse_args();
152 |
153 | if opts.outfile == "overwrite":
154 | opts.outfile = opts.infile
155 |
156 | print("Fixing up tagfile: " + opts.infile)
157 | print("Output to: " + opts.outfile)
158 |
159 | fixEnums(opts.infile, opts.outfile)
160 |
161 | return 0
162 |
163 |
164 | if __name__ == "__main__":
165 | sys.exit(main())
166 |
--------------------------------------------------------------------------------
/src/widgets/RoundedMessageBox.h:
--------------------------------------------------------------------------------
1 | /*
2 | RoundedMessageBox
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef ROUNDEDMESSAGEBOX_H
29 | #define ROUNDEDMESSAGEBOX_H
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | /*!
38 | \brief The RoundedMessageBox class is a frameless \c QMessageBox implementation.
39 | It can be styled using using either QSS (CSS) or by using a custom \c QPalette
40 | in combination with the regular \c QWidget::setForegroundRole() and \c QWidget::setBackgroundRole().
41 |
42 | This was originally created as an answer to a [StackOverflow question][1]. It is very simple, only
43 | re-implementing the \c paintEvent(). There is no way for the user to move the message box around
44 | on the screen (allowing to drag it by the contents area would be a nice TODO).
45 |
46 | Here is an example of using the message box with both ways of styling it, also from the SO answer (see link for screenshot).
47 | \code
48 | int main(int argc, char *argv[])
49 | {
50 | //QApplication::setStyle("Fusion");
51 | QApplication app(argc, argv);
52 |
53 | // Dialog setup
54 | RoundedMessageBox *msgBox = new RoundedMessageBox();
55 | msgBox->setAttribute(Qt::WA_DeleteOnClose);
56 | msgBox->setMinimumSize(300, 300);
57 | msgBox->setWindowTitle("Frameless window test");
58 | msgBox->setText("Frameless rounded message box.
");
59 | msgBox->setInformativeText("Lorem ipsum dolor sit amet, consectetur ....");
60 |
61 | // Styling: two options with the same (garish) result.
62 | if (1) {
63 | // Use QSS style
64 | app.setStyleSheet(QStringLiteral(
65 | "QMessageBox { "
66 | "border-radius: 12px; "
67 | "border: 3.5px solid; "
68 | "border-color: qlineargradient(x1: 1, y1: 1, x2: 0, y2: 0, stop: 0 #ffeb7f, stop: 1 #d09d1e); "
69 | "background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #ffeb7f, stop: 1 #d09d1e); "
70 | "color: #003200; "
71 | "}"
72 | ));
73 | }
74 | else {
75 | // Use "native" styling
76 | msgBox->radius = 12.0;
77 | msgBox->borderWidth = 3.5;
78 |
79 | QLinearGradient bgGrad(0, 0, 1, 1);
80 | bgGrad.setCoordinateMode(QGradient::ObjectMode);
81 | bgGrad.setColorAt(0.0, QColor("gold").lighter());
82 | bgGrad.setColorAt(1.0, QColor("goldenrod").darker(105));
83 | QLinearGradient fgGrad(bgGrad);
84 | fgGrad.setStart(bgGrad.finalStop());
85 | fgGrad.setFinalStop(bgGrad.start());
86 |
87 | QPalette pal;
88 | pal.setBrush(QPalette::Window, QBrush(bgGrad));
89 | pal.setBrush(QPalette::Mid, QBrush(fgGrad));
90 | pal.setBrush(QPalette::WindowText, QColor("darkgreen").darker());
91 | msgBox->setPalette(pal);
92 |
93 | msgBox->setForegroundRole(QPalette::Mid); // default is WindowText
94 | msgBox->setBackgroundRole(QPalette::Window); // this is actually the default already
95 | }
96 |
97 | msgBox->show();
98 | return app.exec();
99 | }
100 | \endcode
101 |
102 | [1]: https://stackoverflow.com/questions/58145272/qdialog-with-rounded-corners-have-black-corners-instead-of-being-translucent/58151965#58151965
103 | */
104 | class RoundedMessageBox : public QMessageBox
105 | {
106 | Q_OBJECT
107 | public:
108 | explicit RoundedMessageBox(QWidget *parent = nullptr) :
109 | QMessageBox(parent)
110 | {
111 | // The FramelessWindowHint flag and WA_TranslucentBackground attribute are vital.
112 | setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
113 | setAttribute(Qt::WA_TranslucentBackground);
114 | }
115 |
116 | qreal radius = 0.0; //!< desired radius in absolute pixels
117 | qreal borderWidth = -1.0; //!< -1 = use style hint frame width; 0 = no border; > 0 = use this width.
118 |
119 | protected:
120 | void paintEvent(QPaintEvent *) override
121 | {
122 | if (!(windowFlags() & Qt::FramelessWindowHint) && !testAttribute(Qt::WA_TranslucentBackground))
123 | return; // nothing to do
124 |
125 | QPainter p(this);
126 | p.setRenderHint(QPainter::Antialiasing);
127 |
128 | // Have style sheet?
129 | if (testAttribute(Qt::WA_StyleSheetTarget)) {
130 | // Let QStylesheetStyle have its way with us.
131 | QStyleOption opt;
132 | opt.initFrom(this);
133 | style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
134 | p.end();
135 | return;
136 | }
137 |
138 | // Paint thyself.
139 | QRectF rect(QPointF(0, 0), size());
140 | // Check for a border size.
141 | qreal penWidth = borderWidth;
142 | if (penWidth < 0.0) {
143 | QStyleOption opt;
144 | opt.initFrom(this);
145 | penWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this);
146 | }
147 | // Got pen?
148 | if (penWidth > 0.0) {
149 | p.setPen(QPen(palette().brush(foregroundRole()), penWidth));
150 | // Ensure border fits inside the available space.
151 | const qreal dlta = penWidth * 0.5;
152 | rect.adjust(dlta, dlta, -dlta, -dlta);
153 | }
154 | else {
155 | // QPainter comes with a default 1px pen when initialized on a QWidget.
156 | p.setPen(Qt::NoPen);
157 | }
158 | // Set the brush from palette role.
159 | p.setBrush(palette().brush(backgroundRole()));
160 | // Got radius? Otherwise draw a quicker rect.
161 | if (radius > 0.0)
162 | p.drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
163 | else
164 | p.drawRect(rect);
165 |
166 | // C'est finí
167 | p.end();
168 | }
169 | };
170 |
171 | #endif // ROUNDEDMESSAGEBOX_H
172 |
--------------------------------------------------------------------------------
/examples/imageviewer/imageviewer.css:
--------------------------------------------------------------------------------
1 |
2 | /* Toobar buttons */
3 | Toolbar QToolButton {
4 | font: bold normal 14px sans-serif;
5 | color: #62777F;
6 | background: transparent;
7 | border-radius: 12px;
8 | padding: 3px 6px 4px;
9 | max-height: 1.2em;
10 | }
11 | Toolbar QToolButton:checked,
12 | Toolbar QToolButton:hover {
13 | color: #D5F2E5;
14 | background-color: #62777F;
15 | }
16 | Toolbar QToolButton:pressed { background-color: #72AF95; }
17 | Toolbar QToolButton:disabled { color: gray; }
18 | Toolbar[orientation='2'] QToolButton { width: 100%; max-width: 4em; }
19 |
20 | /* split buttons */
21 | Toolbar QToolButton[align='left'], Toolbar QToolButton[align='middle'] {
22 | border-top-right-radius: 0; border-bottom-right-radius: 0; border-right: .5px solid gray; }
23 | Toolbar QToolButton[align='right'], Toolbar QToolButton[align='middle'] {
24 | border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: .5px solid gray; }
25 | Toolbar QToolButton[align='middle'] { padding-left: 0; padding-right: 0; }
26 |
27 | /* menu buttons */
28 | Toolbar QToolButton[popupMode='2'] { padding-right: 15px; }
29 | Toolbar QToolButton[popupMode='2'][align='left'] { padding-left: 15px; padding-right: 9px; }
30 | Toolbar QToolButton[popupMode='2'][align='right'] { padding-left: 9px; }
31 | Toolbar[orientation='2'] QToolButton[popupMode='2'] { padding-right: 6px; padding-left: 0; }
32 | Toolbar[orientation='2'] QToolButton[popupMode='2'][align='left'] { padding-left: 9px; padding-right: 0; }
33 | Toolbar[orientation='2'] QToolButton[popupMode='2'][align='right'] { padding-right: 9px; padding-left: 0; }
34 | Toolbar QToolButton::menu-indicator { subcontrol-origin: border; subcontrol-position: bottom right; right: 3px; }
35 | Toolbar QToolButton[align='left']::menu-indicator { subcontrol-position: bottom left; left: 3px; right: 0; }
36 |
37 | /* Toobar separator */
38 | Toolbar #tb_separator { background-color: palette(dark); }
39 | Toolbar[orientation='1'] #tb_separator { min-width: 1px; min-height: 2ex; margin: 1ex 0; }
40 | Toolbar[orientation='2'] #tb_separator { min-height: 1px; min-width: 2ex; margin: 0 1ex; }
41 |
42 | /* Toobar style */
43 | Toolbar {
44 | margin: 0;
45 | qproperty-spacing: 14;
46 | qproperty-hSizePolicy: 1; /* QSizePolicy::Minimum */
47 | qproperty-vSizePolicy: 1;
48 | }
49 | Toolbar[orientation='2'] { qproperty-spacing: 20; }
50 | Toolbar[edge='1'] { qproperty-alignment: 36; } /* top: AlignHCenter|AlignTop */
51 | Toolbar[edge='2'] { qproperty-alignment: 33; } /* left: AlignLeft|AlignTop; */
52 | Toolbar[edge='4'] { qproperty-alignment: 34; } /* right: AlignRogjt|AlignTop; */
53 | Toolbar[edge='8'] { qproperty-alignment: 68; } /* bottom: AlignHCenter|AlignBottom; */
54 |
55 | /* Toobar Gradient background theme */
56 | Toolbar[theme='0'][orientation='1'] { qproperty-hSizePolicy: 7; } /* QSizePolicy::Expanding */
57 | Toolbar[theme='0'][orientation='2'] { qproperty-vSizePolicy: 7; } /* QSizePolicy::Expanding */
58 | Toolbar[theme='0'][edge='1'] {
59 | padding: 14px 3px 28px 3px;
60 | background: qlineargradient(x1:0,y1:0,x2:0,y2:1, stop: 0 black, stop: 1 transparent);
61 | }
62 | Toolbar[theme='0'][edge='2'] {
63 | padding: 14px 28px 14px 14px;
64 | background: qlineargradient(x1:0,y1:0,x2:1,y2:0, stop: 0 black, stop: 1 transparent);
65 | }
66 | Toolbar[theme='0'][edge='4'] {
67 | padding: 14px 14px 14px 28px;
68 | background: qlineargradient(x1:1,y1:0,x2:0,y2:0, stop: 0 black, stop: 1 transparent);
69 | }
70 | Toolbar[theme='0'][edge='8'] {
71 | padding: 28px 3px 14px 3px;
72 | background: qlineargradient(x1:0,y1:1,x2:0,y2:0, stop: 0 black, stop: 1 transparent);
73 | }
74 | Toolbar[theme='0'] QToolButton { color: #D5F2E5; background: #62777F; }
75 | Toolbar[theme='0'] QToolButton:checked,
76 | Toolbar[theme='0'] QToolButton:hover:!pressed { color: #62777F; background: #D5F2E5; }
77 | Toolbar[theme='0'] QToolButton:disabled { background: #8862777F; }
78 |
79 | /* Toobar Solid background theme */
80 | Toolbar[theme='1'] {
81 | background: qlineargradient(x1:0,y1:0,x2:0,y2:1, stop: 0 #ACBFB6, stop: .4 #E5F9F1, stop: .5 #E3F7F7, stop: .6 #E5F9F1, stop: 1 #ACBFB6);
82 | border-radius: 14px;
83 | padding: 4px;
84 | }
85 | Toolbar[theme='1'][orientation='2'] {
86 | background: qlineargradient(x1:0,y1:0,x2:1,y2:0, stop: 0 #ACBFB6, stop: .3 #E5F9F1, stop: .5 #E3F7F7, stop: .7 #E5F9F1, stop: 1 #ACBFB6);
87 | }
88 | Toolbar[theme='1'][edge='1'] { margin-top: 10px; }
89 | Toolbar[theme='1'][edge='2'] { margin-top: 10px; margin-left: 10px; }
90 | Toolbar[theme='1'][edge='4'] { margin-top: 10px; margin-right: 10px; }
91 | Toolbar[theme='1'][edge='8'] { margin-bottom: 10px; }
92 |
93 | /* Toobar for main menu, just used as transparent container. */
94 | Toolbar[theme='2'] {
95 | background: transparent;
96 | border-radius: 24px;
97 | padding: 1px;
98 | border: 0; margin: 0;
99 | min-width: 48px;
100 | min-height: 48px;
101 | qproperty-spacing: 0;
102 | }
103 | /* Main menu floating button */
104 | Toolbar[theme='2'] QToolButton {
105 | font: 900 normal 24px monospace;
106 | padding: 0;
107 | border-radius: 24px;
108 | min-width: 48px;
109 | min-height: 48px;
110 | color: #F3EFF7;
111 | background: #991CB3EA;
112 | }
113 | Toolbar[theme='2'] QToolButton:hover { background: #BB731EEA; }
114 | Toolbar[theme='2'] QToolButton:pressed { background: #CCAE1EE8;}
115 | Toolbar[theme='2'] QToolButton::menu-indicator { image: null; }
116 |
117 | /* Floating "toast" message box */
118 | Infobox {
119 | border: 0;
120 | border-radius: 12px;
121 | background: #88000000;
122 | color: palette(highlighted-text);
123 | padding: 10px 20px;
124 | font-size: 16px;
125 | }
126 |
127 | /* Image grid style */
128 | ImageGrid { padding: 24px; padding-right: 0; }
129 | ImageGrid QScrollBar:vertical {
130 | width: 14px;
131 | padding: 2px;
132 | border-radius: 6px;
133 | background: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, stop: 0.0 #121212, stop: 0.5 #282828, stop: 1 #121212);
134 | }
135 | ImageGrid QScrollBar::handle:vertical {
136 | min-height: 20px;
137 | border-radius: 5px;
138 | background: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #383838, stop: 0.3 #0A516D, stop: 0.7 #0A516D, stop: 1 #383838);
139 | }
140 | ImageGrid QScrollBar::add-page:vertical,
141 | ImageGrid QScrollBar::add-line:vertical,
142 | ImageGrid QScrollBar::sub-page:vertical,
143 | ImageGrid QScrollBar::sub-line:vertical {
144 | background: none;
145 | }
146 |
147 | #ImageGridView_toolbar[edge='4'] {
148 | margin-right: 16px; /* for scrollbar */
149 | }
150 |
--------------------------------------------------------------------------------
/doc/qt-tagfiles.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | __copyright__ = """
3 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
4 |
5 | Released under MIT License:
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy of
8 | this software and associated documentation files (the "Software"), to deal in
9 | the Software without restriction, including without limitation the rights to
10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 | the Software, and to permit persons to whom the Software is furnished to do so,
12 | subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 | """
24 |
25 | DESCRIPTION = """
26 | A utility to find Doxygen-compatible .tags files in a Qt Docs installation
27 | folder and either concatenate them all into one large .tags file (for online
28 | linking), or copy them all to a single folder (for QHP generation with locally
29 | linked references). This simplifies use with Doxygen's TAGFILES feature since
30 | the tagfiles can be referenced from a constant location relative to the other
31 | Doxygen-related assets.
32 |
33 | This utility also fixes enumeration tags which are borked as per QTBUG-61790.
34 | The fixes are applied to the file copy(ies), the originals are not modified.
35 | """
36 |
37 | HELPTEXT = """
38 | argument should point to a Qt documentation installation folder,
39 | for example "/usr/qt/Docs/Qt-5.12.6" or "c:\Qt\Docs\Qt-5.12.6".
40 |
41 | If the docs root argument is not provided, then the value of environment
42 | variables QT_DIR and QTHOME are checked (if both are found, the former takes
43 | precedence). If a valid path was found, it is searched for a "Docs" subfolder
44 | which contains subfolders in the format of "Qt-5.#.#" (where the numbers
45 | represent a Qt version). The newest, or only, version subfolder found is then
46 | used as the Qt docs root.
47 |
48 | The list of modules to import documentation from is essentially a list of
49 | subfolder(s) of this documentation root. Each module folder should contain an
50 | identically named .tags file (eg. /Docs/Qt-5.12.6/qtcore/qtcore.tags).
51 | """
52 |
53 | # Default module set, only import tags for ones we're very likely to need.
54 | QTMODULES = [
55 | "qtcore",
56 | # "qtbluetooth",
57 | # "qtconcurrent",
58 | # "qtdbus", # no tagfile
59 | "qtgui",
60 | # "qtnetwork",
61 | "qtprintsupport",
62 | # "qtqml",
63 | # "qtquick",
64 | # "qtquickcontrols",
65 | # "qtsvg",
66 | "qtwidgets",
67 | ]
68 |
69 | # Default output path/file (only the path part is used when copying with -i opt)
70 | OUTPUT_DEST = "tagfiles/qt.tags"
71 |
72 | import argparse
73 | import glob
74 | import os
75 | import re
76 | import sys
77 | from shutil import copy2
78 | from qttagsfix import fixEnums
79 |
80 | def copyFiles(modules, docsdir, destpath):
81 | for module in modules:
82 | fn = module + ".tags"
83 | tagfile = os.path.join(docsdir, module, fn)
84 | if os.path.isfile(tagfile):
85 | print("Copying tag file: " + tagfile)
86 | copy2(tagfile, destpath)
87 | else:
88 | print("Could not find "+tagfile+", skipping.")
89 | doEnumsFix(os.path.join(destpath, fn))
90 | return 0
91 |
92 |
93 | def concatFiles(modules, docsdir, destfile):
94 | # get Qt version, only for a comment in the created tags file, not vital
95 | qtver = os.getenv("QT_VERSION", os.path.basename(os.path.normpath(docsdir)))
96 | qtver = re.sub("[^\d\.]", "", qtver)
97 | if not qtver:
98 | qtver = "(unknown)"
99 | with open(destfile, "w", encoding="utf-8") as f:
100 | # write an opening header with inserted comment
101 | f.write('\n')
102 | f.write('\n')
103 | f.write("" % (modules, qtver))
105 | for module in modules:
106 | tagfile = os.path.join(docsdir, module, module + ".tags")
107 | if not os.path.isfile(tagfile):
108 | print("Could not find "+tagfile+", skipping.")
109 | continue
110 | with (open(tagfile, "r", encoding="utf-8")) as tf:
111 | print("Adding tag file: " + tagfile)
112 | f.write("\n\n\n\n" % os.path.basename(tagfile))
113 | tagtext = tf.read()
114 | # strip opening header tags from input files
115 | tagtext = tagtext[re.search('\n', tagtext).end():]
116 | # strip closing tag also, but not from the last file
117 | tagtext = tagtext[:tagtext.rfind('\n')]
118 | f.write(tagtext)
119 | f.write('\n')
120 | doEnumsFix(destfile)
121 | return 0
122 |
123 | def doEnumsFix(infile):
124 | print("Fixing up enums in tagfile: " + infile)
125 | fixEnums(infile)
126 |
127 | def main():
128 | parser = argparse.ArgumentParser(
129 | formatter_class=argparse.RawDescriptionHelpFormatter,
130 | description=DESCRIPTION, epilog=HELPTEXT)
131 |
132 | parser.add_argument("docsroot", nargs='?', metavar="",
133 | help="Qt documentation installation folder.")
134 | parser.add_argument("-m", nargs="*", metavar="", default=QTMODULES,
135 | help="Qt module(s) to import (default: %(default)s).")
136 | parser.add_argument("-i", default=False, action="store_true",
137 | help="Copy individual files instead of concatenating.")
138 | parser.add_argument("-o", metavar="", default=OUTPUT_DEST,
139 | help="Output file (if concatenating) or folder (if copying with -i) (default: %(default)s).")
140 |
141 | opts = parser.parse_args();
142 |
143 | if not len(opts.m):
144 | sys.exit("Empty modules list, nothing to import.")
145 |
146 | destfile = os.path.normpath(opts.o)
147 | destpath = destfile
148 | if not opts.i:
149 | destpath = os.path.dirname(destfile)
150 | if not destfile or not os.access(destpath, os.W_OK):
151 | sys.exit("Unable access output location %s (%s)" % (destfile, destpath))
152 | print("Output to: " + destfile)
153 |
154 | docsdir = ""
155 | if opts.docsroot:
156 | docsdir = opts.docsroot
157 | else:
158 | # try to find docs folder based on QTHOME or QT_DIR env. vars
159 | qtdir = os.getenv("QT_DIR", os.getenv('QTHOME', ""))
160 | if qtdir:
161 | qtdir = os.path.join(qtdir, "Docs", "Qt-5*")
162 | dirs = list(filter(os.path.isdir, glob.glob(qtdir)))
163 | if len(dirs):
164 | dirs.sort(key = lambda x: os.path.getctime(x), reverse = True)
165 | docsdir = dirs[0]
166 |
167 | if not docsdir or not os.path.isdir(docsdir) or not os.access(docsdir, os.R_OK):
168 | sys.exit("Unable to determine or access Qt docs directory: " + docsdir)
169 |
170 | print("Using source directory: %s" % docsdir)
171 |
172 | if opts.i:
173 | # just copy individual files
174 | return copyFiles(opts.m, docsdir, destpath)
175 |
176 | # concatenate tagfiles into one
177 | return concatFiles(opts.m, docsdir, destfile)
178 |
179 |
180 | if __name__ == "__main__":
181 | sys.exit(main())
182 |
--------------------------------------------------------------------------------
/examples/imageviewer/ImageViewer.h:
--------------------------------------------------------------------------------
1 | /*
2 | ImageViewer
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef IMAGEVIEWER_H
29 | #define IMAGEVIEWER_H
30 |
31 | #include
32 | #include
33 | #include
34 |
35 | class OverlayStackLayout;
36 | class GraphicsImageView;
37 | class ImageGrid;
38 | QT_BEGIN_NAMESPACE
39 | class QAction;
40 | class QActionGroup;
41 | class QMenu;
42 | class QSpacerItem;
43 | class QToolButton;
44 | QT_END_NAMESPACE
45 |
46 | //! \ingroup examples_imageviewer
47 | class Toolbar : public QFrame
48 | {
49 | Q_OBJECT
50 | Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
51 | Q_PROPERTY(Qt::Edge edge READ edge WRITE setEdge NOTIFY edgeChanged)
52 | Q_PROPERTY(int alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
53 | Q_PROPERTY(int theme READ theme WRITE setTheme NOTIFY themeChanged)
54 | Q_PROPERTY(int spacing READ spacing WRITE setSpacing)
55 | Q_PROPERTY(int hSizePolicy READ hSizePolicy WRITE setHSizePolicy)
56 | Q_PROPERTY(int vSizePolicy READ vSizePolicy WRITE setVSizePolicy)
57 |
58 | public:
59 | explicit Toolbar(QWidget *p = nullptr);
60 |
61 | int theme() const { return m_theme; }
62 | int spacing() const { return m_lo->spacing(); }
63 | Qt::Edge edge() const { return m_edge; }
64 | Qt::Orientation orientation() const { return m_o; }
65 | int alignment() const { return m_align; }
66 | int hSizePolicy() const { return sizePolicy().horizontalPolicy(); }
67 | int vSizePolicy() const { return sizePolicy().verticalPolicy(); }
68 |
69 | QToolButton *addButton(QAction *act, QLayout *lo, Qt::Alignment a = Qt::AlignCenter, const QKeySequence &sc = QKeySequence());
70 | QToolButton *addButton(const QString &ttl, const QString &tip, QLayout *lo, Qt::Alignment a, const QKeySequence &sc = QKeySequence());
71 | QToolButton *addButton(const QString &ttl, const QString &tip, const QKeySequence &sc = QKeySequence()) { return addButton(ttl, tip, m_lo, Qt::AlignCenter, sc); }
72 | QMenu *addMenuButton(const QString &ttl, const QString &tip = QString(), QLayout *lo = nullptr, Qt::Alignment a = Qt::AlignCenter);
73 | QMenu *makeMenuButton(QToolButton *tb, const QString &name = QString()) const;
74 |
75 | QVector addButtonSet(const QStringList &txts, const QStringList &tts, const QVector &scuts = QVector());
76 | QAction *addGroupAction(const QString &ttl, QActionGroup *grp, const QVariant &data = QVariant(), bool chkd = false);
77 | QWidget *addSeparator() const;
78 | QSpacerItem *addSpacer(int w = 0, int h = 0, QSizePolicy::Policy hPol = QSizePolicy::Expanding, QSizePolicy::Policy vPol = QSizePolicy::Expanding) const;
79 | QSpacerItem *addHSpacer() { return addSpacer(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored); }
80 | QSpacerItem *addVSpacer() { return addSpacer(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding); }
81 |
82 | public slots:
83 | void setOrientation(Qt::Orientation o);
84 | void setAlignment(int a);
85 | void setVertical(bool on) { setOrientation(on ? Qt::Vertical : Qt::Horizontal); }
86 | void setHorizontal(bool on) { setVertical(!on); }
87 | void setEdge(Qt::Edge edge);
88 | void setTheme(int idx);
89 | void toggleTheme(bool on) { setTheme(int(on)); }
90 | void setSpacing(int spacing) { m_lo->setSpacing(spacing); }
91 | void setHSizePolicy(int pol) { QSizePolicy p = sizePolicy(); p.setHorizontalPolicy(QSizePolicy::Policy(pol)); setSizePolicy(p); }
92 | void setVSizePolicy(int pol) { QSizePolicy p = sizePolicy(); p.setVerticalPolicy(QSizePolicy::Policy(pol)); setSizePolicy(p); }
93 |
94 | signals:
95 | void orientationChanged(Qt::Orientation o) const;
96 | void edgeChanged(Qt::Edge edge) const;
97 | void themeChanged() const;
98 | void alignmentChanged(Qt::Alignment align) const;
99 |
100 | private:
101 | Qt::Orientation m_o = Qt::Horizontal;
102 | Qt::Edge m_edge = Qt::TopEdge;
103 | Qt::Alignment m_align = Qt::AlignLeft | Qt::AlignTop;
104 | int m_theme = 0;
105 | QBoxLayout *m_lo;
106 |
107 | Q_DISABLE_COPY(Toolbar)
108 | };
109 |
110 |
111 | #include
112 | #include
113 |
114 | QT_BEGIN_NAMESPACE
115 | class QGraphicsOpacityEffect;
116 | class QPropertyAnimation;
117 | class QTimerEvent;
118 | QT_END_NAMESPACE
119 |
120 | //! \ingroup examples_imageviewer
121 | class Infobox : public QLabel
122 | {
123 | Q_OBJECT
124 | Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged)
125 |
126 | public:
127 | explicit Infobox(QWidget *p);
128 | qreal opacity() const;
129 |
130 | public slots:
131 | void showMessage(const QString &msg = QString(), int timeout = 5000);
132 | void setOpacity(qreal opacity);
133 | void fade(bool out = true, int duration = -1);
134 | void fadeIn() { fade(false, -1); }
135 | void fadeIn(int duration) { fade(false, duration); }
136 | void fadeOut() { fade(true, -1); }
137 | void fadeOut(int duration) { fade(true, duration); }
138 |
139 | signals:
140 | void opacityChanged(qreal) const;
141 |
142 | protected:
143 | void timerEvent(QTimerEvent *e) override;
144 |
145 | private:
146 | QGraphicsOpacityEffect *m_opacity;
147 | QPropertyAnimation *m_fade;
148 | QBasicTimer m_msgTim;
149 | };
150 |
151 |
152 | //! \ingroup examples_imageviewer
153 | class ImageViewer : public QWidget
154 | {
155 | Q_OBJECT
156 | public:
157 | explicit ImageViewer(const QString &appCssFile, const QString &imagePath = QString(), QWidget *p = nullptr);
158 |
159 | public slots:
160 | void setImagesPath(const QString &path) const;
161 | void loadImage(const QString &img) const;
162 | void setAppCssFile(const QString &filename);
163 |
164 | signals:
165 | void themeChanged(int theme) const;
166 | void toolbarEdgeChanged(Qt::Edge edge) const;
167 |
168 | private slots:
169 | void showFolderInfo(int count) const;
170 | void showImageInfo(const QString &img) const;
171 | void selectFolder() const;
172 | void setTheme(bool trans);
173 | void updateTheme() const;
174 | void loadStylesheet() const;
175 | void setPaletteColors(bool light = false);
176 | void updateViewPalette() const;
177 |
178 | private:
179 | void setupOptionsMenu();
180 | Toolbar *createPage(QWidget *main);
181 | void setupListViewPage();
182 | void setupImageViewPage();
183 |
184 | int imgInfoTimeout = 6000;
185 | QString appCssFile;
186 | Infobox *msgBox;
187 | ImageGrid *listView;
188 | GraphicsImageView *imageView;
189 | OverlayStackLayout *stackLo;
190 |
191 | Q_DISABLE_COPY(ImageViewer)
192 | };
193 |
194 | #endif // IMAGEVIEWER_H
195 |
--------------------------------------------------------------------------------
/src/widgets/TimerEdit.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | TimerEdit
3 |
4 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
5 | Contact: http://www.WorldDesign.com/contact
6 |
7 | LICENSE:
8 |
9 | Commercial License Usage
10 | Licensees holding valid commercial licenses may use this file in
11 | accordance with the terms contained in a written agreement between
12 | you and the copyright holder.
13 |
14 | GNU General Public License Usage
15 | Alternatively, this file may be used under the terms of the GNU
16 | General Public License as published by the Free Software Foundation,
17 | either version 3 of the License, or (at your option) any later version.
18 |
19 | This program is distributed in the hope that it will be useful,
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 | GNU General Public License for more details.
23 |
24 | A copy of the GNU General Public License is available at .
25 | */
26 |
27 | #include "TimerEdit.h"
28 | #include
29 |
30 | TimerEdit::TimerEdit(QWidget *parent) :
31 | QLineEdit(parent),
32 | m_showSeconds(true),
33 | m_minTime(0),
34 | m_maxTime(24 * 3600 - 1),
35 | m_singleStep(1),
36 | m_pageStep(60),
37 | m_hourDigits(2),
38 | m_validator(new QRegularExpressionValidator(this))
39 | {
40 | setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
41 | setupFormat();
42 | setToolTip(tr("Use mouse scroll or UP/DOWN arrow keys to change time in small steps.\nCTRL + scroll or PAGE UP/DOWN keys to change time in larger steps."));
43 | connect(this, &TimerEdit::textEdited, this, &TimerEdit::textEditedHandler);
44 | }
45 |
46 | int TimerEdit::timeInSeconds() const
47 | {
48 | int ret = 0;
49 |
50 | ret += timepart(text(), TimeParts::Hours) * 3600;
51 | ret += timepart(text(), TimeParts::Minutes) * 60;
52 | ret += timepart(text(), TimeParts::Seconds);
53 | ret *= timepart(text(), TimeParts::Polarity);
54 |
55 | return ret;
56 | }
57 |
58 | int TimerEdit::timepart(const QString &input, TimerEdit::TimeParts part) const
59 | {
60 | int ret = 0;
61 | QRegularExpressionMatch match = m_validator->regularExpression().match(input, 0, QRegularExpression::NormalMatch);
62 |
63 | if (match.hasMatch()) {
64 | switch (part) {
65 | case Hours:
66 | if (!match.captured("hrs").isEmpty())
67 | ret = match.captured("hrs").toInt();
68 | break;
69 |
70 | case Minutes:
71 | if (!match.captured("mins").isEmpty())
72 | ret = match.captured("mins").toInt();
73 | break;
74 |
75 | case Seconds:
76 | if (!match.captured("secs").isEmpty())
77 | ret = match.captured("secs").toInt();
78 | break;
79 |
80 | case Polarity:
81 | if (!match.captured("pol").isEmpty() && !match.captured("pol").compare("-"))
82 | ret = -1;
83 | else
84 | ret = 1;
85 | break;
86 |
87 | default:
88 | break;
89 | }
90 | }
91 |
92 | return ret;
93 | }
94 |
95 |
96 | //// Slots ////
97 |
98 | void TimerEdit::setTime(int seconds)
99 | {
100 | if (seconds > m_maxTime)
101 | seconds = m_maxTime;
102 | else if (seconds < m_minTime)
103 | seconds = m_minTime;
104 |
105 | div_t qrM = div(abs(seconds), 60);
106 | div_t qrH = div(qrM.quot, 60);
107 |
108 | QString val = QString("%1").arg(qrH.rem, 2, 10, QChar('0'));
109 |
110 | if (m_hourDigits)
111 | val.prepend(QString("%1:").arg(qrH.quot, m_hourDigits, 10, QChar('0')));
112 |
113 | if (m_showSeconds)
114 | val.append(QString(":%1").arg(qrM.rem, 2, 10, QChar('0')));
115 |
116 | if (m_minTime < 0) {
117 | if (seconds < 0)
118 | val.prepend("-");
119 | else
120 | val.prepend(" ");
121 | }
122 |
123 | if (text().compare(val)) {
124 | setText(val);
125 | }
126 | }
127 |
128 | void TimerEdit::incrDecr(int seconds)
129 | {
130 | if (!seconds)
131 | return;
132 |
133 | const int cs = timeInSeconds();
134 | int s = seconds + cs;
135 | if (s < m_minTime)
136 | s = m_minTime;
137 | if (s > m_maxTime)
138 | s = m_maxTime;
139 | if (s != cs) {
140 | setTime(s);
141 | emitValueChanged();
142 | }
143 | }
144 |
145 | void TimerEdit::setTimeRange(int minSeconds, int maxSeconds)
146 | {
147 | if (minSeconds <= maxSeconds && (m_minTime != minSeconds || m_maxTime != maxSeconds)) {
148 | int digits = 0;
149 | if (maxSeconds >= 3600)
150 | digits = floorf(logf(round(maxSeconds / 3600)) / logf(10.0f)) + 1;
151 |
152 | bool mod = (m_hourDigits != digits || std::signbit((float)m_minTime) != std::signbit((float)minSeconds));
153 |
154 | m_minTime = minSeconds;
155 | m_maxTime = maxSeconds;
156 | m_hourDigits = digits;
157 |
158 | if (mod)
159 | setupFormat();
160 | }
161 | }
162 |
163 | void TimerEdit::setMinimumTime(int minSeconds)
164 | {
165 | setTimeRange(minSeconds, (m_maxTime > minSeconds ? m_maxTime : minSeconds));
166 | }
167 |
168 | void TimerEdit::setMaximumTime(int maxSeconds)
169 | {
170 | setTimeRange((m_minTime < maxSeconds ? m_minTime : maxSeconds), maxSeconds);
171 | }
172 |
173 | void TimerEdit::setShowSeconds(bool showSeconds)
174 | {
175 | if (m_showSeconds != showSeconds) {
176 | m_showSeconds = showSeconds;
177 | setupFormat();
178 | }
179 | }
180 |
181 |
182 | //// Protected ////
183 |
184 | void TimerEdit::textEditedHandler()
185 | {
186 | int sec = timeInSeconds();
187 | if (sec < m_minTime)
188 | setTime(m_minTime);
189 | else if (sec > m_maxTime)
190 | setTime(m_maxTime);
191 | else
192 | return;
193 |
194 | emitValueChanged();
195 | }
196 |
197 | void TimerEdit::setupFormat()
198 | {
199 | QString inputRe = "(?[0-5][0-9])";
200 | QString inputMsk = "99";
201 | QString suffx = "\\" % tr("m") % "\\" % tr("m");
202 |
203 | if (m_hourDigits) {
204 | inputRe.prepend("(?[0-9]*[0-9]):");
205 | inputMsk.prepend(":");
206 | suffx.prepend(":");
207 | for (int i=0; i < m_hourDigits; ++i) {
208 | inputMsk.prepend("9");
209 | suffx.prepend("\\" % tr("h"));
210 | }
211 | }
212 | if (m_showSeconds) {
213 | inputRe.append(":(?[0-5][0-9])");
214 | inputMsk.append(":99");
215 | suffx.append(":\\" % tr("s") % "\\" % tr("s"));
216 | }
217 | if (m_minTime < 0) {
218 | inputRe.prepend("(?-|\\+|\\s)?");
219 | inputMsk.prepend("#");
220 | }
221 |
222 | inputRe.prepend("^");
223 | inputRe.append("\\s?\\[[^\\]]+\\]$");
224 | inputMsk.append(" \\[" % suffx % "\\]");
225 |
226 | m_validator->setRegularExpression(QRegularExpression(inputRe));
227 |
228 | setInputMask(inputMsk);
229 | setValidator(m_validator);
230 | }
231 |
232 | void TimerEdit::emitValueChanged()
233 | {
234 | emit QLineEdit::textEdited(text());
235 | }
236 |
237 | void TimerEdit::keyPressEvent(QKeyEvent *event)
238 | {
239 | switch (event->key()) {
240 | case Qt::Key_Up:
241 | incrDecr(m_singleStep);
242 | break;
243 |
244 | case Qt::Key_Down:
245 | incrDecr(-(int)m_singleStep);
246 | break;
247 |
248 | case Qt::Key_PageUp:
249 | incrDecr(m_pageStep);
250 | break;
251 |
252 | case Qt::Key_PageDown:
253 | incrDecr(-(int)m_pageStep);
254 | break;
255 |
256 | case Qt::Key_Plus:
257 | case Qt::Key_Equal:
258 | case Qt::Key_Minus:
259 | if (m_minTime < 0) {
260 | setTime(-timeInSeconds());
261 | emitValueChanged();
262 | }
263 | break;
264 |
265 | default:
266 | QLineEdit::keyPressEvent(event);
267 | break;
268 | }
269 | }
270 |
271 | void TimerEdit::wheelEvent(QWheelEvent *event)
272 | {
273 | if (event->angleDelta().isNull()) {
274 | event->ignore();
275 | return;
276 | }
277 | int numSteps = -event->angleDelta().y() / 8 / 15 * -1; // one step per 15deg
278 | numSteps *= (event->modifiers() & Qt::ControlModifier) ? m_pageStep : m_singleStep;
279 | incrDecr(numSteps);
280 | event->accept();
281 | }
282 |
--------------------------------------------------------------------------------
/src/quick/maxLibQt/controls/tests.qml:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of maxLibQt
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2018 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | import QtQuick 2.10
29 | import QtQuick.Controls 2.3
30 | import QtQuick.Layouts 1.3
31 |
32 | /*
33 | Testing page for maxLibQt QtQuick Controls components.
34 | */
35 |
36 | ScrollView {
37 | id: root
38 | width: 400
39 | height: 650
40 | padding: 9
41 |
42 | ColumnLayout {
43 | id: sbTests
44 | spacing: 6
45 | clip: true
46 |
47 | property bool editable: true
48 | property bool wrap: true
49 | property bool useLocaleFormat: true
50 | property bool showGroupSeparator: true
51 |
52 | function setupSb(sb) {
53 | sb.editable = Qt.binding(function() { return sbTests.editable; });
54 | sb.wrap = Qt.binding(function() { return sbTests.wrap; });
55 | sb.useLocaleFormat = Qt.binding(function() { return sbTests.useLocaleFormat; });
56 | sb.showGroupSeparator = Qt.binding(function() { return sbTests.showGroupSeparator; });
57 | //sb.Layout.fillWidth = true
58 | }
59 |
60 | RowLayout {
61 | CheckBox {
62 | text: "Editable"
63 | checked: sbTests.editable
64 | onToggled: sbTests.editable = !sbTests.editable
65 | }
66 | CheckBox {
67 | text: "Wrap"
68 | checked: sbTests.wrap
69 | onToggled: sbTests.wrap = !sbTests.wrap
70 | }
71 | CheckBox {
72 | text: "Group Sep."
73 | checked: sbTests.showGroupSeparator
74 | onToggled: sbTests.showGroupSeparator = !sbTests.showGroupSeparator
75 | }
76 | CheckBox {
77 | text: "Localize"
78 | checked: sbTests.useLocaleFormat
79 | onToggled: sbTests.useLocaleFormat = !sbTests.useLocaleFormat
80 | }
81 | }
82 |
83 | GroupBox {
84 | title: "MLDoubleSpinBox"
85 | Layout.fillWidth: true
86 |
87 | GridLayout {
88 | anchors.fill: parent
89 | columns: 2
90 |
91 | Label { text: "Default" }
92 | MLDoubleSpinBox {
93 | Component.onCompleted: sbTests.setupSb(this)
94 | }
95 |
96 | Label { text: "+/-9e6.4: s:.01; loc: en" }
97 | MLDoubleSpinBox {
98 | to: 9e6; from: -to
99 | decimals: 4
100 | value: 1234567.1234
101 | stepSize: 0.01
102 | locale: Qt.locale("en_US")
103 | Component.onCompleted: sbTests.setupSb(this)
104 | }
105 |
106 | Label { text: "+/-9e6.4; s:.01; loc: fr" }
107 | MLDoubleSpinBox {
108 | to: 9e6; from: -to
109 | decimals: 4
110 | value: 1234567.1234
111 | stepSize: 0.01
112 | locale: Qt.locale("fr")
113 | Component.onCompleted: sbTests.setupSb(this)
114 | }
115 |
116 | Label { text: "+/-9e6.4; s:.01; loc: de" }
117 | MLDoubleSpinBox {
118 | to: 9e6; from: -to
119 | decimals: 4
120 | value: 1234567.1234
121 | stepSize: 0.01
122 | locale: Qt.locale("de")
123 | Component.onCompleted: sbTests.setupSb(this)
124 | }
125 |
126 | Label { text: "+/-9e6.0" }
127 | MLDoubleSpinBox {
128 | to: 9e6; from: -to
129 | decimals: 0
130 | value: 10234567.123456 // invalid value, should auto-correct to 9e6
131 | Component.onCompleted: sbTests.setupSb(this)
132 | }
133 |
134 | Label { text: "+/-9e6.6; exp" }
135 | MLDoubleSpinBox {
136 | to: 9e6; from: -to
137 | decimals: 6
138 | value: 12.3456789
139 | notation: DoubleValidator.ScientificNotation
140 | stepSize: 0.1
141 | Component.onCompleted: sbTests.setupSb(this)
142 | }
143 |
144 | Label { text: "+/-1e4.2; s:.01; pfx/sfx basic" }
145 | MLDoubleSpinBox {
146 | to: 10000; from: -to
147 | value: 55.5
148 | stepSize: 0.01
149 | prefix: "foo: "
150 | suffix: " (bars)"
151 | Component.onCompleted: sbTests.setupSb(this)
152 | }
153 |
154 | Label { text: "+/-1e4.2; s:.01; pfx/sfx inpMask" }
155 | /* This is an example of using a custom input mask to create an editable spin box with prefix/suffix.
156 | It is somewhat generalized, but only supports standard notation, with no group separators. */
157 | MLDoubleSpinBox {
158 | to: 10000; from: -to
159 | value: 55.5
160 | stepSize: 0.01
161 | notation: DoubleValidator.StandardNotation // inputMask and textFromValue do not work with exp. notation
162 | prefix: "foo: "
163 | suffix: "[bar" + (Math.abs(value) !== 1 ? "s" : "") + "]"
164 | inputMask: getInputMask()
165 | // We need to use the "experimental" RegExpValidator since DoubleValidator does not support masks.
166 | validator: regExpValidator
167 | Component.onCompleted: sbTests.setupSb(this)
168 |
169 | function textFromValue(value, locale) {
170 | locale = locale || effectiveLocale;
171 | var text = Math.abs(value).toLocaleString(locale, 'f', Math.max(decimals, 0)),
172 | dig = Math.ceil(Math.abs(topValue)).toString(10).length,
173 | len = Math.floor(Math.abs(value)).toString(10).length;
174 |
175 | text = Math.abs(value).toLocaleString(locale, 'f', Math.max(decimals, 0));
176 |
177 | if (dig > len)
178 | text = "0".repeat(dig - len) + text;
179 | if (botValue < 0)
180 | text = (value < 0 ? "-" : "+") + text;
181 |
182 | if (prefix)
183 | text = prefix + text;
184 | if (suffix)
185 | text = text + suffix;
186 |
187 | return text;
188 | }
189 |
190 | function getInputMask() {
191 | var dig = Math.abs(Math.round(topValue)).toString(10).length - 1,
192 | mask = (botValue < 0 ? "#" : "") + "0".repeat(dig) + "9" + (decimals > 0 ? effectiveLocale.decimalPoint + "0".repeat(decimals) : "");
193 | if (prefix)
194 | mask = escapeInputMaskChars(prefix) + mask;
195 | if (suffix)
196 | mask = mask + escapeInputMaskChars(suffix);
197 | return mask;
198 | }
199 | }
200 |
201 | RowLayout {
202 | Label { text: "+/-9e4.%1: regExp".arg(reBox.decimals) }
203 | CheckBox { id: exp; text: "exp"; checked: false }
204 | }
205 | // This version allows entry in both scientific and standard formats, and will convert to the specified notation after editing is finished.
206 | MLDoubleSpinBox {
207 | id: reBox
208 | to: 9e4; from: -to
209 | decimals: exp.checked ? 4 + 4 : 4
210 | value: 12345.1234
211 | notation: exp.checked ? DoubleValidator.ScientificNotation : DoubleValidator.StandardNotation
212 | validator: regExpValidator
213 | Component.onCompleted: sbTests.setupSb(this)
214 | }
215 |
216 | } // GridLayout
217 | } // GroupBox MLDoubleSpinBox
218 |
219 | GroupBox {
220 | title: "MLHexSpinBox"
221 | Layout.fillWidth: true
222 |
223 | GridLayout {
224 | anchors.fill: parent
225 | columns: 2
226 |
227 | Label { text: "Default" }
228 | MLHexSpinBox {
229 | Component.onCompleted: sbTests.setupSb(this)
230 | }
231 |
232 | Label { text: "to: 0xFFFF; pfx: false" }
233 | MLHexSpinBox {
234 | to: 0xFFFF
235 | showPrefix: false
236 | Component.onCompleted: sbTests.setupSb(this)
237 | }
238 |
239 | Label { text: "+/-0xFFFF; noPad, LCase" }
240 | MLHexSpinBox {
241 | to: 0xFFFF
242 | from: -to
243 | zeroPad: false
244 | upperCase: false
245 | Component.onCompleted: sbTests.setupSb(this)
246 | }
247 |
248 | } // GridLayout
249 | } // GroupBox MLHexSpinBox
250 |
251 | } // ColumnLayout
252 | }
253 |
--------------------------------------------------------------------------------
/doc/DoxygenLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
--------------------------------------------------------------------------------
/doc/Doxyfile:
--------------------------------------------------------------------------------
1 | # Doxyfile 1.8.15
2 |
3 | PROJECT_NAME = maxLibQt
4 | PROJECT_BRIEF =
5 | PROJECT_LOGO = logo_64.png
6 | ABBREVIATE_BRIEF = "The $name class" \
7 | "The $name widget" \
8 | "The $name file" \
9 | is \
10 | provides \
11 | specifies \
12 | contains \
13 | represents \
14 | a \
15 | an \
16 | the
17 |
18 | ALWAYS_DETAILED_SEC = YES
19 | JAVADOC_AUTOBRIEF = YES
20 | QT_AUTOBRIEF = YES
21 | TAB_SIZE = 2
22 | ALIASES = "default{1}=\nDefault value is \c \1.\n" \
23 | "reimp{1}=Reimplemented from \c \1." \
24 | "reimp=Reimplemented from superclass." \
25 | default= \
26 | "pacc=\par Access functions:^^" \
27 | "psig=\par Notifier signal:^^" \
28 | "intern=\parInternal use only."
29 | EXTENSION_MAPPING = qml=C++
30 | DISTRIBUTE_GROUP_DOC = YES
31 | INLINE_SIMPLE_STRUCTS = YES
32 |
33 | #---------------------------------------------------------------------------
34 | # Build related configuration options
35 | #---------------------------------------------------------------------------
36 |
37 | EXTRACT_ALL = YES
38 | EXTRACT_STATIC = YES
39 | EXTRACT_LOCAL_CLASSES = NO
40 | FORCE_LOCAL_INCLUDES = YES
41 | SORT_BRIEF_DOCS = YES
42 | SORT_MEMBERS_CTORS_1ST = YES
43 | SORT_GROUP_NAMES = YES
44 | SORT_BY_SCOPE_NAME = YES
45 | SHOW_USED_FILES = NO
46 | LAYOUT_FILE = DoxygenLayout.xml
47 |
48 | #---------------------------------------------------------------------------
49 | # Configuration options related to warning and progress messages
50 | #---------------------------------------------------------------------------
51 |
52 | QUIET = YES
53 | WARNINGS = YES
54 | WARN_IF_UNDOCUMENTED = NO
55 | WARN_IF_DOC_ERROR = YES
56 | WARN_NO_PARAMDOC = NO
57 | WARN_AS_ERROR = NO
58 |
59 | #---------------------------------------------------------------------------
60 | # Configuration options related to the input files
61 | #---------------------------------------------------------------------------
62 |
63 | INPUT = ../src
64 | RECURSIVE = YES
65 |
66 | FILE_PATTERNS = *.c \
67 | *.cc \
68 | *.cxx \
69 | *.cpp \
70 | *.c++ \
71 | *.java \
72 | *.ii \
73 | *.ixx \
74 | *.ipp \
75 | *.i++ \
76 | *.inl \
77 | *.idl \
78 | *.ddl \
79 | *.odl \
80 | *.h \
81 | *.hh \
82 | *.hxx \
83 | *.hpp \
84 | *.h++ \
85 | *.cs \
86 | *.d \
87 | *.php \
88 | *.php4 \
89 | *.php5 \
90 | *.phtml \
91 | *.inc \
92 | *.m \
93 | *.markdown \
94 | *.md \
95 | *.mm \
96 | *.dox \
97 | *.py \
98 | *.f90 \
99 | *.f \
100 | *.for \
101 | *.tcl \
102 | *.vhd \
103 | *.vhdl \
104 | *.ucf \
105 | *.qsf \
106 | *.as \
107 | *.js \
108 | *.txt \
109 | *.qml
110 |
111 | EXCLUDE =
112 | EXCLUDE_PATTERNS = */.git/* \
113 | */cmake/* \
114 | */CMakeFiles/* \
115 | CMakeLists.txt \
116 | CMakeCache.txt \
117 | */doc?/* \
118 | moc_*.* \
119 | _*.* \
120 | *-?Copy*.* \
121 | */*test* \
122 | *_p.cpp \
123 | *_p.h
124 | EXCLUDE_SYMBOLS = iterator const_iterator
125 |
126 | EXAMPLE_PATH = ../examples
127 | EXAMPLE_PATTERNS = *.h *.cpp
128 | EXAMPLE_RECURSIVE = YES
129 |
130 | FILTER_PATTERNS = *.qml=doxyqml
131 |
132 | USE_MDFILE_AS_MAINPAGE = README.md
133 |
134 | #---------------------------------------------------------------------------
135 | # Configuration options related to source browsing
136 | #---------------------------------------------------------------------------
137 |
138 | SOURCE_BROWSER = YES
139 |
140 | #---------------------------------------------------------------------------
141 | # Configuration options related to the alphabetical class index
142 | #---------------------------------------------------------------------------
143 |
144 | #---------------------------------------------------------------------------
145 | # Configuration options related to the HTML output
146 | #---------------------------------------------------------------------------
147 |
148 | GENERATE_HTML = YES
149 | HTML_OUTPUT = html
150 | HTML_HEADER =
151 | HTML_FOOTER =
152 | HTML_STYLESHEET =
153 | HTML_EXTRA_STYLESHEET = customdoxygen.css
154 | HTML_COLORSTYLE_HUE = 235
155 | HTML_COLORSTYLE_SAT = 19
156 | HTML_COLORSTYLE_GAMMA = 185
157 | HTML_TIMESTAMP = YES
158 | HTML_DYNAMIC_SECTIONS = YES
159 |
160 | GENERATE_TREEVIEW = YES
161 | ENUM_VALUES_PER_LINE = 1
162 | TREEVIEW_WIDTH = 300
163 | EXT_LINKS_IN_WINDOW = YES
164 |
165 | GENERATE_QHP = NO
166 | QCH_FILE = ../maxLibQt.qch
167 | QHP_NAMESPACE = us.paperno.maxLibQt
168 | QHP_VIRTUAL_FOLDER = doc
169 | QHP_CUST_FILTER_NAME = maxLibQt
170 | QHP_CUST_FILTER_ATTRS = maxLibQt
171 | QHP_SECT_FILTER_ATTRS = maxLibQt
172 | QHG_LOCATION =
173 |
174 | #---------------------------------------------------------------------------
175 | # Configuration options related to the LaTeX output
176 | #---------------------------------------------------------------------------
177 |
178 | GENERATE_LATEX = NO
179 |
180 | #---------------------------------------------------------------------------
181 | # Configuration options related to the preprocessor
182 | #---------------------------------------------------------------------------
183 |
184 | ENABLE_PREPROCESSING = YES
185 | MACRO_EXPANSION = YES
186 | PREDEFINED = DOXYGEN_SHOULD_INCLUDE_THIS \
187 | \
188 | Q_WS_X11= \
189 | Q_WS_WIN= \
190 | Q_WS_MAC= \
191 | Q_WS_QWS= \
192 | Q_WS_MAEMO_5= \
193 | Q_OS_LINUX= \
194 | Q_OS_UNIX= \
195 | Q_OS_WIN= \
196 | Q_OS_MAC= \
197 | Q_OS_MACX= \
198 | Q_OS_DARWIN= \
199 | Q_OS_FREEBSD= \
200 | Q_OS_NETBSD= \
201 | Q_OS_OPENBSD= \
202 | Q_OS_BSD4= \
203 | Q_OS_SOLARIS= \
204 | Q_OS_IRIX= \
205 | \
206 | Q_SLOTS="slots" \
207 | Q_SIGNALS="signals" \
208 | Q_DECL_CONSTEXPR= \
209 | Q_DECL_RELAXED_CONSTEXPR= \
210 | Q_DECL_OVERRIDE="override" \
211 | Q_DECL_FINAL="final" \
212 | Q_DECL_EQ_DEFAULT="= default" \
213 | Q_DECL_EQ_DELETE="= delete" \
214 | Q_DECL_NOEXCEPT= \
215 | Q_DECL_DEPRECATED= \
216 | Q_DECL_UNUSED_MEMBER= \
217 | Q_DECL_VARIABLE_DEPRECATED= \
218 | Q_DECL_EXPORT= \
219 | Q_DECL_IMPORT= \
220 | Q_DECL_HIDDEN= \
221 | Q_DECL_NULLPTR="nullptr" \
222 | Q_REQUIRED_RESULT= \
223 | Q_SCRIPTABLE= \
224 | Q_INVOKABLE=
225 |
226 | #---------------------------------------------------------------------------
227 | # Configuration options related to external references
228 | #---------------------------------------------------------------------------
229 |
230 | TAGFILES = tagfiles/qt.tags=https://doc.qt.io/qt-5/
231 | PERL_PATH = perl
232 |
233 | #---------------------------------------------------------------------------
234 | # Configuration options related to the dot tool
235 | #---------------------------------------------------------------------------
236 |
237 | CLASS_DIAGRAMS = NO
238 |
--------------------------------------------------------------------------------
/src/widgets/OverlayStackLayout.h:
--------------------------------------------------------------------------------
1 | /*
2 | OverlayStackLayout
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef OVERLAYSTACKLAYOUT_H
29 | #define OVERLAYSTACKLAYOUT_H
30 |
31 | #include
32 |
33 | /*!
34 | \brief The OverlayStackLayout re-implements a \c QStackedLayout with additional features
35 | to allow stacks with "floating" overlay widgets, such as toolbars, buttons, messages, etc.,
36 | while still allowing interaction with exposed areas of the widget(s) underneath.
37 |
38 | The functionality is similar to \c QStackedLayout::StackAll mode, but instead of forcing all
39 | child widgets to be the same size (as \c QStackedLayout does), this version respects the size
40 | hints of each widget. It also respects the widget alignment settings (like most other `QLayout`s),
41 | and adds a way to fine-tune the position with a `positionOffset` property which can be set on any widget.
42 | These are set with the standard \c QLayout::setAlignment() and custom \c OverlayStackLayout::setOffset()
43 | methods respectively. A few \c addWidget() and \c insertWidget() overloads are also provided to set
44 | these properties at insertion time.
45 |
46 | So for example one could have a "main" widget with \c QSizePolicy::Expanding flags in both directions
47 | which will take up the full size of the layout. Then add a toolbar with fixed/minimum size policy
48 | and `Qt::AlignTop | Qt::AlignHCenter` alignment which will "float" on top of the main widget and
49 | keep itself centered in the available width. Only the actual toolbar area would capture mouse events,
50 | so interaction with the main area widget is still possible. The toolbar could be spaced away from the
51 | top of the window using the \c positionOffset property with a positive `y` value.
52 |
53 | \code
54 | QWidget *w = new QWidget(); // Container widget for stack layout
55 | // overlay layout for the container, toolbar will "float" on top of main widget
56 | OverlayStackLayout *lo = new OverlayStackLayout(w);
57 | lo->setContentsMargins(0,0,0,0); // Clear the default margins
58 | QWidget *main = new QWidget(w); // A content widget
59 | main->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // stretch to fit
60 | Toolbar *toolbar = new Toolbar(w); // A floating toolbar
61 | // Add the main widget
62 | lo->addWidget(main);
63 | // Add toolbar widget with center/top alignment and 20px vertical offset
64 | lo->addWidget(toolbar, Qt::AlignTop | Qt::AlignHCenter, QPoint(0, 20));
65 | lo->setCurrentIndex(1); // stack toolbar on top
66 | \endcode
67 |
68 | \c OverlayStackLayout also respects the \c Qt::WA_LayoutOnEntireRect widget attribute. Widgets with
69 | this attribute set will always be laid out ignoring any \c contentsMargins() set on this layout (that is,
70 | using the full available \c geometry() size, vs. \c contentsRect() size). This allows some widgets to
71 | be spaced away from the edges, while others cover the full available area. Using the example above,
72 | the toolbar could be spaced away from the top by setting a positive top content margin on the layout,
73 | and setting the \c Qt::WA_LayoutOnEntireRect attribute on the main expanding widget.
74 |
75 | By default the layout operates in \c QStackedLayout::StackAll mode, meaning no widgets are
76 | hidden when changing the \c currentIndex, only the stack order changes. However, the
77 | \c QStackedLayout::StackOne mode can also be used, for example to switch between multiple pages like a
78 | "typical" stack. In this mode the \c Qt::WA_LayoutOnEntireRect widget attribute could be set
79 | to ensure one or more widgets are always stacked on top of others.
80 |
81 | Here is another example showing the above concepts in action.
82 |
83 | ```
84 | MyWidget() : QWidget()
85 | {
86 | OverlayStackLayout *stackLo = new OverlayStackLayout(this);
87 | ```
88 | \snippet imageviewer/ImageViewer.cpp 1
89 | \snippet imageviewer/ImageViewer.cpp 2
90 | ```
91 | // ... create more page widgets...
92 | ```
93 | \snippet imageviewer/ImageViewer.cpp 3
94 | \snippet imageviewer/ImageViewer.cpp 4
95 | ```
96 | }
97 | ```
98 |
99 | Note that for non-interactive overlays (eg. messages/information), one can set the
100 | \c Qt::WA_TransparentForMouseEvents widget attribute to avoid interference with any underlying
101 | widgets altogether.
102 |
103 | A complete example application demonstrating the different uses of \c OverlayStackLayout is available
104 | in the \e /examples/imageviewer folder of this project.
105 |
106 | \sa QStackedLayout
107 | */
108 | class OverlayStackLayout : public QStackedLayout
109 | {
110 | Q_OBJECT
111 | public:
112 | //! Constructs a new \c OverlayStackLayout with the optional \a parent widget.
113 | //! If \a parent is specified, this layout will install itself on the parent widget.
114 | explicit OverlayStackLayout(QWidget *parent = nullptr);
115 | //! Constructs a new \c OverlayStackLayout and inserts it into the given \a parentLayout.
116 | explicit OverlayStackLayout(QLayout *parentLayout);
117 |
118 | //! Insert \a widget into the stack at \a index position with specified \a alignment.
119 | //! \sa QStackedLayout::insertWidget()
120 | int insertWidget(int index, QWidget *widget, Qt::Alignment alignment);
121 | //! Insert \a widget into the stack at \a index position with specified \a alignment and position \a offset coordinates.
122 | //! \sa QStackedLayout::insertWidget()
123 | int insertWidget(int index, QWidget *widget, Qt::Alignment alignment, const QPoint &offset);
124 | //! Inherited from \c QStackedLayout::insertWidget(int, QWidget *)
125 | using QStackedLayout::insertWidget;
126 | //! Add \a widget to the stack with specified \a alignment. \sa QStackedLayout::addWidget()
127 | int addWidget(QWidget *widget, Qt::Alignment alignment);
128 | //! Add \a widget to the stack with specified \a alignment and position \a offset coordinates.
129 | //! \sa QStackedLayout::addWidget()
130 | int addWidget(QWidget *widget, Qt::Alignment alignment, const QPoint &offset);
131 | //! Inherited from \c QStackedLayout::addWidget(QWidget *)
132 | using QStackedLayout::addWidget;
133 |
134 | void setGeometry(const QRect &geo) override;
135 |
136 | public slots:
137 | //! Set the layout position offset coordinates for given \a widget.
138 | //! The offset is in absolute pixels and is applied after alignment positioning.
139 | //! \note Note that you can also simply assign a property named "positionOffset"
140 | //! with a \c QPoint type value to any widget and it will be respected by this layout.
141 | void setOffset(QWidget *widget, const QPoint &offset) const;
142 | //! Convenience slot to set the layout position offset on a signal from a \c QWidget::sender().
143 | //! This has no effect if the sender widget is not currently in this layout. \sa setOffset()
144 | void setSenderOffset(const QPoint &offset) const;
145 | //! Convenience slot to set the layout alignment on a signal from a \c QWidget::sender().
146 | //! This has no effect if the sender widget is not currently in this layout. \sa setAlignment()
147 | void setSenderAlignment(Qt::Alignment align);
148 |
149 | //! Re-implemented (shadowing) \c QStackedLayout::setStackingMode() to ensure proper child visibility.
150 | //! The default stacking mode for \c OverlayStackLayout is \c QStackedLayout::StackAll.
151 | //! \note Setting the stacking mode to \c QStackedLayout::StackOne when `currentIndex() > 0` will
152 | //! hide (`setVisible(false)`) all widgets except the current one. Conversely, setting the mode to \c StackAll
153 | //! will set \e all widgets to be visible. This is due to how \c QStackedLayout::setStackingMode() operates.
154 | //! In general it is recommended to set the desired stacking mode \e before adding widgets.
155 | void setStackingMode(StackingMode mode);
156 |
157 | private slots:
158 | void doLayout() const;
159 |
160 | private:
161 | Q_DISABLE_COPY(OverlayStackLayout)
162 | };
163 |
164 | inline
165 | int OverlayStackLayout::addWidget(QWidget *widget, Qt::Alignment alignment)
166 | {
167 | return insertWidget(count(), widget, alignment);
168 | }
169 |
170 | inline
171 | int OverlayStackLayout::addWidget(QWidget *widget, Qt::Alignment alignment, const QPoint &offset)
172 | {
173 | return insertWidget(count(), widget, alignment, offset);
174 | }
175 |
176 | #endif // OVERLAYSTACKLAYOUT_H
177 |
--------------------------------------------------------------------------------
/src/core/AppDebugMessageHandler.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | AppDebugMessageHandler
3 |
4 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
5 | Contact: http://www.WorldDesign.com/contact
6 |
7 | LICENSE:
8 |
9 | Commercial License Usage
10 | Licensees holding valid commercial licenses may use this file in
11 | accordance with the terms contained in a written agreement between
12 | you and the copyright holder.
13 |
14 | GNU General Public License Usage
15 | Alternatively, this file may be used under the terms of the GNU
16 | General Public License as published by the Free Software Foundation,
17 | either version 3 of the License, or (at your option) any later version.
18 |
19 | This program is distributed in the hope that it will be useful,
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 | GNU General Public License for more details.
23 |
24 | A copy of the GNU General Public License is available at .
25 | */
26 |
27 | #include "AppDebugMessageHandler.h"
28 |
29 | #include
30 | #include
31 | #include
32 |
33 | AppDebugMessageHandler::AppDebugMessageHandler() :
34 | QObject(),
35 | m_defaultHandler(nullptr)
36 | {
37 | setAppDebugOutputLevel(APP_DBG_HANDLER_DEFAULT_LEVEL);
38 | setShowSourcePath(APP_DBG_HANDLER_SHOW_SRC_PATH);
39 | setSourceBasePath(QStringLiteral(APP_DBG_HANDLER_SRC_PATH));
40 | setShowFunctionDeclarations(APP_DBG_HANDLER_SHOW_FUNCTION_DECL);
41 | setShowTimestamp(APP_DBG_HANDLER_SHOW_TIMESTAMP);
42 | setTimestampFormat(QStringLiteral(APP_DBG_HANDLER_TIMESTAMP_FORMAT));
43 | defaultMessagePattern(); // preload
44 | #if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
45 | m_functionFilter = QRegularExpression("^.+?(\\w+\\().+$");
46 | #endif
47 | }
48 |
49 | // static
50 | AppDebugMessageHandler *AppDebugMessageHandler::instance()
51 | {
52 | static AppDebugMessageHandler instance;
53 | return &instance;
54 | }
55 |
56 | void AppDebugMessageHandler::setAppDebugOutputLevel(quint8 appDebugOutputLevel)
57 | {
58 | QWriteLocker locker(&m_mutex);
59 | m_appDebugOutputLevel = qMin(appDebugOutputLevel, 4);
60 | }
61 |
62 | void AppDebugMessageHandler::setShowSourcePath(bool showSourcePath)
63 | {
64 | QWriteLocker locker(&m_mutex);
65 | m_showSourcePath = showSourcePath;
66 | m_defaultPattern.clear();
67 | }
68 |
69 | void AppDebugMessageHandler::setSourceBasePath(const QString &path)
70 | {
71 | QWriteLocker locker(&m_mutex);
72 | m_srcPathFilter = path.isEmpty() ? QRegularExpression() : QRegularExpression(QStringLiteral("^%1[\\\\\\/](.*?)$").arg(path), QRegularExpression::InvertedGreedinessOption);
73 | }
74 |
75 | void AppDebugMessageHandler::setShowFunctionDeclarations(bool showFunctionDeclarations)
76 | {
77 | QWriteLocker locker(&m_mutex);
78 | m_showFunctionDeclarations = showFunctionDeclarations;
79 | m_defaultPattern.clear();
80 | }
81 |
82 | void AppDebugMessageHandler::setShowTimestamp(bool showTimestamp)
83 | {
84 | QWriteLocker locker(&m_mutex);
85 | m_showTimestamp = showTimestamp;
86 | m_defaultPattern.clear();
87 | }
88 |
89 | void AppDebugMessageHandler::setTimestampFormat(const QString &timeFormat)
90 | {
91 | QWriteLocker locker(&m_mutex);
92 | m_tsFormat = timeFormat;
93 | m_defaultPattern.clear();
94 | }
95 |
96 | void AppDebugMessageHandler::setMessagePattern(const QString &pattern)
97 | {
98 | QWriteLocker locker(&m_mutex);
99 | m_msgPattern = pattern;
100 | }
101 |
102 | void AppDebugMessageHandler::addOutputDevice(QIODevice *device)
103 | {
104 | QWriteLocker locker(&m_mutex);
105 | if (device && !m_outputDevices.contains(device))
106 | m_outputDevices.append(device);
107 | }
108 |
109 | void AppDebugMessageHandler::removeOutputDevice(QIODevice *device)
110 | {
111 | if (!device)
112 | return;
113 | QWriteLocker locker(&m_mutex);
114 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
115 | m_outputDevices.removeAll(device);
116 | #else
117 | int i = 0;
118 | foreach (QIODevice * d, m_outputDevices) {
119 | if (d == device)
120 | m_outputDevices.remove(i);
121 | ++i;
122 | }
123 | #endif
124 | }
125 |
126 | QString AppDebugMessageHandler::defaultMessagePattern() const
127 | {
128 | if (!m_defaultPattern.isEmpty())
129 | return m_defaultPattern;
130 |
131 | QString msgPattern;
132 | if (m_showTimestamp)
133 | msgPattern.append(QStringLiteral("[%{time %1}] ").arg(m_tsFormat));
134 |
135 | msgPattern.append(QLatin1String("[%{short-type}] "));
136 |
137 | if (m_showSourcePath)
138 | msgPattern.append(QLatin1String("%{file}::"));
139 |
140 | if (m_showFunctionDeclarations)
141 | msgPattern.append(QLatin1String("%{full-function}"));
142 | else
143 | msgPattern.append(QLatin1String("%{function}()"));
144 |
145 | msgPattern.append(QLatin1String(":%{line} -%{if-category} [%{category}]%{endif} %{message}"));
146 |
147 | #ifndef QT_NO_GLIB
148 | msgPattern.append("%{if-fatal}\nBACKTRACE:\n%{backtrace depth=12 separator=\"\n\"}%{endif}");
149 | #endif
150 |
151 | m_defaultPattern = msgPattern;
152 | return msgPattern;
153 | }
154 |
155 | QString AppDebugMessageHandler::messagePattern() const
156 | {
157 | return m_msgPattern.isEmpty() ? defaultMessagePattern() : m_msgPattern;
158 | }
159 |
160 | void AppDebugMessageHandler::installAppMessageHandler()
161 | {
162 | #if APP_DBG_HANDLER_ENABLE
163 | m_defaultHandler = qInstallMessageHandler(g_appDebugMessageHandler);
164 | #else
165 | qInstallMessageHandler(nullptr);
166 | #endif
167 | }
168 |
169 | void AppDebugMessageHandler::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
170 | {
171 | // normalize types, QtDebugMsg stays 0, QtInfoMsg becomes 1, rest are QtMsgType + 1
172 | quint8 lvl = type;
173 | if (type == QtInfoMsg)
174 | lvl = 1;
175 | else if (type > QtDebugMsg)
176 | ++lvl;
177 |
178 | QReadLocker locker(&m_mutex);
179 |
180 | if (lvl < m_appDebugOutputLevel)
181 | return;
182 |
183 | #if defined(Q_OS_LINUX) && (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
184 | // Filter out lots of QPainter warnings from undocked QDockWidgets... hackish but effective (only workaround found so far)
185 | if (lvl == 2 && QString(context.function).contains("QPainter::"))
186 | return;
187 | #endif
188 |
189 | QString msgPattern = messagePattern();
190 |
191 | QString file = context.file;
192 | if (m_srcPathFilter.isValid())
193 | file.replace(m_srcPathFilter, "\\1");
194 |
195 | const bool hasDevices = !m_outputDevices.isEmpty();
196 |
197 | locker.unlock();
198 |
199 | msgPattern.replace(QLatin1String("%{short-type}"), shortTypeNames().value(type, QStringLiteral("?")));
200 | msgPattern.replace(QLatin1String("%{full-function}"), context.function);
201 |
202 | QMessageLogContext newContext(qPrintable(file), context.line, context.function, context.category);
203 |
204 | qSetMessagePattern(msgPattern);
205 |
206 | if (!m_defaultHandler || hasDevices || receivers(SIGNAL(messageOutput(quint8, const QString &)))) {
207 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
208 | msgPattern = qFormatLogMessage(type, newContext, msg);
209 | #else
210 | msgPattern.replace("%{type}", fullTypeNames().value(type, "unknown"));
211 | msgPattern.replace("%{line}", QString::number(context.line));
212 | msgPattern.replace("%{if-category} [%{category}]%{endif}", QString(context.category));
213 | msgPattern.replace("%{message}", msg);
214 | msgPattern.replace("%{function}", QString(context.function).replace(m_functionFilter, "\\1)"));
215 | #endif
216 |
217 | emit messageOutput(lvl, msgPattern);
218 |
219 | if (hasDevices) {
220 | locker.relock();
221 | const QVector devices(m_outputDevices);
222 | locker.unlock();
223 | for (QIODevice * d : devices) {
224 | if (d && d->isWritable() && (!d->property("level").isValid() || d->property("level").toInt() <= lvl)) {
225 | d->write(qPrintable(msgPattern + "\n"));
226 | if (QFileDevice * fd = qobject_cast(d))
227 | fd->flush();
228 | }
229 | }
230 | }
231 | }
232 |
233 | // if (QThread::currentThread() == qApp->thread()) // gui thread
234 |
235 | if (m_defaultHandler) {
236 | m_defaultHandler(type, newContext, msg);
237 | }
238 | else {
239 | fprintf(stderr, "%s", qPrintable(msgPattern));
240 | if (type == QtFatalMsg)
241 | abort();
242 | }
243 | }
244 |
245 | // static
246 | QHash &AppDebugMessageHandler::shortTypeNames()
247 | {
248 | static QHash symbols({
249 | {QtDebugMsg, tr("D")},
250 | {QtWarningMsg, tr("W")},
251 | {QtCriticalMsg, tr("C")},
252 | {QtFatalMsg, tr("F")},
253 | {QtInfoMsg, tr("I")}
254 | });
255 | return symbols;
256 | }
257 |
258 | #if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
259 | // static
260 | QHash &AppDebugMessageHandler::fullTypeNames()
261 | {
262 | static QHash symbols({
263 | {QtDebugMsg, tr("debug")},
264 | {QtWarningMsg, tr("warning")},
265 | {QtCriticalMsg, tr("critical")},
266 | {QtFatalMsg, tr("fatal")},
267 | {QtInfoMsg, tr("info")}
268 | });
269 | return symbols;
270 | }
271 | #endif
272 |
273 | // Message handler which is installed using qInstallMessageHandler. This needs to be global.
274 | void g_appDebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
275 | {
276 | if (AppDebugMessageHandler::instance())
277 | AppDebugMessageHandler::instance()->messageHandler(type, context, msg);
278 | }
279 |
--------------------------------------------------------------------------------
/src/widgets/ExportableTableView.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | ExportableTableView
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #include "ExportableTableView.h"
29 |
30 | ExportableTableView::ExportableTableView(QWidget *parent) : QTableView(parent)
31 | {
32 | setContextMenuPolicy(Qt::CustomContextMenu);
33 | setHtmlStyle(getDefaultHtmlStyle());
34 | setHtmlTemplate(getDefaultHtmlTemplate());
35 |
36 | QAction * selAll = new QAction(tr("Select All"), this);
37 | selAll->setShortcut(QKeySequence::SelectAll);
38 | addAction(selAll);
39 |
40 | QAction * cpyTab = new QAction(tr("Copy selection as TAB-delimited text"), this);
41 | cpyTab->setShortcut(QKeySequence::Copy);
42 | cpyTab->setProperty("delim", "\t");
43 | addAction(cpyTab);
44 |
45 | QAction * cpyCsv = new QAction(tr("Copy selection as comma-delimited text (CSV)"), this);
46 | cpyCsv->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
47 | cpyCsv->setProperty("delim", ", ");
48 | addAction(cpyCsv);
49 |
50 | QAction * cpyPipe = new QAction(tr("Copy selection as pipe-delimited text"), this);
51 | cpyPipe->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_C);
52 | cpyPipe->setProperty("delim", " | ");
53 | addAction(cpyPipe);
54 |
55 | QAction * cpyHtml = new QAction(tr("Copy selection as HTML"), this);
56 | cpyCsv->setShortcut(Qt::CTRL + Qt::ALT + Qt::SHIFT + Qt::Key_C);
57 | cpyHtml->setProperty("delim", "html");
58 | addAction(cpyHtml);
59 |
60 | QAction * saveFile = new QAction(tr("Save selection to file"), this);
61 | saveFile->setShortcut(QKeySequence::Save);
62 | addAction(saveFile);
63 |
64 | connect(selAll, &QAction::triggered, this, &QTableView::selectAll);
65 | connect(cpyTab, &QAction::triggered, this, &ExportableTableView::copy);
66 | connect(cpyCsv, &QAction::triggered, this, &ExportableTableView::copy);
67 | connect(cpyPipe, &QAction::triggered, this, &ExportableTableView::copy);
68 | connect(cpyHtml, &QAction::triggered, this, &ExportableTableView::copy);
69 | connect(saveFile, &QAction::triggered, this, &ExportableTableView::save);
70 | connect(this, &QTableView::customContextMenuRequested, this, &ExportableTableView::onCustomContextMenuRequested);
71 | }
72 |
73 | QString ExportableTableView::getDefaultHtmlStyle()
74 | {
75 | return "th, td {font-family: sans-serif; padding: 3px 15px 3px 3px;} " \
76 | "th {text-align: left;}";
77 | }
78 |
79 | QString ExportableTableView::getDefaultHtmlTemplate()
80 | {
81 | return "\n\n\n\n" \
84 | "\n" \
87 | "";
88 | }
89 |
90 | void ExportableTableView::setHtmlStyle(const QString &value)
91 | {
92 | m_htmlStyle = value;
93 | }
94 |
95 | void ExportableTableView::setHtmlTemplate(const QString &value)
96 | {
97 | m_htmlTemplate = value;
98 | }
99 |
100 | QString ExportableTableView::toPlainText(const QModelIndexList &indexList, const QString &delim) const
101 | {
102 | QString ret, header;
103 | const QChar rowDelim = '\n';
104 | bool firstRow = true;
105 | for (int i = 0; i < indexList.count(); ++i) {
106 | const QModelIndex & idx = indexList.at(i);
107 |
108 | if (firstRow)
109 | header.append(model()->headerData(idx.column(), Qt::Horizontal).toString());
110 |
111 | ret.append(idx.data(Qt::DisplayRole).toString());
112 |
113 | if (i + 1 == indexList.count() || indexList.at(i+1).row() != idx.row()) {
114 | ret.append(rowDelim);
115 | if (firstRow && !header.isEmpty())
116 | header.append(rowDelim);
117 | firstRow = false;
118 | }
119 | else {
120 | ret.append(delim);
121 | if (firstRow && !header.isEmpty())
122 | header.append(delim);
123 | }
124 | }
125 | if (!header.isEmpty())
126 | ret.prepend(header);
127 |
128 | return ret;
129 | }
130 |
131 | QString ExportableTableView::toHtml(const QModelIndexList &indexList) const
132 | {
133 | QString ret, header, row;
134 | bool firstRow = true;
135 |
136 | for (int i = 0; i < indexList.count(); ++i) {
137 | const QModelIndex & idx = indexList.at(i);
138 | const int algn = (idx.data(Qt::TextAlignmentRole).isValid() ? idx.data(Qt::TextAlignmentRole).toInt() : (Qt::AlignLeft | Qt::AlignVCenter));
139 | const QString fg = (idx.data(Qt::ForegroundRole).isValid() ? idx.data(Qt::ForegroundRole).value().color().name(QColor::HexRgb) : "initial");
140 | const QString bg = (idx.data(Qt::BackgroundRole).isValid() ? idx.data(Qt::BackgroundRole).value().color().name(QColor::HexRgb) : "initial");
141 | const QString ttl = (idx.data(Qt::ToolTipRole).isValid() ? idx.data(Qt::ToolTipRole).toString().replace("\"", "\"\"") : QString());
142 |
143 | QString fnt;
144 | if (idx.data(Qt::FontRole).isValid()) {
145 | const QFont font = idx.data(Qt::FontRole).value();
146 | fnt = "font: \"" % font.family() % "\" " % QString::number(font.pointSize()) % "pt;";
147 | }
148 |
149 | if (firstRow)
150 | header.append(QString("%1 | \n").arg(model()->headerData(idx.column(), Qt::Horizontal).toString()));
151 |
152 | row.append("%1 | \n"); // cell template
153 | row = row.arg(idx.data(Qt::DisplayRole).toString()).arg(fg).arg(bg).arg(fnt);
154 | row = row.arg((algn & Qt::AlignRight) ? "right" : (algn & Qt::AlignHCenter) ? "center" : "left");
155 | row = row.arg((algn & Qt::AlignTop) ? "top" : (algn & Qt::AlignBottom) ? "bottom" : "middle");
156 | row = row.arg(ttl.isEmpty() ? ttl : QString("title=\"%1\"").arg(ttl));
157 |
158 | if (i + 1 == indexList.count() || indexList.at(i+1).row() != idx.row()) {
159 | ret.append(QString("\n%1
\n").arg(row));
160 | row.clear();
161 | firstRow = false;
162 | }
163 | }
164 |
165 | ret = QString("\n%1\n").arg(ret);
166 | if (!header.isEmpty())
167 | ret.prepend(QString("\n\n%1
\n\n").arg(header));
168 | ret = m_htmlTemplate.arg(m_htmlStyle).arg(ret);
169 |
170 | return ret;
171 | }
172 |
173 | bool ExportableTableView::saveToFile(const QModelIndexList &indexList, const QString &fileName)
174 | {
175 | static QString lastDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
176 |
177 | if (indexList.isEmpty())
178 | return false;
179 |
180 | QString fname = fileName;
181 | if (fname.isEmpty()) {
182 | QString types = tr("Tab-delimited text") % " (*.tab);;" % tr("Comma-delimited text") % " (*.csv);;" % tr("Pipe-delimited text") % " (*.txt);;" % tr("HTML") % " (*.html)";
183 | fname = QFileDialog::getSaveFileName(this, tr("Save to file"), lastDir, types);
184 | }
185 |
186 | if (fname.isEmpty())
187 | return false;
188 |
189 | QFile file(fname);
190 | QFileInfo fi(file);
191 | lastDir = fi.absolutePath();
192 |
193 | if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
194 | return false;
195 |
196 | if (fi.suffix() == "html")
197 | file.write(toHtml(indexList).toUtf8());
198 | else
199 | file.write(toPlainText(indexList, (fi.suffix() == "tab" ? "\t" : fi.suffix() == "csv" ? ", " : " | ")).toUtf8());
200 | file.close();
201 |
202 | return true;
203 | }
204 |
205 | QSize ExportableTableView::sizeHint() const
206 | {
207 | int w = 0;
208 | for (int i = 0; i < model()->columnCount(); ++i)
209 | w += sizeHintForColumn(i);
210 | return QSize(w + verticalScrollBar()->sizeHint().width() + 60, sizeHintForRow(0) * model()->rowCount());
211 | }
212 |
213 | void ExportableTableView::copyText(const QString &delim)
214 | {
215 | QApplication::clipboard()->setText(toPlainText(getSelectedOrAll(), delim));
216 | }
217 |
218 | void ExportableTableView::copyHtml()
219 | {
220 | QMimeData * mdata = new QMimeData;
221 | mdata->setHtml(toHtml(getSelectedOrAll()));
222 | QApplication::clipboard()->setMimeData(mdata);
223 | }
224 |
225 | void ExportableTableView::copy()
226 | {
227 | QString delim = "\t";
228 | if (sender() && sender()->property("delim").isValid())
229 | delim = sender()->property("delim").toString();
230 | if (delim == "html")
231 | copyHtml();
232 | else
233 | copyText(delim);
234 | }
235 |
236 | void ExportableTableView::save()
237 | {
238 | saveToFile(getSelectedOrAll());
239 | }
240 |
241 | QModelIndexList ExportableTableView::getSelectedOrAll()
242 | {
243 | QModelIndexList sel = selectionModel()->selectedIndexes();
244 | if (sel.isEmpty()) {
245 | selectAll();
246 | sel = selectionModel()->selectedIndexes();
247 | }
248 | return sel;
249 | }
250 |
251 | void ExportableTableView::onCustomContextMenuRequested(const QPoint & pos)
252 | {
253 | QMenu menu;
254 | menu.addActions(actions());
255 | menu.exec(mapToGlobal(pos));
256 | }
257 |
--------------------------------------------------------------------------------
/src/widgets/CollapsingToolBar.h:
--------------------------------------------------------------------------------
1 | /*
2 | CollapsingToolBar
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef COLLAPSINGTOOLBAR_H_
29 | #define COLLAPSINGTOOLBAR_H_
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | /*!
38 | \brief The CollapsingToolBar will change the \c QToolButton::toolButtonStyle of all added \c QToolButtons (via \c QAction or directly)
39 | depending on the total available width. It was originally written as an answer to a [StackOverflow question][1], and is not very sophisticated.
40 | Here is the usage example/test given in the SO question (find a suitable image file to use for an icon).
41 |
42 | \code
43 | class MainWindow : public QMainWindow
44 | {
45 | Q_OBJECT
46 | public:
47 | MainWindow(QWidget *parent = nullptr)
48 | : QMainWindow(parent)
49 | {
50 | QToolBar* tb = new CollapsingToolBar(this);
51 | tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
52 | tb->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
53 |
54 | QIcon icon = QIcon(QPixmap(info_xpm)); // need an image here
55 | for (int i=0; i < 6; ++i)
56 | tb->addAction(icon, QStringLiteral("Action %1").arg(i));
57 |
58 | addToolBar(tb);
59 | show();
60 |
61 | // Adding another action after show() may collapse all the actions if the new toolbar preferred width doesn't fit the window.
62 | // Only an issue if the toolbar size hint was what determined the window width to begin with.
63 | //tb->addAction(icon, QStringLiteral("Action After"));
64 |
65 | // Test setting button style after showing (comment out the one above)
66 | //tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
67 |
68 | // Test changing icon size after showing.
69 | //tb->setIconSize(QSize(48, 48));
70 |
71 | // Try this too...
72 | //tb->setMovable(false);
73 | }
74 | };
75 |
76 | int main(int argc, char *argv[])
77 | {
78 | QApplication app(argc, argv);
79 | //QApplication::setStyle("Fusion");
80 | //QApplication::setStyle("windows");
81 | MainWindow w;
82 | return app.exec();
83 | }
84 | \endcode
85 |
86 | [1]: https://stackoverflow.com/questions/57913277/changing-toolbuttonstyle-of-qtoolbuttons-dynamically-depending-upon-the-size-of/57918115#57918115
87 | */
88 | class CollapsingToolBar : public QToolBar
89 | {
90 | Q_OBJECT
91 | public:
92 | explicit CollapsingToolBar(QWidget *parent = nullptr) : CollapsingToolBar(QString(), parent) {}
93 |
94 | explicit CollapsingToolBar(const QString &title, QWidget *parent = nullptr) :
95 | QToolBar(title, parent)
96 | {
97 | initSizes();
98 | // If icon sizes change we need to recalculate all the size hints, but we need to wait until the buttons have adjusted themselves, so we queue the update.
99 | connect(this, &QToolBar::iconSizeChanged, [this](const QSize &) {
100 | QMetaObject::invokeMethod(this, "recalcExpandedSize", Qt::QueuedConnection);
101 | });
102 | // The drag handle can mess up our sizing, update preferred size if it changes.
103 | connect(this, &QToolBar::movableChanged, [this](bool movable) {
104 | const int handleSz = style()->pixelMetric(QStyle::PM_ToolBarHandleExtent, nullptr, this);;
105 | m_expandedSize = (movable ? m_expandedSize + handleSz : m_expandedSize - handleSz);
106 | adjustForSize();
107 | });
108 | }
109 |
110 | protected:
111 |
112 | // Monitor action events to keep track of required size.
113 | void actionEvent(QActionEvent *e) override
114 | {
115 | QToolBar::actionEvent(e);
116 |
117 | int width = 0;
118 | switch (e->type())
119 | {
120 | case QEvent::ActionAdded:
121 | // Personal pet-peeve... optionally set buttons with menus to have instant popups instead of splits with the main button doing nothing.
122 | //if (QToolButton *tb = qobject_cast(widgetForAction(e->action())))
123 | // tb->setPopupMode(QToolButton::InstantPopup);
124 | //Q_FALLTHROUGH;
125 | case QEvent::ActionChanged:
126 | width = widthForAction(e->action());
127 | if (width <= 0)
128 | return;
129 |
130 | if (e->type() == QEvent::ActionAdded || !m_actionWidths.contains(e->action()))
131 | m_expandedSize += width + m_spacing;
132 | else
133 | m_expandedSize = m_expandedSize - m_actionWidths.value(e->action()) + width;
134 | m_actionWidths.insert(e->action(), width);
135 | break;
136 |
137 | case QEvent::ActionRemoved:
138 | if (!m_actionWidths.contains(e->action()))
139 | return;
140 | width = m_actionWidths.value(e->action());
141 | m_expandedSize -= width + m_spacing;
142 | m_actionWidths.remove(e->action());
143 | break;
144 |
145 | default:
146 | return;
147 | }
148 | adjustForSize();
149 | }
150 |
151 | bool event(QEvent *e) override
152 | {
153 | // Watch for style change
154 | if (e->type() == QEvent::StyleChange)
155 | recalcExpandedSize();
156 | return QToolBar::event(e);
157 | }
158 |
159 | void resizeEvent(QResizeEvent *e) override
160 | {
161 | adjustForSize();
162 | QToolBar::resizeEvent(e);
163 | }
164 |
165 | private slots:
166 | // Here we do the actual switching of tool button style based on available width.
167 | void adjustForSize()
168 | {
169 | int availableWidth = contentsRect().width();
170 | if (!isVisible() || m_expandedSize <= 0 || availableWidth <= 0)
171 | return;
172 |
173 | switch (toolButtonStyle()) {
174 | case Qt::ToolButtonIconOnly:
175 | if (availableWidth > m_expandedSize)
176 | setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
177 | break;
178 |
179 | case Qt::ToolButtonTextBesideIcon:
180 | if (availableWidth <= m_expandedSize)
181 | setToolButtonStyle(Qt::ToolButtonIconOnly);
182 | break;
183 |
184 | default:
185 | break;
186 | }
187 | }
188 |
189 | // Loops over all previously-added actions and re-calculates new size (eg. after icon size change)
190 | void recalcExpandedSize()
191 | {
192 | if (m_actionWidths.isEmpty())
193 | return;
194 | initSizes();
195 | int width = 0;
196 | QHash::iterator it = m_actionWidths.begin();
197 | for ( ; it != m_actionWidths.end(); ++it) {
198 | width = widthForAction(it.key());
199 | if (width <= 0)
200 | continue;
201 | m_expandedSize += width + m_spacing;
202 | it.value() = width;
203 | }
204 | adjustForSize();
205 | }
206 |
207 | private:
208 | void initSizes()
209 | {
210 | // Preload some sizes based on style settings.
211 | // This is the spacing between items
212 | m_spacing = style()->pixelMetric(QStyle::PM_ToolBarItemSpacing, nullptr, this);
213 | // Size of a separator
214 | m_separatorWidth = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, nullptr, this);
215 | // The layout margins (we can't even get the private QToolBarLayout via layout() so we figure it out like it does)
216 | m_expandedSize = (style()->pixelMetric(QStyle::PM_ToolBarItemMargin, nullptr, this) + style()->pixelMetric(QStyle::PM_ToolBarFrameWidth, nullptr, this)) * 2;
217 | // And the size of the drag handle if we have one
218 | if (isMovable())
219 | m_expandedSize += style()->pixelMetric(QStyle::PM_ToolBarHandleExtent, nullptr, this);
220 | }
221 |
222 | int widthForAction(QAction *action) const
223 | {
224 | // Try to find how wide the action representation (widget/separator) is.
225 | if (action->isSeparator())
226 | return m_separatorWidth;
227 |
228 | if (QToolButton *tb = qobject_cast(widgetForAction(action))) {
229 | const Qt::ToolButtonStyle oldStyle = tb->toolButtonStyle();
230 | // force the widest size
231 | tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
232 | const int width = tb->sizeHint().width();
233 | tb->setToolButtonStyle(oldStyle);
234 | return width;
235 | }
236 |
237 | if (const QWidget *w = widgetForAction(action))
238 | return w->sizeHint().width();
239 |
240 | return 0;
241 | }
242 |
243 | int m_expandedSize = -1; // The maximum size we need with all buttons expanded and allowing for margins/etc
244 | int m_spacing = 0; // Layout spacing between items
245 | int m_separatorWidth = 0; // Width of separators
246 | QHash m_actionWidths; // Use this to track action additions/removals/changes
247 | };
248 |
249 | #endif // COLLAPSINGTOOLBAR_H_
250 |
--------------------------------------------------------------------------------
/examples/imageviewer/ImageGrid.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | ImageGrid
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2019 Maxim Paperno; All Rights Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #include "ImageGrid.h"
29 |
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 |
39 | ImageGrid::ImageGrid(QWidget *p) :
40 | QListView(p)
41 | {
42 | setObjectName("ImageGridView");
43 | setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
44 | //setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
45 | setFrameStyle(QFrame::NoFrame);
46 | setBackgroundRole(QPalette::Shadow);
47 | viewport()->setBackgroundRole(QPalette::Shadow);
48 | viewport()->setMouseTracking(false); // avoid unnecessary repaints
49 | setMouseTracking(false);
50 | setItemDelegate(new ImageGridDelegate(this));
51 | setEditTriggers(NoEditTriggers);
52 | setSelectionMode(SingleSelection);
53 | setSelectionBehavior(SelectRows);
54 | setViewMode(IconMode);
55 | setDragDropMode(NoDragDrop);
56 | setMovement(Static);
57 | setLayoutMode(Batched);
58 | setBatchSize(1);
59 | setSpacing(4);
60 | setIconSize(QSize(64, 64)); // reset in resizeEvent
61 | setUniformItemSizes(true); // delegate always returns decorationSize (icon size) for sizeHint()
62 |
63 | connect(this, &QListView::activated, this, &ImageGrid::selectImage);
64 | connect(this, &QListView::clicked, this, &ImageGrid::selectImage);
65 | }
66 |
67 | void ImageGrid::setCurrentPath(const QString &path)
68 | {
69 | if (currentPath() == path)
70 | return;
71 |
72 | resetModel();
73 | Q_ASSERT(m_model != nullptr);
74 | m_model->setRootPath(path);
75 | setRootIndex(m_model->index(path));
76 | // ensure a layout update on next event loop
77 | QMetaObject::invokeMethod(this, "updateLayout", Qt::QueuedConnection, Q_ARG(bool, true));
78 | emit currentPathChanged(path);
79 | }
80 |
81 | void ImageGrid::selectImage(const QModelIndex &idx)
82 | {
83 | QString img;
84 | if (!idx.isValid() || (img = idx.data(QFileSystemModel::FilePathRole).toString()).isEmpty())
85 | return;
86 | if (currentIndex() != idx)
87 | setCurrentIndex(idx);
88 | emit imageSelected(img);
89 | }
90 |
91 | void ImageGrid::setCurrentImage(const QString &imageFile)
92 | {
93 | qDebug("%s : %d", qPrintable(imageFile), count());
94 | if (!count())
95 | return;
96 | selectImage(m_model->match(m_model->index(0, 0, rootIndex()), QFileSystemModel::FilePathRole, imageFile, 1, Qt::MatchContains).value(0));
97 | }
98 |
99 | void ImageGrid::setCurrentFile(int index)
100 | {
101 | if (m_model)
102 | selectImage(m_model->index(index, 0, rootIndex()));
103 | }
104 |
105 | void ImageGrid::sortBy(QDir::SortFlags flags)
106 | {
107 | if (m_sortBy == flags || !m_model)
108 | return;
109 | m_sortBy = flags; // | QDir::IgnoreCase;
110 | const int col = (flags & QDir::Name) ? 0 : (flags & QDir::Size) ? 1 : (flags & QDir::Type) ? 2 : (flags & QDir::Time) ? 3 : 0;
111 | m_model->sort(col, (flags & QDir::Reversed) ? Qt::DescendingOrder : Qt::AscendingOrder);
112 | emit sortChanged(flags);
113 | }
114 |
115 | void ImageGrid::setColumns(uint columns)
116 | {
117 | if (m_columns != columns) {
118 | m_columns = columns;
119 | updateLayout();
120 | }
121 | }
122 |
123 | void ImageGrid::setImageSize(const QSize &size)
124 | {
125 | if (m_icnSize == size)
126 | return;
127 | m_icnSize = size;
128 | if (size.isEmpty())
129 | updateLayout();
130 | else
131 | setIconSize(size);
132 | emit imageSizeChanged(size);
133 | }
134 |
135 | uint ImageGrid::columnsForWidth(int w) const // never returns zero
136 | {
137 | if (m_columns)
138 | return m_columns;
139 | if (!m_model || !rootIndex().isValid())
140 | return 1;
141 | return qMax(uint(qMin(qRound(w / 256.0), count())), 1U);
142 | //return qMax(qRound(w / 256.0), 1);
143 | //return (w >= 2560 ? 6 : w >= 1920 ? 5 : w >= 1200 ? 4 : w >= 800 ? 3 : w > 400 ? 2 : 1);
144 | }
145 |
146 | void ImageGrid::updateLayout(bool force)
147 | {
148 | if (!m_model || !m_icnSize.isEmpty() || !isVisibleTo(parentWidget()))
149 | return;
150 | int w = viewport()->contentsRect().width() -
151 | style()->pixelMetric(QStyle::PM_ScrollBarExtent, nullptr, verticalScrollBar()) -
152 | ((width() - contentsRect().width()) * 2);
153 | const uint cols = columnsForWidth(w);
154 | w -= (spacing() * (int(cols) - 1));
155 | const int sz = qRound(w / float(cols));
156 | // only resize at steps of 12 to avoid excessive redraws
157 | if (force || qAbs(iconSize().width() - sz) > 12)
158 | setIconSize(QSize(sz, qCeil(sz * 0.75)));
159 | }
160 |
161 | void ImageGrid::resetModel()
162 | {
163 | static const QStringList filter = {
164 | QLatin1String("*.jpg"), QLatin1String("*.jpeg"), QLatin1String("*.png"),
165 | QLatin1String("*.gif"), QLatin1String("*.bmp"), QLatin1String("*.pbm"),
166 | #ifdef QT_SVG_LIB
167 | QLatin1String("*.svg")
168 | #endif
169 | };
170 | // Qt bug, QFileSystemModel "forgets" filter() settings when root is reset or tree is navigated, so we need a full reset.
171 | if (m_model)
172 | m_model->deleteLater();
173 | m_model = new QFileSystemModel(this);
174 | m_model->setNameFilters(filter);
175 | m_model->setFilter(QDir::Files);
176 | m_model->setReadOnly(true);
177 | m_model->setNameFilterDisables(false);
178 | setModel(m_model);
179 | connect(m_model, &QFileSystemModel::directoryLoaded, this, [this](const QString &path) {
180 | if (path == currentPath())
181 | emit countChanged(count());
182 | });
183 | connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, [this](const QItemSelection &sel, const QItemSelection &) {
184 | const QModelIndex &idx = sel.indexes().value(0);
185 | if (!idx.isValid())
186 | return;
187 | emit currentFileIndexChanged(idx.row());
188 | emit currentImageChanged(currentImage());
189 | });
190 | }
191 |
192 | QModelIndex ImageGrid::findNextImage(bool prevoius)
193 | {
194 | const int n = count();
195 | if (n < 2)
196 | return QModelIndex();
197 | QModelIndex idx;
198 | bool wrapped = false;
199 | int i = currentIndex().row();
200 | while (!idx.isValid() || m_model->isDir(idx)) {
201 | i = (prevoius ? i - 1 : i + 1);
202 | if (i < 0 || i >= n) {
203 | if (wrapped)
204 | break;
205 | i = (prevoius ? n - 1 : 0);
206 | wrapped = true;
207 | }
208 | idx = m_model->index(i, 0, rootIndex());
209 | }
210 | if (idx.isValid() && m_model->isDir(idx))
211 | idx = QModelIndex();
212 | return idx;
213 | }
214 |
215 |
216 | //
217 | // ImageGridDelegate
218 | //
219 |
220 | void ImageGridDelegate::paint(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &idx) const
221 | {
222 | if (!idx.isValid() || opt.rect.isEmpty() || opt.decorationSize.isEmpty())
223 | return;
224 | const QPixmap pm(pixmap(idx, opt.decorationSize - QSizeF(frameWidth * 2, frameWidth * 2).toSize()));
225 | if (pm.isNull())
226 | return;
227 | QRectF rect = QRectF(opt.rect).adjusted(frameWidth, frameWidth, -frameWidth, -frameWidth);
228 | QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
229 | const QRect arect = style->alignedRect(opt.direction, opt.displayAlignment, pm.size(), QRect(QPoint(0,0), rect.size().toSize()));
230 | rect.moveTopLeft(rect.topLeft() + arect.topLeft());
231 | //qDebug() << opt.rect << rect << opt.decorationSize << pm.size();
232 | p->drawPixmap(rect.topLeft(), pm); // use point position to avoid any painter scaling check
233 | if (opt.state & QStyle::State_Selected) {
234 | // draw selection rectangle
235 | p->setBrush(Qt::NoBrush);
236 | p->setPen(QPen(opt.palette.brush(QPalette::Highlight), frameWidth, Qt::DashLine, Qt::RoundCap));
237 | p->drawRect(rect.adjusted(0, 0, arect.x() * -2, arect.y() * -2));
238 | }
239 | }
240 |
241 | QPixmap ImageGridDelegate::pixmap(const QModelIndex &idx, const QSize &size) const
242 | {
243 | QPixmap pm;
244 | if (!idx.isValid())
245 | return pm;
246 | const QString imgFile = idx.data(QFileSystemModel::FilePathRole).toString();
247 | if (imgFile.isEmpty() || !QFileInfo(imgFile).exists())
248 | return pm;
249 | const QString hash(imgFile.simplified().append(QLatin1String("_")).append(QString::number(quint64(size.width()) << 32 | uint(size.height()))));
250 | QPixmapCache::find(hash, &pm);
251 | if (pm.isNull()) {
252 | //qDebug() << "Cache miss!" << imgFile << sz << hash;
253 | pm.load(imgFile);
254 | if (!pm.isNull()) {
255 | if (pm.size().width() > size.width() || pm.size().height() > size.height())
256 | pm = pm.scaled(size, Qt::KeepAspectRatio, Qt::FastTransformation);
257 | QPixmapCache::insert(hash, pm);
258 | }
259 | }
260 | return pm;
261 | }
262 |
263 | bool ImageGridDelegate::helpEvent(QHelpEvent *e, QAbstractItemView *v, const QStyleOptionViewItem &, const QModelIndex &idx)
264 | {;
265 | if (!idx.isValid() || !e || !v || (e->type() == QEvent::ToolTip && !showTooltips))
266 | return false;
267 | const QFileInfo fi(idx.data(QFileSystemModel::FilePathRole).toString());
268 | if (!fi.isFile())
269 | return false;
270 | const QString tt(fi.fileName() % "\nC: " % fi.created().toString(Qt::TextDate) % "\nM: " %
271 | fi.lastModified().toString(Qt::TextDate) % "\n" % QString::number(fi.size() / 1024) % " KB");
272 | switch (e->type()) {
273 | case QEvent::ToolTip:
274 | QToolTip::showText(e->globalPos(), tt, v);
275 | return true;
276 | case QEvent::WhatsThis:
277 | QWhatsThis::showText(e->globalPos(), tt, v);
278 | return true;
279 | case QEvent::QueryWhatsThis:
280 | return true;
281 | default:
282 | return false;
283 | }
284 | }
285 |
286 | #include "moc_ImageGrid.cpp"
287 |
--------------------------------------------------------------------------------
/src/itemmodels/GroupedItemsProxyModel.h:
--------------------------------------------------------------------------------
1 | /*
2 | GroupedItemsProxyModel
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef GROUPEDITEMSPROXYMODEL_H
29 | #define GROUPEDITEMSPROXYMODEL_H
30 |
31 | #include
32 | #include
33 |
34 | /**
35 | \class GroupedItemsProxyModel
36 | \version 1.0.0
37 |
38 | \brief GroupedItemsProxyModel allows a grouped item presentation of a flat table data model.
39 |
40 | GroupedItemsProxyModel allows a grouped item presentation of a flat table data model. Items can be visually grouped by data in one or more
41 | columns of the original data. Any number of groupings can be nested. GroupedItemsProxyModel inherits from QIdentityProxyModel and
42 | proxies all data from the original model unmodified. Only the visual presentation is affected, for example like \e QSortFilterProxyModel.
43 |
44 | Groupings can be managed with addGroup(), insertGroup() and removeGroup(). Each function takes a column number as a parameter. The source
45 | data is scanned, and a new grouping row is created for each unique value found in the given column. The title (and icon, if any) of the grouping
46 | row is taken from the source data (\c Qt::DisplayRole and \c Qt::DecorationRole respectively). By default, items are grouped based on
47 | their \c Qt::EditRole but you can change this with setGroupMatchRole().
48 |
49 | When grouping is active, a new first column is (optionally) added which can be used to sort the groupings. By default the groups are shown
50 | in the order in which they were found in the source data. The extra column can be disabled with setGroupColumnVisible(). The title of the column
51 | can be set with setGroupHeaderTitle().
52 |
53 | This model can be cascased with other models. For example set this model as the source for a \e QSortFilterProxyModel to enable sorting
54 | the view (including the grouping items as mentioned above).
55 |
56 | This model can only handle flat data models as input (table/list). It will not include any children of the top-level items.
57 |
58 | When no groupings have been added, this acts as a transparent proxy (see \e QIdentityProxyModel). However the limitation of only flat source
59 | models still applies.
60 |
61 | */
62 | class GroupedItemsProxyModel : public QIdentityProxyModel
63 | {
64 | Q_OBJECT
65 | Q_PROPERTY(int groupMatchRole READ groupMatchRole WRITE setGroupMatchRole)
66 | Q_PROPERTY(bool groupColumnVisible READ groupColumnVisible WRITE setGroupColumnVisible)
67 | Q_PROPERTY(bool groupColumnIsProxy READ groupColumnIsProxy WRITE setGroupColumnIsProxy)
68 | Q_PROPERTY(int groupColumnProxySrc READ groupColumnProxySrc WRITE setGroupColumnProxySrc)
69 | Q_PROPERTY(bool groupRowSelectable READ groupRowSelectable WRITE setGroupRowSelectable)
70 | Q_PROPERTY(QString groupHeaderTitle READ groupHeaderTitle WRITE setGroupHeaderTitle)
71 |
72 | public:
73 | enum { SourceRowNumberRole = Qt::UserRole - 5 };
74 |
75 | explicit GroupedItemsProxyModel(QObject *parent = Q_NULLPTR, QAbstractItemModel *sourceModel = Q_NULLPTR, const QVector &groupColumns = QVector());
76 | ~GroupedItemsProxyModel();
77 |
78 | // QAbstractItemModel interface
79 | void setSourceModel(QAbstractItemModel *newSourceModel) Q_DECL_OVERRIDE;
80 | QModelIndex index(int row, int column, const QModelIndex &parent) const Q_DECL_OVERRIDE;
81 | QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE;
82 | int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
83 | int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
84 | bool hasChildren(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
85 | QVariant data(const QModelIndex &index, int role = Qt::EditRole) const Q_DECL_OVERRIDE;
86 | QMap itemData(const QModelIndex &index) const Q_DECL_OVERRIDE;
87 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
88 | Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
89 | QSize span(const QModelIndex &index) const Q_DECL_OVERRIDE;
90 |
91 | // QAbstractProxyModel interface
92 | QModelIndex mapToSource(const QModelIndex &proxyIndex) const Q_DECL_OVERRIDE;
93 | QModelIndex mapFromSource(const QModelIndex &sourceIndex) const Q_DECL_OVERRIDE;
94 |
95 | // GroupedItemProxyModel interface
96 |
97 | /*! \property groupMatchRole Which data role to use for matching group data. Default is \c Qt::EditRole. \sa setGroupMatchRole() */
98 | inline int groupMatchRole() const { return m_groupMatchRole; }
99 | /*! \property groupColumnVisible Show the extra grouping column (eg. to allow sorting on it). Default is \c{true}. \sa setGroupColumnVisible() */
100 | inline int groupColumnVisible() const { return m_groupColumnVisible; }
101 | /*! \property groupColumnIsProxy Proxy data from source column to the extra grouping column (if shown). This includes display data. Default is \c{false}. \sa setGroupColumnIsProxy() */
102 | inline int groupColumnIsProxy() const { return m_groupColumnIsProxy; }
103 | /*! \property groupColumnProxySrc Which column to use for proxy data. A value of \{-1} (default) means to use the grouping column. \sa setGroupColumnProxySrc(), groupColumnIsProxy() */
104 | inline int groupColumnProxySrc() const { return m_groupColumnProxySrc; }
105 | /*! \property groupRowSelectable Allow selecting the grouping row. Default is \c{false} \sa setGroupRowSelectable() */
106 | inline int groupRowSelectable() const { return m_groupRowSelectable; }
107 | /*! \property groupHeaderTitle Title of grouping column. Default is \c "Group". Set an empty string to hide title. \sa setGroupHeaderTitle() */
108 | inline QString groupHeaderTitle() const { return m_root->data(Qt::DisplayRole).toString(); }
109 |
110 | public slots:
111 | /*! Add a new grouping based on \a column. \sa removeGroup() */
112 | inline void addGroup(int column) { addGroups(QVector() << column); }
113 | /*! Add multiple groupings based on \a columns. The groups are appended to any existing group(s). \sa clearGroups() */
114 | virtual void addGroups(const QVector & columns);
115 | /*! Add multiple groupings based on \a columns. Any existing group(s) are first cleared. \sa clearGroups() */
116 | virtual void setGroups(const QVector & columns);
117 | /*! Insert a new grouping at \a index based on \a column. \sa addGroup(), removeGroup() */
118 | virtual void insertGroup(int index, int column);
119 | /*! Remove the previously-added \a column grouping. \sa addGroup() */
120 | virtual void removeGroup(int column);
121 | /*! Remove the previously-added groupings. \sa addGroup(), addGroups() */
122 | virtual void clearGroups();
123 | // properties
124 | virtual void setGroupMatchRole(int role);
125 | virtual void setGroupColumnVisible(bool visible) { m_groupColumnVisible = visible; }
126 | virtual void setGroupColumnIsProxy(bool enable) { m_groupColumnIsProxy = enable; }
127 | virtual void setGroupColumnProxySrc(int column) { m_groupColumnProxySrc = column; }
128 | virtual void setGroupRowSelectable(bool selectable) { m_groupRowSelectable = selectable; }
129 | virtual void setGroupHeaderTitle(const QString &title, const QString &tooltip = QString());
130 |
131 | protected:
132 | class GroupedProxyItem; // forward
133 |
134 | virtual uint extraColumns() const { return (!m_groups.isEmpty() && groupColumnVisible() ? 1 : 0); }
135 | virtual QModelIndex indexForItem(GroupedProxyItem *item, const int col = 0) const;
136 | virtual GroupedProxyItem * itemForIndex(const QModelIndex &index, const bool rootDefault = false) const;
137 | virtual GroupedProxyItem * findGroupItem(const int group, const QVariant &value, GroupedProxyItem *parent = NULL) const;
138 | virtual QModelIndex sourceIndexForProxy(GroupedProxyItem *item) const;
139 | virtual bool isProxyColumn(const QModelIndex &index) const { return (index.isValid() && extraColumns() && index.column() == 0 && groupColumnIsProxy()); }
140 | int totalRowCount(GroupedProxyItem *parent = NULL) const;
141 | GroupedProxyItem * itemForRow(int row, GroupedProxyItem *parent = NULL) const;
142 |
143 | protected slots:
144 | virtual GroupedProxyItem * placeSourceRow(const int row);
145 | virtual void removeItem(GroupedProxyItem *item);
146 | virtual void removeUnusedGroups(GroupedProxyItem *parent = NULL);
147 | virtual void reloadSourceModel();
148 | virtual void modelResetHandler();
149 | virtual void dataChangedHandler(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles);
150 | virtual void rowsInsertedHandler(const QModelIndex &parent, int first, int last);
151 | virtual void rowsRemovedHandler(const QModelIndex &parent, int first, int last);
152 | virtual void rowsMovedHandler(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
153 |
154 | protected:
155 | GroupedProxyItem * m_root;
156 | QHash m_sourceMap;
157 | QVector m_groups;
158 | int m_groupMatchRole;
159 | int m_groupColumnProxySrc;
160 | bool m_groupColumnVisible;
161 | bool m_groupColumnIsProxy;
162 | bool m_groupRowSelectable;
163 |
164 | private:
165 | bool setReloadSuspended(bool enable) { bool prev = m_reloadSuspended; m_reloadSuspended = enable; return prev; }
166 | bool reloadSuspended() const { return m_reloadSuspended; }
167 |
168 | bool m_reloadSuspended;
169 |
170 |
171 | protected:
172 | class GroupedProxyItem
173 | {
174 | public:
175 | explicit GroupedProxyItem(const QModelIndex &srcIndex, const bool isSrcItem = false, GroupedProxyItem *parent = NULL);
176 | explicit GroupedProxyItem(GroupedProxyItem *parent = NULL);
177 | virtual ~GroupedProxyItem();
178 |
179 | int row() const;
180 | QVariant data(int role = Qt::EditRole) const { return m_itemData.value(role, QVariant()); }
181 | QMap itemData() const { return m_itemData; }
182 | int rowCount() const { return m_childItems.size(); }
183 | QVector children() const { return m_childItems; }
184 | GroupedProxyItem * child(const int row) const { return m_childItems.value(row, NULL); }
185 | int childRow(GroupedProxyItem * child) const { return m_childItems.indexOf(child); }
186 | GroupedProxyItem * parent() const { return m_parentItem; }
187 |
188 | QModelIndex sourceIndex() const { return m_sourceIndex; }
189 | bool isSourceItem() const { return m_isSourceItem; }
190 | bool isGroupItem() const { return !isSourceItem(); }
191 |
192 | void clear();
193 | GroupedProxyItem * addChild(const QModelIndex &srcIndex, const bool isSrcItem = true);
194 | void removeChild(GroupedProxyItem * item);
195 | void removeChild(const int row) { removeChild(child(row)); }
196 | bool setData(const QVariant &data, int role = Qt::EditRole);
197 | bool setItemData(const QMap &roles);
198 |
199 | protected:
200 | GroupedProxyItem * m_parentItem;
201 | QVector m_childItems;
202 | QMap m_itemData;
203 | QPersistentModelIndex m_sourceIndex;
204 | bool m_isSourceItem;
205 | }; // GroupedProxyItem
206 | \
207 | }; // GroupedItemsProxyModel
208 |
209 | #endif // GROUPEDITEMSPROXYMODEL_H
210 |
--------------------------------------------------------------------------------
/src/core/AppDebugMessageHandler.h:
--------------------------------------------------------------------------------
1 | /*
2 | AppDebugMessageHandler
3 | https://github.com/mpaperno/maxLibQt
4 |
5 | COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved.
6 | Contact: http://www.WorldDesign.com/contact
7 |
8 | LICENSE:
9 |
10 | Commercial License Usage
11 | Licensees holding valid commercial licenses may use this file in
12 | accordance with the terms contained in a written agreement between
13 | you and the copyright holder.
14 |
15 | GNU General Public License Usage
16 | Alternatively, this file may be used under the terms of the GNU
17 | General Public License as published by the Free Software Foundation,
18 | either version 3 of the License, or (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | A copy of the GNU General Public License is available at .
26 | */
27 |
28 | #ifndef APPDEBUGMESSAGEHANDLER_H
29 | #define APPDEBUGMESSAGEHANDLER_H
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | //! Enable/disable this custom handler handler entirely \relates AppDebugMessageHandler
39 | #ifndef APP_DBG_HANDLER_ENABLE
40 | #define APP_DBG_HANDLER_ENABLE 1
41 | #endif
42 |
43 | //! Default log level. \sa AppDebugMessageHandler::appDebugOutputLevel \relates AppDebugMessageHandler
44 | #ifndef APP_DBG_HANDLER_DEFAULT_LEVEL
45 | #define APP_DBG_HANDLER_DEFAULT_LEVEL 0
46 | #endif
47 |
48 | //! Include source file path/name in output (filtered with APP_DBG_HANDLER_SRC_PATH).
49 | //! \sa AppDebugMessageHandler::showSourcePath \relates AppDebugMessageHandler
50 | #ifndef APP_DBG_HANDLER_SHOW_SRC_PATH
51 | #define APP_DBG_HANDLER_SHOW_SRC_PATH 0
52 | #endif
53 |
54 | //! Show full function declaration with return and attribute types instead of just the Class::Name.
55 | //! \sa AppDebugMessageHandler::showFunctionDeclarations \relates AppDebugMessageHandler
56 | #ifndef APP_DBG_HANDLER_SHOW_FUNCTION_DECL
57 | #define APP_DBG_HANDLER_SHOW_FUNCTION_DECL 0
58 | #endif
59 |
60 | //! Include timestamp in default message pattern. \sa AppDebugMessageHandler::showTimestamp \relates AppDebugMessageHandler
61 | #ifndef APP_DBG_HANDLER_SHOW_TIMESTAMP
62 | #define APP_DBG_HANDLER_SHOW_TIMESTAMP 0
63 | #endif
64 |
65 | //! Default timestamp format (as per Qt message pattern docs for %{time ...} options).
66 | //! \sa AppDebugMessageHandler::timestampFormat \relates AppDebugMessageHandler
67 | #ifndef APP_DBG_HANDLER_TIMESTAMP_FORMAT
68 | #define APP_DBG_HANDLER_TIMESTAMP_FORMAT "process"
69 | #endif
70 |
71 | //! base path for source file name filter, everything after this is kept; RegEx, non-greedy
72 | //! \sa AppDebugMessageHandler::setSourceBasePath() \relates AppDebugMessageHandler
73 | #ifndef APP_DBG_HANDLER_SRC_PATH
74 | #define APP_DBG_HANDLER_SRC_PATH ".*src"
75 | #endif
76 |
77 | // Make sure to include this header if using qInfo()
78 | #if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
79 | #if defined(QT_NO_INFO_OUTPUT)
80 | #define qInfo QT_NO_QDEBUG_MACRO
81 | #else
82 | #define qInfo qDebug
83 | #endif
84 | #define QtInfoMsg QtMsgType(4)
85 | #endif
86 |
87 | /*!
88 | \class AppDebugMessageHandler
89 | \version 2.0.0
90 |
91 | \brief Custom debug/message handler class to work in conjunction with \c qDebug() family of functions.
92 |
93 | AppDebugMessageHandler helps with formatting all stderr output generated by the application in a consistent manner.
94 | For example the function and line from which the debug message is called is always printed.
95 | It can also be used by other event listeners to intercept messages they may be interested in (connect to \c messageOutput() signal
96 | or add a \c QIODevice to receive the output).
97 |
98 | You can set a minimum logging level for all messages by setting the \ref appDebugOutputLevel property or \c APP_DBG_HANDLER_DEFAULT_LEVEL macro.
99 |
100 | There are several properties which control the default output message formatting. Optionally, a custom pattern can be set with
101 | \c setMessagePattern(). This type of custom pattern allows for additional placeholder values which are not available with
102 | the default Qt handler. See \c setMessagePattern() for details.
103 |
104 | Note that the message format can still be overridden by setting \e QT_MESSAGE_PATTERN environment variable at _run-time_.
105 | see: https://doc.qt.io/qt-5/qtglobal.html#qSetMessagePattern
106 |
107 | Include this header to use qInfo() safely with Qt < v5.5 (qInfo becomes alias for qDebug).
108 |
109 | This is an app-wide "global" thread-safe singleton class, use it with \c AppDebugMessageHandler::instance().
110 | For example, at start of application:
111 |
112 | \code
113 | QApplication app(argc, argv);
114 | AppDebugMessageHandler::instance()->installAppMessageHandler();
115 | \endcode
116 |
117 | To connect to a signal:
118 |
119 | \code
120 | connect(AppDebugMessageHandler::instance(), &AppDebugMessageHandler::messageOutput, this, &DebugOutput::onAppDebugMessage);}
121 | \endcode
122 |
123 | To add a new I/O stream for receiving messages (eg. a log file):
124 |
125 | \code
126 | addOutputDevice(myQFile);
127 | \endcode
128 |
129 | When adding a QIODevice in this way, you may optionally set a minimum severity level below which you do not want messages
130 | written to this device. Use the Qt property system to set a properly named \a level on the QIODevice. For example:
131 |
132 | \code
133 | myQFile.setProperty("level", 2); // only warning and higher
134 | \endcode
135 |
136 | */
137 | class AppDebugMessageHandler : public QObject
138 | {
139 | Q_OBJECT
140 | //! Minimum severity level of messages to print. 0 = debug+; 1 = info+; 2 = warning+; 3 = critical+; 4 = fatal only. \pacc appDebugOutputLevel(void), setAppDebugOutputLevel()
141 | Q_PROPERTY(int appDebugOutputLevel READ appDebugOutputLevel WRITE setAppDebugOutputLevel)
142 | //! Enables or disabled showing the file path in the default message pattern. \pacc showSourcePath(void), setShowSourcePath() \sa setSourceBasePath()
143 | Q_PROPERTY(bool showSourcePath READ showSourcePath WRITE setShowSourcePath)
144 | //! Enables or disables showing full function declarations (with return and attribute types) in the default message pattern. \pacc showFunctionDeclarations(void), setShowFunctionDeclarations()
145 | Q_PROPERTY(bool showFunctionDeclarations READ showFunctionDeclarations WRITE setShowFunctionDeclarations)
146 | //! Enables or disables showing timestamps in the default message pattern. \pacc showTimestamp(void), setShowTimestamp() \sa timestampFormat
147 | Q_PROPERTY(bool showTimestamp READ showTimestamp WRITE setShowTimestamp)
148 | //! Set the timestamp format, as per Qt message pattern docs for \c %{time ...} options. E.g. one of "process", "boot", or a \c QDateTime::toString() format. \pacc timestampFormat(void), setTimestampFormat() \sa showTimestamp
149 | Q_PROPERTY(QString timestampFormat READ timestampFormat WRITE setTimestampFormat)
150 |
151 | public:
152 | //! Get the singleton instance of this object. The instance is created automatically if necessary.
153 | static AppDebugMessageHandler *instance();
154 | //! This function must be called to initialize this custom message handler. E.g. \c AppDebugMessageHandler::instance()->installAppMessageHandler()
155 | void installAppMessageHandler();
156 |
157 | inline quint8 appDebugOutputLevel() const { return m_appDebugOutputLevel; } //!< \sa appDebugOutputLevel
158 | void setAppDebugOutputLevel(quint8 appDebugOutputLevel); //!< \sa appDebugOutputLevel
159 |
160 | inline bool showSourcePath() const { return m_showSourcePath; } //!< \sa showSourcePath
161 | void setShowSourcePath(bool showSourcePath); //!< \sa showSourcePath
162 | //! Set the base path for source file name filter, everything after this is kept. The string could be literal or a RegEx pattern (evaluated as non-greedy).
163 | void setSourceBasePath(const QString &path = QString());
164 |
165 | inline bool showFunctionDeclarations() const { return m_showFunctionDeclarations; } //!< \sa showFunctionDeclarations
166 | void setShowFunctionDeclarations(bool showFunctionDeclarations); //!< \sa showFunctionDeclarations
167 |
168 | inline bool showTimestamp() const { return m_showTimestamp; } //!< \sa showTimestamp
169 | void setShowTimestamp(bool showTimestamp); //!< \sa showTimestamp
170 |
171 | inline QString timestampFormat() const { return m_tsFormat; } //!< \sa timestampFormat
172 | void setTimestampFormat(const QString &timeFormat); //!< \sa timestampFormat
173 |
174 | //! Add a new I/O stream for receiving messages.
175 | void addOutputDevice(QIODevice *device);
176 | //! Remove a previously-added I/O stream.
177 | void removeOutputDevice(QIODevice *device);
178 |
179 | //! Returns the current message pattern in use. This will be either the one set specifically with \c setMessagePattern() or the \c defaultMessagePattern() otherwise.
180 | QString messagePattern() const;
181 | //! Returns the default message pattern. This format will take into account any properties set previously, such as \c showSourcePath, \c showFunctionDeclarations, \c showTimestamp and \c timestampFormat.
182 | QString defaultMessagePattern() const;
183 |
184 | /*! Specifies a debug message pattern to use instead of the default.
185 | The \a pattern is the same as for \c qSetMessagePattern() but extended with the following attributes:
186 |
187 | \li \c %{short-type} : Short type name (eg. "W" for warning, "D" for debug, etc) (as returned by \c shortTypeNames())
188 | \li \c %{full-function} : Full function declaration including return and attribute types (if any)
189 |
190 | Call this function with a blank \c QString to reset the message pattern back to default.
191 | */
192 | void setMessagePattern(const QString &pattern = QString());
193 |
194 | //! Handle a debug message. This is typically called by the installed global message handler callback ( \c g_appDebugMessageHandler() ).
195 | void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
196 |
197 | /*!
198 | Returns a reference to the mapping of \c QtMsgType enum values to abbreviated names.
199 | New names can be specified by setting this reference to a new value. For example:
200 | \code
201 | AppDebugMessageHandler::shortTypeNames() = {
202 | {QtDebugMsg, QStringLiteral("DBG")},
203 | {QtWarningMsg, QStringLiteral("WRN")},
204 | {QtCriticalMsg, QStringLiteral("CRT")},
205 | {QtFatalMsg, QStringLiteral("FTL")},
206 | {QtInfoMsg, QStringLiteral("INF")}
207 | };
208 | \endcode
209 | */
210 | static QHash &shortTypeNames();
211 |
212 | #if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
213 | static QHash &fullTypeNames();
214 | #endif
215 |
216 | signals:
217 | //! Notifies when a new log massage is available.
218 | void messageOutput(quint8 level, const QString &msg);
219 |
220 | private:
221 | explicit AppDebugMessageHandler();
222 | Q_DISABLE_COPY(AppDebugMessageHandler)
223 |
224 | QtMessageHandler m_defaultHandler;
225 | QRegularExpression m_srcPathFilter;
226 | quint8 m_appDebugOutputLevel;
227 | QVector m_outputDevices;
228 | bool m_showSourcePath;
229 | bool m_showFunctionDeclarations;
230 | bool m_showTimestamp;
231 | QString m_tsFormat;
232 | QString m_msgPattern;
233 | mutable QString m_defaultPattern;
234 | QReadWriteLock m_mutex;
235 | #if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
236 | QRegularExpression m_functionFilter;
237 | #endif
238 |
239 | };
240 |
241 | //! \relates AppDebugMessageHandler
242 | //! Message handler which is installed using qInstallMessageHandler. This needs to be global.
243 | //! Use AppDebugMessageHandler::instance()->installAppMessageHandler();
244 | //! instead of installing this function directly (it will verify this handler is enabled, etc).
245 | void g_appDebugMessageHandler(QtMsgType, const QMessageLogContext &, const QString &);
246 |
247 | #endif // APPDEBUGMESSAGEHANDLER_H
248 |
--------------------------------------------------------------------------------