Compare commits
30 Commits
aero_suppo
...
python
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0d8f22049 | ||
|
|
c2d8991283 | ||
|
|
a599736c1b | ||
|
|
e6a335a3f7 | ||
|
|
a7878122ac | ||
|
|
fa32054085 | ||
|
|
f3de1ad63d | ||
|
|
8baac15d24 | ||
|
|
4cf1159019 | ||
|
|
ee065795b9 | ||
|
|
7f9d160658 | ||
|
|
daa220d513 | ||
|
|
d5797a3aea | ||
|
|
ab8545e2c2 | ||
|
|
5fc16954f5 | ||
|
|
6610e48cb3 | ||
|
|
dabfeeaf3b | ||
|
|
9601f57050 | ||
|
|
7c442dce85 | ||
|
|
07ea3ff1a6 | ||
|
|
874fd7d69f | ||
|
|
69a737e286 | ||
|
|
ddc49c9358 | ||
|
|
2b3c3b75bf | ||
|
|
296889cace | ||
|
|
4ebe8ed631 | ||
|
|
ecfa43f801 | ||
|
|
f5f39a37a1 | ||
|
|
0a75d89848 | ||
|
|
e418725c13 |
@@ -15,6 +15,7 @@ cmake_policy(SET CMP0020 NEW)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
|
||||
option(OPTION_DEVELOPER_MODE "Developer Mode" OFF)
|
||||
option(OPTION_BUILD_PYTHON_BINDINGS "Build python bindings" OFF)
|
||||
# option(OPTION_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
|
||||
|
||||
find_package(Qt5Widgets)
|
||||
@@ -22,7 +23,8 @@ find_package(Qt5Widgets)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
set(ECM_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ECM/modules/")
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_DIR})
|
||||
set(PYTHON_MODULE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake/Python")
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_DIR} ${PYTHON_MODULE_DIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
if (OPTION_DEVELOPER_MODE)
|
||||
@@ -42,6 +44,9 @@ endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(examples/dockwidgets)
|
||||
if (OPTION_BUILD_PYTHON_BINDINGS)
|
||||
add_subdirectory(python)
|
||||
endif()
|
||||
|
||||
if (OPTION_DEVELOPER_MODE)
|
||||
if (NOT OPTION_QTQUICK)
|
||||
|
||||
168
cmake/Python/FindPySide2.cmake
Normal file
168
cmake/Python/FindPySide2.cmake
Normal file
@@ -0,0 +1,168 @@
|
||||
###
|
||||
# This file is part of KDDockWidgets.
|
||||
#
|
||||
# Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
#
|
||||
# This program is free software you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
# PYSIDE_BASEDIR - Top of the PySide2 installation
|
||||
# PYSIDE_INCLUDE_DIR - Directories to include to use PySide2
|
||||
# PYSIDE_LIBRARY - Files to link against to use PySide2
|
||||
# PYSIDE_TYPESYSTEMS - Type system files that should be used by other bindings extending PySide2
|
||||
#
|
||||
# You can install PySide2 from Qt repository with
|
||||
# pip3 install --index-url=http://download.qt.io/snapshots/ci/pyside/<Qt-Version>/latest/ pyside2 --trusted-host download.qt.io
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(PYSIDE2_PRIV pyside2 QUIET)
|
||||
|
||||
set(PYSIDE2_FOUND FALSE)
|
||||
|
||||
if(PYSIDE2_PRIV_FOUND)
|
||||
set(PYSIDE2_FOUND TRUE)
|
||||
message(STATUS "Using PySide2 found in the system!")
|
||||
pkg_get_variable(SHIBOKEN_BINARY
|
||||
pyside2
|
||||
generator_location
|
||||
)
|
||||
pkg_get_variable(PYSIDE2_BASEDIR
|
||||
pyside2
|
||||
typesystemdir
|
||||
)
|
||||
pkg_get_variable(PYSIDE_INCLUDE_DIR
|
||||
pyside2
|
||||
includedir
|
||||
)
|
||||
set(PYSIDE_TYPESYSTEMS ${PYSIDE2_BASEDIR})
|
||||
set(PYSIDE2_SO_VERSION ${PYSIDE2_PRIV_VERSION})
|
||||
set(PYSIDE_LIBRARY ${PYSIDE2_PRIV_LINK_LIBRARIES})
|
||||
list(GET PYSIDE_LIBRARY 0 PYSIDE_LIBRARY)
|
||||
else()
|
||||
# extract python library basename
|
||||
list(GET Python3_LIBRARIES 0 PYTHON_LIBRARY_FILENAME)
|
||||
get_filename_component(PYTHON_LIBRARY_FILENAME ${PYTHON_LIBRARY_FILENAME} NAME)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -c "if True:
|
||||
import os, sys
|
||||
try:
|
||||
import PySide2.QtCore as QtCore
|
||||
print(os.path.dirname(QtCore.__file__))
|
||||
except Exception as error:
|
||||
print(error, file=sys.stderr)
|
||||
exit()
|
||||
"
|
||||
OUTPUT_VARIABLE PYSIDE2_BASEDIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(PYSIDE2_BASEDIR)
|
||||
set(PYSIDE_BASEDIR ${PYSIDE2_BASEDIR} CACHE PATH "Top level install of PySide2" FORCE)
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -c "if True:
|
||||
import os
|
||||
import PySide2.QtCore as QtCore
|
||||
print(os.path.basename(QtCore.__file__).split('.', 1)[1])
|
||||
"
|
||||
OUTPUT_VARIABLE PYSIDE2_SUFFIX
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -c "if True:
|
||||
import os
|
||||
import PySide2.QtCore as QtCore
|
||||
print(';'.join(map(str, QtCore.__version_info__)))
|
||||
"
|
||||
OUTPUT_VARIABLE PYSIDE2_SO_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
list(GET PYSIDE2_SO_VERSION 0 PYSIDE2_SO_MACRO_VERSION)
|
||||
list(GET PYSIDE2_SO_VERSION 1 PYSIDE2_SO_MICRO_VERSION)
|
||||
list(GET PYSIDE2_SO_VERSION 2 PYSIDE2_SO_MINOR_VERSION)
|
||||
string(REPLACE ";" "." PYSIDE2_SO_VERSION "${PYSIDE2_SO_VERSION}")
|
||||
|
||||
if(NOT APPLE)
|
||||
set(PYSIDE2_SUFFIX "${PYSIDE2_SUFFIX}.${PYSIDE2_SO_MACRO_VERSION}.${PYSIDE2_SO_MICRO_VERSION}")
|
||||
else()
|
||||
string(REPLACE ".so" "" PYSIDE2_SUFFIX ${PYSIDE2_SUFFIX})
|
||||
set(PYSIDE2_SUFFIX "${PYSIDE2_SUFFIX}.${PYSIDE2_SO_MACRO_VERSION}.${PYSIDE2_SO_MICRO_VERSION}.dylib")
|
||||
endif()
|
||||
|
||||
set(PYSIDE2_FOUND TRUE)
|
||||
message(STATUS "PySide2 base dir: ${PYSIDE2_BASEDIR}" )
|
||||
message(STATUS "PySide2 suffix: ${PYSIDE2_SUFFIX}")
|
||||
endif()
|
||||
|
||||
if (PYSIDE2_FOUND)
|
||||
#PySide
|
||||
#===============================================================================
|
||||
find_path(PYSIDE_INCLUDE_DIR
|
||||
pyside.h
|
||||
PATHS ${PYSIDE2_BASEDIR}/include ${PYSIDE2_CUSTOM_PREFIX}/include/PySide2
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
# Platform specific library names
|
||||
if(MSVC)
|
||||
SET(PYSIDE_LIBRARY_BASENAMES "pyside2.abi3.lib")
|
||||
elseif(CYGWIN)
|
||||
SET(PYSIDE_LIBRARY_BASENAMES "")
|
||||
elseif(WIN32)
|
||||
SET(PYSIDE_LIBRARY_BASENAMES "libpyside2.${PYSIDE2_SUFFIX}")
|
||||
else()
|
||||
SET(PYSIDE_LIBRARY_BASENAMES "libpyside2.${PYSIDE2_SUFFIX}")
|
||||
endif()
|
||||
|
||||
find_file(PYSIDE_LIBRARY
|
||||
${PYSIDE_LIBRARY_BASENAMES}
|
||||
PATHS ${PYSIDE2_BASEDIR} ${PYSIDE2_CUSTOM_PREFIX}/lib
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
find_path(PYSIDE_TYPESYSTEMS
|
||||
typesystem_core.xml
|
||||
PATHS ${PYSIDE2_BASEDIR}/typesystems ${PYSIDE2_CUSTOM_PREFIX}/share/PySide2/typesystems
|
||||
NO_DEFAULT_PATH)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(PYSIDE2_FOUND)
|
||||
message(STATUS "PySide include dir: ${PYSIDE_INCLUDE_DIR}")
|
||||
message(STATUS "PySide library: ${PYSIDE_LIBRARY}")
|
||||
message(STATUS "PySide typesystems: ${PYSIDE_TYPESYSTEMS}")
|
||||
message(STATUS "PySide2 version: ${PYSIDE2_SO_VERSION}")
|
||||
|
||||
# Create PySide2 target
|
||||
add_library(PySide2::pyside2 SHARED IMPORTED GLOBAL)
|
||||
if(MSVC)
|
||||
set_property(TARGET PySide2::pyside2 PROPERTY
|
||||
IMPORTED_IMPLIB ${PYSIDE_LIBRARY})
|
||||
endif()
|
||||
set_property(TARGET PySide2::pyside2 PROPERTY
|
||||
IMPORTED_LOCATION ${PYSIDE_LIBRARY})
|
||||
set_property(TARGET PySide2::pyside2 APPEND PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
${PYSIDE_INCLUDE_DIR}
|
||||
${PYSIDE_INCLUDE_DIR}/QtCore/
|
||||
${PYSIDE_INCLUDE_DIR}/QtGui/
|
||||
${PYSIDE_INCLUDE_DIR}/QtWidgets/
|
||||
${Python3_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
find_package_handle_standard_args(PySide2
|
||||
REQUIRED_VARS PYSIDE2_BASEDIR PYSIDE_INCLUDE_DIR PYSIDE_LIBRARY PYSIDE_TYPESYSTEMS
|
||||
VERSION_VAR PYSIDE2_SO_VERSION
|
||||
)
|
||||
190
cmake/Python/FindShiboken2.cmake
Normal file
190
cmake/Python/FindShiboken2.cmake
Normal file
@@ -0,0 +1,190 @@
|
||||
###
|
||||
# This file is part of KDDockWidgets.
|
||||
#
|
||||
# Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
#
|
||||
# This program is free software you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
# SHIBOKEN_INCLUDE_DIR - Directories to include to use SHIBOKEN
|
||||
# SHIBOKEN_LIBRARY - Files to link against to use SHIBOKEN
|
||||
# SHIBOKEN_BINARY - Executable name
|
||||
# SHIBOKEN_BUILD_TYPE - Tells if Shiboken was compiled in Release or Debug mode.
|
||||
|
||||
# You can install Shiboken from Qt repository with
|
||||
# pip3 install --index-url=http://download.qt.io/snapshots/ci/pyside/<Qt-Version>/latest/ shiboken2-generator --trusted-host download.qt.io
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SHIBOKEN2_PRIV shiboken2 QUIET)
|
||||
|
||||
set(SHIBOKEN_FOUND FALSE)
|
||||
|
||||
if(SHIBOKEN2_PRIV_FOUND)
|
||||
set(SHIBOKEN_FOUND TRUE)
|
||||
message(STATUS "Using shiboken found in the system!")
|
||||
pkg_get_variable(SHIBOKEN_BINARY
|
||||
shiboken2
|
||||
generator_location
|
||||
)
|
||||
pkg_get_variable(SHIBOKEN_BASEDIR
|
||||
shiboken2
|
||||
libdir
|
||||
)
|
||||
pkg_get_variable(SHIBOKEN_INCLUDE_DIR
|
||||
shiboken2
|
||||
includedir
|
||||
)
|
||||
set(SHIBOKEN_VERSION ${SHIBOKEN2_PRIV_VERSION})
|
||||
set(SHIBOKEN_LIBRARY ${SHIBOKEN2_PRIV_LINK_LIBRARIES})
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -c "if True:
|
||||
import os
|
||||
try:
|
||||
import shiboken2_generator
|
||||
print(shiboken2_generator.__path__[0])
|
||||
except:
|
||||
exit()
|
||||
"
|
||||
OUTPUT_VARIABLE SHIBOKEN_GENERATOR_BASEDIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -c "if True:
|
||||
import os
|
||||
try:
|
||||
import shiboken2
|
||||
print(shiboken2.__path__[0])
|
||||
except:
|
||||
exit()
|
||||
"
|
||||
OUTPUT_VARIABLE SHIBOKEN_BASEDIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -c "if True:
|
||||
import os
|
||||
import shiboken2
|
||||
print(';'.join(filter(None, map(str, shiboken2.__version_info__))))
|
||||
"
|
||||
OUTPUT_VARIABLE SHIBOKEN_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
list(GET SHIBOKEN_VERSION 0 SHIBOKEN_MACRO_VERSION)
|
||||
list(GET SHIBOKEN_VERSION 1 SHIBOKEN_MICRO_VERSION)
|
||||
list(GET SHIBOKEN_VERSION 2 SHIBOKEN_MINOR_VERSION)
|
||||
string(REPLACE ";" "." SHIBOKEN_VERSION "${SHIBOKEN_VERSION}")
|
||||
|
||||
message(STATUS "ShibokenGenerator base dir: ${SHIBOKEN_GENERATOR_BASEDIR}")
|
||||
message(STATUS "Shiboken base dir: ${SHIBOKEN_BASEDIR}")
|
||||
message(STATUS "Shiboken custom path: ${SHIBOKEN_CUSTOM_PATH}")
|
||||
|
||||
if(SHIBOKEN_BASEDIR)
|
||||
find_path(SHIBOKEN_INCLUDE_DIR
|
||||
shiboken.h
|
||||
PATHS ${SHIBOKEN_CUSTOM_PATH} ${SHIBOKEN_GENERATOR_BASEDIR}/include
|
||||
NO_DEFAULT_PATH)
|
||||
if(MSVC)
|
||||
SET(SHIBOKEN_LIBRARY_BASENAMES "shiboken2.abi3.lib")
|
||||
elseif(CYGWIN)
|
||||
SET(SHIBOKEN_LIBRARY_BASENAMES "")
|
||||
elseif(WIN32)
|
||||
SET(SHIBOKEN_LIBRARY_BASENAMES "libshiboken2.${PYSIDE2_SUFFIX}")
|
||||
else()
|
||||
SET(SHIBOKEN_LIBRARY_BASENAMES
|
||||
libshiboken2.abi3.so
|
||||
libshiboken2.abi3.so.${SHIBOKEN_MACRO_VERSION}
|
||||
libshiboken2.abi3.so.${SHIBOKEN_MACRO_VERSION}.${SHIBOKEN_MICRO_VERSION}
|
||||
libshiboken2.abi3.so.${SHIBOKEN_VERSION}
|
||||
libshiboken2.abi3.so
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT SHIBOKEN_INCLUDE_DIR)
|
||||
return()
|
||||
endif()
|
||||
set(SHIBOKEN_SEARCH_PATHS ${SHIBOKEN_CUSTOM_PATH})
|
||||
list(APPEND SHIBOKEN_SEARCH_PATHS ${SHIBOKEN_BASEDIR})
|
||||
list(APPEND SHIBOKEN_SEARCH_PATHS ${SHIBOKEN_GENERATOR_BASEDIR})
|
||||
|
||||
find_file(SHIBOKEN_LIBRARY
|
||||
${SHIBOKEN_LIBRARY_BASENAMES}
|
||||
PATHS ${SHIBOKEN_SEARCH_PATHS}
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
find_program(SHIBOKEN_BINARY
|
||||
shiboken2
|
||||
PATHS ${SHIBOKEN_SEARCH_PATHS}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
endif()
|
||||
if (SHIBOKEN_INCLUDE_DIR AND SHIBOKEN_LIBRARY AND SHIBOKEN_BINARY)
|
||||
set(SHIBOKEN_FOUND TRUE)
|
||||
endif()
|
||||
|
||||
if(SHIBOKEN_FOUND)
|
||||
endif()
|
||||
|
||||
|
||||
if(MSVC)
|
||||
# On Windows we must link to python3.dll that is a small library that links against python3x.dll
|
||||
# that allow us to choose any python3x.dll at runtime
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -c "if True:
|
||||
for lib in '${Python3_LIBRARIES}'.split(';'):
|
||||
if '/' in lib:
|
||||
prefix, py = lib.rsplit('/', 1)
|
||||
if py.startswith('python3'):
|
||||
print(prefix + '/python3.lib')
|
||||
break
|
||||
"
|
||||
OUTPUT_VARIABLE PYTHON_LIMITED_LIBRARIES
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
else()
|
||||
# On Linux and MacOs our modules should not link with any python library
|
||||
# that must be handled by the main process
|
||||
set(PYTHON_LIMITED_LIBRARIES "")
|
||||
endif()
|
||||
endif()
|
||||
if (SHIBOKEN_FOUND)
|
||||
message(STATUS "Shiboken include dir: ${SHIBOKEN_INCLUDE_DIR}")
|
||||
message(STATUS "Shiboken library: ${SHIBOKEN_LIBRARY}")
|
||||
message(STATUS "Shiboken binary: ${SHIBOKEN_BINARY}")
|
||||
message(STATUS "Shiboken version: ${SHIBOKEN_VERSION}")
|
||||
|
||||
# Create shiboke2 target
|
||||
add_library(Shiboken2::libshiboken SHARED IMPORTED GLOBAL)
|
||||
if(MSVC)
|
||||
set_property(TARGET Shiboken2::libshiboken PROPERTY
|
||||
IMPORTED_IMPLIB ${SHIBOKEN_LIBRARY})
|
||||
endif()
|
||||
set_property(TARGET Shiboken2::libshiboken PROPERTY
|
||||
IMPORTED_LOCATION ${SHIBOKEN_LIBRARY})
|
||||
set_property(TARGET Shiboken2::libshiboken APPEND PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${SHIBOKEN_INCLUDE_DIR} ${Python3_INCLUDE_DIRS})
|
||||
set_property(TARGET Shiboken2::libshiboken APPEND PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES ${PYTHON_LIMITED_LIBRARIES})
|
||||
|
||||
# Generator target
|
||||
add_executable(Shiboken2::shiboken IMPORTED GLOBAL)
|
||||
set_property(TARGET Shiboken2::shiboken PROPERTY
|
||||
IMPORTED_LOCATION ${SHIBOKEN_BINARY})
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(Shiboken2
|
||||
REQUIRED_VARS SHIBOKEN_BASEDIR SHIBOKEN_INCLUDE_DIR SHIBOKEN_LIBRARY SHIBOKEN_BINARY
|
||||
VERSION_VAR SHIBOKEN_VERSION
|
||||
)
|
||||
|
||||
142
cmake/Python/PySide2ModuleBuild.cmake
Normal file
142
cmake/Python/PySide2ModuleBuild.cmake
Normal file
@@ -0,0 +1,142 @@
|
||||
###
|
||||
# This file is part of KDDockWidgets.
|
||||
#
|
||||
# Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
#
|
||||
# This program is free software you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
set(PYTHON_BINDINGS_INSTALL_PREFIX ${Python3_SITELIB} CACHE FILEPATH "Custom path to install python bindings." )
|
||||
message(STATUS "PYTHON INSTALL PREFIX ${PYTHON_BINDINGS_INSTALL_PREFIX}")
|
||||
|
||||
if (WIN32)
|
||||
set(PATH_SEP "\;")
|
||||
else()
|
||||
set(PATH_SEP ":")
|
||||
endif()
|
||||
if (NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
# Flags that we will pass to shiboken-generator
|
||||
# --generator-set=shiboken: tells the generator that we want to use shiboken to generate code,
|
||||
# a doc generator is also available
|
||||
# --enable-parent-ctor-heuristic: Enable heuristics to detect parent relationship on constructors,
|
||||
# this try to guess parent ownership based on the arguments of the constructors
|
||||
# --enable-pyside-extensionsL: This will generate code for Qt based classes, adding extra attributes,
|
||||
# like signal, slot;
|
||||
# --enable-return-value-heuristic: Similar as --enable-parent-ctor-heuristic this use some logic to guess
|
||||
# parent child relationship based on the returned argument
|
||||
# --use-isnull-as-nb_nonzero: If a class have an isNull() const method, it will be used to compute
|
||||
# the value of boolean casts.
|
||||
# Example, QImage::isNull() will be used when on python side you do `if (myQImage)`
|
||||
set(GENERATOR_EXTRA_FLAGS --generator-set=shiboken
|
||||
--enable-parent-ctor-heuristic
|
||||
--enable-pyside-extensions
|
||||
--enable-return-value-heuristic
|
||||
--use-isnull-as-nb_nonzero
|
||||
-std=c++${CMAKE_CXX_STANDARD})
|
||||
macro(make_path varname)
|
||||
# accepts any number of path variables
|
||||
string(REPLACE ";" "${PATH_SEP}" ${varname} "${ARGN}")
|
||||
endmacro()
|
||||
|
||||
# Creates a PySide module target based on the arguments
|
||||
# This will:
|
||||
# 1 - Create a Cmake custom-target that call shiboken-generator passign the correct arguments
|
||||
# 2 - Create a Cmake library target called "Py${LIBRARY_NAME}" the output name of this target
|
||||
# will be changed to match PySide template
|
||||
# Args:
|
||||
# LIBRARY_NAME - The name of the output module
|
||||
# TYPESYSTEM_PATHS - A list of paths where shiboken should look for typesystem files
|
||||
# INCLUDE_PATHS - Include pahts necessary to parse your class. *This is not the same as build*
|
||||
# OUTPUT_SOURCES - The files that will be generated by shiboken
|
||||
# TARGET_INCLUDE_DIRS - This will be passed to target_include_directories
|
||||
# TARGET_LINK_LIBRARIES - This will be passed to target_link_libraries
|
||||
# GLOBAL_INCLUDE - A header-file that contains alls classes that will be generated
|
||||
# TYPESYSTEM_XML - The target binding typesystem (that should be the full path)
|
||||
# DEPENDS - This var will be passed to add_custom_command(DEPENDS) so a new generation will be
|
||||
# trigger if one of these files changes
|
||||
# MODULE_OUTPUT_DIR - Where the library file should be stored
|
||||
macro(CREATE_PYTHON_BINDINGS
|
||||
LIBRARY_NAME
|
||||
TYPESYSTEM_PATHS
|
||||
INCLUDE_PATHS
|
||||
OUTPUT_SOURCES
|
||||
TARGET_INCLUDE_DIRS
|
||||
TARGET_LINK_LIBRARIES
|
||||
GLOBAL_INCLUDE
|
||||
TYPESYSTEM_XML
|
||||
DEPENDS
|
||||
MODULE_OUTPUT_DIR)
|
||||
|
||||
# Transform the path separators into something shiboken understands.
|
||||
make_path(shiboken_include_dirs ${INCLUDE_PATHS})
|
||||
make_path(shiboken_typesystem_dirs ${TYPESYSTEM_PATHS})
|
||||
get_property(raw_python_dir_include_dirs DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
|
||||
make_path(python_dir_include_dirs ${raw_python_dir_include_dirs})
|
||||
set(shiboken_include_dirs "${shiboken_include_dirs}${PATH_SEP}${python_dir_include_dirs}")
|
||||
|
||||
set(shiboken_framework_include_dirs_option "")
|
||||
if(CMAKE_HOST_APPLE)
|
||||
set(shiboken_framework_include_dirs "${QT_FRAMEWORK_INCLUDE_DIR}")
|
||||
make_path(shiboken_framework_include_dirs ${shiboken_framework_include_dirs})
|
||||
set(shiboken_framework_include_dirs_option "--framework-include-paths=${shiboken_framework_include_dirs}")
|
||||
endif()
|
||||
set_property(SOURCE ${OUTPUT_SOURCES} PROPERTY SKIP_AUTOGEN ON)
|
||||
add_custom_command(OUTPUT ${OUTPUT_SOURCES}
|
||||
COMMAND $<TARGET_PROPERTY:Shiboken2::shiboken,LOCATION> ${GENERATOR_EXTRA_FLAGS}
|
||||
${GLOBAL_INCLUDE}
|
||||
--include-paths=${shiboken_include_dirs}
|
||||
--typesystem-paths=${shiboken_typesystem_dirs}
|
||||
${shiboken_framework_include_dirs_option}
|
||||
--output-directory=${CMAKE_CURRENT_BINARY_DIR}
|
||||
${TYPESYSTEM_XML}
|
||||
DEPENDS ${TYPESYSTEM_XML} ${DEPENDS}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Running generator for ${LIBRARY_NAME} binding...")
|
||||
|
||||
set(TARGET_NAME "Py${LIBRARY_NAME}")
|
||||
set(MODULE_NAME "${LIBRARY_NAME}")
|
||||
add_library(${TARGET_NAME} MODULE ${OUTPUT_SOURCES})
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES
|
||||
PREFIX ""
|
||||
OUTPUT_NAME ${MODULE_NAME}
|
||||
LIBRARY_OUTPUT_DIRECTORY ${MODULE_OUTPUT_DIR}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ".pyd")
|
||||
endif()
|
||||
|
||||
target_include_directories(${TARGET_NAME} PUBLIC
|
||||
${TARGET_INCLUDE_DIRS}
|
||||
${PYSIDE_EXTRA_INCLUDES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
${TARGET_LINK_LIBRARIES}
|
||||
PySide2::pyside2
|
||||
Shiboken2::libshiboken
|
||||
)
|
||||
target_compile_definitions(${TARGET_NAME}
|
||||
PRIVATE Py_LIMITED_API=0x03050000
|
||||
)
|
||||
if(APPLE)
|
||||
set_property(TARGET ${TARGET_NAME} APPEND PROPERTY
|
||||
LINK_FLAGS "-undefined dynamic_lookup")
|
||||
endif()
|
||||
install(TARGETS ${TARGET_NAME}
|
||||
LIBRARY DESTINATION ${PYTHON_BINDINGS_INSTALL_PREFIX}/${TARGET_NAME})
|
||||
endmacro()
|
||||
@@ -196,7 +196,6 @@ KDDockWidgets::DockWidgetBase *MyMainWindow::newDockWidget()
|
||||
}
|
||||
|
||||
dock->resize(600, 600);
|
||||
dock->show();
|
||||
m_toggleMenu->addAction(dock->toggleAction());
|
||||
|
||||
count++;
|
||||
|
||||
8
python/CMakeLists.txt
Normal file
8
python/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
set(PYTHON_VERSION "3.7" CACHE STRING "Use specific python version to build the project.")
|
||||
|
||||
find_package(Python3 ${PYTHON_VERSION} REQUIRED COMPONENTS Interpreter Development)
|
||||
find_package(Shiboken2 REQUIRED)
|
||||
find_package(PySide2 ${Qt5Widgets_VERSION} EXACT REQUIRED)
|
||||
include(PySide2ModuleBuild)
|
||||
|
||||
add_subdirectory(PyKDDockWidgets)
|
||||
71
python/PyKDDockWidgets/CMakeLists.txt
Normal file
71
python/PyKDDockWidgets/CMakeLists.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
# Auto-Genereate files every class will have his cpp/h files
|
||||
set(PyKDDockWidgets_SRC
|
||||
# individual classes
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_dockwidgetbase_wrapper.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_dockwidgetbase_wrapper.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_dockwidget_wrapper.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_dockwidget_wrapper.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_mainwindowbase_wrapper.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_mainwindowbase_wrapper.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_mainwindow_wrapper.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_mainwindow_wrapper.h
|
||||
# namespace wrapper
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_wrapper.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_wrapper.h
|
||||
# global module wrapper
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_module_wrapper.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_python.h
|
||||
)
|
||||
|
||||
# includes necessary to parse and build the classes specified on typesystem
|
||||
set(PyKDDockWidgets_include_paths
|
||||
$<JOIN:$<TARGET_PROPERTY:KDAB::kddockwidgets,INTERFACE_INCLUDE_DIRECTORIES>,${PATH_SEP}>
|
||||
)
|
||||
|
||||
# A list of paths where shiboken should look for typesystem
|
||||
set(PyKDDockWidgets_typesystem_paths
|
||||
# PySide path, this variable was exposed by FindPySide2.cmake
|
||||
${PYSIDE_TYPESYSTEMS}
|
||||
)
|
||||
|
||||
# Include flags/path that will be set in 'target_include_directories'
|
||||
set(PyKDDockWidgets_target_include_directories
|
||||
${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# Libraries that will be necessary to link the target, this will used in the command 'target_link_libraries'
|
||||
set(PyKDDockWidgets_target_link_libraries
|
||||
KDAB::kddockwidgets
|
||||
Qt5::Core
|
||||
Qt5::Gui
|
||||
Qt5::Widgets
|
||||
)
|
||||
|
||||
# changes on these files should trigger a new generation
|
||||
set(PyKDDockWidgets_DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kddockwidgets_global.h
|
||||
${CMAKE_SOURCE_DIR}/src/DockWidgetBase.h
|
||||
${CMAKE_SOURCE_DIR}/src/DockWidget.h
|
||||
${CMAKE_SOURCE_DIR}/src/MainWindowBase.h
|
||||
${CMAKE_SOURCE_DIR}/src/MainWindow.h
|
||||
)
|
||||
|
||||
CREATE_PYTHON_BINDINGS(
|
||||
"KDDockWidgets"
|
||||
"${PyKDDockWidgets_typesystem_paths}"
|
||||
"${PyKDDockWidgets_include_paths}"
|
||||
"${PyKDDockWidgets_SRC}"
|
||||
"${PyKDDockWidgets_target_include_directories}"
|
||||
"${PyKDDockWidgets_target_link_libraries}"
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kddockwidgets_global.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/typesystem_kddockwidgets.xml
|
||||
"${PyKDDockWidgets_DEPENDS}"
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Make moduled import from build dir works
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CMAKE_CURRENT_BINARY_DIR}/__init__.py)
|
||||
|
||||
# install
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py DESTINATION ${PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)
|
||||
14
python/PyKDDockWidgets/__init__.py
Normal file
14
python/PyKDDockWidgets/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
__all__ = ['KDDockWidgets']
|
||||
|
||||
# Preload PySide2 libraries to avoid missing libraries while loading KDDockWidgets
|
||||
try:
|
||||
from PySide2 import QtCore
|
||||
except Exception:
|
||||
print("Failed to lod PySide")
|
||||
raise
|
||||
|
||||
# avoid duplicate namespace, due the PYSIDE-1325 bug I will have my package like this
|
||||
# PyKDDockWidgets.KDDockWidgets.KDDockWidgets.MainWindow
|
||||
# To avoid this I add a WORKAROUND to reduce it
|
||||
from .KDDockWidgets import KDDockWidgets as _priv
|
||||
KDDockWidgets = _priv
|
||||
13
python/PyKDDockWidgets/kddockwidgets_global.h
Normal file
13
python/PyKDDockWidgets/kddockwidgets_global.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Make "signals:", "slots:" visible as access specifiers
|
||||
#define QT_ANNOTATE_ACCESS_SPECIFIER(a) __attribute__((annotate(#a)))
|
||||
|
||||
// Define PYTHON_BINDINGS this will be used in some part of c++ to skip problematic parts
|
||||
#define PYTHON_BINDINGS
|
||||
|
||||
#include <MainWindowBase.h>
|
||||
#include <MainWindow.h>
|
||||
#include <DockWidgetBase.h>
|
||||
#include <DockWidget.h>
|
||||
|
||||
39
python/PyKDDockWidgets/typesystem_kddockwidgets.xml
Normal file
39
python/PyKDDockWidgets/typesystem_kddockwidgets.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- The package name -->
|
||||
<typesystem package="KDDockWidgets">
|
||||
<!-- Pre-defined typesystem that contains types used by our class
|
||||
PySide has one typesystem for each module, here we use only the widgets
|
||||
typesystem because it already include gui and core typesystem -->
|
||||
<load-typesystem name="typesystem_widgets.xml" generate="no"/>
|
||||
|
||||
<!-- Our classes are declared in a namespace, so we should define this -->
|
||||
<namespace-type name="KDDockWidgets">
|
||||
<!-- this is used in a public virtual pure function we need to declare it
|
||||
otherwise shiboken will ignore the function and will fail to create a wrapper -->
|
||||
<primitive-type name="DropAreaWithCentralFrame"/>
|
||||
|
||||
<!-- Some plublic enum and flags -->
|
||||
<enum-type name="Location"/>
|
||||
<enum-type name="MainWindowOption" flags="MainWindowOptions"/>
|
||||
<enum-type name="AddingOption"/>
|
||||
<enum-type name="RestoreOption" flags="RestoreOptions"/>
|
||||
<enum-type name="DefaultSizeMode"/>
|
||||
<enum-type name="FrameOption" flags="FrameOptions"/>
|
||||
|
||||
<!-- our classes
|
||||
For class we can use two types:
|
||||
object-type: class that does not have a copy-contructor and can not be passed as value to functions;
|
||||
value-type: class that can be passed as value for functions
|
||||
Here we only use 'object-type' since all our classes are derived from QWidget
|
||||
-->
|
||||
<object-type name="MainWindowBase" />
|
||||
<object-type name="MainWindow" />
|
||||
<object-type name="DockWidgetBase" >
|
||||
<!-- this class contains a internal enum, so it should be declared
|
||||
inside of the object-type -->
|
||||
<enum-type name="Option" flags="Options" />
|
||||
</object-type>
|
||||
|
||||
<object-type name="DockWidget" />
|
||||
</namespace-type>
|
||||
</typesystem>
|
||||
170
python/examples/MyMainWindow.py
Normal file
170
python/examples/MyMainWindow.py
Normal file
@@ -0,0 +1,170 @@
|
||||
################################################################################
|
||||
## This file is part of KDDockWidgets.
|
||||
##
|
||||
## Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
## Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
################################################################################
|
||||
|
||||
from PySide2 import QtCore, QtWidgets, QtGui
|
||||
from PyKDDockWidgets import KDDockWidgets
|
||||
|
||||
from MyWidget1 import MyWidget1
|
||||
from MyWidget2 import MyWidget2
|
||||
from MyWidget3 import MyWidget3
|
||||
|
||||
def newMyWidget(parent = None):
|
||||
randomNumber = QtCore.QRandomGenerator.global_().bounded(0, 100) + 1
|
||||
if (randomNumber < 50):
|
||||
if (randomNumber < 33):
|
||||
return MyWidget1(parent)
|
||||
else:
|
||||
return MyWidget3(parent)
|
||||
else:
|
||||
return MyWidget2(parent)
|
||||
|
||||
class MyMainWindow(KDDockWidgets.MainWindow):
|
||||
s_count = 0
|
||||
s_menuCount = 0
|
||||
|
||||
def __init__(self, uniqueName, options = KDDockWidgets.MainWindowOption_None, dockWidget0IsNonClosable = False, nonDockableDockWidget9 = False, restoreIsRelative = False, maxSizeForDockWidget8 = False, affinityName = "", parent = None):
|
||||
super().__init__(uniqueName, options, parent)
|
||||
self.m_dockWidget0IsNonClosable = dockWidget0IsNonClosable
|
||||
self.m_dockWidget9IsNonDockable = nonDockableDockWidget9
|
||||
self.m_restoreIsRelative = restoreIsRelative
|
||||
self.m_maxSizeForDockWidget8 = maxSizeForDockWidget8
|
||||
self.m_dockwidgets = []
|
||||
|
||||
menubar = self.menuBar()
|
||||
fileMenu = QtWidgets.QMenu("File")
|
||||
self.m_toggleMenu = QtWidgets.QMenu("Toggle")
|
||||
menubar.addMenu(fileMenu)
|
||||
menubar.addMenu(self.m_toggleMenu)
|
||||
|
||||
newAction = fileMenu.addAction("New DockWidget")
|
||||
newAction.triggered.connect(self._newDockWidget)
|
||||
|
||||
saveLayoutAction = fileMenu.addAction("Save Layout")
|
||||
saveLayoutAction.triggered.connect(self._saveLayout)
|
||||
|
||||
restoreLayoutAction = fileMenu.addAction("Restore Layout")
|
||||
restoreLayoutAction.triggered.connect(self._restoreLayout)
|
||||
|
||||
closeAllAction = fileMenu.addAction("Close All")
|
||||
closeAllAction.triggered.connect(self._closeAll)
|
||||
|
||||
layoutEqually = fileMenu.addAction("Layout Equally")
|
||||
layoutEqually.triggered.connect(self.layoutEqually)
|
||||
|
||||
quitAction = fileMenu.addAction("Quit")
|
||||
quitAction.triggered.connect(QtWidgets.QApplication.instance().quit)
|
||||
|
||||
self.setAffinities([ affinityName ])
|
||||
self.createDockWidgets()
|
||||
|
||||
def _newDockWidget(self):
|
||||
MyMainWindow.s_menuCount += 1
|
||||
w = newMyWidget(self)
|
||||
w.setGeometry(100, 100, 400, 400)
|
||||
dock = KDDockWidgets.DockWidget("new dock %d"%(MyMainWindow.s_menuCount))
|
||||
dock.setWidget(w)
|
||||
dock.resize(600, 600)
|
||||
dock.show()
|
||||
self.m_dockwidgets.append(dock)
|
||||
|
||||
def _saveLayout(self):
|
||||
#saver = KDDockWidgets.LayoutSaver()
|
||||
#result = saver.saveToFile("mylayout.json")
|
||||
#print("Saving layout to disk. Result=", result)
|
||||
print("Not available")
|
||||
|
||||
def _restoreLayout(self):
|
||||
#options = KDDockWidgets.RestoreOption_None
|
||||
#if self.m_restoreIsRelative:
|
||||
# options |= KDDockWidgets.RestoreOption_RelativeToMainWindow
|
||||
#saver = KDDockWidgets.LayoutSaver(options)
|
||||
#saver.restoreFromFile("mylayout.json")
|
||||
print("Not available")
|
||||
|
||||
def _closeAll(self):
|
||||
for dw in self.m_dockwidgets:
|
||||
dw.close()
|
||||
|
||||
def createDockWidgets(self):
|
||||
if self.m_dockWidget9IsNonDockable:
|
||||
numDockWidgets = 10
|
||||
else:
|
||||
numDockWidgets = 9
|
||||
|
||||
# numDockWidgets = 2
|
||||
# Create 9 KDDockWidget::DockWidget and the respective widgets they're hosting (MyWidget instances)
|
||||
for i in range(numDockWidgets):
|
||||
self.m_dockwidgets.append(self.newDockWidget())
|
||||
|
||||
# MainWindow::addDockWidget() attaches a dock widget to the main window:
|
||||
self.addDockWidget(self.m_dockwidgets[0], KDDockWidgets.Location_OnTop)
|
||||
|
||||
# Here, for finer granularity we specify right of dockwidgets[0]:
|
||||
self.addDockWidget(self.m_dockwidgets[1], KDDockWidgets.Location_OnRight, self.m_dockwidgets[0])
|
||||
|
||||
self.addDockWidget(self.m_dockwidgets[2], KDDockWidgets.Location_OnLeft)
|
||||
self.addDockWidget(self.m_dockwidgets[3], KDDockWidgets.Location_OnBottom)
|
||||
self.addDockWidget(self.m_dockwidgets[4], KDDockWidgets.Location_OnBottom)
|
||||
|
||||
# Tab two dock widgets together
|
||||
self.m_dockwidgets[3].addDockWidgetAsTab(self.m_dockwidgets[5])
|
||||
|
||||
# 6 is floating, as it wasn't added to the main window via MainWindow::addDockWidget().
|
||||
# and we tab 7 with it.
|
||||
self.m_dockwidgets[6].addDockWidgetAsTab(self.m_dockwidgets[7])
|
||||
|
||||
# Floating windows also support nesting, here we add 8 to the bottom of the group
|
||||
self.m_dockwidgets[6].addDockWidgetToContainingWindow(self.m_dockwidgets[8], KDDockWidgets.Location_OnBottom)
|
||||
|
||||
floatingWindow = self.m_dockwidgets[6].window()
|
||||
floatingWindow.move(100, 100)
|
||||
|
||||
def newDockWidget(self):
|
||||
# Passing options is optional, we just want to illustrate Option_NotClosable here
|
||||
options = KDDockWidgets.DockWidget.Option_None
|
||||
if (MyMainWindow.s_count == 0) and self.m_dockWidget0IsNonClosable:
|
||||
options |= KDDockWidgets.DockWidget.Option_NotClosable
|
||||
|
||||
if (MyMainWindow.s_count == 9) and self.m_dockWidget9IsNonDockable:
|
||||
options |= KDDockWidgets.DockWidget.Option_NotDockable
|
||||
|
||||
dock = KDDockWidgets.DockWidget("DockWidget #%d"%(MyMainWindow.s_count), options)
|
||||
dock.setAffinities(self.affinities()); # optional, just to show the feature. Pass -mi to the example to see incompatible dock widgets
|
||||
|
||||
if MyMainWindow.s_count == 1:
|
||||
dock.setIcon(QtGui.QIcon.fromTheme("mail-message"))
|
||||
|
||||
myWidget = newMyWidget(self)
|
||||
if (MyMainWindow.s_count == 8) and self.m_maxSizeForDockWidget8:
|
||||
# Set a maximum size on dock #8
|
||||
myWidget.setMaximumSize(200, 200)
|
||||
|
||||
dock.setWidget(myWidget)
|
||||
|
||||
if dock.options() & KDDockWidgets.DockWidget.Option_NotDockable:
|
||||
dock.setTitle("DockWidget #%d (%s)" %(MyMainWindow.s_count, "non dockable"))
|
||||
else:
|
||||
dock.setTitle("DockWidget #%d"%(MyMainWindow.s_count))
|
||||
|
||||
dock.resize(600, 600)
|
||||
self.m_toggleMenu.addAction(dock.toggleAction())
|
||||
MyMainWindow.s_count += 1
|
||||
return dock
|
||||
|
||||
58
python/examples/MyWidget.py
Normal file
58
python/examples/MyWidget.py
Normal file
@@ -0,0 +1,58 @@
|
||||
################################################################################
|
||||
## This file is part of KDDockWidgets.
|
||||
##
|
||||
## Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
## Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
################################################################################
|
||||
|
||||
from PySide2 import QtWidgets, QtGui, QtCore
|
||||
|
||||
class MyWidget(QtWidgets.QWidget):
|
||||
s_images = {}
|
||||
def __init__(self, backgroundFile, logoFile, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.m_background = self._lookupImage(backgroundFile)
|
||||
self.m_logo = self._lookupImage(logoFile)
|
||||
|
||||
def _lookupImage(self, imageName):
|
||||
if imageName == "":
|
||||
return None
|
||||
|
||||
if imageName not in MyWidget.s_images:
|
||||
MyWidget.s_images[imageName] = QtGui.QImage(imageName)
|
||||
|
||||
return MyWidget.s_images[imageName]
|
||||
|
||||
def drawLogo(self, p):
|
||||
|
||||
if not self.m_logo:
|
||||
return
|
||||
|
||||
ratio = self.m_logo.height() / (self.m_logo.width() * 1.0)
|
||||
maxWidth = int(0.80 * self.size().width())
|
||||
maxHeight = int(0.80 * self.size().height())
|
||||
proposedHeight = int(maxWidth * ratio)
|
||||
if (proposedHeight <= maxHeight):
|
||||
width = maxWidth
|
||||
else:
|
||||
width = int(maxHeight / ratio)
|
||||
|
||||
height = int(width * ratio)
|
||||
targetLogoRect = QtCore.QRect(0,0, width, height)
|
||||
targetLogoRect.moveCenter(self.rect().center() + QtCore.QPoint(0, -int(self.size().height() * 0.00)))
|
||||
p.drawImage(targetLogoRect, self.m_logo, self.m_logo.rect());
|
||||
|
||||
36
python/examples/MyWidget1.py
Normal file
36
python/examples/MyWidget1.py
Normal file
@@ -0,0 +1,36 @@
|
||||
################################################################################
|
||||
## This file is part of KDDockWidgets.
|
||||
##
|
||||
## Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
## Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
################################################################################
|
||||
|
||||
from PySide2 import QtWidgets, QtGui
|
||||
|
||||
from MyWidget import MyWidget
|
||||
|
||||
|
||||
class MyWidget1(MyWidget):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(":/assets/triangles.png", ":/assets/KDAB_bubble_white.png", parent)
|
||||
|
||||
|
||||
def paintEvent(self, ev):
|
||||
p = QtGui.QPainter(self)
|
||||
p.fillRect(self.rect(), QtGui.QColor(0xCC, 0xCC, 0xCC))
|
||||
p.drawImage(self.m_background.rect(), self.m_background, self.m_background.rect())
|
||||
self.drawLogo(p)
|
||||
|
||||
34
python/examples/MyWidget2.py
Normal file
34
python/examples/MyWidget2.py
Normal file
@@ -0,0 +1,34 @@
|
||||
################################################################################
|
||||
## This file is part of KDDockWidgets.
|
||||
##
|
||||
## Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
## Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
################################################################################
|
||||
|
||||
from PySide2 import QtWidgets, QtGui, QtCore
|
||||
|
||||
from MyWidget import MyWidget
|
||||
|
||||
class MyWidget2(MyWidget):
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__("", ":/assets/KDAB_bubble_blue.png", parent)
|
||||
|
||||
def paintEvent(self, ev):
|
||||
p = QtGui.QPainter(self)
|
||||
p.fillRect(self.rect(), QtCore.Qt.white);
|
||||
self.drawLogo(p)
|
||||
|
||||
39
python/examples/MyWidget3.py
Normal file
39
python/examples/MyWidget3.py
Normal file
@@ -0,0 +1,39 @@
|
||||
################################################################################
|
||||
## This file is part of KDDockWidgets.
|
||||
##
|
||||
## Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
## Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
################################################################################
|
||||
|
||||
from PySide2 import QtWidgets, QtGui, QtCore
|
||||
|
||||
from MyWidget import MyWidget
|
||||
|
||||
class MyWidget3(MyWidget):
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(":/assets/base.png", ":/assets/KDAB_bubble_fulcolor.png", parent)
|
||||
self.m_triangle = QtGui.QImage(":/assets/tri.png")
|
||||
|
||||
def paintEvent(self, ev):
|
||||
p = QtGui.QPainter(self)
|
||||
p.fillRect(self.rect(), QtGui.QColor(0xD5, 0xD5, 0xD5))
|
||||
p.drawImage(self.m_background.rect(), self.m_background, self.m_background.rect())
|
||||
|
||||
targetRect = QtCore.QRect(QtCore.QPoint(self.width() - self.m_triangle.width(), self.height() - self.m_triangle.height()), self.m_triangle.size())
|
||||
|
||||
self.drawLogo(p)
|
||||
|
||||
8
python/examples/README.txt
Normal file
8
python/examples/README.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Running python example
|
||||
======================
|
||||
|
||||
Generate resource file with:
|
||||
~# rcc -g python -o rc_assets.py ../../examples/dockwidgets/resources_example.qrc
|
||||
|
||||
Run the app:
|
||||
~# python3 main.py
|
||||
44
python/examples/main.py
Normal file
44
python/examples/main.py
Normal file
@@ -0,0 +1,44 @@
|
||||
################################################################################
|
||||
## This file is part of KDDockWidgets.
|
||||
##
|
||||
## Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
## Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
################################################################################
|
||||
|
||||
|
||||
from PySide2 import QtWidgets, QtCore
|
||||
from PyKDDockWidgets import KDDockWidgets
|
||||
from MyMainWindow import MyMainWindow
|
||||
|
||||
import sys
|
||||
import rc_assets
|
||||
|
||||
if __name__ == "__main__":
|
||||
QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
|
||||
QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps)
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
app.setOrganizationName("KDAB")
|
||||
app.setApplicationName("Test app")
|
||||
app.setStyle(QtWidgets.QStyleFactory.create("Fusion"))
|
||||
|
||||
mainWindow = MyMainWindow("MyMainWindow", )
|
||||
mainWindow.setWindowTitle("Main Window 1")
|
||||
mainWindow.resize(1200, 1200)
|
||||
mainWindow.show()
|
||||
|
||||
app.exec_()
|
||||
|
||||
@@ -6,7 +6,6 @@ add_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS
|
||||
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||
-DQT_STRICT_ITERATORS
|
||||
-DQT_NO_KEYWORDS
|
||||
-DQT_DISABLE_DEPRECATED_BEFORE=0x060000
|
||||
-DQT_NO_FOREACH
|
||||
)
|
||||
|
||||
@@ -154,7 +153,7 @@ if (NOT WIN32 AND NOT APPLE)
|
||||
target_link_libraries(kddockwidgets PUBLIC Qt5::X11Extras)
|
||||
endif()
|
||||
|
||||
install (TARGETS kddockwidgets kddockwidgets_multisplitter
|
||||
install (TARGETS kddockwidgets
|
||||
EXPORT kddockwidgetsTargets
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
@@ -188,6 +187,9 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgetsConfig.cmake"
|
||||
)
|
||||
|
||||
if (OPTION_DEVELOPER_MODE)
|
||||
# Under developer mode since kddw might be a sub-folder of a project setting a different value for QT_DISABLE_DEPRECATED_BEFORE
|
||||
target_compile_definitions(kddockwidgets PRIVATE QT_DISABLE_DEPRECATED_BEFORE=0x060000)
|
||||
|
||||
add_executable(kddockwidgets_linter layoutlinter_main.cpp)
|
||||
target_link_libraries(kddockwidgets_linter kddockwidgets kddockwidgets_multisplitter Qt5::Widgets)
|
||||
endif()
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
* when visible, or stays without a parent when hidden. This allows to support docking
|
||||
* to different main windows.
|
||||
*/
|
||||
explicit DockWidget(const QString &uniqueName, Options options = {});
|
||||
explicit DockWidget(const QString &uniqueName, Options options = DockWidgetBase::Options());
|
||||
|
||||
///@brief destructor
|
||||
~DockWidget() override;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "Frame_p.h"
|
||||
#include "FloatingWindow_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "TabWidget_p.h"
|
||||
#include "Utils_p.h"
|
||||
#include "DockRegistry_p.h"
|
||||
#include "WidgetResizeHandler_p.h"
|
||||
@@ -85,6 +84,11 @@ public:
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
FloatingWindow *floatingWindow() const
|
||||
{
|
||||
return qobject_cast<FloatingWindow*>(q->window());
|
||||
}
|
||||
|
||||
QPoint defaultCenterPosForFloating();
|
||||
|
||||
void updateTitle();
|
||||
@@ -94,7 +98,6 @@ public:
|
||||
void updateFloatAction();
|
||||
void onDockWidgetShown();
|
||||
void onDockWidgetHidden();
|
||||
TabWidget *parentTabWidget() const;
|
||||
void show();
|
||||
void close();
|
||||
void restoreToPreviousPosition();
|
||||
@@ -210,7 +213,7 @@ void DockWidgetBase::addDockWidgetToContainingWindow(DockWidgetBase *other, Loca
|
||||
if (isWindow())
|
||||
morphIntoFloatingWindow();
|
||||
|
||||
if (auto fw = qobject_cast<FloatingWindow *>(window())) {
|
||||
if (auto fw = floatingWindow()) {
|
||||
fw->dropArea()->addDockWidget(other, location, relativeTo);
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "Couldn't find floating nested window";
|
||||
@@ -223,6 +226,7 @@ void DockWidgetBase::setWidget(QWidget *w)
|
||||
qCDebug(addwidget) << Q_FUNC_INFO << w;
|
||||
|
||||
d->widget = w;
|
||||
setSizePolicy(w->sizePolicy());
|
||||
Q_EMIT widgetChanged(w);
|
||||
setWindowTitle(uniqueName());
|
||||
}
|
||||
@@ -237,7 +241,7 @@ bool DockWidgetBase::isFloating() const
|
||||
if (isWindow())
|
||||
return true;
|
||||
|
||||
auto fw = qobject_cast<FloatingWindow *>(window());
|
||||
auto fw = floatingWindow();
|
||||
return fw && fw->hasSingleDockWidget();
|
||||
}
|
||||
|
||||
@@ -254,22 +258,23 @@ void DockWidgetBase::setFloating(bool floats)
|
||||
if (floats) {
|
||||
d->saveTabIndex();
|
||||
if (isTabbed()) {
|
||||
TabWidget *tabWidget= d->parentTabWidget();
|
||||
if (!tabWidget) {
|
||||
qWarning() << "DockWidget::setFloating: Tabbed but no tabbar exists"
|
||||
auto frame = this->frame();
|
||||
if (!frame) {
|
||||
qWarning() << "DockWidget::setFloating: Tabbed but no frame exists"
|
||||
<< this;
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
tabWidget->detachTab(this);
|
||||
frame->detachTab(this);
|
||||
} else {
|
||||
frame()->titleBar()->makeWindow();
|
||||
}
|
||||
|
||||
auto lastGeo = lastPositions().lastFloatingGeometry();
|
||||
if (lastGeo.isValid())
|
||||
window()->setGeometry(lastGeo);
|
||||
|
||||
if (lastGeo.isValid()) {
|
||||
if (auto fw = floatingWindow())
|
||||
fw->setSuggestedGeometry(lastGeo, /*preserveCenter=*/true);
|
||||
}
|
||||
} else {
|
||||
saveLastFloatingGeometry();
|
||||
d->restoreToPreviousPosition();
|
||||
@@ -327,8 +332,8 @@ void DockWidgetBase::setOptions(Options options)
|
||||
|
||||
bool DockWidgetBase::isTabbed() const
|
||||
{
|
||||
if (TabWidget* tabWidget = d->parentTabWidget()) {
|
||||
return frame()->alwaysShowsTabs() || tabWidget->numDockWidgets() > 1;
|
||||
if (Frame *frame = this->frame()) {
|
||||
return frame->alwaysShowsTabs() || frame->dockWidgetCount() > 1;
|
||||
} else {
|
||||
if (!isFloating())
|
||||
qWarning() << "DockWidget::isTabbed() Couldn't find any tab widget.";
|
||||
@@ -338,8 +343,8 @@ bool DockWidgetBase::isTabbed() const
|
||||
|
||||
bool DockWidgetBase::isCurrentTab() const
|
||||
{
|
||||
if (TabWidget* tabWidget = d->parentTabWidget()) {
|
||||
return tabWidget->currentIndex() == tabWidget->indexOfDockWidget(const_cast<DockWidgetBase*>(this));
|
||||
if (Frame *frame = this->frame()) {
|
||||
return frame->currentIndex() == frame->indexOfDockWidget(const_cast<DockWidgetBase*>(this));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
@@ -347,8 +352,8 @@ bool DockWidgetBase::isCurrentTab() const
|
||||
|
||||
void DockWidgetBase::setAsCurrentTab()
|
||||
{
|
||||
if (TabWidget* tabWidget = d->parentTabWidget())
|
||||
tabWidget->setCurrentDockWidget(this);
|
||||
if (Frame *frame = this->frame())
|
||||
frame->setCurrentDockWidget(this);
|
||||
}
|
||||
|
||||
void DockWidgetBase::setIcon(const QIcon &icon)
|
||||
@@ -405,7 +410,7 @@ void DockWidgetBase::raise()
|
||||
|
||||
setAsCurrentTab();
|
||||
|
||||
if (auto fw = qobject_cast<FloatingWindow*>(window())) {
|
||||
if (auto fw = floatingWindow()) {
|
||||
fw->raise();
|
||||
fw->activateWindow();
|
||||
}
|
||||
@@ -444,7 +449,7 @@ FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
|
||||
qCDebug(creation) << "DockWidget::morphIntoFloatingWindow() this=" << this
|
||||
<< "; visible=" << isVisible();
|
||||
|
||||
if (auto fw = qobject_cast<FloatingWindow*>(window()))
|
||||
if (auto fw = floatingWindow())
|
||||
return fw; // Nothing to do
|
||||
|
||||
if (isWindow()) {
|
||||
@@ -459,7 +464,7 @@ FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
|
||||
auto frame = Config::self().frameworkWidgetFactory()->createFrame();
|
||||
frame->addWidget(this);
|
||||
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(frame);
|
||||
floatingWindow->setGeometry(geo);
|
||||
floatingWindow->setSuggestedGeometry(geo);
|
||||
floatingWindow->show();
|
||||
|
||||
return floatingWindow;
|
||||
@@ -487,7 +492,7 @@ Frame *DockWidgetBase::frame() const
|
||||
|
||||
FloatingWindow *DockWidgetBase::floatingWindow() const
|
||||
{
|
||||
return qobject_cast<FloatingWindow*>(window());
|
||||
return d->floatingWindow();
|
||||
}
|
||||
|
||||
void DockWidgetBase::addPlaceholderItem(Layouting::Item *item)
|
||||
@@ -547,9 +552,9 @@ void DockWidgetBase::Private::updateToggleAction()
|
||||
{
|
||||
QScopedValueRollback<bool> recursionGuard(m_updatingToggleAction, true); // Guard against recursiveness
|
||||
m_updatingToggleAction = true;
|
||||
if ((q->isVisible() || parentTabWidget()) && !toggleAction->isChecked()) {
|
||||
if ((q->isVisible() || q->frame()) && !toggleAction->isChecked()) {
|
||||
toggleAction->setChecked(true);
|
||||
} else if ((!q->isVisible() && !parentTabWidget()) && toggleAction->isChecked()) {
|
||||
} else if ((!q->isVisible() && !q->frame()) && toggleAction->isChecked()) {
|
||||
toggleAction->setChecked(false);
|
||||
}
|
||||
}
|
||||
@@ -583,14 +588,6 @@ void DockWidgetBase::Private::onDockWidgetHidden()
|
||||
qCDebug(hiding) << Q_FUNC_INFO << "parent=" << q->parentWidget();
|
||||
}
|
||||
|
||||
TabWidget *DockWidgetBase::Private::parentTabWidget() const
|
||||
{
|
||||
if (auto f = q->frame())
|
||||
return f->tabWidget();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::close()
|
||||
{
|
||||
if (!m_isForceClosing && q->isFloating() && q->isVisible()) { // only user-closing is interesting to save the geometry
|
||||
@@ -602,18 +599,16 @@ void DockWidgetBase::Private::close()
|
||||
saveTabIndex();
|
||||
|
||||
// Do some cleaning. Widget is hidden, but we must hide the tab containing it.
|
||||
if (auto tabWidget = parentTabWidget()) {
|
||||
tabWidget->removeDockWidget(q);
|
||||
if (Frame *frame = q->frame()) {
|
||||
frame->removeWidget(q);
|
||||
q->setParent(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::restoreToPreviousPosition()
|
||||
{
|
||||
if (!m_lastPositions.isValid()) {
|
||||
qWarning() << Q_FUNC_INFO << "Only restoring to MainWindow supported for now";
|
||||
if (!m_lastPositions.isValid())
|
||||
return;
|
||||
}
|
||||
|
||||
Layouting::Item *item = m_lastPositions.lastItem();
|
||||
|
||||
@@ -660,8 +655,8 @@ void DockWidgetBase::Private::maybeRestoreToPreviousPosition()
|
||||
|
||||
int DockWidgetBase::Private::currentTabIndex() const
|
||||
{
|
||||
TabWidget *tabWidget = parentTabWidget();
|
||||
return tabWidget ? tabWidget->indexOfDockWidget(q) : 0;
|
||||
Frame *frame = q->frame();
|
||||
return frame ? frame->indexOfDockWidget(q) : 0;
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::saveTabIndex()
|
||||
|
||||
@@ -64,7 +64,11 @@ class StateDragging;
|
||||
*
|
||||
* Do not use instantiate directly in user code. Use DockWidget instead.
|
||||
*/
|
||||
#ifndef PYTHON_BINDINGS //Pyside bug: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1327
|
||||
class DOCKS_EXPORT DockWidgetBase : public QWidgetOrQuick
|
||||
#else
|
||||
class DOCKS_EXPORT DockWidgetBase : public QWidget
|
||||
#endif
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -86,7 +90,7 @@ public:
|
||||
* There's no parent argument. The DockWidget is either parented to FloatingWindow or MainWindow
|
||||
* when visible, or stays without a parent when hidden.
|
||||
*/
|
||||
explicit DockWidgetBase(const QString &uniqueName, Options options = {});
|
||||
explicit DockWidgetBase(const QString &uniqueName, Options options = DockWidgetBase::Options());
|
||||
|
||||
///@brief destructor
|
||||
~DockWidgetBase() override;
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
///@param parent QObject *parent to pass to QMainWindow constructor.
|
||||
///@param flags Window flags to pass to QMainWindow constructor.
|
||||
explicit MainWindow(const QString &uniqueName, MainWindowOptions options = MainWindowOption_None,
|
||||
QWidget *parent = nullptr, Qt::WindowFlags flags = {});
|
||||
QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
|
||||
///@brief Destructor
|
||||
~MainWindow() override;
|
||||
|
||||
@@ -51,13 +51,17 @@ class DropAreaWithCentralFrame;
|
||||
*
|
||||
* Do not use instantiate directly in user code. Use MainWindow instead.
|
||||
*/
|
||||
#ifndef PYTHON_BINDINGS //Pyside bug: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1327
|
||||
class DOCKS_EXPORT MainWindowBase : public QMainWindowOrQuick
|
||||
#else
|
||||
class DOCKS_EXPORT MainWindowBase : public QMainWindow
|
||||
#endif
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef QVector<MainWindowBase*> List;
|
||||
explicit MainWindowBase(const QString &uniqueName, MainWindowOptions options = MainWindowOption_HasCentralFrame,
|
||||
QWidgetOrQuick *parent = nullptr, Qt::WindowFlags flags = {});
|
||||
QWidgetOrQuick *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
|
||||
~MainWindowBase() override;
|
||||
|
||||
@@ -79,7 +83,7 @@ public:
|
||||
*/
|
||||
void addDockWidget(DockWidgetBase *dockWidget,
|
||||
KDDockWidgets::Location location,
|
||||
DockWidgetBase *relativeTo = nullptr, AddingOption option = {});
|
||||
DockWidgetBase *relativeTo = nullptr, AddingOption option = KDDockWidgets::AddingOption());
|
||||
|
||||
/**
|
||||
* @brief Returns the unique name that was passed via constructor.
|
||||
|
||||
@@ -184,6 +184,16 @@ void StateDragging::onEntry(QEvent *)
|
||||
q->m_windowBeingDragged = q->m_draggable->makeWindow();
|
||||
if (q->m_windowBeingDragged) {
|
||||
qCDebug(state) << "StateDragging entered. m_draggable=" << q->m_draggable << "; m_windowBeingDragged=" << q->m_windowBeingDragged->floatingWindow();
|
||||
|
||||
auto fw = q->m_windowBeingDragged->floatingWindow();
|
||||
if (!fw->geometry().contains(q->m_pressPos)) {
|
||||
// The window shrunk when the drag started, this can happen if it has max-size constraints
|
||||
// we make the floating window smaller. Has the downside that it might not be under the mouse
|
||||
// cursor anymore, so make the change
|
||||
if (fw->width() < q->m_offset.x()) { // make sure it shrunk
|
||||
q->m_offset.setX(fw->width() / 2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Shouldn't happen
|
||||
qWarning() << Q_FUNC_INFO << "No window being dragged for " << q->m_draggable->asWidget();
|
||||
|
||||
@@ -42,8 +42,6 @@
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
static int s_dbg_numFloatingWindows = 0;
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -223,6 +221,28 @@ const Frame::List FloatingWindow::frames() const
|
||||
return m_dropArea->findChildren<Frame *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
}
|
||||
|
||||
void FloatingWindow::setSuggestedGeometry(QRect suggestedRect, bool preserveCenter)
|
||||
{
|
||||
const Frame::List frames = this->frames();
|
||||
if (frames.size() == 1) {
|
||||
// Let's honour max-size when we have a single-frame.
|
||||
// multi-frame cases are more complicated and we're not sure if we want the window to bounce around.
|
||||
// single-frame is the most common case, like floating a dock widget, so let's do that first, it's also
|
||||
// easy.
|
||||
Frame *frame = frames[0];
|
||||
const QSize waste = (minimumSize() - frame->minSize()).expandedTo(QSize(0, 0));
|
||||
const QSize size = (frame->maxSizeHint() + waste).boundedTo(suggestedRect.size());
|
||||
|
||||
// Resize to new size but preserve center
|
||||
const QPoint originalCenter = suggestedRect.center();
|
||||
suggestedRect.setSize(size);
|
||||
if (preserveCenter)
|
||||
suggestedRect.moveCenter(originalCenter);
|
||||
}
|
||||
|
||||
setGeometry(suggestedRect);
|
||||
}
|
||||
|
||||
void FloatingWindow::scheduleDeleteLater()
|
||||
{
|
||||
m_beingDeleted = true;
|
||||
@@ -299,11 +319,6 @@ bool FloatingWindow::beingDeleted() const
|
||||
return true;
|
||||
}
|
||||
|
||||
int FloatingWindow::dbg_numFrames()
|
||||
{
|
||||
return s_dbg_numFloatingWindows;
|
||||
}
|
||||
|
||||
void FloatingWindow::onFrameCountChanged(int count)
|
||||
{
|
||||
qCDebug(docking) << "FloatingWindow::onFrameCountChanged" << count;
|
||||
|
||||
@@ -65,6 +65,16 @@ public:
|
||||
*/
|
||||
TitleBar *titleBar() const { return m_titleBar; }
|
||||
|
||||
/**
|
||||
* @brief Equivalent to setGeometry(), but the value might be adjusted.
|
||||
*
|
||||
* For example, if the suggestedRect is bigger than max size, we'll make it smaller.
|
||||
*
|
||||
* @param preserveCenter, if true, then the center is preserved
|
||||
*
|
||||
*/
|
||||
void setSuggestedGeometry(QRect suggestedRect, bool preserveCenter = false);
|
||||
|
||||
bool anyNonClosable() const;
|
||||
bool anyNonDockable() const;
|
||||
|
||||
@@ -105,10 +115,6 @@ public:
|
||||
*/
|
||||
bool isInTitleBar(QPoint globalPoint) const;
|
||||
|
||||
///@brief For tests-only. Returns the number of Frame instances in the whole application.
|
||||
static int dbg_numFrames();
|
||||
|
||||
|
||||
///@brief updates the title and the icon
|
||||
void updateTitleAndIcon();
|
||||
void updateTitleBarVisibility();
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
*/
|
||||
|
||||
#include "Frame_p.h"
|
||||
#include "TabWidget_p.h"
|
||||
#include "DropArea_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "DragController_p.h"
|
||||
@@ -37,7 +36,6 @@
|
||||
#include "Config.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
|
||||
#include <QTabBar>
|
||||
#include <QCloseEvent>
|
||||
#include <QTimer>
|
||||
|
||||
@@ -60,7 +58,6 @@ static FrameOptions actualOptions(FrameOptions options)
|
||||
Frame::Frame(QWidgetOrQuick *parent, FrameOptions options)
|
||||
: QWidgetAdapter(parent)
|
||||
, Layouting::Widget_qwidget(this)
|
||||
, m_tabWidget(Config::self().frameworkWidgetFactory()->createTabWidget(this))
|
||||
, m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
|
||||
, m_options(actualOptions(options))
|
||||
{
|
||||
@@ -69,12 +66,12 @@ Frame::Frame(QWidgetOrQuick *parent, FrameOptions options)
|
||||
qCDebug(creation) << "Frame" << ((void*)this) << s_dbg_numFrames;
|
||||
|
||||
connect(this, &Frame::currentDockWidgetChanged, this, &Frame::updateTitleAndIcon);
|
||||
|
||||
m_tabWidget->setTabBarAutoHide(!alwaysShowsTabs());
|
||||
m_inCtor = false;
|
||||
}
|
||||
|
||||
Frame::~Frame()
|
||||
{
|
||||
m_inDtor = true;
|
||||
s_dbg_numFrames--;
|
||||
if (m_layoutItem)
|
||||
m_layoutItem->unref();
|
||||
@@ -107,7 +104,7 @@ void Frame::updateTitleAndIcon()
|
||||
|
||||
void Frame::addWidget(DockWidgetBase *dockWidget, AddingOption addingOption)
|
||||
{
|
||||
insertWidget(dockWidget, m_tabWidget->numDockWidgets(), addingOption); // append
|
||||
insertWidget(dockWidget, dockWidgetCount(), addingOption); // append
|
||||
}
|
||||
|
||||
void Frame::addWidget(Frame *frame, AddingOption addingOption)
|
||||
@@ -143,7 +140,7 @@ void Frame::insertWidget(DockWidgetBase *dockWidget, int index, AddingOption add
|
||||
if (m_layoutItem)
|
||||
dockWidget->addPlaceholderItem(m_layoutItem);
|
||||
|
||||
m_tabWidget->insertDockWidget(dockWidget, index);
|
||||
insertDockWidget(dockWidget, index);
|
||||
|
||||
if (addingOption == AddingOption_StartHidden) {
|
||||
dockWidget->close(); // Ensure closed
|
||||
@@ -169,7 +166,70 @@ void Frame::removeWidget(DockWidgetBase *dw)
|
||||
{
|
||||
disconnect(dw, &DockWidgetBase::titleChanged, this, &Frame::updateTitleAndIcon);
|
||||
disconnect(dw, &DockWidgetBase::iconChanged, this, &Frame::updateTitleAndIcon);
|
||||
m_tabWidget->removeDockWidget(dw);
|
||||
removeWidget_impl(dw);
|
||||
}
|
||||
|
||||
void Frame::detachTab(DockWidgetBase *dw)
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return;
|
||||
|
||||
detachTab_impl(dw);
|
||||
}
|
||||
|
||||
int Frame::indexOfDockWidget(DockWidgetBase *dw)
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return -1;
|
||||
|
||||
return indexOfDockWidget_impl(dw);
|
||||
}
|
||||
|
||||
int Frame::currentIndex() const
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return -1;
|
||||
|
||||
return currentIndex_impl();
|
||||
}
|
||||
|
||||
void Frame::setCurrentTabIndex(int index)
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return;
|
||||
|
||||
setCurrentTabIndex_impl(index);
|
||||
}
|
||||
|
||||
void Frame::setCurrentDockWidget(DockWidgetBase *dw)
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return;
|
||||
|
||||
setCurrentDockWidget_impl(dw);
|
||||
}
|
||||
|
||||
void Frame::insertDockWidget(DockWidgetBase *dw, int index)
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return;
|
||||
|
||||
insertDockWidget_impl(dw, index);
|
||||
}
|
||||
|
||||
DockWidgetBase *Frame::dockWidgetAt(int index) const
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return nullptr;
|
||||
|
||||
return dockWidgetAt_impl(index);
|
||||
}
|
||||
|
||||
DockWidgetBase *Frame::currentDockWidget() const
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return nullptr;
|
||||
|
||||
return currentDockWidget_impl();
|
||||
}
|
||||
|
||||
int Frame::dockWidgetCount() const
|
||||
{
|
||||
if (m_inCtor || m_inDtor) return 0;
|
||||
|
||||
return dockWidgetCount_impl();
|
||||
}
|
||||
|
||||
void Frame::onDockWidgetCountChanged()
|
||||
@@ -201,7 +261,7 @@ void Frame::onCurrentTabChanged(int index)
|
||||
|
||||
void Frame::updateTitleBarVisibility()
|
||||
{
|
||||
if (m_updatingTitleBar) {
|
||||
if (m_updatingTitleBar || m_beingDeleted) {
|
||||
// To break a cyclic dependency
|
||||
return;
|
||||
}
|
||||
@@ -261,6 +321,9 @@ QIcon Frame::icon() const
|
||||
|
||||
const DockWidgetBase::List Frame::dockWidgets() const
|
||||
{
|
||||
if (m_inCtor || m_inDtor)
|
||||
return {};
|
||||
|
||||
DockWidgetBase::List dockWidgets;
|
||||
const int count = dockWidgetCount();
|
||||
dockWidgets.reserve(count);
|
||||
@@ -329,7 +392,7 @@ void Frame::restoreToPreviousPosition()
|
||||
|
||||
int Frame::currentTabIndex() const
|
||||
{
|
||||
return m_tabWidget->currentIndex();
|
||||
return currentIndex();
|
||||
}
|
||||
|
||||
void Frame::onCloseEvent(QCloseEvent *e)
|
||||
@@ -344,16 +407,6 @@ void Frame::onCloseEvent(QCloseEvent *e)
|
||||
}
|
||||
}
|
||||
|
||||
void Frame::setCurrentTabIndex(int index)
|
||||
{
|
||||
m_tabWidget->setCurrentDockWidget(index);
|
||||
}
|
||||
|
||||
DockWidgetBase *Frame::currentDockWidget() const
|
||||
{
|
||||
return m_tabWidget->dockwidgetAt(m_tabWidget->currentIndex());
|
||||
}
|
||||
|
||||
bool Frame::anyNonClosable() const
|
||||
{
|
||||
for (auto dw : dockWidgets()) {
|
||||
@@ -433,13 +486,11 @@ bool Frame::beingDeletedLater() const
|
||||
return m_beingDeleted;
|
||||
}
|
||||
|
||||
TabWidget *Frame::tabWidget() const
|
||||
{
|
||||
return m_tabWidget;
|
||||
}
|
||||
|
||||
bool Frame::hasTabsVisible() const
|
||||
{
|
||||
if (m_beingDeleted)
|
||||
return false;
|
||||
|
||||
return alwaysShowsTabs() || dockWidgetCount() > 1;
|
||||
}
|
||||
|
||||
@@ -452,11 +503,6 @@ QStringList Frame::affinities() const
|
||||
}
|
||||
}
|
||||
|
||||
DockWidgetBase *Frame::dockWidgetAt(int index) const
|
||||
{
|
||||
return qobject_cast<DockWidgetBase *>(m_tabWidget->dockwidgetAt(index));
|
||||
}
|
||||
|
||||
void Frame::setDropArea(DropArea *dt)
|
||||
{
|
||||
if (dt != m_dropArea) {
|
||||
@@ -503,11 +549,6 @@ bool Frame::isInMainWindow() const
|
||||
return m_dropArea && m_dropArea->isInMainWindow();
|
||||
}
|
||||
|
||||
int Frame::dockWidgetCount() const
|
||||
{
|
||||
return m_tabWidget->numDockWidgets();
|
||||
}
|
||||
|
||||
bool Frame::event(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::ParentChange) {
|
||||
@@ -585,16 +626,22 @@ QSize Frame::biggestDockWidgetMaxSize() const
|
||||
{
|
||||
QSize size = Layouting::Item::hardcodedMaximumSize;
|
||||
for (DockWidgetBase *dw : dockWidgets()) {
|
||||
const QSize dwMax = widgetMaxSize(dw);
|
||||
if (size == Layouting::Item::hardcodedMaximumSize) {
|
||||
size = dw->maximumSize();
|
||||
size = dwMax;
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool hasMaxSize = dw->maximumSize() != Layouting::Item::hardcodedMaximumSize;
|
||||
const bool hasMaxSize = dwMax != Layouting::Item::hardcodedMaximumSize;
|
||||
if (hasMaxSize)
|
||||
size = dw->maximumSize().expandedTo(size);
|
||||
}
|
||||
|
||||
// Interpret 0 max-size as not having one too.
|
||||
if (size.width() == 0)
|
||||
size.setWidth(Layouting::Item::hardcodedMaximumSize.width());
|
||||
if (size.height() == 0)
|
||||
size.setHeight(Layouting::Item::hardcodedMaximumSize.height());
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class TitleBar;
|
||||
class TabWidget;
|
||||
class DropArea;
|
||||
class DockWidgetBase;
|
||||
class FloatingWindow;
|
||||
@@ -85,16 +84,41 @@ public:
|
||||
///@brief removes a dockwidget from the frame
|
||||
void removeWidget(DockWidgetBase *);
|
||||
|
||||
///@brief detaches this dock widget
|
||||
void detachTab(DockWidgetBase *);
|
||||
|
||||
///@brief returns the index of the specified dock widget
|
||||
int indexOfDockWidget(DockWidgetBase *);
|
||||
|
||||
///@brief returns the index of the current tab
|
||||
int currentIndex() const;
|
||||
|
||||
///@brief sets the current tab index
|
||||
void setCurrentTabIndex(int index);
|
||||
|
||||
///@brief Sets the specified dock widget to be the current tab
|
||||
void setCurrentDockWidget(DockWidgetBase *);
|
||||
|
||||
///@brief Inserts a dock widget into the specified index
|
||||
void insertDockWidget(DockWidgetBase *, int index);
|
||||
|
||||
/// @brief Returns the dock widget at @p index
|
||||
DockWidgetBase *dockWidgetAt(int index) const;
|
||||
|
||||
///@brief Returns the current dock widget
|
||||
DockWidgetBase *currentDockWidget() const;
|
||||
|
||||
/// @brief returns the number of dock widgets inside the frame
|
||||
int dockWidgetCount() const;
|
||||
|
||||
void updateTitleAndIcon();
|
||||
void updateTitleBarVisibility();
|
||||
bool containsMouse(QPoint globalPos) const;
|
||||
TitleBar *titleBar() const;
|
||||
TitleBar *actualTitleBar() const;
|
||||
TabWidget *tabWidget() const;
|
||||
QString title() const;
|
||||
QIcon icon() const;
|
||||
const QVector<DockWidgetBase *> dockWidgets() const;
|
||||
DockWidgetBase *dockWidgetAt(int index) const;
|
||||
void setDropArea(DropArea *);
|
||||
|
||||
bool isTheOnlyFrame() const;
|
||||
@@ -139,9 +163,6 @@ public:
|
||||
bool alwaysShowsTabs() const { return m_options & FrameOption_AlwaysShowsTabs; }
|
||||
|
||||
|
||||
/// @brief returns the number of dock widgets inside the frame
|
||||
int dockWidgetCount() const;
|
||||
|
||||
/// @brief returns whether the dockwidget @p w is inside this frame
|
||||
bool contains(DockWidgetBase *w) const;
|
||||
|
||||
@@ -160,9 +181,6 @@ public:
|
||||
|
||||
void onCloseEvent(QCloseEvent *e) override;
|
||||
int currentTabIndex() const;
|
||||
void setCurrentTabIndex(int);
|
||||
|
||||
DockWidgetBase *currentDockWidget() const;
|
||||
|
||||
FrameOptions options() const { return m_options; }
|
||||
bool anyNonClosable() const;
|
||||
@@ -231,6 +249,17 @@ protected:
|
||||
*/
|
||||
QSize biggestDockWidgetMaxSize() const;
|
||||
|
||||
virtual void removeWidget_impl(DockWidgetBase *) = 0;
|
||||
virtual void detachTab_impl(DockWidgetBase *) = 0;
|
||||
virtual int indexOfDockWidget_impl(DockWidgetBase *) = 0;
|
||||
virtual int currentIndex_impl() const = 0;
|
||||
virtual void setCurrentTabIndex_impl(int index) = 0;
|
||||
virtual void setCurrentDockWidget_impl(DockWidgetBase *) = 0;
|
||||
virtual void insertDockWidget_impl(DockWidgetBase *, int index) = 0;
|
||||
virtual DockWidgetBase *dockWidgetAt_impl(int index) const = 0;
|
||||
virtual DockWidgetBase *currentDockWidget_impl() const = 0;
|
||||
virtual int dockWidgetCount_impl() const = 0;
|
||||
bool m_inDtor = false;
|
||||
private:
|
||||
Q_DISABLE_COPY(Frame)
|
||||
friend class TestDocks;
|
||||
@@ -239,7 +268,7 @@ private:
|
||||
void onCurrentTabChanged(int index);
|
||||
void scheduleDeleteLater();
|
||||
bool event(QEvent *) override;
|
||||
TabWidget *const m_tabWidget;
|
||||
bool m_inCtor = true;
|
||||
TitleBar *const m_titleBar;
|
||||
DropArea *m_dropArea = nullptr;
|
||||
const FrameOptions m_options;
|
||||
|
||||
@@ -113,7 +113,7 @@ FloatingWindow * TabBar::detachTab(DockWidgetBase *dockWidget)
|
||||
|
||||
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(newFrame);
|
||||
r.moveTopLeft(globalPoint);
|
||||
floatingWindow->setGeometry(r);
|
||||
floatingWindow->setSuggestedGeometry(r);
|
||||
floatingWindow->show();
|
||||
|
||||
return floatingWindow;
|
||||
@@ -231,7 +231,7 @@ std::unique_ptr<WindowBeingDragged> TabWidget::makeWindow()
|
||||
|
||||
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(m_frame);
|
||||
r.moveTopLeft(globalPoint);
|
||||
floatingWindow->setGeometry(r);
|
||||
floatingWindow->setSuggestedGeometry(r);
|
||||
floatingWindow->show();
|
||||
|
||||
return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(floatingWindow, this));
|
||||
|
||||
@@ -144,7 +144,7 @@ std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
|
||||
r.moveTopLeft(m_frame->mapToGlobal(QPoint(0, 0)));
|
||||
|
||||
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(m_frame);
|
||||
floatingWindow->setGeometry(r);
|
||||
floatingWindow->setSuggestedGeometry(r);
|
||||
floatingWindow->show();
|
||||
qCDebug(hovering) << "TitleBar::makeWindow setting geometry" << r << "actual=" << floatingWindow->geometry();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ set(MULTISPLITTER_SRCS
|
||||
MultiSplitterConfig.h
|
||||
Separator.cpp
|
||||
Separator_p.h
|
||||
Widget.cpp
|
||||
Widget.h
|
||||
multisplitter_export.h
|
||||
)
|
||||
@@ -58,6 +59,9 @@ endif()
|
||||
target_compile_definitions(kddockwidgets_multisplitter PRIVATE BUILDING_MULTISPLITTER_LIBRARY)
|
||||
|
||||
if (OPTION_DEVELOPER_MODE)
|
||||
# Under developer mode since kddw might be a sub-folder of a project setting a different value for QT_DISABLE_DEPRECATED_BEFORE
|
||||
target_compile_definitions(kddockwidgets_multisplitter PRIVATE QT_DISABLE_DEPRECATED_BEFORE=0x060000)
|
||||
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
@@ -68,3 +72,10 @@ target_include_directories(kddockwidgets_multisplitter
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
|
||||
install (TARGETS kddockwidgets_multisplitter
|
||||
EXPORT kddockwidgetsTargets
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib)
|
||||
|
||||
@@ -446,7 +446,7 @@ QSize Item::minSize() const
|
||||
|
||||
QSize Item::maxSizeHint() const
|
||||
{
|
||||
return m_sizingInfo.maxSizeHint;
|
||||
return m_sizingInfo.maxSizeHint.boundedTo(QSize(KDDOCKWIDGETS_MAX_WIDTH, KDDOCKWIDGETS_MAX_HEIGHT));
|
||||
}
|
||||
|
||||
void Item::setPos(QPoint pos)
|
||||
@@ -1820,27 +1820,37 @@ QSize ItemContainer::minSize() const
|
||||
|
||||
QSize ItemContainer::maxSizeHint() const
|
||||
{
|
||||
int maxW = KDDOCKWIDGETS_MAX_WIDTH;
|
||||
int maxH = KDDOCKWIDGETS_MAX_HEIGHT;
|
||||
int maxW = isVertical() ? KDDOCKWIDGETS_MAX_WIDTH : 0;
|
||||
int maxH = isVertical() ? 0 : KDDOCKWIDGETS_MAX_HEIGHT;
|
||||
|
||||
if (!isEmpty()) {
|
||||
const Item::List visibleChildren = this->visibleChildren();
|
||||
if (!visibleChildren.isEmpty()) {
|
||||
for (Item *item : visibleChildren) {
|
||||
const QSize itemMaxSz = item->maxSizeHint();
|
||||
const int itemMaxWidth = itemMaxSz.width();
|
||||
const int itemMaxHeight = itemMaxSz.height();
|
||||
if (isVertical()) {
|
||||
maxW = qMin(maxW, item->maxSizeHint().width());
|
||||
maxH += item->maxSizeHint().height();
|
||||
maxW = qMin(maxW, itemMaxWidth);
|
||||
maxH = qMin(maxH + itemMaxHeight, KDDOCKWIDGETS_MAX_HEIGHT);
|
||||
} else {
|
||||
maxH = qMin(maxH, item->maxSizeHint().height());
|
||||
maxW += item->maxSizeHint().width();
|
||||
maxH = qMin(maxH, itemMaxHeight);
|
||||
maxW = qMin(maxW + itemMaxWidth, KDDOCKWIDGETS_MAX_WIDTH);
|
||||
}
|
||||
}
|
||||
|
||||
const int separatorWaste = (visibleChildren.size() - 1) * separatorThickness;
|
||||
if (isVertical())
|
||||
maxH += separatorWaste;
|
||||
else
|
||||
maxW += separatorWaste;
|
||||
if (isVertical()) {
|
||||
maxH = qMin(maxH + separatorWaste, KDDOCKWIDGETS_MAX_HEIGHT);
|
||||
} else {
|
||||
maxW = qMin(maxW + separatorWaste, KDDOCKWIDGETS_MAX_WIDTH);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxW == 0)
|
||||
maxW = KDDOCKWIDGETS_MAX_WIDTH;
|
||||
|
||||
if (maxH == 0)
|
||||
maxH = KDDOCKWIDGETS_MAX_HEIGHT;
|
||||
|
||||
return QSize(maxW, maxH).expandedTo(minSize());
|
||||
}
|
||||
@@ -2016,11 +2026,18 @@ void ItemContainer::dumpLayout(int level)
|
||||
const QString typeStr = isRoot() ? QStringLiteral("* Root: ")
|
||||
: QStringLiteral("* Layout: ");
|
||||
|
||||
qDebug().noquote() << indent << typeStr << d->m_orientation
|
||||
{
|
||||
auto dbg = qDebug().noquote();
|
||||
dbg << indent << typeStr << d->m_orientation
|
||||
<< m_sizingInfo.geometry /*<< "r=" << m_geometry.right() << "b=" << m_geometry.bottom()*/
|
||||
<< "; min=" << minSize()
|
||||
<< "; this=" << this << beingInserted << visible
|
||||
<< "; %=" << d->childPercentages();
|
||||
|
||||
if (maxSizeHint() != Item::hardcodedMaximumSize)
|
||||
dbg << "; max=" << maxSizeHint();
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (Item *item : qAsConst(d->m_children)) {
|
||||
item->dumpLayout(level + 1);
|
||||
|
||||
44
src/private/multisplitter/Widget.cpp
Normal file
44
src/private/multisplitter/Widget.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Widget.h"
|
||||
#include "Item_p.h"
|
||||
|
||||
using namespace Layouting;
|
||||
|
||||
Widget::~Widget()
|
||||
{
|
||||
}
|
||||
|
||||
QSize Widget::boundedMaxSize(QSize min, QSize max)
|
||||
{
|
||||
// Max should be bigger than min, but not bigger than the hardcoded max
|
||||
max = max.boundedTo(QSize(KDDOCKWIDGETS_MAX_WIDTH, KDDOCKWIDGETS_MAX_HEIGHT));
|
||||
|
||||
// 0 interpreted as not having max
|
||||
if (max.width() <= 0)
|
||||
max.setWidth(KDDOCKWIDGETS_MAX_WIDTH);
|
||||
if (max.height() <= 0)
|
||||
max.setHeight(KDDOCKWIDGETS_MAX_HEIGHT);
|
||||
|
||||
max = max.expandedTo(min);
|
||||
|
||||
return max;
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "multisplitter_export.h"
|
||||
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
#include <QDebug>
|
||||
@@ -47,19 +49,20 @@ class Item;
|
||||
* Inherit from it via multi-inheritance so this wrapper is deleted when the actual QWidget/QQuickItem
|
||||
* is deleted.
|
||||
*/
|
||||
class Widget
|
||||
class MULTISPLITTER_EXPORT Widget
|
||||
{
|
||||
public:
|
||||
explicit Widget(QObject *thisObj)
|
||||
: m_thisObj(thisObj) {}
|
||||
|
||||
virtual ~Widget() {}
|
||||
virtual ~Widget();
|
||||
|
||||
virtual void setLayoutItem(Item *) = 0;
|
||||
|
||||
// Not strickly necessary, but it's nice conveniance for kddw which is widget based.
|
||||
virtual QWidget *asQWidget() const { return nullptr; };
|
||||
virtual QWidget *asQWidget() const { return nullptr; }
|
||||
|
||||
virtual QSize sizeHint() const { return {}; }
|
||||
virtual QSize minSize() const = 0;
|
||||
virtual QSize maxSizeHint() const = 0;
|
||||
virtual QRect geometry() const = 0;
|
||||
@@ -100,6 +103,9 @@ public:
|
||||
return obj == m_thisObj;
|
||||
}
|
||||
|
||||
protected:
|
||||
static QSize boundedMaxSize(QSize min, QSize max);
|
||||
|
||||
private:
|
||||
QObject *const m_thisObj;
|
||||
Q_DISABLE_COPY(Widget)
|
||||
|
||||
@@ -29,6 +29,11 @@ Widget_qwidget::~Widget_qwidget()
|
||||
{
|
||||
}
|
||||
|
||||
QSize Widget_qwidget::sizeHint() const
|
||||
{
|
||||
return m_thisWidget->sizeHint();
|
||||
}
|
||||
|
||||
QSize Widget_qwidget::minSize() const
|
||||
{
|
||||
return widgetMinSize(m_thisWidget);
|
||||
@@ -36,7 +41,7 @@ QSize Widget_qwidget::minSize() const
|
||||
|
||||
QSize Widget_qwidget::maxSizeHint() const
|
||||
{
|
||||
return m_thisWidget->maximumSize();
|
||||
return widgetMaxSize(m_thisWidget);
|
||||
}
|
||||
|
||||
QRect Widget_qwidget::geometry() const
|
||||
@@ -128,6 +133,26 @@ QSize Widget_qwidget::widgetMinSize(const QWidget *w)
|
||||
return QSize(minW, minH).expandedTo(Item::hardcodedMinimumSize);
|
||||
}
|
||||
|
||||
QSize Widget_qwidget::widgetMaxSize(const QWidget *w)
|
||||
{
|
||||
// The max size is usually QWidget::maximumSize(), but we also honour the QSizePolicy::Fixed+sizeHint() case
|
||||
// as widgets don't need to have QWidget::maximumSize() to have a max size honoured
|
||||
|
||||
const QSize min = widgetMinSize(w);
|
||||
QSize max = w->maximumSize();
|
||||
max = boundedMaxSize(min, max); // for safety against weird values
|
||||
|
||||
const QSizePolicy policy = w->sizePolicy();
|
||||
|
||||
if (policy.verticalPolicy() == QSizePolicy::Fixed || policy.verticalPolicy() == QSizePolicy::Maximum)
|
||||
max.setHeight(qMin(max.height(), w->sizeHint().height()));
|
||||
if (policy.horizontalPolicy() == QSizePolicy::Fixed || policy.horizontalPolicy() == QSizePolicy::Maximum)
|
||||
max.setWidth(qMin(max.width(), w->sizeHint().width()));
|
||||
|
||||
max = boundedMaxSize(min, max); // for safety against weird values
|
||||
return max;
|
||||
}
|
||||
|
||||
void Widget_qwidget::setSize(int width, int height)
|
||||
{
|
||||
m_thisWidget->resize(QSize(width, height));
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Widget.h"
|
||||
#include "multisplitter_export.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
@@ -47,6 +46,7 @@ public:
|
||||
return m_thisWidget;
|
||||
}
|
||||
|
||||
QSize sizeHint() const override;
|
||||
QSize minSize() const override;
|
||||
QSize maxSizeHint() const override;
|
||||
QRect geometry() const override;
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
void setHeight(int height) override;
|
||||
|
||||
static QSize widgetMinSize(const QWidget *w);
|
||||
|
||||
static QSize widgetMaxSize(const QWidget *w);
|
||||
private:
|
||||
QWidget *const m_thisWidget;
|
||||
Q_DISABLE_COPY(Widget_qwidget)
|
||||
|
||||
@@ -196,6 +196,7 @@ private Q_SLOTS:
|
||||
void tst_minSizeChangedBeforeRestore();
|
||||
void tst_separatorMoveCrash();
|
||||
void tst_maxSizeHonoured1();
|
||||
void tst_maxSizeHonoured2();
|
||||
};
|
||||
|
||||
class MyHostWidget : public QWidget
|
||||
@@ -1561,6 +1562,24 @@ void TestMultiSplitter::tst_maxSizeHonoured1()
|
||||
QCOMPARE(item2->height(), maxHeight);
|
||||
}
|
||||
|
||||
void TestMultiSplitter::tst_maxSizeHonoured2()
|
||||
{
|
||||
// Tests that a container gets the max size of its children
|
||||
|
||||
auto root1 = createRoot();
|
||||
auto root2 = createRoot();
|
||||
auto item1 = createItem();
|
||||
auto item2 = createItem();
|
||||
|
||||
root1->insertItem(item1, Item::Location_OnTop);
|
||||
root2->insertItem(item2, Item::Location_OnTop);
|
||||
|
||||
item2->setMaxSizeHint(QSize(200, 200));
|
||||
|
||||
root1->insertItem(root2.release(), Item::Location_OnBottom);
|
||||
QCOMPARE(item2->parentContainer()->maxSizeHint(), item2->maxSizeHint());
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool qpaPassed = false;
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief The QWidget counter part of TabWidget. Handles GUI while TabWidget handles state.
|
||||
*
|
||||
* @author Sérgio Martins \<sergio.martins@kdab.com\>
|
||||
*/
|
||||
|
||||
#include "TabWidgetQuick_p.h"
|
||||
#include "Frame_p.h"
|
||||
#include "Config.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
TabWidgetQuick::TabWidgetQuick(Frame *parent)
|
||||
: TabWidget(this, parent)
|
||||
, m_tabBar(Config::self().frameworkWidgetFactory()->createTabBar(this))
|
||||
{
|
||||
}
|
||||
|
||||
TabBar *TabWidgetQuick::tabBar() const
|
||||
{
|
||||
return m_tabBar;
|
||||
}
|
||||
|
||||
int TabWidgetQuick::numDockWidgets() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TabWidgetQuick::removeDockWidget(DockWidgetBase *)
|
||||
{
|
||||
}
|
||||
|
||||
int TabWidgetQuick::indexOfDockWidget(DockWidgetBase *) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TabWidgetQuick::isPositionDraggable(QPoint) const
|
||||
{
|
||||
/* if (tabPosition() != QTabWidget::North) {
|
||||
qWarning() << Q_FUNC_INFO << "Not implemented yet. Only North is supported";
|
||||
return false;
|
||||
}*/
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TabWidgetQuick::setCurrentDockWidget(int)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TabWidgetQuick::insertDockWidget(int , DockWidgetBase *,
|
||||
const QIcon &, const QString &)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TabWidgetQuick::setTabBarAutoHide(bool)
|
||||
{
|
||||
}
|
||||
|
||||
void TabWidgetQuick::detachTab(DockWidgetBase *dockWidget)
|
||||
{
|
||||
tabBar()->detachTab(dockWidget);
|
||||
}
|
||||
|
||||
DockWidgetBase *TabWidgetQuick::dockwidgetAt(int) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TabWidgetQuick::currentIndex() const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief The QWidget counter part of TabWidget. Handles GUI while TabWidget handles state.
|
||||
*
|
||||
* @author Sérgio Martins \<sergio.martins@kdab.com\>
|
||||
*/
|
||||
|
||||
#ifndef KDTABWIDGETQUICK_P_H
|
||||
#define KDTABWIDGETQUICK_P_H
|
||||
|
||||
#include "TabWidget_p.h"
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class Frame;
|
||||
class TabBar;
|
||||
|
||||
class DOCKS_EXPORT TabWidgetQuick : public TabWidget, public QWidgetAdapter
|
||||
{
|
||||
public:
|
||||
explicit TabWidgetQuick(Frame *parent);
|
||||
|
||||
TabBar *tabBar() const override;
|
||||
|
||||
int numDockWidgets() const override;
|
||||
void removeDockWidget(DockWidgetBase *) override;
|
||||
int indexOfDockWidget(DockWidgetBase *) const override;
|
||||
protected:
|
||||
bool isPositionDraggable(QPoint p) const override;
|
||||
void setCurrentDockWidget(int index) override;
|
||||
void insertDockWidget(int index, DockWidgetBase *, const QIcon&, const QString &title) override;
|
||||
void setTabBarAutoHide(bool) override;
|
||||
void detachTab(DockWidgetBase *dockWidget) override;
|
||||
|
||||
|
||||
DockWidgetBase *dockwidgetAt(int index) const override;
|
||||
int currentIndex() const override;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(TabWidgetQuick)
|
||||
TabBar *const m_tabBar;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "FrameWidget_p.h"
|
||||
#include "TitleBar_p.h"
|
||||
#include "TabWidget_p.h"
|
||||
#include "Config.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QPainter>
|
||||
@@ -58,12 +60,20 @@ VBoxLayout::~VBoxLayout() = default;
|
||||
|
||||
FrameWidget::FrameWidget(QWidget *parent, FrameOptions options)
|
||||
: Frame(parent, options)
|
||||
, m_tabWidget(Config::self().frameworkWidgetFactory()->createTabWidget(this))
|
||||
{
|
||||
auto vlayout = new VBoxLayout(this);
|
||||
vlayout->setContentsMargins(0, 0, 0, 0);
|
||||
vlayout->setSpacing(0);
|
||||
vlayout->addWidget(titleBar());
|
||||
vlayout->addWidget(tabWidget()->asWidget());
|
||||
vlayout->addWidget(m_tabWidget->asWidget());
|
||||
|
||||
m_tabWidget->setTabBarAutoHide(!alwaysShowsTabs());
|
||||
}
|
||||
|
||||
FrameWidget::~FrameWidget()
|
||||
{
|
||||
m_inDtor = true;
|
||||
}
|
||||
|
||||
void FrameWidget::paintEvent(QPaintEvent *)
|
||||
@@ -83,8 +93,64 @@ QSize FrameWidget::maxSizeHint() const
|
||||
return waste + biggestDockWidgetMaxSize();
|
||||
}
|
||||
|
||||
void FrameWidget::detachTab_impl(DockWidgetBase *dw)
|
||||
{
|
||||
m_tabWidget->detachTab(dw);
|
||||
}
|
||||
|
||||
int FrameWidget::indexOfDockWidget_impl(DockWidgetBase *dw)
|
||||
{
|
||||
return m_tabWidget->indexOfDockWidget(dw);
|
||||
}
|
||||
|
||||
void FrameWidget::setCurrentDockWidget_impl(DockWidgetBase *dw)
|
||||
{
|
||||
m_tabWidget->setCurrentDockWidget(dw);
|
||||
}
|
||||
|
||||
int FrameWidget::currentIndex_impl() const
|
||||
{
|
||||
return m_tabWidget->currentIndex();
|
||||
}
|
||||
|
||||
void FrameWidget::insertDockWidget_impl(DockWidgetBase *dw, int index)
|
||||
{
|
||||
m_tabWidget->insertDockWidget(dw, index);
|
||||
}
|
||||
|
||||
void FrameWidget::removeWidget_impl(DockWidgetBase *dw)
|
||||
{
|
||||
m_tabWidget->removeDockWidget(dw);
|
||||
}
|
||||
|
||||
void FrameWidget::setCurrentTabIndex_impl(int index)
|
||||
{
|
||||
m_tabWidget->setCurrentDockWidget(index);
|
||||
}
|
||||
|
||||
DockWidgetBase *FrameWidget::currentDockWidget_impl() const
|
||||
{
|
||||
return m_tabWidget->dockwidgetAt(m_tabWidget->currentIndex());
|
||||
}
|
||||
|
||||
DockWidgetBase *FrameWidget::dockWidgetAt_impl(int index) const
|
||||
{
|
||||
return qobject_cast<DockWidgetBase *>(m_tabWidget->dockwidgetAt(index));
|
||||
}
|
||||
|
||||
QTabBar *FrameWidget::tabBar() const
|
||||
{
|
||||
auto tw = static_cast<QTabWidget*>(tabWidget()->asWidget());
|
||||
auto tw = static_cast<QTabWidget*>(m_tabWidget->asWidget());
|
||||
return tw->tabBar();
|
||||
}
|
||||
|
||||
TabWidget *FrameWidget::tabWidget() const
|
||||
{
|
||||
return m_tabWidget;
|
||||
}
|
||||
|
||||
int FrameWidget::dockWidgetCount_impl() const
|
||||
{
|
||||
return m_tabWidget->numDockWidgets();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class TestDocks;
|
||||
class TabWidget;
|
||||
|
||||
/**
|
||||
* @brief The GUI counterpart of Frame. Inherits Frame and implements paintEvent().
|
||||
*/
|
||||
@@ -38,10 +41,26 @@ class DOCKS_EXPORT FrameWidget : public Frame
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FrameWidget(QWidget *parent = nullptr, FrameOptions = FrameOption_None);
|
||||
~FrameWidget();
|
||||
QTabBar *tabBar() const;
|
||||
TabWidget *tabWidget() const;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
QSize maxSizeHint() const override;
|
||||
void detachTab_impl(DockWidgetBase *) override;
|
||||
int indexOfDockWidget_impl(DockWidgetBase *) override;
|
||||
void setCurrentDockWidget_impl(DockWidgetBase *) override;
|
||||
int currentIndex_impl() const override;
|
||||
void insertDockWidget_impl(DockWidgetBase *, int index) override;
|
||||
void removeWidget_impl(DockWidgetBase *) override;
|
||||
void setCurrentTabIndex_impl(int) override;
|
||||
DockWidgetBase *currentDockWidget_impl() const override;
|
||||
DockWidgetBase *dockWidgetAt_impl(int index) const override;
|
||||
int dockWidgetCount_impl() const override;
|
||||
private:
|
||||
friend class TestDocks;
|
||||
TabWidget *const m_tabWidget;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location,
|
||||
newItem->setGuestWidget(frame);
|
||||
} else if (dw) {
|
||||
newItem = new Layouting::Item(m_multiSplitter);
|
||||
frame = new Frame();
|
||||
frame = Config::self().frameworkWidgetFactory()->createFrame();
|
||||
newItem->setGuestWidget(frame);
|
||||
frame->addWidget(dw, option);
|
||||
} else if (auto ms = qobject_cast<MultiSplitter*>(w)) {
|
||||
|
||||
@@ -356,6 +356,8 @@ private Q_SLOTS:
|
||||
void tst_moreTitleBarCornerCases();
|
||||
void tst_maxSizePropagates();
|
||||
void tst_maxSizeHonouredWhenDropped();
|
||||
void tst_fixedSizePolicy();
|
||||
void tst_maximumSizePolicy();
|
||||
|
||||
private:
|
||||
std::unique_ptr<MultiSplitter> createMultiSplitterFromSetup(MultiSplitterSetup setup, QHash<QWidget *, Frame *> &frameMap) const;
|
||||
@@ -386,13 +388,14 @@ public:
|
||||
|
||||
explicit MyWidget2(QSize minSz = QSize(1,1))
|
||||
: m_minSz(minSz)
|
||||
, m_sizeHint(minSz)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QSize sizeHint() const override
|
||||
{
|
||||
return m_minSz;
|
||||
return m_sizeHint;
|
||||
}
|
||||
|
||||
QSize minimumSizeHint() const override
|
||||
@@ -406,7 +409,13 @@ public:
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void setSizeHint(QSize s)
|
||||
{
|
||||
m_sizeHint = s;
|
||||
}
|
||||
|
||||
QSize m_minSz;
|
||||
QSize m_sizeHint;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2190,7 +2199,7 @@ void TestDocks::tst_setFloatingWhenWasTabbed()
|
||||
dock2->setVisible(false);
|
||||
QVERIFY(dock2->isTabbed());
|
||||
QVERIFY(!dock1->isFloating());
|
||||
QCOMPARE(dock2->frame()->m_tabWidget->numDockWidgets(), 2);
|
||||
QCOMPARE(static_cast<FrameWidget*>(dock2->frame())->m_tabWidget->numDockWidgets(), 2);
|
||||
|
||||
// 3. Set one floating. Now both cease to be tabbed, and both are floating.
|
||||
dock1->setFloating(true);
|
||||
@@ -2245,7 +2254,7 @@ void TestDocks::tst_setFloatingWhenWasTabbed()
|
||||
dock2->setFloating(false);
|
||||
QVERIFY(!dock2->isFloating());
|
||||
QVERIFY(dock2->isTabbed());
|
||||
QCOMPARE(dock2->frame()->m_tabWidget->indexOfDockWidget(dock2), 1);
|
||||
QCOMPARE(static_cast<FrameWidget*>(dock2->frame())->m_tabWidget->indexOfDockWidget(dock2), 1);
|
||||
|
||||
|
||||
// 10. Float dock1, and dock it to main window as tab. This tests Option_AlwaysShowsTabs.
|
||||
@@ -2258,7 +2267,7 @@ void TestDocks::tst_setFloatingWhenWasTabbed()
|
||||
QVERIFY(dock1->isTabbed());
|
||||
dock1->setFloating(true);
|
||||
dock1->setFloating(false);
|
||||
QCOMPARE(dock1->frame()->m_tabWidget->numDockWidgets(), 1);
|
||||
QCOMPARE(static_cast<FrameWidget*>(dock1->frame())->m_tabWidget->numDockWidgets(), 1);
|
||||
|
||||
// Cleanup
|
||||
m->addDockWidgetAsTab(dock2);
|
||||
@@ -2367,7 +2376,7 @@ void TestDocks::tst_setFloatingAfterDraggedFromTabToSideBySide()
|
||||
|
||||
|
||||
// Detach tab
|
||||
dock1->frame()->m_tabWidget->detachTab(dock2);
|
||||
dock1->frame()->detachTab(dock2);
|
||||
QVERIFY(layout->checkSanity());
|
||||
auto fw2 = dock2->floatingWindow();
|
||||
QVERIFY(fw2);
|
||||
@@ -3655,6 +3664,7 @@ void TestDocks::tst_toggleDockWidgetWithHiddenTitleBar()
|
||||
auto f1 = d1->frame();
|
||||
Testing::waitForDeleted(f1);
|
||||
d1->toggleAction()->setChecked(true);
|
||||
QVERIFY(d1->frame());
|
||||
QVERIFY(!d1->frame()->titleBar()->isVisible());
|
||||
}
|
||||
|
||||
@@ -3694,7 +3704,7 @@ void TestDocks::tst_dragByTabBar()
|
||||
|
||||
dock2->addDockWidgetAsTab(dock3);
|
||||
if (documentMode)
|
||||
static_cast<QTabWidget*>(dock2->frame()->tabWidget()->asWidget())->setDocumentMode(true);
|
||||
static_cast<QTabWidget*>(static_cast<FrameWidget*>(dock2->frame())->tabWidget()->asWidget())->setDocumentMode(true);
|
||||
|
||||
auto fw = dock2->floatingWindow();
|
||||
fw->move(m->pos() + QPoint(500, 500));
|
||||
@@ -4345,7 +4355,7 @@ void TestDocks::tst_invalidLayoutAfterRestore()
|
||||
|
||||
// Detach dock2
|
||||
QPointer<Frame> f2 = dock2->frame();
|
||||
f2->m_tabWidget->detachTab(dock2);
|
||||
f2->detachTab(dock2);
|
||||
QVERIFY(!f2.data());
|
||||
QTest::qWait(200); // Not sure why. Some event we're waiting for. TODO: Investigate
|
||||
auto fw2 = dock2->floatingWindow();
|
||||
@@ -5222,7 +5232,7 @@ void TestDocks::tst_lastFloatingPositionIsRestored()
|
||||
EnsureTopLevelsDeleted e;
|
||||
|
||||
auto m1 = createMainWindow();
|
||||
auto dock1 = createDockWidget("dock1", new QPushButton("foo"));
|
||||
auto dock1 = createDockWidget("dock1", new QWidget());
|
||||
dock1->show();
|
||||
QPoint targetPos = QPoint(340, 340);
|
||||
dock1->window()->move(targetPos);
|
||||
@@ -5384,6 +5394,54 @@ void TestDocks::tst_maxSizeHonouredWhenDropped()
|
||||
QCOMPARE(dock2->frame()->width(), droppedWidth);
|
||||
}
|
||||
|
||||
void TestDocks::tst_fixedSizePolicy()
|
||||
{
|
||||
// tests that KDDW also takes into account QSizePolicy::Fixed for calculating the max size hint.
|
||||
// Since QPushButton for example doesn't set QWidget::maximumSize(), but instead uses sizeHint()
|
||||
// + QSizePolicy::Fixed.
|
||||
EnsureTopLevelsDeleted e;
|
||||
auto button = new QPushButton("one");
|
||||
auto dock1 = createDockWidget("dock1", button);
|
||||
Frame *frame = dock1->frame();
|
||||
|
||||
// Just a precondition from the test. If QPushButton ever changes, replace with a QWidget and set fixed size policy
|
||||
QCOMPARE(button->sizePolicy().verticalPolicy(), QSizePolicy::Fixed);
|
||||
|
||||
const int buttonMaxHeight = button->sizeHint().height();
|
||||
|
||||
QCOMPARE(dock1->sizeHint(), button->sizeHint());
|
||||
QCOMPARE(dock1->sizePolicy().verticalPolicy(), button->sizePolicy().verticalPolicy());
|
||||
QCOMPARE(dock1->sizePolicy().horizontalPolicy(), button->sizePolicy().horizontalPolicy());
|
||||
|
||||
QCOMPARE(frame->maxSizeHint().height(), qMax(buttonMaxHeight, KDDOCKWIDGETS_MIN_HEIGHT));
|
||||
|
||||
delete dock1->window();
|
||||
}
|
||||
|
||||
void TestDocks::tst_maximumSizePolicy()
|
||||
{
|
||||
EnsureTopLevelsDeleted e;
|
||||
|
||||
auto widget = new MyWidget2();
|
||||
const int maxHeight = 250;
|
||||
widget->setMinSize(QSize(200, 200));
|
||||
widget->setSizeHint(QSize(250, maxHeight));
|
||||
widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
|
||||
|
||||
auto dock1 = createDockWidget("dock1", widget);
|
||||
dock1->show();
|
||||
dock1->window()->resize(QSize(500, 500));
|
||||
auto oldFw = dock1->window();
|
||||
dock1->close();
|
||||
dock1->show();
|
||||
|
||||
QVERIFY(dock1->window()->height() <= maxHeight + 20); // + 20 as the floating window is a bit bigger, due to margins etc.
|
||||
QVERIFY(dock1->height() <= maxHeight);
|
||||
|
||||
delete oldFw;
|
||||
delete dock1->window();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (!qpaPassedAsArgument(argc, argv)) {
|
||||
|
||||
@@ -173,7 +173,9 @@ QWidget *KDDockWidgets::Tests::draggableFor(QWidget *w)
|
||||
if (auto frame = dock->frame())
|
||||
draggable = frame->titleBar();
|
||||
} else if (auto fw = qobject_cast<FloatingWindow *>(w)) {
|
||||
draggable = ((Config::self().flags() & Config::Flag_HideTitleBarWhenTabsVisible) && fw->hasSingleFrame() && fw->frames().first()->hasTabsVisible()) ? static_cast<QWidget*>(fw->frames().first()->tabWidget()->asWidget())
|
||||
auto frame = fw->hasSingleFrame() ? static_cast<FrameWidget*>(fw->frames().first())
|
||||
: nullptr;
|
||||
draggable = ((Config::self().flags() & Config::Flag_HideTitleBarWhenTabsVisible) && frame && frame->hasTabsVisible()) ? static_cast<QWidget*>(frame->tabWidget()->asWidget())
|
||||
: static_cast<QWidget*>(fw->titleBar());
|
||||
|
||||
} else if (qobject_cast<TabWidgetWidget *>(w) || qobject_cast<TitleBar *>(w)) {
|
||||
|
||||
Reference in New Issue
Block a user