forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/4.12'
Change-Id: I3e7049da2c3da6f784e3cb3407c22ada556e5d24
This commit is contained in:
54
.github/workflows/build_cmake.yml
vendored
54
.github/workflows/build_cmake.yml
vendored
@@ -168,6 +168,47 @@ jobs:
|
||||
string(REPLACE "licheck_mac" "" qtconfig "${qtconfig}")
|
||||
file(WRITE "qt5/${qt_dir_prefix}/mkspecs/qconfig.pri" "${qtconfig}")
|
||||
|
||||
- name: Download OpenSSL
|
||||
shell: cmake -P {0}
|
||||
run: |
|
||||
if ("${{ runner.os }}" STREQUAL "Windows")
|
||||
set(url_os "windows_x86")
|
||||
set(openssl_localdir "Tools/OpenSSL/Win_x64/bin")
|
||||
set(openssl_dest_dir "instdir/bin")
|
||||
set(shared_suffix ".dll")
|
||||
elseif ("${{ runner.os }}" STREQUAL "Linux")
|
||||
set(url_os "linux_x64")
|
||||
set(openssl_localdir "Tools/OpenSSL/binary/lib")
|
||||
set(openssl_dest_dir "instdir/lib/Qt/lib")
|
||||
set(shared_suffix ".so*")
|
||||
elseif ("${{ runner.os }}" STREQUAL "macOS")
|
||||
# Not needed on macOS
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(openssl_base_url "https://download.qt.io/online/qtsdkrepository/${url_os}/desktop/tools_openssl_x64")
|
||||
file(DOWNLOAD "${openssl_base_url}/Updates.xml" ./Updates.xml SHOW_PROGRESS)
|
||||
|
||||
file(READ ./Updates.xml updates_xml)
|
||||
string(REGEX MATCH
|
||||
"<Name>(qt.tools.openssl.*)</Name>.*<Version>([0-9+-.]+)</Version>.*<DownloadableArchives>(.*)</DownloadableArchives>" updates_xml_output "${updates_xml}")
|
||||
|
||||
set(openssl_directory ${CMAKE_MATCH_1})
|
||||
set(openssl_version ${CMAKE_MATCH_2})
|
||||
set(openssl_archive ${CMAKE_MATCH_3})
|
||||
|
||||
set(url "${openssl_base_url}/${openssl_directory}/${openssl_version}${openssl_archive}")
|
||||
|
||||
file(MAKE_DIRECTORY openssl)
|
||||
file(MAKE_DIRECTORY ${openssl_dest_dir})
|
||||
|
||||
message("Downloading ${url}")
|
||||
file(DOWNLOAD "${url}" ./openssl.7z SHOW_PROGRESS)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../openssl.7z WORKING_DIRECTORY openssl)
|
||||
|
||||
file(GLOB openssl_shared_objects "openssl/${openssl_localdir}/*${shared_suffix}")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${openssl_shared_objects} ${openssl_dest_dir})
|
||||
|
||||
- name: Download libclang
|
||||
id: libclang
|
||||
@@ -358,13 +399,18 @@ jobs:
|
||||
COMMAND ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/ctest -j ${N}
|
||||
WORKING_DIRECTORY build
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE stdout
|
||||
ERROR_VARIABLE stdout
|
||||
)
|
||||
|
||||
# Do not fail on ctest failure
|
||||
#if (NOT result EQUAL 0)
|
||||
# message(FATAL_ERROR "Running tests failed!")
|
||||
#endif()
|
||||
message("${stdout}")
|
||||
|
||||
if (NOT result EQUAL 0)
|
||||
string(REGEX MATCH "[0-9]+% tests.*[0-9.]+ sec" pass_rate "${stdout}")
|
||||
|
||||
# Do not fail on ctest failure
|
||||
message("::warning::${pass_rate}")
|
||||
endif()
|
||||
|
||||
- name: Install Strip
|
||||
run: |
|
||||
|
@@ -54,10 +54,23 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16)
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP ON)
|
||||
include(InstallRequiredsystemLibraries)
|
||||
|
||||
# For Qt Creator
|
||||
install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
|
||||
DESTINATION ${IDE_APP_PATH}
|
||||
COMPONENT Dependencies
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
|
||||
# For qtcreatorcdbext
|
||||
set(ArchSuffix 32)
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(ArchSuffix 64)
|
||||
endif()
|
||||
|
||||
install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
|
||||
DESTINATION lib/qtcreatorcdbext${ArchSuffix}
|
||||
COMPONENT Dependencies
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
@@ -433,7 +433,7 @@ endfunction()
|
||||
#
|
||||
|
||||
function(add_qtc_library name)
|
||||
cmake_parse_arguments(_arg "STATIC;OBJECT;SKIP_TRANSLATION;BUILD_BY_DEFAULT;ALLOW_ASCII_CASTS"
|
||||
cmake_parse_arguments(_arg "STATIC;OBJECT;SKIP_TRANSLATION;BUILD_BY_DEFAULT;ALLOW_ASCII_CASTS;UNVERSIONED"
|
||||
"DESTINATION;COMPONENT"
|
||||
"DEFINES;DEPENDS;EXTRA_TRANSLATIONS;INCLUDES;PUBLIC_DEFINES;PUBLIC_DEPENDS;PUBLIC_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;PROPERTIES" ${ARGN}
|
||||
)
|
||||
@@ -536,6 +536,7 @@ function(add_qtc_library name)
|
||||
SOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
VERSION "${IDE_VERSION}"
|
||||
SOVERSION "${PROJECT_VERSION_MAJOR}"
|
||||
CXX_EXTENSIONS OFF
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN ON
|
||||
BUILD_RPATH "${_LIB_RPATH}"
|
||||
@@ -547,7 +548,7 @@ function(add_qtc_library name)
|
||||
)
|
||||
enable_pch(${name})
|
||||
|
||||
if (WIN32 AND library_type STREQUAL "SHARED")
|
||||
if (WIN32 AND library_type STREQUAL "SHARED" AND NOT _arg_UNVERSIONED)
|
||||
# Match qmake naming scheme e.g. Library4.dll
|
||||
set_target_properties(${name} PROPERTIES
|
||||
SUFFIX "${PROJECT_VERSION_MAJOR}${CMAKE_SHARED_LIBRARY_SUFFIX}"
|
||||
@@ -781,6 +782,7 @@ function(add_qtc_plugin target_name)
|
||||
qtc_output_binary_dir(_output_binary_dir)
|
||||
set_target_properties(${target_name} PROPERTIES
|
||||
SOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
CXX_EXTENSIONS OFF
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN ON
|
||||
_arg_DEPENDS "${_arg_PLUGIN_DEPENDS}"
|
||||
@@ -991,6 +993,7 @@ function(add_qtc_executable name)
|
||||
INSTALL_RPATH "${install_rpath}"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${_output_binary_dir}/${_DESTINATION}"
|
||||
QT_SKIP_TRANSLATION "${skip_translation}"
|
||||
CXX_EXTENSIONS OFF
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN ON
|
||||
${_arg_PROPERTIES}
|
||||
@@ -1121,3 +1124,16 @@ function(finalize_qtc_gtest test_name)
|
||||
finalize_test_setup(${test})
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# This is the CMake equivalent of "RESOURCES = $$files()" from qmake
|
||||
function(qtc_glob_resources)
|
||||
cmake_parse_arguments(_arg "" "QRC_FILE;ROOT;GLOB" "" ${ARGN})
|
||||
|
||||
file(GLOB_RECURSE fileList RELATIVE "${_arg_ROOT}" "${_arg_ROOT}/${_arg_GLOB}")
|
||||
set(qrcData "<RCC><qresource>\n")
|
||||
foreach(file IN LISTS fileList)
|
||||
string(APPEND qrcData " <file alias=\"${file}\">${_arg_ROOT}/${file}</file>\n")
|
||||
endforeach()
|
||||
string(APPEND qrcData "</qresource></RCC>")
|
||||
file(WRITE "${_arg_QRC_FILE}" "${qrcData}")
|
||||
endfunction()
|
||||
|
10
dist/installer/mac/entitlements.plist
vendored
Normal file
10
dist/installer/mac/entitlements.plist
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.debugger</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@@ -10,6 +10,33 @@ add_feature_info("Build online documentation" WITH_ONLINE_DOCS "")
|
||||
option(BUILD_DEVELOPER_DOCS "Include developer documentation" OFF)
|
||||
add_feature_info("Include developer documentation" BUILD_DEVELOPER_DOCS "")
|
||||
|
||||
function(_find_all_includes _ret_includes _ret_framework_paths)
|
||||
set(_all_includes "${PROJECT_SOURCE_DIR}/src/plugins;${PROJECT_SOURCE_DIR}/src/libs")
|
||||
foreach(_target ${__QTC_PLUGINS} ${__QTC_LIBRARIES})
|
||||
if (NOT TARGET ${_target})
|
||||
continue()
|
||||
endif()
|
||||
get_target_property(_includes ${_target} INCLUDE_DIRECTORIES)
|
||||
foreach(_include ${_includes})
|
||||
string(FIND "${_include}" "/src/plugins/" _in_plugins)
|
||||
string(FIND "${_include}" "/src/libs/" _in_libs)
|
||||
string(FIND "${_include}" "${CMAKE_BINARY_DIR}" _in_build)
|
||||
if(_in_plugins LESS 0 AND _in_libs LESS 0 AND _in_build LESS 0)
|
||||
list(APPEND _all_includes ${_include})
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
list(APPEND _all_includes ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
|
||||
list(REMOVE_DUPLICATES _all_includes)
|
||||
set("${_ret_includes}" "${_all_includes}" PARENT_SCOPE)
|
||||
|
||||
# framework path
|
||||
if (APPLE)
|
||||
get_target_property(_qt_target Qt5::Core LOCATION) # <fw_path>/QtCore.framework/QtCore
|
||||
get_filename_component(_qt_loc "${_qt_target}" DIRECTORY)
|
||||
set("${_ret_framework_paths}" "${_qt_loc}/.." PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Find programs:
|
||||
function(_doc_find_program result_var)
|
||||
@@ -51,7 +78,7 @@ endfunction()
|
||||
|
||||
function(_setup_qdoc_targets _qdocconf_file _retval)
|
||||
cmake_parse_arguments(_arg "" "HTML_DIR;INSTALL_DIR;POSTFIX"
|
||||
"INDEXES;ENVIRONMENT_EXPORTS" ${ARGN})
|
||||
"INDEXES;INCLUDE_DIRECTORIES;FRAMEWORK_PATHS;ENVIRONMENT_EXPORTS" ${ARGN})
|
||||
|
||||
foreach(_index ${_arg_INDEXES})
|
||||
list(APPEND _qdoc_index_args "-indexdir;${_index}")
|
||||
@@ -79,9 +106,30 @@ function(_setup_qdoc_targets _qdocconf_file _retval)
|
||||
set(_html_outputdir "${_arg_HTML_DIR}/${_target}${_arg_POSTFIX}")
|
||||
file(MAKE_DIRECTORY "${_html_outputdir}")
|
||||
|
||||
set(_qdoc_include_args "")
|
||||
if (_arg_INCLUDE_DIRECTORIES OR _arg_FRAMEWORK_PATHS)
|
||||
# pass include directories to qdoc via hidden @ option, since we need to generate a file
|
||||
# to be able to resolve the generators inside the include paths
|
||||
set(_qdoc_includes "${CMAKE_CURRENT_BINARY_DIR}/cmake/qdoc_${_target}.inc")
|
||||
set(_qdoc_include_args "@${_qdoc_includes}")
|
||||
set(_includes "")
|
||||
if (_arg_INCLUDE_DIRECTORIES)
|
||||
set(_includes "-I$<JOIN:${_arg_INCLUDE_DIRECTORIES},\n-I>\n")
|
||||
endif()
|
||||
set(_frameworks "")
|
||||
if (_arg_FRAMEWORK_PATHS)
|
||||
set(_frameworks "-F$<JOIN:${_arg_FRAMEWORK_PATHS},\n-F>\n")
|
||||
endif()
|
||||
file(GENERATE
|
||||
OUTPUT "${_qdoc_includes}"
|
||||
CONTENT "${_includes}${_frameworks}"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(_html_target "html_docs_${_target}")
|
||||
add_custom_target("${_html_target}"
|
||||
${_full_qdoc_command} "-outputdir" "${_html_outputdir}" "${_qdocconf_file}" ${_qdoc_index_args}
|
||||
${_full_qdoc_command} -outputdir "${_html_outputdir}" "${_qdocconf_file}"
|
||||
${_qdoc_index_args} ${_qdoc_include_args}
|
||||
COMMENT "Build HTML documentation from ${_qdocconf_file}"
|
||||
DEPENDS "${_qdocconf_file}"
|
||||
SOURCES "${_qdocconf_file}"
|
||||
@@ -176,7 +224,7 @@ function(qdoc_build_qdocconf_file _qdocconf_file)
|
||||
endif()
|
||||
|
||||
cmake_parse_arguments(_arg "QCH" "HTML_DIR;QCH_DIR;INSTALL_DIR;POSTFIX"
|
||||
"INDEXES;ENVIRONMENT_EXPORTS" ${ARGN})
|
||||
"INDEXES;INCLUDE_DIRECTORIES;FRAMEWORK_PATHS;ENVIRONMENT_EXPORTS" ${ARGN})
|
||||
if (_arg_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "qdoc_build_qdocconf_file has unknown arguments: ${_arg_UNPARSED_ARGUMENTS}.")
|
||||
endif()
|
||||
@@ -188,7 +236,10 @@ function(qdoc_build_qdocconf_file _qdocconf_file)
|
||||
_setup_qdoc_targets("${_qdocconf_file}" _html_outputdir
|
||||
HTML_DIR "${_arg_HTML_DIR}" INSTALL_DIR "${_arg_INSTALL_DIR}"
|
||||
INDEXES ${_arg_INDEXES} ENVIRONMENT_EXPORTS ${_arg_ENVIRONMENT_EXPORTS}
|
||||
POSTFIX "${_arg_POSTFIX}")
|
||||
POSTFIX "${_arg_POSTFIX}"
|
||||
INCLUDE_DIRECTORIES ${_arg_INCLUDE_DIRECTORIES}
|
||||
FRAMEWORK_PATHS ${_arg_FRAMEWORK_PATHS}
|
||||
)
|
||||
|
||||
if (_arg_QCH)
|
||||
_setup_qhelpgenerator_targets("${_qdocconf_file}" "${_html_outputdir}"
|
||||
@@ -231,13 +282,21 @@ if (WITH_ONLINE_DOCS OR WITH_DOCS)
|
||||
if (WITH_DOCS)
|
||||
qdoc_build_qdocconf_file("qtcreator/qtcreator.qdocconf" ${_qch_params} ${_qdoc_params})
|
||||
if (BUILD_DEVELOPER_DOCS)
|
||||
qdoc_build_qdocconf_file("qtcreatordev/qtcreator-dev.qdocconf" ${_qch_params} ${_qdoc_params})
|
||||
_find_all_includes(_all_includes _framework_paths)
|
||||
qdoc_build_qdocconf_file("qtcreatordev/qtcreator-dev.qdocconf" ${_qch_params} ${_qdoc_params}
|
||||
INCLUDE_DIRECTORIES ${_all_includes}
|
||||
FRAMEWORK_PATHS ${_framework_paths}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
if(WITH_ONLINE_DOCS)
|
||||
qdoc_build_qdocconf_file("qtcreator/qtcreator-online.qdocconf" ${_qdoc_params})
|
||||
if (BUILD_DEVELOPER_DOCS)
|
||||
qdoc_build_qdocconf_file("qtcreatordev/qtcreator-dev-online.qdocconf" ${_qdoc_params})
|
||||
_find_all_includes(_all_includes _framework_paths)
|
||||
qdoc_build_qdocconf_file("qtcreatordev/qtcreator-dev-online.qdocconf" ${_qdoc_params}
|
||||
INCLUDE_DIRECTORIES ${_all_includes}
|
||||
FRAMEWORK_PATHS ${_framework_paths}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
@@ -1,7 +1,7 @@
|
||||
build_online_docs: \
|
||||
DOC_FILES += $$PWD/qtcreator/qtcreator-online.qdocconf $$PWD/qtcreatordev/qtcreator-dev-online.qdocconf
|
||||
DOC_FILES += $$IDE_DOC_FILES_ONLINE
|
||||
else: \
|
||||
DOC_FILES += $$PWD/qtcreator/qtcreator.qdocconf $$PWD/qtcreatordev/qtcreator-dev.qdocconf
|
||||
DOC_FILES += $$IDE_DOC_FILES
|
||||
|
||||
include(../docs.pri)
|
||||
|
||||
|
BIN
doc/qtcreator/images/qtcreator-workspace-manager.png
Normal file
BIN
doc/qtcreator/images/qtcreator-workspace-manager.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
57
doc/qtcreator/src/howto/creator-sidebars.qdoc
Normal file
57
doc/qtcreator/src/howto/creator-sidebars.qdoc
Normal file
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Free Documentation License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Free
|
||||
** Documentation License version 1.3 as published by the Free Software
|
||||
** Foundation and appearing in the file included in the packaging of
|
||||
** this file. Please review the following information to ensure
|
||||
** the GNU Free Documentation License version 1.3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\page creator-sidebars.html
|
||||
\previouspage creator-views.html
|
||||
\nextpage creator-project-managing-workspaces.html
|
||||
|
||||
\title Working with Sidebars
|
||||
|
||||
In the \uicontrol Edit mode, you can use a left and right sidebar to
|
||||
organize different views into project contents. Only views that are
|
||||
relevant to the \l{Selecting Modes}{mode} you are working in are
|
||||
available in it.
|
||||
|
||||
You can select views in the sidebar menu (1):
|
||||
|
||||
\image qtcreator-sidebar.png
|
||||
|
||||
You can change the view of the sidebars in the following ways:
|
||||
|
||||
\list
|
||||
\li To toggle the left sidebar, click \inlineimage leftsidebaricon.png
|
||||
(\uicontrol {Hide Left Sidebar/Show Left Sidebar}) or press
|
||||
\key Alt+0 (\key Cmd+0 on \macos). To toggle the right
|
||||
sidebar, click \inlineimage rightsidebaricon.png
|
||||
(\uicontrol {Hide Right Sidebar/Show Right Sidebar}) or press
|
||||
\key Alt+Shift+0 (\key Cmd+Shift+0 on \macos).
|
||||
\li To split a sidebar, click \inlineimage splitbutton_horizontal.png
|
||||
(\uicontrol {Split}). Select new content to view in the split view.
|
||||
\li To close a sidebar view, click \inlineimage splitbutton_closetop.png
|
||||
(\uicontrol {Close}).
|
||||
\endlist
|
||||
*/
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
@@ -87,11 +87,13 @@
|
||||
\endif
|
||||
|
||||
|
||||
The following sections describe some of these controls in more detail:
|
||||
The following sections describe some \QC controls in more detail:
|
||||
|
||||
\list
|
||||
\li \l{Selecting Modes}{Mode selector}
|
||||
\li \l{Browsing Project Contents}{Sidebars}
|
||||
\li \l{Browsing Project Contents}{Views}
|
||||
\li \l{Working with Sidebars}{Sidebars}
|
||||
\li \l{Managing Workspaces}{Workspaces}
|
||||
\li \l{Viewing Output}{Output panes}
|
||||
\endlist
|
||||
|
||||
@@ -253,7 +255,7 @@
|
||||
\contentspage index.html
|
||||
\page creator-modes.html
|
||||
\previouspage creator-quick-tour.html
|
||||
\nextpage creator-sidebar-views.html
|
||||
\nextpage creator-views.html
|
||||
|
||||
\title Selecting Modes
|
||||
|
||||
@@ -327,7 +329,7 @@
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\page creator-output-panes.html
|
||||
\previouspage creator-sidebar-views.html
|
||||
\previouspage creator-project-managing-workspaces.html
|
||||
\if defined(qtdesignstudio)
|
||||
\nextpage creator-using-qt-quick-designer.html
|
||||
\else
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
@@ -25,22 +25,18 @@
|
||||
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\page creator-sidebar-views.html
|
||||
\page creator-views.html
|
||||
\previouspage creator-modes.html
|
||||
\nextpage creator-output-panes.html
|
||||
\nextpage creator-sidebars.html
|
||||
|
||||
\title Browsing Project Contents
|
||||
|
||||
A left and right sidebar are available in most \QC \l{Selecting Modes}
|
||||
{modes}. The availability of the sidebars and their contents depend on
|
||||
the mode.
|
||||
You can organize \QC views in \l {Working with Sidebars}{sidebars} or as
|
||||
\l {Managing Workspaces}{workspaces}, depending on the \l{Selecting Modes}
|
||||
{mode} you are working in. Only views that are relevant to a mode are
|
||||
available in it.
|
||||
|
||||
In the \uicontrol Edit and \uicontrol Design mode, you can use the sidebars
|
||||
to browse the project contents.
|
||||
|
||||
\image qtcreator-sidebar.png
|
||||
|
||||
You can select the contents of the sidebars in the sidebar menu (1):
|
||||
The following views are related to managing projects and files:
|
||||
|
||||
\list
|
||||
\li \uicontrol Projects shows a list of projects open in the current
|
||||
@@ -72,28 +68,8 @@
|
||||
\endlist
|
||||
\endif
|
||||
|
||||
For more information about the sidebar views that are only
|
||||
available when editing QML files in the Design mode, see
|
||||
\l{Editing QML Files in Design Mode}.
|
||||
|
||||
You can change the view of the sidebars in the following ways:
|
||||
|
||||
\list
|
||||
|
||||
\li To toggle the left sidebar, click \inlineimage leftsidebaricon.png
|
||||
(\uicontrol {Hide Left Sidebar/Show Left Sidebar}) or press
|
||||
\key Alt+0 (\key Cmd+0 on \macos). To toggle the right
|
||||
sidebar, click \inlineimage rightsidebaricon.png
|
||||
(\uicontrol {Hide Right Sidebar/Show Right Sidebar}) or press
|
||||
\key Alt+Shift+0 (\key Cmd+Shift+0 on \macos).
|
||||
|
||||
\li To split a sidebar, click \inlineimage splitbutton_horizontal.png
|
||||
(\uicontrol {Split}). Select new content to view in the split view.
|
||||
|
||||
\li To close a sidebar view, click \inlineimage splitbutton_closetop.png
|
||||
(\uicontrol {Close}).
|
||||
|
||||
\endlist
|
||||
For more information about views that are only available when editing QML
|
||||
files in the Design mode, see \l{Editing QML Files in Design Mode}.
|
||||
|
||||
The additional options in each view are described in the following
|
||||
sections.
|
||||
@@ -103,7 +79,8 @@
|
||||
|
||||
\section1 Viewing Project Files
|
||||
|
||||
The sidebar displays projects in a project tree. The project tree contains
|
||||
The \uicontrol Projects view displays projects in a project tree. The
|
||||
project tree contains
|
||||
a list of all projects open in the current session. For each project, the
|
||||
tree visualizes the build system structure of the project and lists all
|
||||
files that are part of the project.
|
64
doc/qtcreator/src/howto/creator-workspaces.qdoc
Normal file
64
doc/qtcreator/src/howto/creator-workspaces.qdoc
Normal file
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Free Documentation License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Free
|
||||
** Documentation License version 1.3 as published by the Free Software
|
||||
** Foundation and appearing in the file included in the packaging of
|
||||
** this file. Please review the following information to ensure
|
||||
** the GNU Free Documentation License version 1.3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\previouspage creator-sidebars.html
|
||||
\page creator-project-managing-workspaces.html
|
||||
\nextpage creator-output-panes.html
|
||||
|
||||
\title Managing Workspaces
|
||||
|
||||
In the Design and Debug modes, you can arrange a set of \QC
|
||||
views as a \e workspace on the screen. For a list of views,
|
||||
select \uicontrol Window > \uicontrol Views.
|
||||
|
||||
In \QMLD, you can select the \uicontrol {Restore last workspace on startup}
|
||||
check box to save the current workspace as a \e default workspace when you
|
||||
exit \QC and to restore it the next time you start \QC.
|
||||
|
||||
To manage workspaces, select \uicontrol Window > \uicontrol Workspaces >
|
||||
\uicontrol Manage.
|
||||
|
||||
\image qtcreator-workspace-manager.png "Workspace Manager"
|
||||
|
||||
To save a workspace under a new name, select \uicontrol Clone.
|
||||
|
||||
To delete the selected workspace, select \uicontrol Delete.
|
||||
|
||||
To switch between workspaces, select \uicontrol {Switch To}.
|
||||
|
||||
To create a new workspace:
|
||||
|
||||
\list 1
|
||||
\li Select \uicontrol New.
|
||||
\li In the \uicontrol {Enter the name of the workspace} field,
|
||||
enter a name for the workspace.
|
||||
\li Select \uicontrol Create to create a new empty workspace or
|
||||
\uicontrol {Create and Open} to create a workspace and to
|
||||
switch to it.
|
||||
\endlist
|
||||
|
||||
*/
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
/*!
|
||||
\contentspage index.html
|
||||
\previouspage creator-quick-tour.html
|
||||
\previouspage creator-output-panes.html
|
||||
\page creator-configuring.html
|
||||
\nextpage creator-build-example-application.html
|
||||
|
||||
|
@@ -38,6 +38,8 @@
|
||||
\list
|
||||
\li \l{Selecting Modes}
|
||||
\li \l{Browsing Project Contents}
|
||||
\li \l{Working with Sidebars}
|
||||
\li \l{Managing Workspaces}
|
||||
\li \l{Viewing Output}
|
||||
\endlist
|
||||
\li \l{Configuring Qt Creator}
|
||||
|
@@ -48,7 +48,7 @@
|
||||
|
||||
\li Open the Qt Quick UI form in the \uicontrol Design mode.
|
||||
|
||||
\li In the \uicontrol Library, select \uicontrol Imports >
|
||||
\li In the \uicontrol Library, select \uicontrol {QML Imports} >
|
||||
\uicontrol {Add Import} > \uicontrol {QtQuick.Extras} to import the
|
||||
\l {Qt Quick Extras} module.
|
||||
|
||||
|
@@ -76,7 +76,7 @@
|
||||
|
||||
\endlist
|
||||
|
||||
Your module should now appear in the \uicontrol Imports tab in the
|
||||
Your module should now appear in the \uicontrol {QML Imports} tab in the
|
||||
\uicontrol Library in the Design mode. Your components should appear in the
|
||||
\uicontrol {QML Types} tab if a valid \c .metainfo file is in place.
|
||||
|
||||
|
@@ -48,7 +48,7 @@
|
||||
applications.
|
||||
|
||||
The \uicontrol {Library} pane lists the available QML types, UI
|
||||
components, assets, and imports.
|
||||
components, assets, and QML imports.
|
||||
|
||||
\image qmldesigner-qml-components.png "QML Components"
|
||||
|
||||
@@ -60,11 +60,11 @@
|
||||
Quick Controls, Dialogs, and Layouts are available for creating user
|
||||
interfaces using Qt Quick 2. The components and controls are based on
|
||||
standard QML types. To view the components and controls in the
|
||||
\uicontrol {Library}, import the component sets in \uicontrol Imports.
|
||||
\uicontrol {Library}, import the component sets in \uicontrol {QML Imports}.
|
||||
|
||||
The \uicontrol {Qt Quick Application} wizards for a particular platform add
|
||||
the import statements automatically. You can remove import statements in
|
||||
\uicontrol Imports
|
||||
\uicontrol {QML Imports}
|
||||
|
||||
\uicontrol {Assets} displays the images and other files that you copy
|
||||
to the project folder (to the same subfolder as the QML files).
|
||||
|
@@ -6,12 +6,15 @@ language = Cpp
|
||||
headerdirs = . \
|
||||
../src \
|
||||
../../../src/libs/aggregation \
|
||||
../../../src/libs/extensionsystem
|
||||
../../../src/libs/extensionsystem \
|
||||
../../../src/plugins/coreplugin
|
||||
|
||||
sourcedirs = . \
|
||||
../src \
|
||||
../../../src/libs/aggregation \
|
||||
../../../src/libs/extensionsystem
|
||||
../../../src/libs/extensionsystem \
|
||||
../../../src/plugins/coreplugin
|
||||
|
||||
|
||||
excludedirs = ../../../src/libs/aggregation/examples
|
||||
|
||||
|
@@ -60,11 +60,16 @@
|
||||
|
||||
\row
|
||||
\li Add a new wizard.
|
||||
\li You can extend the wizards in File > New File or Project with
|
||||
your own file and project templates.
|
||||
\li \l{Core::IWizard}, \l{Core::StandardFileWizard},
|
||||
\l{Core::BaseFileWizard}, \l{Core::BaseFileWizardParameters}
|
||||
\li You can extend the wizards in \uicontrol File >
|
||||
\uicontrol {New File or Project} with your own file
|
||||
and project templates. We recommend that you create
|
||||
JSON-based wizards instead of implementing new
|
||||
wizards in C++ code.
|
||||
\li \l{https://doc.qt.io/qtcreator/creator-project-wizards.html}
|
||||
{Adding New Custom Wizards}
|
||||
|
||||
\l{Core::IWizardFactory}, \l{Core::BaseFileWizardFactory},
|
||||
\l{Core::BaseFileWizard}, \l{Core::WizardDialogParameters}
|
||||
\row
|
||||
\li Add support for a new version control system.
|
||||
\li Version control systems integrated in \QC are Bazaar, CVS, Git,
|
||||
|
@@ -77,8 +77,10 @@
|
||||
\row
|
||||
\li \l{qtcreatorcdbext}
|
||||
\li Windows CDB debugger extension
|
||||
\endomit
|
||||
\endtable
|
||||
|
||||
|
||||
\section1 Plugins
|
||||
|
||||
As already mentioned, \QC is basically only a plugin loader framework
|
||||
@@ -98,9 +100,10 @@
|
||||
most important ones.
|
||||
|
||||
This plugin also contains classes necessary to hook into the
|
||||
\l{Locator} as well as support for searching text in arbitrary
|
||||
widgets.
|
||||
\l{Core::ILocatorFilter}{Locator} as well as support for
|
||||
searching text in arbitrary widgets.
|
||||
|
||||
\omit
|
||||
\row
|
||||
\li \l{ProjectExplorer}
|
||||
\li The project explorer plugin. Provides base classes for project
|
||||
|
@@ -168,8 +168,8 @@
|
||||
example, if you drew a rectangle, you can export it as a
|
||||
\l Rectangle component.
|
||||
You can provide the import statement of the module where the QML
|
||||
type is defined in the \uicontrol Imports field.
|
||||
\li In the \uicontrol Imports field, enter
|
||||
type is defined in the \uicontrol {QML Imports} field.
|
||||
\li In the \uicontrol {QML Imports} field, enter
|
||||
additional import statements to have them added to the generated QML
|
||||
file. For example, to use Qt Quick Controls 2.3, you need the
|
||||
import statement \c {QtQuick.Controls 2.3} and to use Qt Quick
|
||||
|
@@ -10,3 +10,8 @@ IDE_CASED_ID = QtCreator
|
||||
|
||||
PRODUCT_BUNDLE_ORGANIZATION = org.qt-project
|
||||
PROJECT_USER_FILE_EXTENSION = .user
|
||||
|
||||
IDE_DOC_FILES_ONLINE = $$PWD/doc/qtcreator/qtcreator-online.qdocconf \
|
||||
$$PWD/doc/qtcreatordev/qtcreator-dev-online.qdocconf
|
||||
IDE_DOC_FILES = $$PWD/doc/qtcreator/qtcreator.qdocconf \
|
||||
$$PWD/doc/qtcreatordev/qtcreator-dev.qdocconf
|
||||
|
244
scripts/build.py
Executable file
244
scripts/build.py
Executable file
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env python
|
||||
#############################################################################
|
||||
##
|
||||
## Copyright (C) 2020 The Qt Company Ltd.
|
||||
## Contact: https://www.qt.io/licensing/
|
||||
##
|
||||
## This file is part of the release tools of the Qt Toolkit.
|
||||
##
|
||||
## $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
## Commercial License Usage
|
||||
## Licensees holding valid commercial Qt licenses may use this file in
|
||||
## accordance with the commercial license agreement provided with the
|
||||
## Software or, alternatively, in accordance with the terms contained in
|
||||
## a written agreement between you and The Qt Company. For licensing terms
|
||||
## and conditions see https://www.qt.io/terms-conditions. For further
|
||||
## information use the contact form at https://www.qt.io/contact-us.
|
||||
##
|
||||
## GNU General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU
|
||||
## General Public License version 3 as published by the Free Software
|
||||
## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
## included in the packaging of this file. Please review the following
|
||||
## information to ensure the GNU General Public License requirements will
|
||||
## be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
##
|
||||
## $QT_END_LICENSE$
|
||||
##
|
||||
#############################################################################
|
||||
|
||||
# import the print function which is used in python 3.x
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import glob
|
||||
import os
|
||||
|
||||
import common
|
||||
|
||||
def existing_path(path):
|
||||
return path if os.path.exists(path) else None
|
||||
|
||||
def default_python3():
|
||||
path_system = os.path.join('/usr', 'bin') if not common.is_windows_platform() else None
|
||||
path = os.environ.get('PYTHON3_PATH') or path_system
|
||||
postfix = '.exe' if common.is_windows_platform() else ''
|
||||
return existing_path(os.path.join(path, 'python3' + postfix)) or existing_path(os.path.join(path, 'python' + postfix))
|
||||
|
||||
def get_arguments():
|
||||
parser = argparse.ArgumentParser(description='Build Qt Creator for packaging')
|
||||
parser.add_argument('--src', help='path to sources', required=True)
|
||||
parser.add_argument('--build', help='path that should be used for building', required=True)
|
||||
parser.add_argument('--qt-path', help='Path to Qt', required=True)
|
||||
|
||||
parser.add_argument('--debug', help='Enable debug builds', action='store_true', default=False)
|
||||
|
||||
# clang codemodel
|
||||
parser.add_argument('--llvm-path', help='Path to LLVM installation for Clang code model',
|
||||
default=os.environ.get('LLVM_INSTALL_DIR'))
|
||||
|
||||
# perfparser
|
||||
parser.add_argument('--elfutils-path',
|
||||
help='Path to elfutils installation for use by perfprofiler (Windows, Linux)')
|
||||
|
||||
# signing
|
||||
parser.add_argument('--keychain-unlock-script',
|
||||
help='Path to script for unlocking the keychain used for signing (macOS)')
|
||||
|
||||
# cdbextension
|
||||
parser.add_argument('--python-path',
|
||||
help='Path to python libraries for use by cdbextension (Windows)')
|
||||
|
||||
parser.add_argument('--app-target', help='File name of the executable / app bundle',
|
||||
default=('Qt Creator.app' if common.is_mac_platform()
|
||||
else 'qtcreator'))
|
||||
parser.add_argument('--python3', help='File path to python3 executable for generating translations',
|
||||
default=default_python3())
|
||||
|
||||
parser.add_argument('--no-cdb',
|
||||
help='Skip cdbextension and the python dependency packaging step (Windows)',
|
||||
action='store_true', default=(not common.is_windows_platform()))
|
||||
parser.add_argument('--no-docs', help='Skip documentation generation',
|
||||
action='store_true', default=False)
|
||||
return parser.parse_args()
|
||||
|
||||
def build_qtcreator(args, paths):
|
||||
if not os.path.exists(paths.build):
|
||||
os.makedirs(paths.build)
|
||||
prefix_paths = [paths.qt]
|
||||
if args.llvm_path:
|
||||
prefix_paths += [args.llvm_path]
|
||||
if args.elfutils_path:
|
||||
prefix_paths += [args.elfutils_path]
|
||||
build_type = 'Debug' if args.debug else 'Release'
|
||||
with_docs_str = 'OFF' if args.no_docs else 'ON'
|
||||
cmake_args = ['cmake',
|
||||
'-DCMAKE_PREFIX_PATH=' + ';'.join(prefix_paths),
|
||||
'-DCMAKE_BUILD_TYPE=' + build_type,
|
||||
'-DWITH_DOCS=' + with_docs_str,
|
||||
'-DBUILD_DEVELOPER_DOCS=' + with_docs_str,
|
||||
'-DBUILD_EXECUTABLE_SDKTOOL=OFF',
|
||||
'-DCMAKE_INSTALL_PREFIX=' + paths.install,
|
||||
'-DWITH_TESTS=OFF',
|
||||
'-G', 'Ninja']
|
||||
|
||||
if args.python3:
|
||||
cmake_args += ['-DPYTHON_EXECUTABLE=' + args.python3]
|
||||
|
||||
# force MSVC on Windows, because it looks for GCC in the PATH first,
|
||||
# even if MSVC is first mentioned in the PATH...
|
||||
# TODO would be nicer if we only did this if cl.exe is indeed first in the PATH
|
||||
if common.is_windows_platform():
|
||||
cmake_args += ['-DCMAKE_C_COMPILER=cl',
|
||||
'-DCMAKE_CXX_COMPILER=cl',
|
||||
'-DBUILD_EXECUTABLE_WIN32INTERRUPT=OFF',
|
||||
'-DBUILD_EXECUTABLE_WIN64INTERRUPT=OFF',
|
||||
'-DBUILD_LIBRARY_QTCREATORCDBEXT=OFF']
|
||||
if args.python_path:
|
||||
python_library = glob.glob(os.path.join(args.python_path, 'libs', 'python??.lib'))
|
||||
if python_library:
|
||||
cmake_args += ['-DPYTHON_LIBRARY=' + python_library[0],
|
||||
'-DPYTHON_INCLUDE_DIR=' + os.path.join(args.python_path, 'include')]
|
||||
|
||||
# TODO this works around a CMake bug https://gitlab.kitware.com/cmake/cmake/issues/20119
|
||||
if common.is_linux_platform():
|
||||
cmake_args += ['-DBUILD_WITH_PCH=OFF']
|
||||
|
||||
ide_revision = common.get_commit_SHA(paths.src)
|
||||
if ide_revision:
|
||||
cmake_args += ['-DIDE_REVISION=ON',
|
||||
'-DIDE_REVISION_STR=' + ide_revision,
|
||||
'-DIDE_REVISION_URL_STR=https://code.qt.io/cgit/qt-creator/qt-creator.git/log/?id=' + ide_revision]
|
||||
|
||||
common.check_print_call(cmake_args + [paths.src], paths.build)
|
||||
common.check_print_call(['cmake', '--build', '.'], paths.build)
|
||||
if not args.no_docs:
|
||||
common.check_print_call(['cmake', '--build', '.', '--target', 'docs'], paths.build)
|
||||
|
||||
common.check_print_call(['cmake', '--install', '.', '--prefix', paths.install, '--strip'],
|
||||
paths.build)
|
||||
common.check_print_call(['cmake', '--install', '.', '--prefix', paths.dev_install,
|
||||
'--component', 'Devel'],
|
||||
paths.build)
|
||||
|
||||
def build_wininterrupt(args, paths):
|
||||
if not common.is_windows_platform():
|
||||
return
|
||||
# assumes existing Qt Creator build
|
||||
cmake_args = ['-DBUILD_EXECUTABLE_WIN32INTERRUPT=ON',
|
||||
'-DBUILD_EXECUTABLE_WIN64INTERRUPT=ON',
|
||||
'-DBUILD_LIBRARY_QTCREATORCDBEXT=OFF']
|
||||
common.check_print_call(['cmake'] + cmake_args + [paths.src], paths.build)
|
||||
common.check_print_call(['cmake', '--build', '.'], paths.build)
|
||||
common.check_print_call(['cmake', '--install', '.', '--prefix', paths.wininterrupt_install,
|
||||
'--component', 'wininterrupt'],
|
||||
paths.build)
|
||||
|
||||
def build_qtcreatorcdbext(args, paths):
|
||||
if args.no_cdb:
|
||||
return
|
||||
# assumes existing Qt Creator build
|
||||
cmake_args = ['-DBUILD_EXECUTABLE_WIN32INTERRUPT=OFF',
|
||||
'-DBUILD_EXECUTABLE_WIN64INTERRUPT=OFF',
|
||||
'-DBUILD_LIBRARY_QTCREATORCDBEXT=ON']
|
||||
common.check_print_call(['cmake'] + cmake_args + [paths.src], paths.build)
|
||||
common.check_print_call(['cmake', '--build', '.'], paths.build)
|
||||
common.check_print_call(['cmake', '--install', '.', '--prefix', paths.qtcreatorcdbext_install,
|
||||
'--component', 'qtcreatorcdbext'],
|
||||
paths.build)
|
||||
|
||||
def deploy_qt(args, paths):
|
||||
if common.is_mac_platform():
|
||||
script = os.path.join(paths.src, 'scripts', 'deployqtHelper_mac.sh')
|
||||
app = os.path.join(paths.install, args.app_target)
|
||||
# TODO this is wrong if Qt is set up non-standard
|
||||
# TODO integrate deployqtHelper_mac.sh into deployqt.py, finally
|
||||
qt_bins = os.path.join(paths.qt, 'bin')
|
||||
qt_translations = os.path.join(paths.qt, 'translations')
|
||||
qt_plugins = os.path.join(paths.qt, 'plugins')
|
||||
qt_imports = os.path.join(paths.qt, 'imports')
|
||||
qt_qml = os.path.join(paths.qt, 'qml')
|
||||
common.check_print_call([script, app, qt_bins, qt_translations, qt_plugins,
|
||||
qt_imports, qt_qml],
|
||||
paths.build)
|
||||
else:
|
||||
exe = os.path.join(paths.install, 'bin', args.app_target)
|
||||
common.check_print_call(['python', '-u', os.path.join(paths.src, 'scripts', 'deployqt.py'),
|
||||
'-i', exe, os.path.join(paths.qt, 'bin', 'qmake')],
|
||||
paths.build)
|
||||
|
||||
def package_qtcreator(args, paths):
|
||||
common.check_print_call(['7z', 'a', '-mmt2', os.path.join(paths.result, 'qtcreator.7z'), '*'],
|
||||
paths.install)
|
||||
common.check_print_call(['7z', 'a', '-mmt2',
|
||||
os.path.join(paths.result, 'qtcreator_dev.7z'), '*'],
|
||||
paths.dev_install)
|
||||
if common.is_windows_platform():
|
||||
common.check_print_call(['7z', 'a', '-mmt2',
|
||||
os.path.join(paths.result, 'wininterrupt.7z'), '*'],
|
||||
paths.wininterrupt_install)
|
||||
if not args.no_cdb:
|
||||
common.check_print_call(['7z', 'a', '-mmt2',
|
||||
os.path.join(paths.result, 'qtcreatorcdbext.7z'), '*'],
|
||||
paths.qtcreatorcdbext_install)
|
||||
|
||||
if common.is_mac_platform():
|
||||
if args.keychain_unlock_script:
|
||||
common.check_print_call([args.keychain_unlock_script], paths.install)
|
||||
common.check_print_call(['python', '-u',
|
||||
os.path.join(paths.src, 'scripts', 'makedmg.py'),
|
||||
'qt-creator.dmg',
|
||||
'Qt Creator',
|
||||
paths.src,
|
||||
paths.install],
|
||||
paths.result)
|
||||
|
||||
def get_paths(args):
|
||||
Paths = collections.namedtuple('Paths',
|
||||
['qt', 'src', 'build',
|
||||
'install', 'dev_install', 'wininterrupt_install',
|
||||
'qtcreatorcdbext_install', 'result'])
|
||||
build_path = os.path.abspath(args.build)
|
||||
install_path = os.path.join(build_path, 'install')
|
||||
return Paths(qt=os.path.abspath(args.qt_path),
|
||||
src=os.path.abspath(args.src),
|
||||
build=os.path.join(build_path, 'build'),
|
||||
install=os.path.join(install_path, 'qt-creator'),
|
||||
dev_install=os.path.join(install_path, 'qt-creator-dev'),
|
||||
wininterrupt_install=os.path.join(install_path, 'wininterrupt'),
|
||||
qtcreatorcdbext_install=os.path.join(install_path, 'qtcreatorcdbext'),
|
||||
result=build_path)
|
||||
|
||||
def main():
|
||||
args = get_arguments()
|
||||
paths = get_paths(args)
|
||||
|
||||
build_qtcreator(args, paths)
|
||||
build_wininterrupt(args, paths)
|
||||
build_qtcreatorcdbext(args, paths)
|
||||
deploy_qt(args, paths)
|
||||
package_qtcreator(args, paths)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@@ -40,6 +40,33 @@ def is_linux_platform():
|
||||
def is_mac_platform():
|
||||
return sys.platform.startswith('darwin')
|
||||
|
||||
def check_print_call(command, workdir):
|
||||
print('------------------------------------------')
|
||||
print('COMMAND:')
|
||||
print(' '.join(['"' + c.replace('"', '\\"') + '"' for c in command]))
|
||||
print('PWD: "' + workdir + '"')
|
||||
print('------------------------------------------')
|
||||
subprocess.check_call(command, cwd=workdir)
|
||||
|
||||
|
||||
def get_git_SHA(path):
|
||||
try:
|
||||
return subprocess.check_output(['git', 'rev-list', '-n1', 'HEAD'], cwd=path).strip()
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
# get commit SHA either directly from git, or from a .tag file in the source directory
|
||||
def get_commit_SHA(path):
|
||||
git_sha = get_git_SHA(path)
|
||||
if not git_sha:
|
||||
tagfile = os.path.join(path, '.tag')
|
||||
if os.path.exists(tagfile):
|
||||
with open(tagfile, 'r') as f:
|
||||
git_sha = f.read().strip()
|
||||
return git_sha
|
||||
|
||||
# copy of shutil.copytree that does not bail out if the target directory already exists
|
||||
# and that does not create empty directories
|
||||
def copytree(src, dst, symlinks=False, ignore=None):
|
||||
@@ -211,5 +238,7 @@ def codesign(app_path):
|
||||
lambda ff: ff.endswith('.dylib'))
|
||||
codesign = codesign_call()
|
||||
if is_mac_platform() and codesign:
|
||||
entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist',
|
||||
'installer', 'mac', 'entitlements.plist')
|
||||
# sign the whole bundle
|
||||
subprocess.check_call(codesign + ['--deep', app_path])
|
||||
subprocess.check_call(codesign + ['--deep', app_path, '--entitlements', entitlements_path])
|
||||
|
@@ -23,7 +23,7 @@
|
||||
#
|
||||
############################################################################
|
||||
|
||||
from dumper import *
|
||||
from dumper import Children
|
||||
|
||||
def qdump__boost__bimaps__bimap(d, value):
|
||||
#leftType = value.type[0]
|
||||
|
@@ -28,10 +28,12 @@ import os
|
||||
import sys
|
||||
import cdbext
|
||||
import re
|
||||
import threading
|
||||
from utils import TypeCode
|
||||
|
||||
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
|
||||
|
||||
from dumper import *
|
||||
from dumper import DumperBase, SubItem
|
||||
|
||||
class FakeVoidType(cdbext.Type):
|
||||
def __init__(self, name , dumper):
|
||||
@@ -47,19 +49,19 @@ class FakeVoidType(cdbext.Type):
|
||||
|
||||
def code(self):
|
||||
if self.typeName.endswith('*'):
|
||||
return TypeCodePointer
|
||||
return TypeCode.TypeCodePointer
|
||||
if self.typeName.endswith(']'):
|
||||
return TypeCodeArray
|
||||
return TypeCodeVoid
|
||||
return TypeCode.TypeCodeArray
|
||||
return TypeCode.TypeCodeVoid
|
||||
|
||||
def unqualified(self):
|
||||
return self
|
||||
|
||||
def target(self):
|
||||
code = self.code()
|
||||
if code == TypeCodePointer:
|
||||
if code == TypeCode.TypeCodePointer:
|
||||
return FakeVoidType(self.typeName[:-1], self.dumper)
|
||||
if code == TypeCodeVoid:
|
||||
if code == TypeCode.TypeCodeVoid:
|
||||
return self
|
||||
try:
|
||||
return FakeVoidType(self.typeName[:self.typeName.rindex('[')], self.dumper)
|
||||
@@ -105,7 +107,7 @@ class Dumper(DumperBase):
|
||||
val.type = self.fromNativeType(nativeValue.type())
|
||||
# There is no cdb api for the size of bitfields.
|
||||
# Workaround this issue by parsing the native debugger text for integral types.
|
||||
if val.type.code == TypeCodeIntegral:
|
||||
if val.type.code == TypeCode.TypeCodeIntegral:
|
||||
integerString = nativeValue.nativeDebuggerValue()
|
||||
if integerString == 'true':
|
||||
val.ldata = int(1).to_bytes(1, byteorder='little')
|
||||
@@ -128,7 +130,7 @@ class Dumper(DumperBase):
|
||||
except:
|
||||
# read raw memory in case the integerString can not be interpreted
|
||||
pass
|
||||
if val.type.code == TypeCodeEnum:
|
||||
if val.type.code == TypeCode.TypeCodeEnum:
|
||||
val.ldisplay = self.enumValue(nativeValue)
|
||||
val.isBaseClass = val.name == val.type.name
|
||||
val.nativeValue = nativeValue
|
||||
@@ -159,21 +161,21 @@ class Dumper(DumperBase):
|
||||
nativeType = FakeVoidType(nativeType.name(), self)
|
||||
|
||||
code = nativeType.code()
|
||||
if code == TypeCodePointer:
|
||||
if code == TypeCode.TypeCodePointer:
|
||||
if not nativeType.name().startswith('<function>'):
|
||||
targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId())
|
||||
if targetType is not None:
|
||||
return self.createPointerType(targetType)
|
||||
code = TypeCodeFunction
|
||||
code = TypeCode.TypeCodeFunction
|
||||
|
||||
if code == TypeCodeArray:
|
||||
if code == TypeCode.TypeCodeArray:
|
||||
# cdb reports virtual function tables as arrays those ar handled separetly by
|
||||
# the DumperBase. Declare those types as structs prevents a lookup to a none existing type
|
||||
if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith('<gentype '):
|
||||
targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId())
|
||||
if targetType is not None:
|
||||
return self.createArrayType(targetType, nativeType.arrayElements())
|
||||
code = TypeCodeStruct
|
||||
code = TypeCode.TypeCodeStruct
|
||||
|
||||
tdata = self.TypeData(self)
|
||||
tdata.name = nativeType.name()
|
||||
@@ -182,12 +184,12 @@ class Dumper(DumperBase):
|
||||
tdata.code = code
|
||||
tdata.moduleName = nativeType.module()
|
||||
self.registerType(typeId, tdata) # Prevent recursion in fields.
|
||||
if code == TypeCodeStruct:
|
||||
if code == TypeCode.TypeCodeStruct:
|
||||
tdata.lfields = lambda value : \
|
||||
self.listFields(nativeType, value)
|
||||
tdata.lalignment = lambda : \
|
||||
self.nativeStructAlignment(nativeType)
|
||||
if code == TypeCodeEnum:
|
||||
if code == TypeCode.TypeCodeEnum:
|
||||
tdata.enumDisplay = lambda intval, addr, form : \
|
||||
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
||||
tdata.templateArguments = self.listTemplateParameters(nativeType.name())
|
||||
@@ -206,7 +208,7 @@ class Dumper(DumperBase):
|
||||
nativeMember = nativeValue.childFromIndex(index)
|
||||
|
||||
def nativeStructAlignment(self, nativeType):
|
||||
#warn("NATIVE ALIGN FOR %s" % nativeType.name)
|
||||
#DumperBase.warn("NATIVE ALIGN FOR %s" % nativeType.name)
|
||||
def handleItem(nativeFieldType, align):
|
||||
a = self.fromNativeType(nativeFieldType).alignment()
|
||||
return a if a > align else align
|
||||
|
@@ -23,8 +23,6 @@
|
||||
#
|
||||
############################################################################
|
||||
|
||||
from dumper import *
|
||||
|
||||
def typeTarget(type):
|
||||
target = type.target()
|
||||
if target:
|
||||
@@ -234,7 +232,7 @@ def qdump__CPlusPlus__Internal__PPToken(d, value):
|
||||
data, size, alloc = d.byteArrayData(value["m_src"])
|
||||
length = value["f"]["utf16chars"].integer()
|
||||
offset = value["utf16charOffset"].integer()
|
||||
#warn("size: %s, alloc: %s, offset: %s, length: %s, data: %s"
|
||||
#DumperBase.warn("size: %s, alloc: %s, offset: %s, length: %s, data: %s"
|
||||
# % (size, alloc, offset, length, data))
|
||||
d.putValue(d.readMemory(data + offset, min(100, length)), "latin1")
|
||||
d.putPlainChildren(value)
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -37,8 +37,8 @@ import struct
|
||||
import tempfile
|
||||
import types
|
||||
|
||||
from dumper import *
|
||||
|
||||
from dumper import DumperBase, Children, toInteger, TopLevelItem
|
||||
from utils import TypeCode
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
@@ -168,7 +168,7 @@ def importPlainDumpers(args):
|
||||
gdb.execute('disable pretty-printer .* .*')
|
||||
except:
|
||||
# Might occur in non-ASCII directories
|
||||
warn('COULD NOT DISABLE PRETTY PRINTERS')
|
||||
DumperBase.warn('COULD NOT DISABLE PRETTY PRINTERS')
|
||||
else:
|
||||
theDumper.importPlainDumpers()
|
||||
|
||||
@@ -186,7 +186,7 @@ class OutputSaver:
|
||||
|
||||
def __exit__(self, exType, exValue, exTraceBack):
|
||||
if self.d.passExceptions and not exType is None:
|
||||
showException('OUTPUTSAVER', exType, exValue, exTraceBack)
|
||||
self.d.showException('OUTPUTSAVER', exType, exValue, exTraceBack)
|
||||
self.d.output = self.savedOutput
|
||||
else:
|
||||
self.savedOutput += self.d.output
|
||||
@@ -217,7 +217,7 @@ class Dumper(DumperBase):
|
||||
self.setVariableFetchingOptions(args)
|
||||
|
||||
def fromFrameValue(self, nativeValue):
|
||||
#warn('FROM FRAME VALUE: %s' % nativeValue.address)
|
||||
#DumperBase.warn('FROM FRAME VALUE: %s' % nativeValue.address)
|
||||
val = nativeValue
|
||||
try:
|
||||
val = nativeValue.cast(nativeValue.dynamic_type)
|
||||
@@ -226,7 +226,7 @@ class Dumper(DumperBase):
|
||||
return self.fromNativeValue(val)
|
||||
|
||||
def fromNativeValue(self, nativeValue):
|
||||
#warn('FROM NATIVE VALUE: %s' % nativeValue)
|
||||
#DumperBase.warn('FROM NATIVE VALUE: %s' % nativeValue)
|
||||
self.check(isinstance(nativeValue, gdb.Value))
|
||||
nativeType = nativeValue.type
|
||||
code = nativeType.code
|
||||
@@ -234,7 +234,7 @@ class Dumper(DumperBase):
|
||||
targetType = self.fromNativeType(nativeType.target().unqualified())
|
||||
val = self.createReferenceValue(toInteger(nativeValue.address), targetType)
|
||||
val.nativeValue = nativeValue
|
||||
#warn('CREATED REF: %s' % val)
|
||||
#DumperBase.warn('CREATED REF: %s' % val)
|
||||
return val
|
||||
if code == gdb.TYPE_CODE_PTR:
|
||||
try:
|
||||
@@ -248,14 +248,14 @@ class Dumper(DumperBase):
|
||||
# later which
|
||||
# is surprisingly expensive.
|
||||
val.nativeValue = nativeValue
|
||||
#warn('CREATED PTR 1: %s' % val)
|
||||
#DumperBase.warn('CREATED PTR 1: %s' % val)
|
||||
if not nativeValue.address is None:
|
||||
val.laddress = toInteger(nativeValue.address)
|
||||
#warn('CREATED PTR 2: %s' % val)
|
||||
#DumperBase.warn('CREATED PTR 2: %s' % val)
|
||||
return val
|
||||
if code == gdb.TYPE_CODE_TYPEDEF:
|
||||
targetType = nativeType.strip_typedefs().unqualified()
|
||||
#warn('TARGET TYPE: %s' % targetType)
|
||||
#DumperBase.warn('TARGET TYPE: %s' % targetType)
|
||||
if targetType.code == gdb.TYPE_CODE_ARRAY:
|
||||
val = self.Value(self)
|
||||
else:
|
||||
@@ -267,7 +267,7 @@ class Dumper(DumperBase):
|
||||
val = self.fromNativeValue(nativeValue.cast(targetType))
|
||||
except:
|
||||
val = self.Value(self)
|
||||
#warn('CREATED TYPEDEF: %s' % val)
|
||||
#DumperBase.warn('CREATED TYPEDEF: %s' % val)
|
||||
else:
|
||||
val = self.Value(self)
|
||||
|
||||
@@ -308,34 +308,34 @@ class Dumper(DumperBase):
|
||||
def fromNativeType(self, nativeType):
|
||||
self.check(isinstance(nativeType, gdb.Type))
|
||||
code = nativeType.code
|
||||
#warn('FROM NATIVE TYPE: %s' % nativeType)
|
||||
#DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType)
|
||||
nativeType = nativeType.unqualified()
|
||||
|
||||
if code == gdb.TYPE_CODE_PTR:
|
||||
#warn('PTR')
|
||||
#DumperBase.warn('PTR')
|
||||
targetType = self.fromNativeType(nativeType.target().unqualified())
|
||||
return self.createPointerType(targetType)
|
||||
|
||||
if code == gdb.TYPE_CODE_REF:
|
||||
#warn('REF')
|
||||
#DumperBase.warn('REF')
|
||||
targetType = self.fromNativeType(nativeType.target().unqualified())
|
||||
return self.createReferenceType(targetType)
|
||||
|
||||
if hasattr(gdb, "TYPE_CODE_RVALUE_REF"):
|
||||
if code == gdb.TYPE_CODE_RVALUE_REF:
|
||||
#warn('RVALUEREF')
|
||||
#DumperBase.warn('RVALUEREF')
|
||||
targetType = self.fromNativeType(nativeType.target())
|
||||
return self.createRValueReferenceType(targetType)
|
||||
|
||||
if code == gdb.TYPE_CODE_ARRAY:
|
||||
#warn('ARRAY')
|
||||
#DumperBase.warn('ARRAY')
|
||||
nativeTargetType = nativeType.target().unqualified()
|
||||
targetType = self.fromNativeType(nativeTargetType)
|
||||
count = nativeType.sizeof // nativeTargetType.sizeof
|
||||
return self.createArrayType(targetType, count)
|
||||
|
||||
if code == gdb.TYPE_CODE_TYPEDEF:
|
||||
#warn('TYPEDEF')
|
||||
#DumperBase.warn('TYPEDEF')
|
||||
nativeTargetType = nativeType.unqualified()
|
||||
while nativeTargetType.code == gdb.TYPE_CODE_TYPEDEF:
|
||||
nativeTargetType = nativeTargetType.strip_typedefs().unqualified()
|
||||
@@ -344,7 +344,7 @@ class Dumper(DumperBase):
|
||||
self.nativeTypeId(nativeType))
|
||||
|
||||
if code == gdb.TYPE_CODE_ERROR:
|
||||
warn('Type error: %s' % nativeType)
|
||||
self.warn('Type error: %s' % nativeType)
|
||||
return self.Type(self, '')
|
||||
|
||||
typeId = self.nativeTypeId(nativeType)
|
||||
@@ -356,28 +356,28 @@ class Dumper(DumperBase):
|
||||
tdata.lbitsize = nativeType.sizeof * 8
|
||||
tdata.code = {
|
||||
#gdb.TYPE_CODE_TYPEDEF : TypeCodeTypedef, # Handled above.
|
||||
gdb.TYPE_CODE_METHOD : TypeCodeFunction,
|
||||
gdb.TYPE_CODE_VOID : TypeCodeVoid,
|
||||
gdb.TYPE_CODE_FUNC : TypeCodeFunction,
|
||||
gdb.TYPE_CODE_METHODPTR : TypeCodeFunction,
|
||||
gdb.TYPE_CODE_MEMBERPTR : TypeCodeFunction,
|
||||
#gdb.TYPE_CODE_PTR : TypeCodePointer, # Handled above.
|
||||
#gdb.TYPE_CODE_REF : TypeCodeReference, # Handled above.
|
||||
gdb.TYPE_CODE_BOOL : TypeCodeIntegral,
|
||||
gdb.TYPE_CODE_CHAR : TypeCodeIntegral,
|
||||
gdb.TYPE_CODE_INT : TypeCodeIntegral,
|
||||
gdb.TYPE_CODE_FLT : TypeCodeFloat,
|
||||
gdb.TYPE_CODE_ENUM : TypeCodeEnum,
|
||||
#gdb.TYPE_CODE_ARRAY : TypeCodeArray,
|
||||
gdb.TYPE_CODE_STRUCT : TypeCodeStruct,
|
||||
gdb.TYPE_CODE_UNION : TypeCodeStruct,
|
||||
gdb.TYPE_CODE_COMPLEX : TypeCodeComplex,
|
||||
gdb.TYPE_CODE_STRING : TypeCodeFortranString,
|
||||
gdb.TYPE_CODE_METHOD : TypeCode.TypeCodeFunction,
|
||||
gdb.TYPE_CODE_VOID : TypeCode.TypeCodeVoid,
|
||||
gdb.TYPE_CODE_FUNC : TypeCode.TypeCodeFunction,
|
||||
gdb.TYPE_CODE_METHODPTR : TypeCode.TypeCodeFunction,
|
||||
gdb.TYPE_CODE_MEMBERPTR : TypeCode.TypeCodeFunction,
|
||||
#gdb.TYPE_CODE_PTR : TypeCode.TypeCodePointer, # Handled above.
|
||||
#gdb.TYPE_CODE_REF : TypeCode.TypeCodeReference, # Handled above.
|
||||
gdb.TYPE_CODE_BOOL : TypeCode.TypeCodeIntegral,
|
||||
gdb.TYPE_CODE_CHAR : TypeCode.TypeCodeIntegral,
|
||||
gdb.TYPE_CODE_INT : TypeCode.TypeCodeIntegral,
|
||||
gdb.TYPE_CODE_FLT : TypeCode.TypeCodeFloat,
|
||||
gdb.TYPE_CODE_ENUM : TypeCode.TypeCodeEnum,
|
||||
#gdb.TYPE_CODE_ARRAY : TypeCode.TypeCodeArray,
|
||||
gdb.TYPE_CODE_STRUCT : TypeCode.TypeCodeStruct,
|
||||
gdb.TYPE_CODE_UNION : TypeCode.TypeCodeStruct,
|
||||
gdb.TYPE_CODE_COMPLEX : TypeCode.TypeCodeComplex,
|
||||
gdb.TYPE_CODE_STRING : TypeCode.TypeCodeFortranString,
|
||||
}[code]
|
||||
if tdata.code == TypeCodeEnum:
|
||||
if tdata.code == TypeCode.TypeCodeEnum:
|
||||
tdata.enumDisplay = lambda intval, addr, form : \
|
||||
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
||||
if tdata.code == TypeCodeStruct:
|
||||
if tdata.code == TypeCode.TypeCodeStruct:
|
||||
tdata.lalignment = lambda : \
|
||||
self.nativeStructAlignment(nativeType)
|
||||
tdata.lfields = lambda value : \
|
||||
@@ -402,7 +402,7 @@ class Dumper(DumperBase):
|
||||
elif isinstance(targ, gdb.Value):
|
||||
targs.append(self.fromNativeValue(targ).value())
|
||||
else:
|
||||
error('UNKNOWN TEMPLATE PARAMETER')
|
||||
raise RuntimeError('UNKNOWN TEMPLATE PARAMETER')
|
||||
pos += 1
|
||||
targs2 = self.listTemplateParametersManually(str(nativeType))
|
||||
return targs if len(targs) >= len(targs2) else targs2
|
||||
@@ -451,7 +451,7 @@ class Dumper(DumperBase):
|
||||
return typeId
|
||||
|
||||
def nativeStructAlignment(self, nativeType):
|
||||
#warn('NATIVE ALIGN FOR %s' % nativeType.name)
|
||||
#DumperBase.warn('NATIVE ALIGN FOR %s' % nativeType.name)
|
||||
def handleItem(nativeFieldType, align):
|
||||
a = self.fromNativeType(nativeFieldType).alignment()
|
||||
return a if a > align else align
|
||||
@@ -507,7 +507,7 @@ class Dumper(DumperBase):
|
||||
|
||||
anonNumber = 0
|
||||
|
||||
#warn('LISTING FIELDS FOR %s' % nativeType)
|
||||
#DumperBase.warn('LISTING FIELDS FOR %s' % nativeType)
|
||||
for nativeField in nativeType.fields():
|
||||
fieldName = nativeField.name
|
||||
# Something without a name.
|
||||
@@ -521,7 +521,7 @@ class Dumper(DumperBase):
|
||||
# multiple anonymous unions in the struct.
|
||||
anonNumber += 1
|
||||
fieldName = '#%s' % anonNumber
|
||||
#warn('FIELD: %s' % fieldName)
|
||||
#DumperBase.warn('FIELD: %s' % fieldName)
|
||||
# hasattr(nativeField, 'bitpos') == False indicates a static field,
|
||||
# but if we have access to a nativeValue .fromNativeField will
|
||||
# also succeed. We essentially skip only static members from
|
||||
@@ -531,8 +531,8 @@ class Dumper(DumperBase):
|
||||
|
||||
def fromNativeField(self, nativeField, nativeValue, fieldName):
|
||||
nativeFieldType = nativeField.type.unqualified()
|
||||
#warn(' TYPE: %s' % nativeFieldType)
|
||||
#warn(' TYPEID: %s' % self.nativeTypeId(nativeFieldType))
|
||||
#DumperBase.warn(' TYPE: %s' % nativeFieldType)
|
||||
#DumperBase.warn(' TYPEID: %s' % self.nativeTypeId(nativeFieldType))
|
||||
|
||||
if hasattr(nativeField, 'bitpos'):
|
||||
bitpos = nativeField.bitpos
|
||||
@@ -562,7 +562,7 @@ class Dumper(DumperBase):
|
||||
capturedFieldName,
|
||||
value)
|
||||
|
||||
#warn("FOUND NATIVE FIELD: %s bitpos: %s" % (fieldName, bitpos))
|
||||
#DumperBase.warn("FOUND NATIVE FIELD: %s bitpos: %s" % (fieldName, bitpos))
|
||||
return self.Field(dumper=self, name=fieldName, isBase=nativeField.is_base_class,
|
||||
bitsize=bitsize, bitpos=bitpos, type=fieldType,
|
||||
extractor=extractor)
|
||||
@@ -574,19 +574,19 @@ class Dumper(DumperBase):
|
||||
|
||||
try:
|
||||
block = frame.block()
|
||||
#warn('BLOCK: %s ' % block)
|
||||
#DumperBase.warn('BLOCK: %s ' % block)
|
||||
except RuntimeError as error:
|
||||
#warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error)
|
||||
#DumperBase.warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error)
|
||||
return []
|
||||
except:
|
||||
warn('BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS')
|
||||
self.warn('BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS')
|
||||
return []
|
||||
|
||||
items = []
|
||||
shadowed = {}
|
||||
while True:
|
||||
if block is None:
|
||||
warn("UNEXPECTED 'None' BLOCK")
|
||||
self.warn("UNEXPECTED 'None' BLOCK")
|
||||
break
|
||||
for symbol in block:
|
||||
|
||||
@@ -602,12 +602,12 @@ class Dumper(DumperBase):
|
||||
|
||||
# 'NotImplementedError: Symbol type not yet supported in
|
||||
# Python scripts.'
|
||||
#warn('SYMBOL %s (%s, %s)): ' % (symbol, name, symbol.name))
|
||||
#DumperBase.warn('SYMBOL %s (%s, %s)): ' % (symbol, name, symbol.name))
|
||||
if self.passExceptions and not self.isTesting:
|
||||
nativeValue = frame.read_var(name, block)
|
||||
value = self.fromFrameValue(nativeValue)
|
||||
value.name = name
|
||||
#warn('READ 0: %s' % value.stringify())
|
||||
#DumperBase.warn('READ 0: %s' % value.stringify())
|
||||
items.append(value)
|
||||
continue
|
||||
|
||||
@@ -616,14 +616,14 @@ class Dumper(DumperBase):
|
||||
nativeValue = frame.read_var(name, block)
|
||||
value = self.fromFrameValue(nativeValue)
|
||||
value.name = name
|
||||
#warn('READ 1: %s' % value.stringify())
|
||||
#DumperBase.warn('READ 1: %s' % value.stringify())
|
||||
items.append(value)
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
#warn('READ 2: %s' % item.value)
|
||||
#DumperBase.warn('READ 2: %s' % item.value)
|
||||
value = self.fromFrameValue(frame.read_var(name))
|
||||
value.name = name
|
||||
items.append(value)
|
||||
@@ -637,8 +637,8 @@ class Dumper(DumperBase):
|
||||
pass
|
||||
|
||||
try:
|
||||
#warn('READ 3: %s %s' % (name, item.value))
|
||||
#warn('ITEM 3: %s' % item.value)
|
||||
#DumperBase.warn('READ 3: %s %s' % (name, item.value))
|
||||
#DumperBase.warn('ITEM 3: %s' % item.value)
|
||||
value = self.fromFrameValue(gdb.parse_and_eval(name))
|
||||
value.name = name
|
||||
items.append(value)
|
||||
@@ -685,7 +685,7 @@ class Dumper(DumperBase):
|
||||
partialName = partialVar.split('.')[1].split('@')[0] if isPartial else None
|
||||
|
||||
variables = self.listLocals(partialName)
|
||||
#warn('VARIABLES: %s' % variables)
|
||||
#DumperBase.warn('VARIABLES: %s' % variables)
|
||||
|
||||
# Take care of the return value of the last function call.
|
||||
if len(self.resultVarName) > 0:
|
||||
@@ -728,13 +728,13 @@ class Dumper(DumperBase):
|
||||
return None if val is None else self.fromNativeValue(val)
|
||||
|
||||
def nativeParseAndEvaluate(self, exp):
|
||||
#warn('EVALUATE "%s"' % exp)
|
||||
#DumperBase.warn('EVALUATE "%s"' % exp)
|
||||
try:
|
||||
val = gdb.parse_and_eval(exp)
|
||||
return val
|
||||
except RuntimeError as error:
|
||||
if self.passExceptions:
|
||||
warn("Cannot evaluate '%s': %s" % (exp, error))
|
||||
self.warn("Cannot evaluate '%s': %s" % (exp, error))
|
||||
return None
|
||||
|
||||
def callHelper(self, rettype, value, function, args):
|
||||
@@ -749,7 +749,7 @@ class Dumper(DumperBase):
|
||||
else:
|
||||
arg += a
|
||||
|
||||
#warn('CALL: %s -> %s(%s)' % (value, function, arg))
|
||||
#DumperBase.warn('CALL: %s -> %s(%s)' % (value, function, arg))
|
||||
typeName = value.type.name
|
||||
if typeName.find(':') >= 0:
|
||||
typeName = "'" + typeName + "'"
|
||||
@@ -758,11 +758,11 @@ class Dumper(DumperBase):
|
||||
addr = value.address()
|
||||
if addr is None:
|
||||
addr = self.pokeValue(value)
|
||||
#warn('PTR: %s -> %s(%s)' % (value, function, addr))
|
||||
#DumperBase.warn('PTR: %s -> %s(%s)' % (value, function, addr))
|
||||
exp = '((%s*)0x%x)->%s(%s)' % (typeName, addr, function, arg)
|
||||
#warn('CALL: %s' % exp)
|
||||
#DumperBase.warn('CALL: %s' % exp)
|
||||
result = gdb.parse_and_eval(exp)
|
||||
#warn(' -> %s' % result)
|
||||
#DumperBase.warn(' -> %s' % result)
|
||||
res = self.fromNativeValue(result)
|
||||
if value.address() is None:
|
||||
self.releaseValue(addr)
|
||||
@@ -770,9 +770,9 @@ class Dumper(DumperBase):
|
||||
|
||||
def makeExpression(self, value):
|
||||
typename = '::' + value.type.name
|
||||
#warn(' TYPE: %s' % typename)
|
||||
#DumperBase.warn(' TYPE: %s' % typename)
|
||||
exp = '(*(%s*)(0x%x))' % (typename, value.address())
|
||||
#warn(' EXP: %s' % exp)
|
||||
#DumperBase.warn(' EXP: %s' % exp)
|
||||
return exp
|
||||
|
||||
def makeStdString(init):
|
||||
@@ -790,13 +790,13 @@ class Dumper(DumperBase):
|
||||
size = value.type.size()
|
||||
data = value.data()
|
||||
h = self.hexencode(data)
|
||||
#warn('DATA: %s' % h)
|
||||
#DumperBase.warn('DATA: %s' % h)
|
||||
string = ''.join('\\x' + h[2*i:2*i+2] for i in range(size))
|
||||
exp = '(%s*)memcpy(calloc(%d, 1), "%s", %d)' \
|
||||
% (value.type.name, size, string, size)
|
||||
#warn('EXP: %s' % exp)
|
||||
#DumperBase.warn('EXP: %s' % exp)
|
||||
res = gdb.parse_and_eval(exp)
|
||||
#warn('RES: %s' % res)
|
||||
#DumperBase.warn('RES: %s' % res)
|
||||
return toInteger(res)
|
||||
|
||||
def releaseValue(self, address):
|
||||
@@ -824,7 +824,7 @@ class Dumper(DumperBase):
|
||||
return self.cachedInferior
|
||||
|
||||
def readRawMemory(self, address, size):
|
||||
#warn('READ: %s FROM 0x%x' % (size, address))
|
||||
#DumperBase.warn('READ: %s FROM 0x%x' % (size, address))
|
||||
if address == 0 or size == 0:
|
||||
return bytes()
|
||||
res = self.selectedInferior().read_memory(address, size)
|
||||
@@ -1185,7 +1185,7 @@ class Dumper(DumperBase):
|
||||
|
||||
def lookupNativeTypeHelper(self, typeName):
|
||||
typeobj = self.typeCache.get(typeName)
|
||||
#warn('LOOKUP 1: %s -> %s' % (typeName, typeobj))
|
||||
#DumperBase.warn('LOOKUP 1: %s -> %s' % (typeName, typeobj))
|
||||
if not typeobj is None:
|
||||
return typeobj
|
||||
|
||||
@@ -1217,7 +1217,7 @@ class Dumper(DumperBase):
|
||||
self.typesToReport[typeName] = typeobj
|
||||
return typeobj
|
||||
|
||||
#warn(" RESULT FOR 7.2: '%s': %s" % (typeName, typeobj))
|
||||
#DumperBase.warn(" RESULT FOR 7.2: '%s': %s" % (typeName, typeobj))
|
||||
|
||||
# This part should only trigger for
|
||||
# gdb 7.1 for types with namespace separators.
|
||||
@@ -1255,24 +1255,24 @@ class Dumper(DumperBase):
|
||||
return typeobj
|
||||
|
||||
try:
|
||||
#warn("LOOKING UP 1 '%s'" % ts)
|
||||
#DumperBase.warn("LOOKING UP 1 '%s'" % ts)
|
||||
typeobj = gdb.lookup_type(ts)
|
||||
except RuntimeError as error:
|
||||
#warn("LOOKING UP 2 '%s' ERROR %s" % (ts, error))
|
||||
#DumperBase.warn("LOOKING UP 2 '%s' ERROR %s" % (ts, error))
|
||||
# See http://sourceware.org/bugzilla/show_bug.cgi?id=11912
|
||||
exp = "(class '%s'*)0" % ts
|
||||
try:
|
||||
typeobj = self.parse_and_eval(exp).type.target()
|
||||
#warn("LOOKING UP 3 '%s'" % typeobj)
|
||||
#DumperBase.warn("LOOKING UP 3 '%s'" % typeobj)
|
||||
except:
|
||||
# Can throw 'RuntimeError: No type named class Foo.'
|
||||
pass
|
||||
except:
|
||||
#warn("LOOKING UP '%s' FAILED" % ts)
|
||||
#DumperBase.warn("LOOKING UP '%s' FAILED" % ts)
|
||||
pass
|
||||
|
||||
if not typeobj is None:
|
||||
#warn('CACHING: %s' % typeobj)
|
||||
#DumperBase.warn('CACHING: %s' % typeobj)
|
||||
self.typeCache[typeName] = typeobj
|
||||
self.typesToReport[typeName] = typeobj
|
||||
|
||||
|
@@ -31,6 +31,8 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
import lldb
|
||||
import utils
|
||||
from utils import DebuggerStartMode, BreakpointType, TypeCode
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
@@ -40,7 +42,7 @@ sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.curre
|
||||
if 'dumper' in sys.modules:
|
||||
reload(sys.modules['dumper'])
|
||||
|
||||
from dumper import *
|
||||
from dumper import DumperBase, SubItem, Children, TopLevelItem
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
@@ -154,23 +156,23 @@ class Dumper(DumperBase):
|
||||
targetType = self.fromNativeType(nativeTargetType)
|
||||
val = self.createReferenceValue(nativeValue.GetValueAsUnsigned(), targetType)
|
||||
val.laddress = nativeValue.AddressOf().GetValueAsUnsigned()
|
||||
#warn('CREATED REF: %s' % val)
|
||||
#DumperBase.warn('CREATED REF: %s' % val)
|
||||
elif code == lldb.eTypeClassPointer:
|
||||
nativeTargetType = nativeType.GetPointeeType()
|
||||
if not nativeTargetType.IsPointerType():
|
||||
nativeTargetType = nativeTargetType.GetUnqualifiedType()
|
||||
targetType = self.fromNativeType(nativeTargetType)
|
||||
val = self.createPointerValue(nativeValue.GetValueAsUnsigned(), targetType)
|
||||
#warn('CREATED PTR 1: %s' % val)
|
||||
#DumperBase.warn('CREATED PTR 1: %s' % val)
|
||||
val.laddress = nativeValue.AddressOf().GetValueAsUnsigned()
|
||||
#warn('CREATED PTR 2: %s' % val)
|
||||
#DumperBase.warn('CREATED PTR 2: %s' % val)
|
||||
elif code == lldb.eTypeClassTypedef:
|
||||
nativeTargetType = nativeType.GetUnqualifiedType()
|
||||
if hasattr(nativeTargetType, 'GetCanonicalType'):
|
||||
nativeTargetType = nativeTargetType.GetCanonicalType()
|
||||
val = self.fromNativeValue(nativeValue.Cast(nativeTargetType))
|
||||
val.type = self.fromNativeType(nativeType)
|
||||
#warn('CREATED TYPEDEF: %s' % val)
|
||||
#DumperBase.warn('CREATED TYPEDEF: %s' % val)
|
||||
else:
|
||||
val = self.Value(self)
|
||||
address = nativeValue.GetLoadAddress()
|
||||
@@ -233,7 +235,7 @@ class Dumper(DumperBase):
|
||||
return align
|
||||
|
||||
def listMembers(self, value, nativeType):
|
||||
#warn("ADDR: 0x%x" % self.fakeAddress)
|
||||
#DumperBase.warn("ADDR: 0x%x" % self.fakeAddress)
|
||||
fakeAddress = self.fakeAddress if value.laddress is None else value.laddress
|
||||
sbaddr = lldb.SBAddress(fakeAddress, self.target)
|
||||
fakeValue = self.target.CreateValueFromAddress('x', sbaddr, nativeType)
|
||||
@@ -359,8 +361,8 @@ class Dumper(DumperBase):
|
||||
# // Define a mask that can be used for any type when finding types
|
||||
# eTypeClassAny = (0xffffffffu)
|
||||
|
||||
#warn('CURRENT: %s' % self.typeData.keys())
|
||||
#warn('FROM NATIVE TYPE: %s' % nativeType.GetName())
|
||||
#DumperBase.warn('CURRENT: %s' % self.typeData.keys())
|
||||
#DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType.GetName())
|
||||
if code == lldb.eTypeClassInvalid:
|
||||
return None
|
||||
|
||||
@@ -368,23 +370,23 @@ class Dumper(DumperBase):
|
||||
nativeType = nativeType.GetUnqualifiedType()
|
||||
|
||||
if code == lldb.eTypeClassPointer:
|
||||
#warn('PTR')
|
||||
#DumperBase.warn('PTR')
|
||||
nativeTargetType = nativeType.GetPointeeType()
|
||||
if not nativeTargetType.IsPointerType():
|
||||
nativeTargetType = nativeTargetType.GetUnqualifiedType()
|
||||
#warn('PTR: %s' % nativeTargetType.name)
|
||||
#DumperBase.warn('PTR: %s' % nativeTargetType.name)
|
||||
return self.createPointerType(self.fromNativeType(nativeTargetType))
|
||||
|
||||
if code == lldb.eTypeClassReference:
|
||||
#warn('REF')
|
||||
#DumperBase.warn('REF')
|
||||
nativeTargetType = nativeType.GetDereferencedType()
|
||||
if not nativeTargetType.IsPointerType():
|
||||
nativeTargetType = nativeTargetType.GetUnqualifiedType()
|
||||
#warn('REF: %s' % nativeTargetType.name)
|
||||
#DumperBase.warn('REF: %s' % nativeTargetType.name)
|
||||
return self.createReferenceType(self.fromNativeType(nativeTargetType))
|
||||
|
||||
if code == lldb.eTypeClassTypedef:
|
||||
#warn('TYPEDEF')
|
||||
#DumperBase.warn('TYPEDEF')
|
||||
nativeTargetType = nativeType.GetUnqualifiedType()
|
||||
if hasattr(nativeTargetType, 'GetCanonicalType'):
|
||||
nativeTargetType = nativeTargetType.GetCanonicalType()
|
||||
@@ -395,12 +397,12 @@ class Dumper(DumperBase):
|
||||
typeName = self.typeName(nativeType)
|
||||
|
||||
if code in (lldb.eTypeClassArray, lldb.eTypeClassVector):
|
||||
#warn('ARRAY: %s' % nativeType.GetName())
|
||||
#DumperBase.warn('ARRAY: %s' % nativeType.GetName())
|
||||
if hasattr(nativeType, 'GetArrayElementType'): # New in 3.8(?) / 350.x
|
||||
nativeTargetType = nativeType.GetArrayElementType()
|
||||
if not nativeTargetType.IsValid():
|
||||
if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x
|
||||
#warn('BAD: %s ' % nativeTargetType.get_fields_array())
|
||||
#DumperBase.warn('BAD: %s ' % nativeTargetType.get_fields_array())
|
||||
nativeTargetType = nativeType.GetVectorElementType()
|
||||
count = nativeType.GetByteSize() // nativeTargetType.GetByteSize()
|
||||
targetTypeName = nativeTargetType.GetName()
|
||||
@@ -408,7 +410,7 @@ class Dumper(DumperBase):
|
||||
typeName = nativeType.GetName()
|
||||
pos1 = typeName.rfind('[')
|
||||
targetTypeName = typeName[0:pos1].strip()
|
||||
#warn("TARGET TYPENAME: %s" % targetTypeName)
|
||||
#DumperBase.warn("TARGET TYPENAME: %s" % targetTypeName)
|
||||
targetType = self.fromNativeType(nativeTargetType)
|
||||
tdata = targetType.typeData().copy()
|
||||
tdata.name = targetTypeName
|
||||
@@ -431,33 +433,33 @@ class Dumper(DumperBase):
|
||||
tdata.name = typeName
|
||||
tdata.lbitsize = nativeType.GetByteSize() * 8
|
||||
if code == lldb.eTypeClassBuiltin:
|
||||
if isFloatingPointTypeName(typeName):
|
||||
tdata.code = TypeCodeFloat
|
||||
elif isIntegralTypeName(typeName):
|
||||
tdata.code = TypeCodeIntegral
|
||||
if utils.isFloatingPointTypeName(typeName):
|
||||
tdata.code = TypeCode.TypeCodeFloat
|
||||
elif utils.isIntegralTypeName(typeName):
|
||||
tdata.code = TypeCode.TypeCodeIntegral
|
||||
elif typeName in ('__int128', 'unsigned __int128'):
|
||||
tdata.code = TypeCodeIntegral
|
||||
tdata.code = TypeCode.TypeCodeIntegral
|
||||
elif typeName == 'void':
|
||||
tdata.code = TypeCodeVoid
|
||||
tdata.code = TypeCode.TypeCodeVoid
|
||||
else:
|
||||
warn('UNKNOWN TYPE KEY: %s: %s' % (typeName, code))
|
||||
self.warn('UNKNOWN TYPE KEY: %s: %s' % (typeName, code))
|
||||
elif code == lldb.eTypeClassEnumeration:
|
||||
tdata.code = TypeCodeEnum
|
||||
tdata.code = TypeCode.TypeCodeEnum
|
||||
tdata.enumDisplay = lambda intval, addr, form : \
|
||||
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
||||
elif code in (lldb.eTypeClassComplexInteger, lldb.eTypeClassComplexFloat):
|
||||
tdata.code = TypeCodeComplex
|
||||
tdata.code = TypeCode.TypeCodeComplex
|
||||
elif code in (lldb.eTypeClassClass, lldb.eTypeClassStruct, lldb.eTypeClassUnion):
|
||||
tdata.code = TypeCodeStruct
|
||||
tdata.code = TypeCode.TypeCodeStruct
|
||||
tdata.lalignment = lambda : \
|
||||
self.nativeStructAlignment(nativeType)
|
||||
tdata.lfields = lambda value : \
|
||||
self.listMembers(value, nativeType)
|
||||
tdata.templateArguments = self.listTemplateParametersHelper(nativeType)
|
||||
elif code == lldb.eTypeClassFunction:
|
||||
tdata.code = TypeCodeFunction
|
||||
tdata.code = TypeCode.TypeCodeFunction
|
||||
elif code == lldb.eTypeClassMemberPointer:
|
||||
tdata.code = TypeCodeMemberPointer
|
||||
tdata.code = TypeCode.TypeCodeMemberPointer
|
||||
|
||||
self.registerType(typeId, tdata) # Fix up fields and template args
|
||||
# warn('CREATE TYPE: %s' % typeId)
|
||||
@@ -491,12 +493,12 @@ class Dumper(DumperBase):
|
||||
targs.append(self.fromNativeType(innerType))
|
||||
#elif kind == lldb.eTemplateArgumentKindIntegral:
|
||||
# innerType = nativeType.GetTemplateArgumentType(i).GetUnqualifiedType().GetCanonicalType()
|
||||
# #warn('INNER TYP: %s' % innerType)
|
||||
# #DumperBase.warn('INNER TYP: %s' % innerType)
|
||||
# basicType = innerType.GetBasicType()
|
||||
# #warn('IBASIC TYP: %s' % basicType)
|
||||
# #DumperBase.warn('IBASIC TYP: %s' % basicType)
|
||||
# inner = self.extractTemplateArgument(nativeType.GetName(), i)
|
||||
# exp = '(%s)%s' % (innerType.GetName(), inner)
|
||||
# #warn('EXP : %s' % exp)
|
||||
# #DumperBase.warn('EXP : %s' % exp)
|
||||
# val = self.nativeParseAndEvaluate('(%s)%s' % (innerType.GetName(), inner))
|
||||
# # Clang writes 'int' and '0xfffffff' into the debug info
|
||||
# # LLDB manages to read a value of 0xfffffff...
|
||||
@@ -504,12 +506,12 @@ class Dumper(DumperBase):
|
||||
# value = val.GetValueAsUnsigned()
|
||||
# if value >= 0x8000000:
|
||||
# value -= 0x100000000
|
||||
# #warn('KIND: %s' % kind)
|
||||
# #DumperBase.warn('KIND: %s' % kind)
|
||||
# targs.append(value)
|
||||
else:
|
||||
#warn('UNHANDLED TEMPLATE TYPE : %s' % kind)
|
||||
#DumperBase.warn('UNHANDLED TEMPLATE TYPE : %s' % kind)
|
||||
targs.append(stringArgs[i]) # Best we can do.
|
||||
#warn('TARGS: %s %s' % (nativeType.GetName(), [str(x) for x in targs]))
|
||||
#DumperBase.warn('TARGS: %s %s' % (nativeType.GetName(), [str(x) for x in targs]))
|
||||
return targs
|
||||
|
||||
def typeName(self, nativeType):
|
||||
@@ -529,7 +531,7 @@ class Dumper(DumperBase):
|
||||
return name
|
||||
fields = nativeType.get_fields_array()
|
||||
typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.GetType())) for f in fields])
|
||||
#warn('NATIVE TYPE ID FOR %s IS %s' % (name, typeId))
|
||||
#DumperBase.warn('NATIVE TYPE ID FOR %s IS %s' % (name, typeId))
|
||||
return typeId
|
||||
|
||||
def nativeTypeEnumDisplay(self, nativeType, intval, form):
|
||||
@@ -614,12 +616,12 @@ class Dumper(DumperBase):
|
||||
def callHelper(self, rettype, value, func, args):
|
||||
# args is a tuple.
|
||||
arg = ','.join(args)
|
||||
#warn('PRECALL: %s -> %s(%s)' % (value.address(), func, arg))
|
||||
#DumperBase.warn('PRECALL: %s -> %s(%s)' % (value.address(), func, arg))
|
||||
typename = value.type.name
|
||||
exp = '((%s*)0x%x)->%s(%s)' % (typename, value.address(), func, arg)
|
||||
#warn('CALL: %s' % exp)
|
||||
#DumperBase.warn('CALL: %s' % exp)
|
||||
result = self.currentContextValue.CreateValueFromExpression('', exp)
|
||||
#warn(' -> %s' % result)
|
||||
#DumperBase.warn(' -> %s' % result)
|
||||
return self.fromNativeValue(result)
|
||||
|
||||
def pokeValue(self, typeName, *args):
|
||||
@@ -627,9 +629,9 @@ class Dumper(DumperBase):
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
inner = ','.join(args)
|
||||
value = frame.EvaluateExpression(typeName + '{' + inner + '}')
|
||||
#self.warn(' TYPE: %s' % value.type)
|
||||
#self.warn(' ADDR: 0x%x' % value.address)
|
||||
#self.warn(' VALUE: %s' % value)
|
||||
#DumperBase.warn(' TYPE: %s' % value.type)
|
||||
#DumperBase.warn(' ADDR: 0x%x' % value.address)
|
||||
#DumperBase.warn(' VALUE: %s' % value)
|
||||
return value
|
||||
|
||||
def nativeParseAndEvaluate(self, exp):
|
||||
@@ -640,10 +642,10 @@ class Dumper(DumperBase):
|
||||
#val = self.target.EvaluateExpression(exp, options)
|
||||
err = val.GetError()
|
||||
if err.Fail():
|
||||
#warn('FAILING TO EVAL: %s' % exp)
|
||||
#DumperBase.warn('FAILING TO EVAL: %s' % exp)
|
||||
return None
|
||||
#warn('NO ERROR.')
|
||||
#warn('EVAL: %s -> %s' % (exp, val.IsValid()))
|
||||
#DumperBase.warn('NO ERROR.')
|
||||
#DumperBase.warn('EVAL: %s -> %s' % (exp, val.IsValid()))
|
||||
return val
|
||||
|
||||
def parseAndEvaluate(self, exp):
|
||||
@@ -749,14 +751,14 @@ class Dumper(DumperBase):
|
||||
return re.sub('^(struct|class|union|enum|typedef) ', '', name)
|
||||
|
||||
def lookupNativeType(self, name):
|
||||
#warn('LOOKUP TYPE NAME: %s' % name)
|
||||
#DumperBase.warn('LOOKUP TYPE NAME: %s' % name)
|
||||
typeobj = self.typeCache.get(name)
|
||||
if not typeobj is None:
|
||||
#warn('CACHED: %s' % name)
|
||||
#DumperBase.warn('CACHED: %s' % name)
|
||||
return typeobj
|
||||
typeobj = self.target.FindFirstType(name)
|
||||
if typeobj.IsValid():
|
||||
#warn('VALID FIRST : %s' % typeobj)
|
||||
#DumperBase.warn('VALID FIRST : %s' % typeobj)
|
||||
self.typeCache[name] = typeobj
|
||||
return typeobj
|
||||
|
||||
@@ -773,16 +775,16 @@ class Dumper(DumperBase):
|
||||
for typeobj in typeobjlist:
|
||||
n = self.canonicalTypeName(self.removeTypePrefix(typeobj.GetDisplayTypeName()))
|
||||
if n == nonPrefixedName:
|
||||
#warn('FOUND TYPE USING FindTypes : %s' % typeobj)
|
||||
#DumperBase.warn('FOUND TYPE USING FindTypes : %s' % typeobj)
|
||||
self.typeCache[name] = typeobj
|
||||
return typeobj
|
||||
if name.endswith('*'):
|
||||
#warn('RECURSE PTR')
|
||||
#DumperBase.warn('RECURSE PTR')
|
||||
typeobj = self.lookupNativeType(name[:-1].strip())
|
||||
if typeobj is not None:
|
||||
#warn('RECURSE RESULT X: %s' % typeobj)
|
||||
#DumperBase.warn('RECURSE RESULT X: %s' % typeobj)
|
||||
self.fromNativeType(typeobj.GetPointerType())
|
||||
#warn('RECURSE RESULT: %s' % typeobj.GetPointerType())
|
||||
#DumperBase.warn('RECURSE RESULT: %s' % typeobj.GetPointerType())
|
||||
return typeobj.GetPointerType()
|
||||
|
||||
#typeobj = self.target.FindFirstType(name[:-1].strip())
|
||||
@@ -791,13 +793,13 @@ class Dumper(DumperBase):
|
||||
# return typeobj.GetPointerType()
|
||||
|
||||
if name.endswith(' const'):
|
||||
#warn('LOOKUP END CONST')
|
||||
#DumperBase.warn('LOOKUP END CONST')
|
||||
typeobj = self.lookupNativeType(name[:-6])
|
||||
if typeobj is not None:
|
||||
return typeobj
|
||||
|
||||
if name.startswith('const '):
|
||||
#warn('LOOKUP START CONST')
|
||||
#DumperBase.warn('LOOKUP START CONST')
|
||||
typeobj = self.lookupNativeType(name[6:])
|
||||
if typeobj is not None:
|
||||
return typeobj
|
||||
@@ -806,17 +808,17 @@ class Dumper(DumperBase):
|
||||
|
||||
def lookupNativeTypeInAllModules(self, name):
|
||||
needle = self.canonicalTypeName(name)
|
||||
#warn('NEEDLE: %s ' % needle)
|
||||
warn('Searching for type %s across all target modules, this could be very slow' % name)
|
||||
#DumperBase.warn('NEEDLE: %s ' % needle)
|
||||
self.warn('Searching for type %s across all target modules, this could be very slow' % name)
|
||||
for i in xrange(self.target.GetNumModules()):
|
||||
module = self.target.GetModuleAtIndex(i)
|
||||
# SBModule.GetType is new somewhere after early 300.x
|
||||
# So this may fail.
|
||||
for t in module.GetTypes():
|
||||
n = self.canonicalTypeName(t.GetName())
|
||||
#warn('N: %s' % n)
|
||||
#DumperBase.warn('N: %s' % n)
|
||||
if n == needle:
|
||||
#warn('FOUND TYPE DIRECT 2: %s ' % t)
|
||||
#DumperBase.warn('FOUND TYPE DIRECT 2: %s ' % t)
|
||||
self.typeCache[name] = t
|
||||
return t
|
||||
if n == needle + '*':
|
||||
@@ -824,16 +826,16 @@ class Dumper(DumperBase):
|
||||
self.typeCache[name] = res
|
||||
x = self.fromNativeType(res) # Register under both names
|
||||
self.registerTypeAlias(x.typeId, name)
|
||||
#warn('FOUND TYPE BY POINTER: %s ' % res.name)
|
||||
#DumperBase.warn('FOUND TYPE BY POINTER: %s ' % res.name)
|
||||
return res
|
||||
if n == needle + '&':
|
||||
res = t.GetDereferencedType().GetUnqualifiedType()
|
||||
self.typeCache[name] = res
|
||||
x = self.fromNativeType(res) # Register under both names
|
||||
self.registerTypeAlias(x.typeId, name)
|
||||
#warn('FOUND TYPE BY REFERENCE: %s ' % res.name)
|
||||
#DumperBase.warn('FOUND TYPE BY REFERENCE: %s ' % res.name)
|
||||
return res
|
||||
#warn('NOT FOUND: %s ' % needle)
|
||||
#DumperBase.warn('NOT FOUND: %s ' % needle)
|
||||
return None
|
||||
|
||||
def setupInferior(self, args):
|
||||
@@ -861,7 +863,7 @@ class Dumper(DumperBase):
|
||||
|
||||
self.ignoreStops = 0
|
||||
if platform.system() == 'Linux':
|
||||
if self.startMode_ == AttachCore:
|
||||
if self.startMode_ == DebuggerStartMode.AttachCore:
|
||||
pass
|
||||
else:
|
||||
if self.useTerminal_:
|
||||
@@ -911,7 +913,8 @@ class Dumper(DumperBase):
|
||||
self.reportState('enginerunandinferiorstopok')
|
||||
else:
|
||||
self.reportState('enginerunandinferiorrunok')
|
||||
elif self.startMode_ == AttachToRemoteServer or self.startMode_ == AttachToRemoteProcess:
|
||||
elif (self.startMode_ == DebuggerStartMode.AttachToRemoteServer
|
||||
or self.startMode_ == DebuggerStartMode.AttachToRemoteProcess):
|
||||
self.process = self.target.ConnectRemote(
|
||||
self.debugger.GetListener(),
|
||||
self.remoteChannel_, None, error)
|
||||
@@ -923,7 +926,7 @@ class Dumper(DumperBase):
|
||||
# and later detects that it did stop after all, so it is be
|
||||
# better to mirror that and wait for the spontaneous stop.
|
||||
self.reportState('enginerunandinferiorrunok')
|
||||
elif self.startMode_ == AttachCore:
|
||||
elif self.startMode_ == DebuggerStartMode.AttachCore:
|
||||
coreFile = args.get('coreFile', '');
|
||||
self.process = self.target.LoadCore(coreFile)
|
||||
if self.process.IsValid():
|
||||
@@ -1124,7 +1127,7 @@ class Dumper(DumperBase):
|
||||
if size == 0:
|
||||
return bytes()
|
||||
error = lldb.SBError()
|
||||
#warn("READ: %s %s" % (address, size))
|
||||
#DumperBase.warn("READ: %s %s" % (address, size))
|
||||
res = self.process.ReadMemory(address, size, error)
|
||||
if res is None or len(res) != size:
|
||||
# Using code in e.g. readToFirstZero relies on exceptions.
|
||||
@@ -1307,12 +1310,12 @@ class Dumper(DumperBase):
|
||||
self.handleBreakpointEvent(event)
|
||||
return
|
||||
if not lldb.SBProcess.EventIsProcessEvent(event):
|
||||
warn("UNEXPECTED event (%s)" % event.GetType())
|
||||
self.warn("UNEXPECTED event (%s)" % event.GetType())
|
||||
return
|
||||
|
||||
out = lldb.SBStream()
|
||||
event.GetDescription(out)
|
||||
#warn("EVENT: %s" % event)
|
||||
#DumperBase.warn("EVENT: %s" % event)
|
||||
eventType = event.GetType()
|
||||
msg = lldb.SBEvent.GetCStringFromEvent(event)
|
||||
flavor = event.GetDataFlavor()
|
||||
@@ -1430,7 +1433,7 @@ class Dumper(DumperBase):
|
||||
|
||||
def insertBreakpoint(self, args):
|
||||
bpType = args['type']
|
||||
if bpType == BreakpointByFileAndLine:
|
||||
if bpType == BreakpointType.BreakpointByFileAndLine:
|
||||
fileName = args['file']
|
||||
if fileName.endswith('.js') or fileName.endswith('.qml'):
|
||||
self.insertInterpreterBreakpoint(args)
|
||||
@@ -1438,28 +1441,28 @@ class Dumper(DumperBase):
|
||||
|
||||
extra = ''
|
||||
more = True
|
||||
if bpType == BreakpointByFileAndLine:
|
||||
if bpType == BreakpointType.BreakpointByFileAndLine:
|
||||
bp = self.target.BreakpointCreateByLocation(
|
||||
str(args['file']), int(args['line']))
|
||||
elif bpType == BreakpointByFunction:
|
||||
elif bpType == BreakpointType.BreakpointByFunction:
|
||||
bp = self.target.BreakpointCreateByName(args['function'])
|
||||
elif bpType == BreakpointByAddress:
|
||||
elif bpType == BreakpointType.BreakpointByAddress:
|
||||
bp = self.target.BreakpointCreateByAddress(args['address'])
|
||||
elif bpType == BreakpointAtMain:
|
||||
elif bpType == BreakpointType.BreakpointAtMain:
|
||||
bp = self.createBreakpointAtMain()
|
||||
elif bpType == BreakpointAtThrow:
|
||||
elif bpType == BreakpointType.BreakpointAtThrow:
|
||||
bp = self.target.BreakpointCreateForException(
|
||||
lldb.eLanguageTypeC_plus_plus, False, True)
|
||||
elif bpType == BreakpointAtCatch:
|
||||
elif bpType == BreakpointType.BreakpointAtCatch:
|
||||
bp = self.target.BreakpointCreateForException(
|
||||
lldb.eLanguageTypeC_plus_plus, True, False)
|
||||
elif bpType == WatchpointAtAddress:
|
||||
elif bpType == BreakpointType.WatchpointAtAddress:
|
||||
error = lldb.SBError()
|
||||
# This might yield bp.IsValid() == False and
|
||||
# error.desc == 'process is not alive'.
|
||||
bp = self.target.WatchAddress(args['address'], 4, False, True, error)
|
||||
extra = self.describeError(error)
|
||||
elif bpType == WatchpointAtExpression:
|
||||
elif bpType == BreakpointType.WatchpointAtExpression:
|
||||
# FIXME: Top level-only for now.
|
||||
try:
|
||||
frame = self.currentFrame()
|
||||
@@ -1702,7 +1705,7 @@ class Dumper(DumperBase):
|
||||
else:
|
||||
base = args.get('address', 0)
|
||||
if int(base) == 0xffffffffffffffff:
|
||||
warn('INVALID DISASSEMBLER BASE')
|
||||
self.warn('INVALID DISASSEMBLER BASE')
|
||||
return
|
||||
addr = lldb.SBAddress(base, self.target)
|
||||
instructions = self.target.ReadInstructions(addr, 100)
|
||||
@@ -1736,7 +1739,7 @@ class Dumper(DumperBase):
|
||||
# With lldb-3.8 files like /data/dev/creator-3.6/tests/
|
||||
# auto/debugger/qt_tst_dumpers_StdVector_bfNWZa/main.cpp
|
||||
# with non-existent directories appear.
|
||||
warn('FILE: %s ERROR: %s' % (fileName, error))
|
||||
self.warn('FILE: %s ERROR: %s' % (fileName, error))
|
||||
source = ''
|
||||
result += '{line="%s"' % lineNumber
|
||||
result += ',file="%s"' % fileName
|
||||
@@ -1836,7 +1839,7 @@ class Tester(Dumper):
|
||||
self.target = self.debugger.CreateTarget(binary, None, None, True, error)
|
||||
|
||||
if error.GetType():
|
||||
warn('ERROR: %s' % error)
|
||||
self.warn('ERROR: %s' % error)
|
||||
return
|
||||
|
||||
s = threading.Thread(target=self.testLoop, args=(args,))
|
||||
@@ -1856,14 +1859,14 @@ class Tester(Dumper):
|
||||
|
||||
self.process = self.target.Launch(launchInfo, error)
|
||||
if error.GetType():
|
||||
warn('ERROR: %s' % error)
|
||||
self.warn('ERROR: %s' % error)
|
||||
|
||||
event = lldb.SBEvent()
|
||||
listener = self.debugger.GetListener()
|
||||
while True:
|
||||
state = self.process.GetState()
|
||||
if listener.WaitForEvent(100, event):
|
||||
#warn('EVENT: %s' % event)
|
||||
#DumperBase.warn('EVENT: %s' % event)
|
||||
state = lldb.SBProcess.GetStateFromEvent(event)
|
||||
if state == lldb.eStateExited: # 10
|
||||
break
|
||||
@@ -1872,7 +1875,7 @@ class Tester(Dumper):
|
||||
for i in xrange(0, self.process.GetNumThreads()):
|
||||
thread = self.process.GetThreadAtIndex(i)
|
||||
reason = thread.GetStopReason()
|
||||
#warn('THREAD: %s REASON: %s' % (thread, reason))
|
||||
#DumperBase.warn('THREAD: %s REASON: %s' % (thread, reason))
|
||||
if (reason == lldb.eStopReasonBreakpoint or
|
||||
reason == lldb.eStopReasonException or
|
||||
reason == lldb.eStopReasonSignal):
|
||||
@@ -1894,8 +1897,8 @@ class Tester(Dumper):
|
||||
break
|
||||
|
||||
else:
|
||||
warn('TIMEOUT')
|
||||
warn('Cannot determined stopped thread')
|
||||
self.warn('TIMEOUT')
|
||||
self.warn('Cannot determined stopped thread')
|
||||
|
||||
lldb.SBDebugger.Destroy(self.debugger)
|
||||
|
||||
@@ -1981,7 +1984,7 @@ class SummaryDumper(Dumper, LogMixin):
|
||||
return # Don't mess up lldb output
|
||||
|
||||
def lookupNativeTypeInAllModules(self, name):
|
||||
warn('Failed to resolve type %s' % name)
|
||||
self.warn('Failed to resolve type %s' % name)
|
||||
return None
|
||||
|
||||
def dump_summary(self, valobj, expanded = False):
|
||||
|
@@ -23,7 +23,8 @@
|
||||
#
|
||||
############################################################################
|
||||
|
||||
from dumper import *
|
||||
from dumper import Children, SubItem
|
||||
from utils import TypeCode, DisplayFormat
|
||||
import re
|
||||
|
||||
#######################################################################
|
||||
@@ -100,7 +101,7 @@ def qdump____m512i(d, value):
|
||||
#######################################################################
|
||||
|
||||
def qform__std__array():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__gsl__span(d, value):
|
||||
size, pointer = value.split('pp')
|
||||
@@ -186,7 +187,7 @@ def qdump__NimStringDesc(d, value):
|
||||
|
||||
def qdump__NimGenericSequence__(d, value, regex = '^TY[\d]+$'):
|
||||
code = value.type.stripTypedefs().code
|
||||
if code == TypeCodeStruct:
|
||||
if code == TypeCode.TypeCodeStruct:
|
||||
size, reserved = d.split('pp', value)
|
||||
data = value.address() + 2 * d.ptrSize()
|
||||
typeobj = value['data'].type.dereference()
|
||||
@@ -322,7 +323,7 @@ def qdump__KDSoapValue1(d, value):
|
||||
d.putPlainChildren(inner)
|
||||
|
||||
def qdump__KDSoapValue(d, value):
|
||||
p = (value.cast(lookupType('char*')) + 4).dereference().cast(lookupType('QString'))
|
||||
p = (value.cast(d.lookupType('char*')) + 4).dereference().cast(d.lookupType('QString'))
|
||||
d.putStringValue(p)
|
||||
d.putPlainChildren(value['d']['d'].dereference())
|
||||
|
||||
|
@@ -23,14 +23,15 @@
|
||||
#
|
||||
############################################################################
|
||||
|
||||
from dumper import *
|
||||
from dumper import Children, SubItem
|
||||
from utils import TypeCode, DisplayFormat
|
||||
|
||||
def qdump__cv__Size_(d, value):
|
||||
d.putValue('(%s, %s)' % (value[0].display(), value[1].display()))
|
||||
d.putPlainChildren(value)
|
||||
|
||||
def qform__cv__Mat():
|
||||
return [SeparateFormat]
|
||||
return [DisplayFormat.SeparateFormat]
|
||||
|
||||
def qdump__cv__Mat(d, value):
|
||||
(flag, dims, rows, cols, data, refcount, datastart, dataend,
|
||||
@@ -43,7 +44,7 @@ def qdump__cv__Mat(d, value):
|
||||
d.putPlainChildren(value)
|
||||
return
|
||||
|
||||
if d.currentItemFormat() == SeparateFormat:
|
||||
if d.currentItemFormat() == DisplayFormat.SeparateFormat:
|
||||
rs = steps[0] * innerSize
|
||||
cs = cols * innerSize
|
||||
dform = 'arraydata:separate:int:%d::2:%d:%d' % (innerSize, cols, rows)
|
||||
@@ -53,7 +54,7 @@ def qdump__cv__Mat(d, value):
|
||||
d.putValue('(%s x %s)' % (rows, cols))
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
innerType = d.createType(TypeCodeIntegral, innerSize)
|
||||
innerType = d.createType(TypeCode.TypeCodeIntegral, innerSize)
|
||||
for i in range(rows):
|
||||
for j in range(cols):
|
||||
with SubItem(d, None):
|
||||
|
@@ -1552,10 +1552,10 @@ class QtcInternalDumper:
|
||||
self.put('name="%s",' % name)
|
||||
|
||||
def isExpanded(self, iname):
|
||||
# self.warn('IS EXPANDED: %s in %s' % (iname, self.expandedINames))
|
||||
# DumperBase.warn('IS EXPANDED: %s in %s' % (iname, self.expandedINames))
|
||||
if iname.startswith('None'):
|
||||
raise "Illegal iname '%s'" % iname
|
||||
# self.warn(' --> %s' % (iname in self.expandedINames))
|
||||
# DumperBase.warn(' --> %s' % (iname in self.expandedINames))
|
||||
return iname in self.expandedINames
|
||||
|
||||
def isExpandedIName(self, iname):
|
||||
|
@@ -56,6 +56,6 @@
|
||||
# for more details or look at qttypes.py, stdtypes.py, boosttypes.py
|
||||
# for more complex examples.
|
||||
|
||||
from dumper import *
|
||||
import dumper
|
||||
|
||||
######################## Your code below #######################
|
||||
|
@@ -25,8 +25,8 @@
|
||||
|
||||
import platform
|
||||
import re
|
||||
from dumper import *
|
||||
|
||||
from dumper import Children, SubItem, UnnamedSubItem, toInteger
|
||||
from utils import DisplayFormat
|
||||
|
||||
def qdump__QAtomicInt(d, value):
|
||||
d.putValue(value.integer())
|
||||
@@ -44,8 +44,8 @@ def qdump__QAtomicPointer(d, value):
|
||||
|
||||
|
||||
def qform__QByteArray():
|
||||
return [Latin1StringFormat, SeparateLatin1StringFormat,
|
||||
Utf8StringFormat, SeparateUtf8StringFormat ]
|
||||
return [DisplayFormat.Latin1StringFormat, DisplayFormat.SeparateLatin1StringFormat,
|
||||
DisplayFormat.Utf8StringFormat, DisplayFormat.SeparateUtf8StringFormat ]
|
||||
|
||||
def qedit__QByteArray(d, value, data):
|
||||
d.call('void', value, 'resize', str(len(data)))
|
||||
@@ -58,14 +58,14 @@ def qdump__QByteArray(d, value):
|
||||
d.putNumChild(size)
|
||||
elided, p = d.encodeByteArrayHelper(d.extractPointer(value), d.displayStringLimit)
|
||||
displayFormat = d.currentItemFormat()
|
||||
if displayFormat == AutomaticFormat or displayFormat == Latin1StringFormat:
|
||||
if displayFormat == DisplayFormat.AutomaticFormat or displayFormat == DisplayFormat.Latin1StringFormat:
|
||||
d.putValue(p, 'latin1', elided=elided)
|
||||
elif displayFormat == SeparateLatin1StringFormat:
|
||||
elif displayFormat == DisplayFormat.SeparateLatin1StringFormat:
|
||||
d.putValue(p, 'latin1', elided=elided)
|
||||
d.putDisplay('latin1:separate', d.encodeByteArray(value, limit=100000))
|
||||
elif displayFormat == Utf8StringFormat:
|
||||
elif displayFormat == DisplayFormat.Utf8StringFormat:
|
||||
d.putValue(p, 'utf8', elided=elided)
|
||||
elif displayFormat == SeparateUtf8StringFormat:
|
||||
elif displayFormat == DisplayFormat.SeparateUtf8StringFormat:
|
||||
d.putValue(p, 'utf8', elided=elided)
|
||||
d.putDisplay('utf8:separate', d.encodeByteArray(value, limit=100000))
|
||||
if d.isExpanded():
|
||||
@@ -103,11 +103,11 @@ def qdump__QChar(d, value):
|
||||
|
||||
|
||||
def qform_X_QAbstractItemModel():
|
||||
return [SimpleFormat, EnhancedFormat]
|
||||
return [DisplayFormat.SimpleFormat, DisplayFormat.EnhancedFormat]
|
||||
|
||||
def qdump_X_QAbstractItemModel(d, value):
|
||||
displayFormat = d.currentItemFormat()
|
||||
if displayFormat == SimpleFormat:
|
||||
if displayFormat == DisplayFormat.SimpleFormat:
|
||||
d.putPlainChildren(value)
|
||||
return
|
||||
#displayFormat == EnhancedFormat:
|
||||
@@ -137,11 +137,11 @@ def qdump_X_QAbstractItemModel(d, value):
|
||||
#gdb.execute('call free($ri)')
|
||||
|
||||
def qform_X_QModelIndex():
|
||||
return [SimpleFormat, EnhancedFormat]
|
||||
return [DisplayFormat.SimpleFormat, DisplayFormat.EnhancedFormat]
|
||||
|
||||
def qdump_X_QModelIndex(d, value):
|
||||
displayFormat = d.currentItemFormat()
|
||||
if displayFormat == SimpleFormat:
|
||||
if displayFormat == DisplayFormat.SimpleFormat:
|
||||
d.putPlainChildren(value)
|
||||
return
|
||||
r = value['r']
|
||||
@@ -306,12 +306,12 @@ def qdump__QDateTime(d, value):
|
||||
is32bit = d.ptrSize() == 4
|
||||
if qtVersion >= 0x050200:
|
||||
tiVersion = d.qtTypeInfoVersion()
|
||||
#warn('TI VERSION: %s' % tiVersion)
|
||||
#DumperBase.warn('TI VERSION: %s' % tiVersion)
|
||||
if tiVersion is None:
|
||||
tiVersion = 4
|
||||
if tiVersion > 10:
|
||||
status = d.extractByte(value)
|
||||
#warn('STATUS: %s' % status)
|
||||
#DumperBase.warn('STATUS: %s' % status)
|
||||
if status & 0x01:
|
||||
# Short data
|
||||
msecs = d.extractUInt64(value) >> 8
|
||||
@@ -757,7 +757,7 @@ def qdump__QFixed(d, value):
|
||||
|
||||
|
||||
def qform__QFiniteStack():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__QFiniteStack(d, value):
|
||||
array, alloc, size = value.split('pii')
|
||||
@@ -775,7 +775,7 @@ def qdump__QFlags(d, value):
|
||||
|
||||
|
||||
def qform__QHash():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__QHash(d, value):
|
||||
qdumpHelper_QHash(d, value, value.type[0], value.type[1])
|
||||
@@ -835,7 +835,7 @@ def qdumpHelper_QHash(d, value, keyType, valueType):
|
||||
|
||||
|
||||
def qform__QHashNode():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__QHashNode(d, value):
|
||||
d.putPairItem(None, value)
|
||||
@@ -872,7 +872,7 @@ def qdump__QHostAddress(d, value):
|
||||
dd = d.extractPointer(value)
|
||||
qtVersion = d.qtVersion()
|
||||
tiVersion = d.qtTypeInfoVersion()
|
||||
#warn('QT: %x, TI: %s' % (qtVersion, tiVersion))
|
||||
#DumperBase.warn('QT: %x, TI: %s' % (qtVersion, tiVersion))
|
||||
mayNeedParse = True
|
||||
if tiVersion is not None:
|
||||
if tiVersion >= 16:
|
||||
@@ -942,7 +942,7 @@ def qdump__QIPv6Address(d, value):
|
||||
d.putArrayData(value.address(), 16, d.lookupType('unsigned char'))
|
||||
|
||||
def qform__QList():
|
||||
return [DirectQListStorageFormat, IndirectQListStorageFormat]
|
||||
return [DisplayFormat.DirectQListStorageFormat, DisplayFormat.IndirectQListStorageFormat]
|
||||
|
||||
def qdump__QList(d, value):
|
||||
return qdumpHelper_QList(d, value, value.type[0])
|
||||
@@ -972,9 +972,9 @@ def qdumpHelper_QList(d, value, innerType):
|
||||
# in the frontend.
|
||||
# So as first approximation only do the 'isLarge' check:
|
||||
displayFormat = d.currentItemFormat()
|
||||
if displayFormat == DirectQListStorageFormat:
|
||||
if displayFormat == DisplayFormat.DirectQListStorageFormat:
|
||||
isInternal = True
|
||||
elif displayFormat == IndirectQListStorageFormat:
|
||||
elif displayFormat == DisplayFormat.IndirectQListStorageFormat:
|
||||
isInternal = False
|
||||
else:
|
||||
isInternal = innerSize <= stepSize and innerType.isMovableType()
|
||||
@@ -995,7 +995,7 @@ def qdumpHelper_QList(d, value, innerType):
|
||||
d.putSubItem(i, x)
|
||||
|
||||
def qform__QImage():
|
||||
return [SimpleFormat, SeparateFormat]
|
||||
return [DisplayFormat.SimpleFormat, DisplayFormat.SeparateFormat]
|
||||
|
||||
def qdump__QImage(d, value):
|
||||
if d.qtVersion() < 0x050000:
|
||||
@@ -1024,7 +1024,7 @@ def qdump__QImage(d, value):
|
||||
d.putType('void *')
|
||||
|
||||
displayFormat = d.currentItemFormat()
|
||||
if displayFormat == SeparateFormat:
|
||||
if displayFormat == DisplayFormat.SeparateFormat:
|
||||
d.putDisplay('imagedata:separate', '%08x%08x%08x%08x' % (width, height, nbytes, iformat)
|
||||
+ d.readMemory(bits, nbytes))
|
||||
|
||||
@@ -1167,7 +1167,7 @@ def qdumpHelper_Qt5_QMap(d, value, keyType, valueType):
|
||||
|
||||
|
||||
def qform__QMap():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__QMap(d, value):
|
||||
qdumpHelper_QMap(d, value, value.type[0], value.type[1])
|
||||
@@ -1179,13 +1179,13 @@ def qdumpHelper_QMap(d, value, keyType, valueType):
|
||||
qdumpHelper_Qt5_QMap(d, value, keyType, valueType)
|
||||
|
||||
def qform__QMultiMap():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__QMultiMap(d, value):
|
||||
qdump__QMap(d, value)
|
||||
|
||||
def qform__QVariantMap():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__QVariantMap(d, value):
|
||||
qdumpHelper_QMap(d, value, d.createType('QString'), d.createType('QVariant'))
|
||||
@@ -1458,7 +1458,7 @@ def qdump__QSizePolicy(d, value):
|
||||
|
||||
|
||||
def qform__QStack():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__QStack(d, value):
|
||||
qdump__QVector(d, value)
|
||||
@@ -1492,14 +1492,14 @@ def qedit__QString(d, value, data):
|
||||
d.setValues(base, 'short', [ord(c) for c in data])
|
||||
|
||||
def qform__QString():
|
||||
return [SimpleFormat, SeparateFormat]
|
||||
return [DisplayFormat.SimpleFormat, DisplayFormat.SeparateFormat]
|
||||
|
||||
def qdump__QString(d, value):
|
||||
d.putStringValue(value)
|
||||
(data, size, alloc) = d.stringData(value)
|
||||
d.putNumChild(size)
|
||||
displayFormat = d.currentItemFormat()
|
||||
if displayFormat == SeparateFormat:
|
||||
if displayFormat == DisplayFormat.SeparateFormat:
|
||||
d.putDisplay('utf16:separate', d.encodeString(value, limit=100000))
|
||||
if d.isExpanded():
|
||||
d.putArrayData(data, size, d.createType('QChar'))
|
||||
@@ -1596,7 +1596,7 @@ def qdump__QTextDocument(d, value):
|
||||
|
||||
|
||||
def qform__QUrl():
|
||||
return [SimpleFormat, SeparateFormat]
|
||||
return [DisplayFormat.SimpleFormat, DisplayFormat.SeparateFormat]
|
||||
|
||||
def qdump__QUrl(d, value):
|
||||
privAddress = d.extractPointer(value)
|
||||
@@ -1637,7 +1637,7 @@ def qdump__QUrl(d, value):
|
||||
d.putValue(url, 'utf16')
|
||||
|
||||
displayFormat = d.currentItemFormat()
|
||||
if displayFormat == SeparateFormat:
|
||||
if displayFormat == DisplayFormat.SeparateFormat:
|
||||
d.putDisplay('utf16:separate', url)
|
||||
|
||||
d.putNumChild(1)
|
||||
@@ -1851,7 +1851,7 @@ def qdump__QVariant(d, value):
|
||||
d.putNumChild(0)
|
||||
return None
|
||||
|
||||
#warn('TYPE: %s' % variantType)
|
||||
#DumperBase.warn('TYPE: %s' % variantType)
|
||||
|
||||
if variantType <= 86:
|
||||
# Known Core or Gui type.
|
||||
@@ -1867,15 +1867,15 @@ def qdump__QVariant(d, value):
|
||||
#data = value['d']['data']
|
||||
innerType = d.qtNamespace() + innert
|
||||
|
||||
#warn('SHARED: %s' % isShared)
|
||||
#DumperBase.warn('SHARED: %s' % isShared)
|
||||
if isShared:
|
||||
base1 = d.extractPointer(value)
|
||||
#warn('BASE 1: %s %s' % (base1, innert))
|
||||
#DumperBase.warn('BASE 1: %s %s' % (base1, innert))
|
||||
base = d.extractPointer(base1)
|
||||
#warn('SIZE 1: %s' % size)
|
||||
#DumperBase.warn('SIZE 1: %s' % size)
|
||||
val = d.createValue(base, innerType)
|
||||
else:
|
||||
#warn('DIRECT ITEM 1: %s' % innerType)
|
||||
#DumperBase.warn('DIRECT ITEM 1: %s' % innerType)
|
||||
val = d.createValue(data, innerType)
|
||||
val.laddress = value.laddress
|
||||
|
||||
@@ -1939,7 +1939,7 @@ def qedit__QVector(d, value, data):
|
||||
|
||||
|
||||
def qform__QVector():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
|
||||
def qdump__QVector(d, value):
|
||||
@@ -2053,17 +2053,17 @@ def qdump__QXmlStreamAttribute(d, value):
|
||||
#######################################################################
|
||||
|
||||
def extractQmlData(d, value):
|
||||
#if value.type.code == TypeCodePointer:
|
||||
#if value.type.code == TypeCode.TypeCodePointer:
|
||||
# value = value.dereference()
|
||||
base = value.split('p')[0]
|
||||
#mmdata = d.split('Q', base)[0]
|
||||
#PointerMask = 0xfffffffffffffffd
|
||||
#vtable = mmdata & PointerMask
|
||||
#warn('QML DATA: %s' % value.stringify())
|
||||
#DumperBase.warn('QML DATA: %s' % value.stringify())
|
||||
#data = value['data']
|
||||
#return #data.cast(d.lookupType(value.type.name.replace('QV4::', 'QV4::Heap::')))
|
||||
typeName = value.type.name.replace('QV4::', 'QV4::Heap::')
|
||||
#warn('TYOE DATA: %s' % typeName)
|
||||
#DumperBase.warn('TYOE DATA: %s' % typeName)
|
||||
return d.createValue(base, typeName)
|
||||
|
||||
def qdump__QV4__Heap__Base(d, value):
|
||||
@@ -2236,7 +2236,7 @@ def QV4_valueForData(d, jsval): # (Dumper, QJSValue *jsval) -> QV4::Value *
|
||||
v = QV4_getValue(d, jsval)
|
||||
if v:
|
||||
return v
|
||||
warn('Not implemented: VARIANT')
|
||||
d.warn('Not implemented: VARIANT')
|
||||
return 0
|
||||
|
||||
def QV4_putObjectValue(d, objectPtr):
|
||||
@@ -2424,7 +2424,7 @@ def qdump_64__QV4__Value(d, value):
|
||||
d.putBetterType('%sQV4::Value (object)' % ns)
|
||||
#QV4_putObjectValue(d, d.extractPointer(value) + 2 * d.ptrSize())
|
||||
arrayVTable = d.symbolAddress(ns + 'QV4::ArrayObject::static_vtbl')
|
||||
#warn('ARRAY VTABLE: 0x%x' % arrayVTable)
|
||||
#DumperBase.warn('ARRAY VTABLE: 0x%x' % arrayVTable)
|
||||
d.putNumChild(1)
|
||||
d.putItem(d.createValue(d.extractPointer(value) + 2 * d.ptrSize(), ns + 'QV4::Object'))
|
||||
return
|
||||
@@ -2630,7 +2630,7 @@ def qdump__QScriptValue(d, value):
|
||||
payload = x['asBits']['payload']
|
||||
#isValid = int(x['asBits']['tag']) != -6 # Empty
|
||||
#isCell = int(x['asBits']['tag']) == -2
|
||||
#warn('IS CELL: %s ' % isCell)
|
||||
#DumperBase.warn('IS CELL: %s ' % isCell)
|
||||
#isObject = False
|
||||
#className = 'UNKNOWN NAME'
|
||||
#if isCell:
|
||||
@@ -2648,7 +2648,7 @@ def qdump__QScriptValue(d, value):
|
||||
# type = cell['m_structure']['m_typeInfo']['m_type']
|
||||
# isObject = int(type) == 7 # ObjectType;
|
||||
# className = 'UNKNOWN NAME'
|
||||
#warn('IS OBJECT: %s ' % isObject)
|
||||
#DumperBase.warn('IS OBJECT: %s ' % isObject)
|
||||
|
||||
#inline bool JSCell::inherits(const ClassInfo* info) const
|
||||
#for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
|
||||
|
@@ -23,10 +23,11 @@
|
||||
#
|
||||
############################################################################
|
||||
|
||||
from dumper import *
|
||||
from utils import DisplayFormat
|
||||
from dumper import Children, SubItem
|
||||
|
||||
def qform__std__array():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__std__array(d, value):
|
||||
size = value.type[1]
|
||||
@@ -36,7 +37,7 @@ def qdump__std__array(d, value):
|
||||
|
||||
|
||||
def qform__std____1__array():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__std____1__array(d, value):
|
||||
qdump__std__array(d, value)
|
||||
@@ -260,7 +261,7 @@ def qdump__std____1__list(d, value):
|
||||
d.putSubItem(i, val)
|
||||
|
||||
def qform__std__map():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__std__map(d, value):
|
||||
if d.isQnxTarget() or d.isMsvcTarget():
|
||||
@@ -334,7 +335,7 @@ def qdump__std____cxx1998__map(d, value):
|
||||
qdump__std__map(d, value)
|
||||
|
||||
def qform__std__multimap():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__std__multimap(d, value):
|
||||
return qdump__std__map(d, value)
|
||||
@@ -537,7 +538,7 @@ def qdump__std____1__multiset(d, value):
|
||||
qdump__std____1__set(d, value)
|
||||
|
||||
def qform__std____1__map():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__std____1__map(d, value):
|
||||
try:
|
||||
@@ -574,7 +575,7 @@ def qdump__std____1__map(d, value):
|
||||
d.putPairItem(i, pair, 'key', 'value')
|
||||
|
||||
def qform__std____1__multimap():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__std____1__multimap(d, value):
|
||||
qdump__std____1__map(d, value)
|
||||
@@ -620,8 +621,8 @@ def qdump__std____1__stack(d, value):
|
||||
d.putBetterType(value.type)
|
||||
|
||||
def qform__std__string():
|
||||
return [Latin1StringFormat, SeparateLatin1StringFormat,
|
||||
Utf8StringFormat, SeparateUtf8StringFormat ]
|
||||
return [DisplayFormat.Latin1StringFormat, DisplayFormat.SeparateLatin1StringFormat,
|
||||
DisplayFormat.Utf8StringFormat, DisplayFormat.SeparateUtf8StringFormat ]
|
||||
|
||||
def qdump__std__string(d, value):
|
||||
qdumpHelper_std__string(d, value, d.createType("char"), d.currentItemFormat())
|
||||
@@ -760,10 +761,10 @@ def qdump__std__pair(d, value):
|
||||
d.putValue(value.value, value.encoding)
|
||||
|
||||
def qform__std__unordered_map():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qform__std____debug__unordered_map():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__std__unordered_map(d, value):
|
||||
if d.isQnxTarget() or d.isMsvcTarget():
|
||||
@@ -861,7 +862,7 @@ def qdump__std__unordered_set(d, value):
|
||||
p = d.extractPointer(p + offset)
|
||||
|
||||
def qform__std____1__unordered_map():
|
||||
return mapForms()
|
||||
return [DisplayFormat.CompactMapFormat]
|
||||
|
||||
def qdump__std____1__unordered_map(d, value):
|
||||
(size, _) = value["__table_"]["__p2_"].split("pp")
|
||||
@@ -916,7 +917,7 @@ def qdump__std____debug__unordered_multiset(d, value):
|
||||
|
||||
|
||||
def qform__std__valarray():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__std__valarray(d, value):
|
||||
if d.isMsvcTarget():
|
||||
@@ -928,7 +929,7 @@ def qdump__std__valarray(d, value):
|
||||
|
||||
|
||||
def qform__std____1__valarray():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__std____1__valarray(d, value):
|
||||
innerType = value.type[0]
|
||||
@@ -939,7 +940,7 @@ def qdump__std____1__valarray(d, value):
|
||||
|
||||
|
||||
def qform__std__vector():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qedit__std__vector(d, value, data):
|
||||
import gdb
|
||||
@@ -1036,13 +1037,13 @@ def qdumpHelper__std__vector__QNX(d, value):
|
||||
d.putPlotData(start, size, innerType)
|
||||
|
||||
def qform__std____1__vector():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__std____1__vector(d, value):
|
||||
qdumpHelper__std__vector(d, value, True)
|
||||
|
||||
def qform__std____debug__vector():
|
||||
return arrayForms()
|
||||
return [DisplayFormat.ArrayPlotFormat]
|
||||
|
||||
def qdump__std____debug__vector(d, value):
|
||||
qdump__std__vector(d, value)
|
||||
@@ -1088,7 +1089,7 @@ def qdump__string(d, value):
|
||||
qdump__std__string(d, value)
|
||||
|
||||
def qform__std__wstring():
|
||||
return [SimpleFormat, SeparateFormat]
|
||||
return [DisplayFormat.SimpleFormat, DisplayFormat.SeparateFormat]
|
||||
|
||||
def qdump__std__wstring(d, value):
|
||||
qdumpHelper_std__string(d, value, d.createType('wchar_t'), d.currentItemFormat())
|
||||
@@ -1131,7 +1132,7 @@ def qdump__std____1__basic_string(d, value):
|
||||
elif innerType == "wchar_t":
|
||||
qdump__std____1__wstring(d, value)
|
||||
else:
|
||||
warn("UNKNOWN INNER TYPE %s" % innerType)
|
||||
d.warn("UNKNOWN INNER TYPE %s" % innerType)
|
||||
|
||||
def qdump__wstring(d, value):
|
||||
qdump__std__wstring(d, value)
|
||||
@@ -1183,7 +1184,7 @@ def qdump__std__byte(d, value):
|
||||
|
||||
def qdump__std__optional(d, value):
|
||||
innerType = value.type[0]
|
||||
(initialized, pad, payload) = d.split('b@{%s}' % innerType.name, value)
|
||||
(payload, pad, initialized) = d.split('{%s}@b' % innerType.name, value)
|
||||
if initialized:
|
||||
d.putItem(payload)
|
||||
d.putBetterType(value.type)
|
||||
|
130
share/qtcreator/debugger/utils.py
Normal file
130
share/qtcreator/debugger/utils.py
Normal file
@@ -0,0 +1,130 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (C) 2016 The Qt Company Ltd.
|
||||
# Contact: https://www.qt.io/licensing/
|
||||
#
|
||||
# This file is part of Qt Creator.
|
||||
#
|
||||
# Commercial License Usage
|
||||
# Licensees holding valid commercial Qt licenses may use this file in
|
||||
# accordance with the commercial license agreement provided with the
|
||||
# Software or, alternatively, in accordance with the terms contained in
|
||||
# a written agreement between you and The Qt Company. For licensing terms
|
||||
# and conditions see https://www.qt.io/terms-conditions. For further
|
||||
# information use the contact form at https://www.qt.io/contact-us.
|
||||
#
|
||||
# GNU General Public License Usage
|
||||
# Alternatively, this file may be used under the terms of the GNU
|
||||
# General Public License version 3 as published by the Free Software
|
||||
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
# included in the packaging of this file. Please review the following
|
||||
# information to ensure the GNU General Public License requirements will
|
||||
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
# Debugger start modes. Keep in sync with DebuggerStartMode in debuggerconstants.h
|
||||
|
||||
|
||||
class DebuggerStartMode:
|
||||
(
|
||||
NoStartMode,
|
||||
StartInternal,
|
||||
StartExternal,
|
||||
AttachExternal,
|
||||
AttachCrashedExternal,
|
||||
AttachCore,
|
||||
AttachToRemoteServer,
|
||||
AttachToRemoteProcess,
|
||||
StartRemoteProcess,
|
||||
) = range(0, 9)
|
||||
|
||||
|
||||
# Known special formats. Keep in sync with DisplayFormat in debuggerprotocol.h
|
||||
class DisplayFormat:
|
||||
(
|
||||
AutomaticFormat,
|
||||
RawFormat,
|
||||
SimpleFormat,
|
||||
EnhancedFormat,
|
||||
SeparateFormat,
|
||||
Latin1StringFormat,
|
||||
SeparateLatin1StringFormat,
|
||||
Utf8StringFormat,
|
||||
SeparateUtf8StringFormat,
|
||||
Local8BitStringFormat,
|
||||
Utf16StringFormat,
|
||||
Ucs4StringFormat,
|
||||
Array10Format,
|
||||
Array100Format,
|
||||
Array1000Format,
|
||||
Array10000Format,
|
||||
ArrayPlotFormat,
|
||||
CompactMapFormat,
|
||||
DirectQListStorageFormat,
|
||||
IndirectQListStorageFormat,
|
||||
) = range(0, 20)
|
||||
|
||||
|
||||
# Breakpoints. Keep synchronized with BreakpointType in breakpoint.h
|
||||
class BreakpointType:
|
||||
(
|
||||
UnknownType,
|
||||
BreakpointByFileAndLine,
|
||||
BreakpointByFunction,
|
||||
BreakpointByAddress,
|
||||
BreakpointAtThrow,
|
||||
BreakpointAtCatch,
|
||||
BreakpointAtMain,
|
||||
BreakpointAtFork,
|
||||
BreakpointAtExec,
|
||||
BreakpointAtSysCall,
|
||||
WatchpointAtAddress,
|
||||
WatchpointAtExpression,
|
||||
BreakpointOnQmlSignalEmit,
|
||||
BreakpointAtJavaScriptThrow,
|
||||
) = range(0, 14)
|
||||
|
||||
|
||||
# Internal codes for types keep in sync with cdbextensions pytype.cpp
|
||||
class TypeCode:
|
||||
(
|
||||
TypeCodeTypedef,
|
||||
TypeCodeStruct,
|
||||
TypeCodeVoid,
|
||||
TypeCodeIntegral,
|
||||
TypeCodeFloat,
|
||||
TypeCodeEnum,
|
||||
TypeCodePointer,
|
||||
TypeCodeArray,
|
||||
TypeCodeComplex,
|
||||
TypeCodeReference,
|
||||
TypeCodeFunction,
|
||||
TypeCodeMemberPointer,
|
||||
TypeCodeFortranString,
|
||||
TypeCodeUnresolvable,
|
||||
TypeCodeBitfield,
|
||||
TypeCodeRValueReference,
|
||||
) = range(0, 16)
|
||||
|
||||
|
||||
def isIntegralTypeName(name):
|
||||
return name in (
|
||||
"int",
|
||||
"unsigned int",
|
||||
"signed int",
|
||||
"short",
|
||||
"unsigned short",
|
||||
"long",
|
||||
"unsigned long",
|
||||
"long long",
|
||||
"unsigned long long",
|
||||
"char",
|
||||
"signed char",
|
||||
"unsigned char",
|
||||
"bool",
|
||||
)
|
||||
|
||||
|
||||
def isFloatingPointTypeName(name):
|
||||
return name in ("float", "double", "long double")
|
@@ -29,9 +29,10 @@ HEADERS += $$PWD/puppetalivecommand.h
|
||||
HEADERS += $$PWD/changeselectioncommand.h
|
||||
HEADERS += $$PWD/drop3dlibraryitemcommand.h
|
||||
HEADERS += $$PWD/update3dviewstatecommand.h
|
||||
HEADERS += $$PWD/enable3dviewcommand.h
|
||||
HEADERS += $$PWD/view3dclosedcommand.h
|
||||
HEADERS += $$PWD/puppettocreatorcommand.h
|
||||
HEADERS += $$PWD/inputeventcommand.h
|
||||
HEADERS += $$PWD/view3dactioncommand.h
|
||||
|
||||
SOURCES += $$PWD/synchronizecommand.cpp
|
||||
SOURCES += $$PWD/debugoutputcommand.cpp
|
||||
@@ -62,6 +63,7 @@ SOURCES += $$PWD/puppetalivecommand.cpp
|
||||
SOURCES += $$PWD/changeselectioncommand.cpp
|
||||
SOURCES += $$PWD/drop3dlibraryitemcommand.cpp
|
||||
SOURCES += $$PWD/update3dviewstatecommand.cpp
|
||||
SOURCES += $$PWD/enable3dviewcommand.cpp
|
||||
SOURCES += $$PWD/view3dclosedcommand.cpp
|
||||
SOURCES += $$PWD/puppettocreatorcommand.cpp
|
||||
SOURCES += $$PWD/inputeventcommand.cpp
|
||||
SOURCES += $$PWD/view3dactioncommand.cpp
|
||||
|
96
share/qtcreator/qml/qmlpuppet/commands/inputeventcommand.cpp
Normal file
96
share/qtcreator/qml/qmlpuppet/commands/inputeventcommand.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "inputeventcommand.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
InputEventCommand::InputEventCommand() = default;
|
||||
|
||||
InputEventCommand::InputEventCommand(QInputEvent *e)
|
||||
: m_type(e->type()),
|
||||
m_modifiers(e->modifiers())
|
||||
{
|
||||
if (m_type == QEvent::Wheel) {
|
||||
auto we = static_cast<QWheelEvent *>(e);
|
||||
#if QT_VERSION <= QT_VERSION_CHECK(5, 15, 0)
|
||||
m_pos = we->pos();
|
||||
#else
|
||||
m_pos = we->position().toPoint();
|
||||
#endif
|
||||
m_buttons = we->buttons();
|
||||
m_angleDelta = we->angleDelta().y();
|
||||
} else {
|
||||
auto me = static_cast<QMouseEvent *>(e);
|
||||
m_pos = me->pos();
|
||||
m_button = me->button();
|
||||
m_buttons = me->buttons();
|
||||
}
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const InputEventCommand &command)
|
||||
{
|
||||
out << command.type();
|
||||
out << command.pos();
|
||||
out << command.button();
|
||||
out << command.buttons();
|
||||
out << command.modifiers();
|
||||
out << command.angleDelta();
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &in, InputEventCommand &command)
|
||||
{
|
||||
int type;
|
||||
int button;
|
||||
in >> type;
|
||||
command.m_type = (QEvent::Type)type;
|
||||
in >> command.m_pos;
|
||||
in >> button;
|
||||
command.m_button = (Qt::MouseButton)button;
|
||||
in >> command.m_buttons;
|
||||
in >> command.m_modifiers;
|
||||
in >> command.m_angleDelta;
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
QDebug operator <<(QDebug debug, const InputEventCommand &command)
|
||||
{
|
||||
return debug.nospace() << "InputEventCommand("
|
||||
<< "type: " << command.type() << ", "
|
||||
<< "pos: " << command.pos() << ", "
|
||||
<< "button: " << command.button() << ", "
|
||||
<< "buttons: " << command.buttons() << ", "
|
||||
<< "modifiers: " << command.modifiers() << ", "
|
||||
<< "angleDelta: " << command.angleDelta() << ")";
|
||||
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
68
share/qtcreator/qml/qmlpuppet/commands/inputeventcommand.h
Normal file
68
share/qtcreator/qml/qmlpuppet/commands/inputeventcommand.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/qmetatype.h>
|
||||
#include <QtCore/qdatastream.h>
|
||||
#include <QtGui/qevent.h>
|
||||
|
||||
#include "instancecontainer.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class InputEventCommand
|
||||
{
|
||||
friend QDataStream &operator>>(QDataStream &in, InputEventCommand &command);
|
||||
friend QDebug operator <<(QDebug debug, const InputEventCommand &command);
|
||||
|
||||
public:
|
||||
InputEventCommand();
|
||||
explicit InputEventCommand(QInputEvent *e);
|
||||
|
||||
QEvent::Type type() const { return m_type; }
|
||||
QPoint pos() const { return m_pos; }
|
||||
Qt::MouseButton button() const { return m_button; }
|
||||
Qt::MouseButtons buttons() const { return m_buttons; }
|
||||
Qt::KeyboardModifiers modifiers() const { return m_modifiers; }
|
||||
int angleDelta() const { return m_angleDelta; }
|
||||
|
||||
private:
|
||||
QEvent::Type m_type;
|
||||
QPoint m_pos;
|
||||
Qt::MouseButton m_button = Qt::NoButton;
|
||||
Qt::MouseButtons m_buttons = Qt::NoButton;
|
||||
Qt::KeyboardModifiers m_modifiers = Qt::NoModifier;
|
||||
int m_angleDelta = 0;
|
||||
};
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const InputEventCommand &command);
|
||||
QDataStream &operator>>(QDataStream &in, InputEventCommand &command);
|
||||
|
||||
QDebug operator <<(QDebug debug, const InputEventCommand &command);
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
Q_DECLARE_METATYPE(QmlDesigner::InputEventCommand)
|
@@ -34,7 +34,7 @@ namespace QmlDesigner {
|
||||
class PuppetToCreatorCommand
|
||||
{
|
||||
public:
|
||||
enum Type { KeyPressed, Edit3DToolState, None };
|
||||
enum Type { KeyPressed, Edit3DToolState, Render3DView, ActiveSceneChanged, None };
|
||||
|
||||
PuppetToCreatorCommand(Type type, const QVariant &data);
|
||||
PuppetToCreatorCommand() = default;
|
||||
|
@@ -45,6 +45,12 @@ Update3dViewStateCommand::Update3dViewStateCommand(bool active, bool hasPopup)
|
||||
{
|
||||
}
|
||||
|
||||
Update3dViewStateCommand::Update3dViewStateCommand(const QSize &size)
|
||||
: m_size(size)
|
||||
, m_type(Update3dViewStateCommand::SizeChange)
|
||||
{
|
||||
}
|
||||
|
||||
Qt::WindowStates Update3dViewStateCommand::previousStates() const
|
||||
{
|
||||
return m_previousStates;
|
||||
@@ -65,6 +71,11 @@ bool Update3dViewStateCommand::hasPopup() const
|
||||
return m_hasPopup;
|
||||
}
|
||||
|
||||
QSize Update3dViewStateCommand::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
Update3dViewStateCommand::Type Update3dViewStateCommand::type() const
|
||||
{
|
||||
return m_type;
|
||||
@@ -77,6 +88,7 @@ QDataStream &operator<<(QDataStream &out, const Update3dViewStateCommand &comman
|
||||
out << qint32(command.isActive());
|
||||
out << qint32(command.hasPopup());
|
||||
out << qint32(command.type());
|
||||
out << command.size();
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -94,13 +106,16 @@ QDataStream &operator>>(QDataStream &in, Update3dViewStateCommand &command)
|
||||
command.m_active = active;
|
||||
command.m_hasPopup = hasPopup;
|
||||
command.m_type = Update3dViewStateCommand::Type(type);
|
||||
in >> command.m_size;
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const Update3dViewStateCommand &command)
|
||||
{
|
||||
return debug.nospace() << "Update3dViewStateCommand(type: " << command.m_type << ")";
|
||||
return debug.nospace() << "Update3dViewStateCommand(type: "
|
||||
<< command.m_type << ","
|
||||
<< command.m_size << ")";
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QtCore/qsize.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -35,10 +36,11 @@ class Update3dViewStateCommand
|
||||
friend QDebug operator<<(QDebug debug, const Update3dViewStateCommand &command);
|
||||
|
||||
public:
|
||||
enum Type { StateChange, ActiveChange, Empty };
|
||||
enum Type { StateChange, ActiveChange, SizeChange, Empty };
|
||||
|
||||
explicit Update3dViewStateCommand(Qt::WindowStates previousStates, Qt::WindowStates currentStates);
|
||||
explicit Update3dViewStateCommand(bool active, bool hasPopup);
|
||||
explicit Update3dViewStateCommand(const QSize &size);
|
||||
Update3dViewStateCommand() = default;
|
||||
|
||||
Qt::WindowStates previousStates() const;
|
||||
@@ -46,6 +48,7 @@ public:
|
||||
|
||||
bool isActive() const;
|
||||
bool hasPopup() const;
|
||||
QSize size() const;
|
||||
|
||||
Type type() const;
|
||||
|
||||
@@ -55,6 +58,7 @@ private:
|
||||
|
||||
bool m_active = false;
|
||||
bool m_hasPopup = false;
|
||||
QSize m_size;
|
||||
|
||||
Type m_type = Empty;
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
@@ -23,43 +23,54 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "enable3dviewcommand.h"
|
||||
#include "view3dactioncommand.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
// open / close edit view 3D command
|
||||
Enable3DViewCommand::Enable3DViewCommand(bool enable)
|
||||
: m_enable(enable)
|
||||
View3DActionCommand::View3DActionCommand(Type type, bool enable)
|
||||
: m_type(type)
|
||||
, m_enabled(enable)
|
||||
{
|
||||
}
|
||||
|
||||
bool Enable3DViewCommand::isEnable() const
|
||||
bool View3DActionCommand::isEnabled() const
|
||||
{
|
||||
return m_enable;
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const Enable3DViewCommand &command)
|
||||
View3DActionCommand::Type View3DActionCommand::type() const
|
||||
{
|
||||
out << qint32(command.isEnable());
|
||||
return m_type;
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const View3DActionCommand &command)
|
||||
{
|
||||
out << qint32(command.isEnabled());
|
||||
out << qint32(command.type());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &in, Enable3DViewCommand &command)
|
||||
QDataStream &operator>>(QDataStream &in, View3DActionCommand &command)
|
||||
{
|
||||
qint32 enable;
|
||||
in >> enable;
|
||||
command.m_enable = enable;
|
||||
qint32 enabled;
|
||||
qint32 type;
|
||||
in >> enabled;
|
||||
in >> type;
|
||||
command.m_enabled = bool(enabled);
|
||||
command.m_type = View3DActionCommand::Type(type);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const Enable3DViewCommand &command)
|
||||
QDebug operator<<(QDebug debug, const View3DActionCommand &command)
|
||||
{
|
||||
return debug.nospace() << "Enable3DViewCommand(enable: " << command.m_enable << ")";
|
||||
return debug.nospace() << "View3DActionCommand(type: "
|
||||
<< command.m_type << ","
|
||||
<< command.m_enabled << ")";
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
67
share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
Normal file
67
share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class View3DActionCommand
|
||||
{
|
||||
friend QDataStream &operator>>(QDataStream &in, View3DActionCommand &command);
|
||||
friend QDebug operator<<(QDebug debug, const View3DActionCommand &command);
|
||||
|
||||
public:
|
||||
enum Type { Empty,
|
||||
MoveTool,
|
||||
ScaleTool,
|
||||
RotateTool,
|
||||
FitToView,
|
||||
SelectionModeToggle,
|
||||
CameraToggle,
|
||||
OrientationToggle,
|
||||
EditLightToggle
|
||||
};
|
||||
|
||||
explicit View3DActionCommand(Type type, bool enable);
|
||||
View3DActionCommand() = default;
|
||||
|
||||
bool isEnabled() const;
|
||||
Type type() const;
|
||||
|
||||
private:
|
||||
Type m_type = Empty;
|
||||
bool m_enabled = false;
|
||||
};
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const View3DActionCommand &command);
|
||||
QDataStream &operator>>(QDataStream &in, View3DActionCommand &command);
|
||||
|
||||
QDebug operator<<(QDebug debug, const View3DActionCommand &command);
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
Q_DECLARE_METATYPE(QmlDesigner::View3DActionCommand)
|
@@ -42,7 +42,6 @@
|
||||
#include "createinstancescommand.h"
|
||||
#include "createscenecommand.h"
|
||||
#include "update3dviewstatecommand.h"
|
||||
#include "enable3dviewcommand.h"
|
||||
#include "changevaluescommand.h"
|
||||
#include "changebindingscommand.h"
|
||||
#include "changeauxiliarycommand.h"
|
||||
@@ -57,6 +56,8 @@
|
||||
#include "synchronizecommand.h"
|
||||
#include "removesharedmemorycommand.h"
|
||||
#include "tokencommand.h"
|
||||
#include "inputeventcommand.h"
|
||||
#include "view3dactioncommand.h"
|
||||
|
||||
#include "informationchangedcommand.h"
|
||||
#include "pixmapchangedcommand.h"
|
||||
@@ -76,6 +77,13 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
constexpr void (QLocalSocket::*LocalSocketErrorFunction)(QLocalSocket::LocalSocketError)
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
= &QLocalSocket::error;
|
||||
#else
|
||||
= &QLocalSocket::errorOccurred;
|
||||
#endif
|
||||
|
||||
NodeInstanceClientProxy::NodeInstanceClientProxy(QObject *parent)
|
||||
: QObject(parent),
|
||||
m_inputIoDevice(nullptr),
|
||||
@@ -93,7 +101,7 @@ void NodeInstanceClientProxy::initializeSocket()
|
||||
{
|
||||
QLocalSocket *localSocket = new QLocalSocket(this);
|
||||
connect(localSocket, &QIODevice::readyRead, this, &NodeInstanceClientProxy::readDataStream);
|
||||
connect(localSocket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error),
|
||||
connect(localSocket, LocalSocketErrorFunction,
|
||||
QCoreApplication::instance(), &QCoreApplication::quit);
|
||||
connect(localSocket, &QLocalSocket::disconnected, QCoreApplication::instance(), &QCoreApplication::quit);
|
||||
localSocket->connectToServer(QCoreApplication::arguments().at(1), QIODevice::ReadWrite | QIODevice::Unbuffered);
|
||||
@@ -321,6 +329,16 @@ QVariant NodeInstanceClientProxy::readCommandFromIOStream(QIODevice *ioDevice, q
|
||||
return command;
|
||||
}
|
||||
|
||||
void NodeInstanceClientProxy::inputEvent(const InputEventCommand &command)
|
||||
{
|
||||
nodeInstanceServer()->inputEvent(command);
|
||||
}
|
||||
|
||||
void NodeInstanceClientProxy::view3DAction(const View3DActionCommand &command)
|
||||
{
|
||||
nodeInstanceServer()->view3DAction(command);
|
||||
}
|
||||
|
||||
void NodeInstanceClientProxy::readDataStream()
|
||||
{
|
||||
QList<QVariant> commandList;
|
||||
@@ -379,11 +397,6 @@ void NodeInstanceClientProxy::update3DViewState(const Update3dViewStateCommand &
|
||||
nodeInstanceServer()->update3DViewState(command);
|
||||
}
|
||||
|
||||
void NodeInstanceClientProxy::enable3DView(const Enable3DViewCommand &command)
|
||||
{
|
||||
nodeInstanceServer()->enable3DView(command);
|
||||
}
|
||||
|
||||
void NodeInstanceClientProxy::clearScene(const ClearSceneCommand &command)
|
||||
{
|
||||
nodeInstanceServer()->clearScene(command);
|
||||
@@ -472,7 +485,6 @@ void NodeInstanceClientProxy::dispatchCommand(const QVariant &command)
|
||||
{
|
||||
static const int createInstancesCommandType = QMetaType::type("CreateInstancesCommand");
|
||||
static const int update3dViewStateCommand = QMetaType::type("Update3dViewStateCommand");
|
||||
static const int enable3DViewCommandType = QMetaType::type("Enable3DViewCommand");
|
||||
static const int changeFileUrlCommandType = QMetaType::type("ChangeFileUrlCommand");
|
||||
static const int createSceneCommandType = QMetaType::type("CreateSceneCommand");
|
||||
static const int clearSceneCommandType = QMetaType::type("ClearSceneCommand");
|
||||
@@ -491,15 +503,17 @@ void NodeInstanceClientProxy::dispatchCommand(const QVariant &command)
|
||||
static const int tokenCommandType = QMetaType::type("TokenCommand");
|
||||
static const int endPuppetCommandType = QMetaType::type("EndPuppetCommand");
|
||||
static const int changeSelectionCommandType = QMetaType::type("ChangeSelectionCommand");
|
||||
static const int inputEventCommandType = QMetaType::type("InputEventCommand");
|
||||
static const int view3DActionCommandType = QMetaType::type("View3DActionCommand");
|
||||
|
||||
const int commandType = command.userType();
|
||||
|
||||
if (commandType == createInstancesCommandType)
|
||||
if (commandType == inputEventCommandType)
|
||||
inputEvent(command.value<InputEventCommand>());
|
||||
else if (commandType == createInstancesCommandType)
|
||||
createInstances(command.value<CreateInstancesCommand>());
|
||||
else if (commandType == update3dViewStateCommand)
|
||||
update3DViewState(command.value<Update3dViewStateCommand>());
|
||||
else if (commandType == enable3DViewCommandType)
|
||||
enable3DView(command.value<Enable3DViewCommand>());
|
||||
else if (commandType == changeFileUrlCommandType)
|
||||
changeFileUrl(command.value<ChangeFileUrlCommand>());
|
||||
else if (commandType == createSceneCommandType)
|
||||
@@ -532,6 +546,8 @@ void NodeInstanceClientProxy::dispatchCommand(const QVariant &command)
|
||||
redirectToken(command.value<TokenCommand>());
|
||||
else if (commandType == endPuppetCommandType)
|
||||
redirectToken(command.value<EndPuppetCommand>());
|
||||
else if (commandType == view3DActionCommandType)
|
||||
view3DAction(command.value<View3DActionCommand>());
|
||||
else if (commandType == synchronizeCommandType) {
|
||||
SynchronizeCommand synchronizeCommand = command.value<SynchronizeCommand>();
|
||||
m_synchronizeId = synchronizeCommand.synchronizeId();
|
||||
|
@@ -46,7 +46,6 @@ class CreateInstancesCommand;
|
||||
class ClearSceneCommand;
|
||||
class ReparentInstancesCommand;
|
||||
class Update3dViewStateCommand;
|
||||
class Enable3DViewCommand;
|
||||
class ChangeFileUrlCommand;
|
||||
class ChangeValuesCommand;
|
||||
class ChangeAuxiliaryCommand;
|
||||
@@ -62,6 +61,8 @@ class ChangeSelectionCommand;
|
||||
class Drop3DLibraryItemCommand;
|
||||
class PuppetToCreatorCommand;
|
||||
class View3DClosedCommand;
|
||||
class InputEventCommand;
|
||||
class View3DActionCommand;
|
||||
|
||||
class NodeInstanceClientProxy : public QObject, public NodeInstanceClientInterface
|
||||
{
|
||||
@@ -102,7 +103,6 @@ protected:
|
||||
void createScene(const CreateSceneCommand &command);
|
||||
void clearScene(const ClearSceneCommand &command);
|
||||
void update3DViewState(const Update3dViewStateCommand &command);
|
||||
void enable3DView(const Enable3DViewCommand &command);
|
||||
void removeInstances(const RemoveInstancesCommand &command);
|
||||
void removeProperties(const RemovePropertiesCommand &command);
|
||||
void changePropertyBindings(const ChangeBindingsCommand &command);
|
||||
@@ -118,6 +118,8 @@ protected:
|
||||
void redirectToken(const EndPuppetCommand &command);
|
||||
void changeSelection(const ChangeSelectionCommand &command);
|
||||
static QVariant readCommandFromIOStream(QIODevice *ioDevice, quint32 *readCommandCounter, quint32 *blockSize);
|
||||
void inputEvent(const InputEventCommand &command);
|
||||
void view3DAction(const View3DActionCommand &command);
|
||||
|
||||
protected slots:
|
||||
void readDataStream();
|
||||
|
@@ -33,7 +33,6 @@
|
||||
#include "createinstancescommand.h"
|
||||
#include "createscenecommand.h"
|
||||
#include "update3dviewstatecommand.h"
|
||||
#include "enable3dviewcommand.h"
|
||||
#include "changevaluescommand.h"
|
||||
#include "changebindingscommand.h"
|
||||
#include "changeauxiliarycommand.h"
|
||||
@@ -49,6 +48,8 @@
|
||||
#include "changenodesourcecommand.h"
|
||||
#include "changeselectioncommand.h"
|
||||
#include "drop3dlibraryitemcommand.h"
|
||||
#include "inputeventcommand.h"
|
||||
#include "view3dactioncommand.h"
|
||||
|
||||
#include "informationchangedcommand.h"
|
||||
#include "pixmapchangedcommand.h"
|
||||
@@ -97,9 +98,6 @@ void NodeInstanceServerInterface::registerCommands()
|
||||
qRegisterMetaType<Update3dViewStateCommand>("Update3dViewStateCommand");
|
||||
qRegisterMetaTypeStreamOperators<Update3dViewStateCommand>("Update3dViewStateCommand");
|
||||
|
||||
qRegisterMetaType<Enable3DViewCommand>("Enable3DViewCommand");
|
||||
qRegisterMetaTypeStreamOperators<Enable3DViewCommand>("Enable3DViewCommand");
|
||||
|
||||
qRegisterMetaType<ChangeBindingsCommand>("ChangeBindingsCommand");
|
||||
qRegisterMetaTypeStreamOperators<ChangeBindingsCommand>("ChangeBindingsCommand");
|
||||
|
||||
@@ -214,6 +212,12 @@ void NodeInstanceServerInterface::registerCommands()
|
||||
qRegisterMetaType<PuppetToCreatorCommand>("PuppetToCreatorCommand");
|
||||
qRegisterMetaTypeStreamOperators<PuppetToCreatorCommand>("PuppetToCreatorCommand");
|
||||
|
||||
qRegisterMetaType<InputEventCommand>("InputEventCommand");
|
||||
qRegisterMetaTypeStreamOperators<InputEventCommand>("InputEventCommand");
|
||||
|
||||
qRegisterMetaType<View3DActionCommand>("View3DActionCommand");
|
||||
qRegisterMetaTypeStreamOperators<View3DActionCommand>("View3DActionCommand");
|
||||
|
||||
qRegisterMetaType<QPair<int, int>>("QPairIntInt");
|
||||
qRegisterMetaTypeStreamOperators<QPair<int, int>>("QPairIntInt");
|
||||
}
|
||||
|
@@ -34,7 +34,6 @@ class PropertyBindingContainer;
|
||||
class PropertyValueContainer;
|
||||
|
||||
class Update3dViewStateCommand;
|
||||
class Enable3DViewCommand;
|
||||
class ChangeFileUrlCommand;
|
||||
class ChangeValuesCommand;
|
||||
class ChangeBindingsCommand;
|
||||
@@ -52,6 +51,8 @@ class ChangeNodeSourceCommand;
|
||||
class TokenCommand;
|
||||
class RemoveSharedMemoryCommand;
|
||||
class ChangeSelectionCommand;
|
||||
class InputEventCommand;
|
||||
class View3DActionCommand;
|
||||
|
||||
class NodeInstanceServerInterface : public QObject
|
||||
{
|
||||
@@ -69,7 +70,6 @@ public:
|
||||
virtual void createScene(const CreateSceneCommand &command) = 0;
|
||||
virtual void clearScene(const ClearSceneCommand &command) = 0;
|
||||
virtual void update3DViewState(const Update3dViewStateCommand &command) = 0;
|
||||
virtual void enable3DView(const Enable3DViewCommand &command) = 0;
|
||||
virtual void removeInstances(const RemoveInstancesCommand &command) = 0;
|
||||
virtual void removeProperties(const RemovePropertiesCommand &command) = 0;
|
||||
virtual void changePropertyBindings(const ChangeBindingsCommand &command) = 0;
|
||||
@@ -83,6 +83,8 @@ public:
|
||||
virtual void token(const TokenCommand &command) = 0;
|
||||
virtual void removeSharedMemory(const RemoveSharedMemoryCommand &command) = 0;
|
||||
virtual void changeSelection(const ChangeSelectionCommand &command) = 0;
|
||||
virtual void inputEvent(const InputEventCommand &command) = 0;
|
||||
virtual void view3DAction(const View3DActionCommand &command) = 0;
|
||||
|
||||
virtual void benchmark(const QString &)
|
||||
{}
|
||||
|
@@ -30,16 +30,11 @@ import QtQuick.Controls 2.0
|
||||
import QtGraphicalEffects 1.0
|
||||
import MouseArea3D 1.0
|
||||
|
||||
Window {
|
||||
id: viewWindow
|
||||
Item {
|
||||
id: viewRoot
|
||||
width: 1024
|
||||
height: 768
|
||||
minimumHeight: 200
|
||||
minimumWidth: 200
|
||||
visible: true
|
||||
title: qsTr("3D Edit View [") + sceneId + qsTr("]")
|
||||
// need all those flags otherwise the title bar disappears after setting WindowStaysOnTopHint flag later
|
||||
flags: Qt.Window | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint
|
||||
|
||||
property Node activeScene: null
|
||||
property View3D editView: null
|
||||
@@ -48,6 +43,7 @@ Window {
|
||||
property alias showEditLight: btnEditViewLight.toggled
|
||||
property alias usePerspective: btnPerspective.toggled
|
||||
property alias globalOrientation: btnLocalGlobal.toggled
|
||||
property alias contentItem: contentItem
|
||||
|
||||
property Node selectedNode: null // This is non-null only in single selection case
|
||||
property var selectedNodes: [] // All selected nodes
|
||||
@@ -57,6 +53,8 @@ Window {
|
||||
property var selectionBoxes: []
|
||||
property rect viewPortRect: Qt.rect(0, 0, 1000, 1000)
|
||||
|
||||
property bool showButtons: false
|
||||
|
||||
signal selectionChanged(var selectedNodes)
|
||||
signal commitObjectProperty(var object, var propName)
|
||||
signal changeObjectProperty(var object, var propName)
|
||||
@@ -86,7 +84,7 @@ Window {
|
||||
editView.cameraZoomFactor = Qt.binding(function() {return cameraControl._zoomFactor;});
|
||||
|
||||
selectionBoxes.length = 0;
|
||||
updateToolStates();
|
||||
updateToolStates(_generalHelper.getToolStates(sceneId), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,54 +97,62 @@ Window {
|
||||
_generalHelper.enableItemUpdate(editView, (scene && scene === activeScene));
|
||||
}
|
||||
|
||||
|
||||
function restoreWindowState()
|
||||
function fitToView()
|
||||
{
|
||||
// It is expected that tool states have been initialized before calling this
|
||||
_generalHelper.restoreWindowState(viewWindow);
|
||||
if (editView) {
|
||||
var targetNode = selectedNodes.length > 0
|
||||
? selectionBoxes[0].model : null;
|
||||
cameraControl.focusObject(targetNode, editView.camera.rotation, true);
|
||||
}
|
||||
}
|
||||
|
||||
function updateToolStates()
|
||||
// If resetToDefault is true, tool states not specifically set to anything will be reset to
|
||||
// their default state.
|
||||
function updateToolStates(toolStates, resetToDefault)
|
||||
{
|
||||
var toolStates = _generalHelper.getToolStates(sceneId);
|
||||
if ("showEditLight" in toolStates)
|
||||
showEditLight = toolStates.showEditLight;
|
||||
else
|
||||
else if (resetToDefault)
|
||||
showEditLight = false;
|
||||
if ("usePerspective" in toolStates)
|
||||
usePerspective = toolStates.usePerspective;
|
||||
else
|
||||
else if (resetToDefault)
|
||||
usePerspective = false;
|
||||
if ("globalOrientation" in toolStates)
|
||||
globalOrientation = toolStates.globalOrientation;
|
||||
else
|
||||
else if (resetToDefault)
|
||||
globalOrientation = false;
|
||||
|
||||
var groupIndex;
|
||||
var group;
|
||||
var i;
|
||||
btnSelectItem.selected = false;
|
||||
btnSelectGroup.selected = true;
|
||||
|
||||
if ("groupSelect" in toolStates) {
|
||||
groupIndex = toolStates.groupSelect;
|
||||
group = toolbarButtons.buttonGroups["groupSelect"];
|
||||
for (i = 0; i < group.length; ++i)
|
||||
group[i].selected = (i === groupIndex);
|
||||
_generalHelper.storeToolState(sceneId, "groupSelect", groupIndex)
|
||||
} else if (resetToDefault) {
|
||||
btnSelectItem.selected = true;
|
||||
btnSelectGroup.selected = false;
|
||||
}
|
||||
|
||||
btnRotate.selected = false;
|
||||
btnScale.selected = false;
|
||||
btnMove.selected = true;
|
||||
if ("groupTransform" in toolStates) {
|
||||
groupIndex = toolStates.groupTransform;
|
||||
group = toolbarButtons.buttonGroups["groupTransform"];
|
||||
for (i = 0; i < group.length; ++i)
|
||||
group[i].selected = (i === groupIndex);
|
||||
_generalHelper.storeToolState(sceneId, "groupTransform", groupIndex)
|
||||
} else if (resetToDefault) {
|
||||
btnRotate.selected = false;
|
||||
btnScale.selected = false;
|
||||
btnMove.selected = true;
|
||||
}
|
||||
|
||||
if ("editCamState" in toolStates)
|
||||
cameraControl.restoreCameraState(toolStates.editCamState);
|
||||
else
|
||||
else if (resetToDefault)
|
||||
cameraControl.restoreDefaultState();
|
||||
}
|
||||
|
||||
@@ -324,37 +330,27 @@ Window {
|
||||
_generalHelper.requestOverlayUpdate();
|
||||
}
|
||||
|
||||
onWidthChanged: {
|
||||
_generalHelper.requestOverlayUpdate();
|
||||
_generalHelper.storeWindowState(viewWindow);
|
||||
}
|
||||
onHeightChanged: {
|
||||
_generalHelper.requestOverlayUpdate();
|
||||
_generalHelper.storeWindowState(viewWindow);
|
||||
|
||||
}
|
||||
onXChanged: _generalHelper.storeWindowState(viewWindow);
|
||||
onYChanged: _generalHelper.storeWindowState(viewWindow);
|
||||
onWindowStateChanged: _generalHelper.storeWindowState(viewWindow);
|
||||
onWidthChanged: _generalHelper.requestOverlayUpdate()
|
||||
onHeightChanged: _generalHelper.requestOverlayUpdate()
|
||||
|
||||
Node {
|
||||
id: overlayScene
|
||||
|
||||
PerspectiveCamera {
|
||||
id: overlayPerspectiveCamera
|
||||
clipFar: viewWindow.editView ? viewWindow.editView.perpectiveCamera.clipFar : 1000
|
||||
clipNear: viewWindow.editView ? viewWindow.editView.perpectiveCamera.clipNear : 1
|
||||
position: viewWindow.editView ? viewWindow.editView.perpectiveCamera.position : Qt.vector3d(0, 0, 0)
|
||||
rotation: viewWindow.editView ? viewWindow.editView.perpectiveCamera.rotation : Qt.vector3d(0, 0, 0)
|
||||
clipFar: viewRoot.editView ? viewRoot.editView.perpectiveCamera.clipFar : 1000
|
||||
clipNear: viewRoot.editView ? viewRoot.editView.perpectiveCamera.clipNear : 1
|
||||
position: viewRoot.editView ? viewRoot.editView.perpectiveCamera.position : Qt.vector3d(0, 0, 0)
|
||||
rotation: viewRoot.editView ? viewRoot.editView.perpectiveCamera.rotation : Qt.vector3d(0, 0, 0)
|
||||
}
|
||||
|
||||
OrthographicCamera {
|
||||
id: overlayOrthoCamera
|
||||
clipFar: viewWindow.editView ? viewWindow.editView.orthoCamera.clipFar : 1000
|
||||
clipNear: viewWindow.editView ? viewWindow.editView.orthoCamera.clipNear : 1
|
||||
position: viewWindow.editView ? viewWindow.editView.orthoCamera.position : Qt.vector3d(0, 0, 0)
|
||||
rotation: viewWindow.editView ? viewWindow.editView.orthoCamera.rotation : Qt.vector3d(0, 0, 0)
|
||||
scale: viewWindow.editView ? viewWindow.editView.orthoCamera.scale : Qt.vector3d(0, 0, 0)
|
||||
clipFar: viewRoot.editView ? viewRoot.editView.orthoCamera.clipFar : 1000
|
||||
clipNear: viewRoot.editView ? viewRoot.editView.orthoCamera.clipNear : 1
|
||||
position: viewRoot.editView ? viewRoot.editView.orthoCamera.position : Qt.vector3d(0, 0, 0)
|
||||
rotation: viewRoot.editView ? viewRoot.editView.orthoCamera.rotation : Qt.vector3d(0, 0, 0)
|
||||
scale: viewRoot.editView ? viewRoot.editView.orthoCamera.scale : Qt.vector3d(0, 0, 0)
|
||||
}
|
||||
|
||||
MouseArea3D {
|
||||
@@ -366,41 +362,41 @@ Window {
|
||||
id: moveGizmo
|
||||
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
|
||||
highlightOnHover: true
|
||||
targetNode: viewWindow.selectedNode
|
||||
globalOrientation: viewWindow.globalOrientation
|
||||
visible: viewWindow.selectedNode && btnMove.selected
|
||||
targetNode: viewRoot.selectedNode
|
||||
globalOrientation: viewRoot.globalOrientation
|
||||
visible: viewRoot.selectedNode && btnMove.selected
|
||||
view3D: overlayView
|
||||
dragHelper: gizmoDragHelper
|
||||
|
||||
onPositionCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "position")
|
||||
onPositionMove: viewWindow.changeObjectProperty(viewWindow.selectedNode, "position")
|
||||
onPositionCommit: viewRoot.commitObjectProperty(viewRoot.selectedNode, "position")
|
||||
onPositionMove: viewRoot.changeObjectProperty(viewRoot.selectedNode, "position")
|
||||
}
|
||||
|
||||
ScaleGizmo {
|
||||
id: scaleGizmo
|
||||
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
|
||||
highlightOnHover: true
|
||||
targetNode: viewWindow.selectedNode
|
||||
visible: viewWindow.selectedNode && btnScale.selected
|
||||
targetNode: viewRoot.selectedNode
|
||||
visible: viewRoot.selectedNode && btnScale.selected
|
||||
view3D: overlayView
|
||||
dragHelper: gizmoDragHelper
|
||||
|
||||
onScaleCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "scale")
|
||||
onScaleChange: viewWindow.changeObjectProperty(viewWindow.selectedNode, "scale")
|
||||
onScaleCommit: viewRoot.commitObjectProperty(viewRoot.selectedNode, "scale")
|
||||
onScaleChange: viewRoot.changeObjectProperty(viewRoot.selectedNode, "scale")
|
||||
}
|
||||
|
||||
RotateGizmo {
|
||||
id: rotateGizmo
|
||||
scale: autoScale.getScale(Qt.vector3d(7, 7, 7))
|
||||
highlightOnHover: true
|
||||
targetNode: viewWindow.selectedNode
|
||||
globalOrientation: viewWindow.globalOrientation
|
||||
visible: viewWindow.selectedNode && btnRotate.selected
|
||||
targetNode: viewRoot.selectedNode
|
||||
globalOrientation: viewRoot.globalOrientation
|
||||
visible: viewRoot.selectedNode && btnRotate.selected
|
||||
view3D: overlayView
|
||||
dragHelper: gizmoDragHelper
|
||||
|
||||
onRotateCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "rotation")
|
||||
onRotateChange: viewWindow.changeObjectProperty(viewWindow.selectedNode, "rotation")
|
||||
onRotateCommit: viewRoot.commitObjectProperty(viewRoot.selectedNode, "rotation")
|
||||
onRotateChange: viewRoot.changeObjectProperty(viewRoot.selectedNode, "rotation")
|
||||
}
|
||||
|
||||
AutoScaleHelper {
|
||||
@@ -412,31 +408,31 @@ Window {
|
||||
|
||||
Line3D {
|
||||
id: pivotLine
|
||||
visible: viewWindow.selectedNode
|
||||
visible: viewRoot.selectedNode
|
||||
name: "3D Edit View Pivot Line"
|
||||
color: "#ddd600"
|
||||
|
||||
function flipIfNeeded(vec) {
|
||||
if (!viewWindow.selectedNode || viewWindow.selectedNode.orientation === Node.LeftHanded)
|
||||
if (!viewRoot.selectedNode || viewRoot.selectedNode.orientation === Node.LeftHanded)
|
||||
return vec;
|
||||
else
|
||||
return Qt.vector3d(vec.x, vec.y, -vec.z);
|
||||
}
|
||||
|
||||
startPos: viewWindow.selectedNode ? flipIfNeeded(viewWindow.selectedNode.scenePosition)
|
||||
startPos: viewRoot.selectedNode ? flipIfNeeded(viewRoot.selectedNode.scenePosition)
|
||||
: Qt.vector3d(0, 0, 0)
|
||||
Connections {
|
||||
target: viewWindow
|
||||
target: viewRoot
|
||||
onSelectedNodeChanged: {
|
||||
pivotLine.endPos = pivotLine.flipIfNeeded(gizmoDragHelper.pivotScenePosition(
|
||||
viewWindow.selectedNode));
|
||||
viewRoot.selectedNode));
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: viewWindow.selectedNode
|
||||
target: viewRoot.selectedNode
|
||||
onSceneTransformChanged: {
|
||||
pivotLine.endPos = pivotLine.flipIfNeeded(gizmoDragHelper.pivotScenePosition(
|
||||
viewWindow.selectedNode));
|
||||
viewRoot.selectedNode));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,243 +453,235 @@ Window {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: viewRect
|
||||
Item {
|
||||
id: contentItem
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 1.0; color: "#222222" }
|
||||
GradientStop { position: 0.0; color: "#999999" }
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
Rectangle {
|
||||
id: viewRect
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
if (viewWindow.editView) {
|
||||
var pickResult = viewWindow.editView.pick(mouse.x, mouse.y);
|
||||
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
|
||||
mouse.modifiers & Qt.ControlModifier);
|
||||
if (!pickResult.objectHit)
|
||||
mouse.accepted = false;
|
||||
focus: true
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 1.0; color: "#222222" }
|
||||
GradientStop { position: 0.0; color: "#999999" }
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
if (viewRoot.editView) {
|
||||
var pickResult = viewRoot.editView.pick(mouse.x, mouse.y);
|
||||
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
|
||||
mouse.modifiers & Qt.ControlModifier);
|
||||
if (!pickResult.objectHit)
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
View3D {
|
||||
id: overlayView
|
||||
anchors.fill: parent
|
||||
camera: usePerspective ? overlayPerspectiveCamera : overlayOrthoCamera
|
||||
importScene: overlayScene
|
||||
z: 2
|
||||
}
|
||||
View3D {
|
||||
id: overlayView
|
||||
anchors.fill: parent
|
||||
camera: usePerspective ? overlayPerspectiveCamera : overlayOrthoCamera
|
||||
importScene: overlayScene
|
||||
z: 2
|
||||
}
|
||||
|
||||
Overlay2D {
|
||||
id: gizmoLabel
|
||||
targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo
|
||||
targetView: overlayView
|
||||
visible: targetNode.dragging
|
||||
z: 3
|
||||
Overlay2D {
|
||||
id: gizmoLabel
|
||||
targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo
|
||||
targetView: overlayView
|
||||
visible: targetNode.dragging
|
||||
z: 3
|
||||
|
||||
Rectangle {
|
||||
color: "white"
|
||||
x: -width / 2
|
||||
y: -height - 8
|
||||
width: gizmoLabelText.width + 4
|
||||
height: gizmoLabelText.height + 4
|
||||
border.width: 1
|
||||
Text {
|
||||
id: gizmoLabelText
|
||||
text: {
|
||||
var l = Qt.locale();
|
||||
var targetProperty;
|
||||
if (viewWindow.selectedNode) {
|
||||
if (gizmoLabel.targetNode === moveGizmo)
|
||||
targetProperty = viewWindow.selectedNode.position;
|
||||
else
|
||||
targetProperty = viewWindow.selectedNode.scale;
|
||||
return qsTr("x:") + Number(targetProperty.x).toLocaleString(l, 'f', 1)
|
||||
+ qsTr(" y:") + Number(targetProperty.y).toLocaleString(l, 'f', 1)
|
||||
+ qsTr(" z:") + Number(targetProperty.z).toLocaleString(l, 'f', 1);
|
||||
} else {
|
||||
return "";
|
||||
Rectangle {
|
||||
color: "white"
|
||||
x: -width / 2
|
||||
y: -height - 8
|
||||
width: gizmoLabelText.width + 4
|
||||
height: gizmoLabelText.height + 4
|
||||
border.width: 1
|
||||
Text {
|
||||
id: gizmoLabelText
|
||||
text: {
|
||||
var l = Qt.locale();
|
||||
var targetProperty;
|
||||
if (viewRoot.selectedNode) {
|
||||
if (gizmoLabel.targetNode === moveGizmo)
|
||||
targetProperty = viewRoot.selectedNode.position;
|
||||
else
|
||||
targetProperty = viewRoot.selectedNode.scale;
|
||||
return qsTr("x:") + Number(targetProperty.x).toLocaleString(l, 'f', 1)
|
||||
+ qsTr(" y:") + Number(targetProperty.y).toLocaleString(l, 'f', 1)
|
||||
+ qsTr(" z:") + Number(targetProperty.z).toLocaleString(l, 'f', 1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
EditCameraController {
|
||||
id: cameraControl
|
||||
camera: viewRoot.editView ? viewRoot.editView.camera : null
|
||||
anchors.fill: parent
|
||||
view3d: viewRoot.editView
|
||||
sceneId: viewRoot.sceneId
|
||||
}
|
||||
}
|
||||
|
||||
EditCameraController {
|
||||
id: cameraControl
|
||||
camera: viewWindow.editView ? viewWindow.editView.camera : null
|
||||
anchors.fill: parent
|
||||
view3d: viewWindow.editView
|
||||
sceneId: viewWindow.sceneId
|
||||
}
|
||||
}
|
||||
Rectangle { // toolbar
|
||||
id: toolbar
|
||||
color: "#9F000000"
|
||||
width: 35
|
||||
height: toolbarButtons.height
|
||||
visible: viewRoot.showButtons
|
||||
|
||||
Rectangle { // toolbar
|
||||
id: toolbar
|
||||
color: "#9F000000"
|
||||
width: 35
|
||||
height: toolbarButtons.height
|
||||
|
||||
Column {
|
||||
id: toolbarButtons
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 5
|
||||
padding: 5
|
||||
|
||||
// Button groups must be defined in parent object of buttons
|
||||
property var buttonGroups: {
|
||||
"groupSelect": [btnSelectGroup, btnSelectItem],
|
||||
"groupTransform": [btnMove, btnRotate, btnScale]
|
||||
}
|
||||
|
||||
ToolBarButton {
|
||||
id: btnSelectItem
|
||||
selected: true
|
||||
tooltip: qsTr("Select Item")
|
||||
shortcut: "Q"
|
||||
currentShortcut: selected ? "" : shortcut
|
||||
tool: "item_selection"
|
||||
buttonGroup: "groupSelect"
|
||||
sceneId: viewWindow.sceneId
|
||||
}
|
||||
|
||||
ToolBarButton {
|
||||
id: btnSelectGroup
|
||||
tooltip: qsTr("Select Group")
|
||||
shortcut: "Q"
|
||||
currentShortcut: btnSelectItem.currentShortcut === shortcut ? "" : shortcut
|
||||
tool: "group_selection"
|
||||
buttonGroup: "groupSelect"
|
||||
sceneId: viewWindow.sceneId
|
||||
}
|
||||
|
||||
Rectangle { // separator
|
||||
width: 25
|
||||
height: 1
|
||||
color: "#f1f1f1"
|
||||
Column {
|
||||
id: toolbarButtons
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
spacing: 5
|
||||
padding: 5
|
||||
|
||||
ToolBarButton {
|
||||
id: btnMove
|
||||
selected: true
|
||||
tooltip: qsTr("Move current selection")
|
||||
shortcut: "W"
|
||||
currentShortcut: shortcut
|
||||
tool: "move"
|
||||
buttonGroup: "groupTransform"
|
||||
sceneId: viewWindow.sceneId
|
||||
}
|
||||
// Button groups must be defined in parent object of buttons
|
||||
property var buttonGroups: {
|
||||
"groupSelect": [btnSelectGroup, btnSelectItem],
|
||||
"groupTransform": [btnMove, btnRotate, btnScale]
|
||||
}
|
||||
|
||||
ToolBarButton {
|
||||
id: btnRotate
|
||||
tooltip: qsTr("Rotate current selection")
|
||||
shortcut: "E"
|
||||
currentShortcut: shortcut
|
||||
tool: "rotate"
|
||||
buttonGroup: "groupTransform"
|
||||
sceneId: viewWindow.sceneId
|
||||
}
|
||||
ToolBarButton {
|
||||
id: btnSelectItem
|
||||
selected: true
|
||||
tooltip: qsTr("Select Item")
|
||||
shortcut: "Q"
|
||||
currentShortcut: selected ? "" : shortcut
|
||||
tool: "item_selection"
|
||||
buttonGroup: "groupSelect"
|
||||
sceneId: viewRoot.sceneId
|
||||
}
|
||||
|
||||
ToolBarButton {
|
||||
id: btnScale
|
||||
tooltip: qsTr("Scale current selection")
|
||||
shortcut: "R"
|
||||
currentShortcut: shortcut
|
||||
tool: "scale"
|
||||
buttonGroup: "groupTransform"
|
||||
sceneId: viewWindow.sceneId
|
||||
}
|
||||
ToolBarButton {
|
||||
id: btnSelectGroup
|
||||
tooltip: qsTr("Select Group")
|
||||
shortcut: "Q"
|
||||
currentShortcut: btnSelectItem.currentShortcut === shortcut ? "" : shortcut
|
||||
tool: "group_selection"
|
||||
buttonGroup: "groupSelect"
|
||||
sceneId: viewRoot.sceneId
|
||||
}
|
||||
|
||||
Rectangle { // separator
|
||||
width: 25
|
||||
height: 1
|
||||
color: "#f1f1f1"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
Rectangle { // separator
|
||||
width: 25
|
||||
height: 1
|
||||
color: "#f1f1f1"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
ToolBarButton {
|
||||
id: btnFit
|
||||
tooltip: qsTr("Fit camera to current selection")
|
||||
shortcut: "F"
|
||||
currentShortcut: shortcut
|
||||
tool: "fit"
|
||||
togglable: false
|
||||
ToolBarButton {
|
||||
id: btnMove
|
||||
selected: true
|
||||
tooltip: qsTr("Move current selection")
|
||||
shortcut: "W"
|
||||
currentShortcut: shortcut
|
||||
tool: "move"
|
||||
buttonGroup: "groupTransform"
|
||||
sceneId: viewRoot.sceneId
|
||||
}
|
||||
|
||||
onSelectedChanged: {
|
||||
if (viewWindow.editView && selected) {
|
||||
var targetNode = viewWindow.selectedNodes.length > 0
|
||||
? selectionBoxes[0].model : null;
|
||||
cameraControl.focusObject(targetNode, viewWindow.editView.camera.rotation, true);
|
||||
ToolBarButton {
|
||||
id: btnRotate
|
||||
tooltip: qsTr("Rotate current selection")
|
||||
shortcut: "E"
|
||||
currentShortcut: shortcut
|
||||
tool: "rotate"
|
||||
buttonGroup: "groupTransform"
|
||||
sceneId: viewRoot.sceneId
|
||||
}
|
||||
|
||||
ToolBarButton {
|
||||
id: btnScale
|
||||
tooltip: qsTr("Scale current selection")
|
||||
shortcut: "R"
|
||||
currentShortcut: shortcut
|
||||
tool: "scale"
|
||||
buttonGroup: "groupTransform"
|
||||
sceneId: viewRoot.sceneId
|
||||
}
|
||||
|
||||
Rectangle { // separator
|
||||
width: 25
|
||||
height: 1
|
||||
color: "#f1f1f1"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
ToolBarButton {
|
||||
id: btnFit
|
||||
tooltip: qsTr("Fit camera to current selection")
|
||||
shortcut: "F"
|
||||
currentShortcut: shortcut
|
||||
tool: "fit"
|
||||
togglable: false
|
||||
|
||||
onSelectedChanged: {
|
||||
if (selected)
|
||||
viewRoot.fitToView();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AxisHelper {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: 100
|
||||
height: width
|
||||
editCameraCtrl: cameraControl
|
||||
selectedNode : viewWindow.selectedNodes.length ? selectionBoxes[0].model : null
|
||||
}
|
||||
|
||||
Rectangle { // top controls bar
|
||||
color: "#aa000000"
|
||||
width: 290
|
||||
height: btnPerspective.height + 10
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 100
|
||||
|
||||
Row {
|
||||
padding: 5
|
||||
anchors.fill: parent
|
||||
ToggleButton {
|
||||
id: btnPerspective
|
||||
width: 105
|
||||
tooltip: qsTr("Toggle Perspective / Orthographic Projection")
|
||||
states: [{iconId: "ortho", text: qsTr("Orthographic")}, {iconId: "persp", text: qsTr("Perspective")}]
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
id: btnLocalGlobal
|
||||
width: 65
|
||||
tooltip: qsTr("Toggle Global / Local Orientation")
|
||||
states: [{iconId: "local", text: qsTr("Local")}, {iconId: "global", text: qsTr("Global")}]
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
id: btnEditViewLight
|
||||
width: 110
|
||||
toggleBackground: true
|
||||
tooltip: qsTr("Toggle Edit Light")
|
||||
states: [{iconId: "edit_light_off", text: qsTr("Edit Light Off")}, {iconId: "edit_light_on", text: qsTr("Edit Light On")}]
|
||||
}
|
||||
AxisHelper {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: 100
|
||||
height: width
|
||||
editCameraCtrl: cameraControl
|
||||
selectedNode : viewRoot.selectedNodes.length ? selectionBoxes[0].model : null
|
||||
}
|
||||
|
||||
}
|
||||
Rectangle { // top controls bar
|
||||
color: "#aa000000"
|
||||
width: 290
|
||||
height: btnPerspective.height + 10
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 100
|
||||
visible: viewRoot.showButtons
|
||||
|
||||
Text {
|
||||
id: helpText
|
||||
Row {
|
||||
padding: 5
|
||||
anchors.fill: parent
|
||||
ToggleButton {
|
||||
id: btnPerspective
|
||||
width: 105
|
||||
tooltip: qsTr("Toggle Perspective / Orthographic Projection")
|
||||
states: [{iconId: "ortho", text: qsTr("Orthographic")}, {iconId: "persp", text: qsTr("Perspective")}]
|
||||
}
|
||||
|
||||
property string modKey: _generalHelper.isMacOS ? qsTr("Option") : qsTr("Alt")
|
||||
ToggleButton {
|
||||
id: btnLocalGlobal
|
||||
width: 65
|
||||
tooltip: qsTr("Toggle Global / Local Orientation")
|
||||
states: [{iconId: "local", text: qsTr("Local")}, {iconId: "global", text: qsTr("Global")}]
|
||||
}
|
||||
|
||||
color: "white"
|
||||
text: qsTr("Camera controls: ") + modKey
|
||||
+ qsTr(" + mouse press and drag. Left: Rotate, Middle: Pan, Right/Wheel: Zoom.")
|
||||
anchors.bottom: parent.bottom
|
||||
ToggleButton {
|
||||
id: btnEditViewLight
|
||||
width: 110
|
||||
toggleBackground: true
|
||||
tooltip: qsTr("Toggle Edit Light")
|
||||
states: [{iconId: "edit_light_off", text: qsTr("Edit Light Off")}, {iconId: "edit_light_on", text: qsTr("Edit Light On")}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
50
share/qtcreator/qml/qmlpuppet/mockfiles/EditWindow3D.qml
Normal file
50
share/qtcreator/qml/qmlpuppet/mockfiles/EditWindow3D.qml
Normal file
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
|
||||
Window {
|
||||
id: viewWindow
|
||||
width: 1024
|
||||
height: 768
|
||||
visible: true
|
||||
title: qsTr("3D Edit View [") + sceneId + qsTr("]")
|
||||
// need all those flags otherwise the title bar disappears after setting WindowStaysOnTopHint flag later
|
||||
flags: Qt.Window | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint
|
||||
|
||||
property alias editViewRoot: windowContentItem
|
||||
|
||||
onWidthChanged: _generalHelper.storeWindowState();
|
||||
onHeightChanged: _generalHelper.storeWindowState();
|
||||
onXChanged: _generalHelper.storeWindowState();
|
||||
onYChanged: _generalHelper.storeWindowState();
|
||||
onWindowStateChanged: _generalHelper.storeWindowState();
|
||||
|
||||
EditView3D {
|
||||
id: windowContentItem
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
@@ -45,7 +45,7 @@ Node {
|
||||
}
|
||||
|
||||
property alias iconSource: iconImage.source
|
||||
property alias overlayColor: colorOverlay.color
|
||||
//property alias overlayColor: colorOverlay.color
|
||||
|
||||
signal positionCommit()
|
||||
signal clicked(Node node, bool multi)
|
||||
@@ -94,15 +94,15 @@ Node {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
}
|
||||
}
|
||||
ColorOverlay {
|
||||
id: colorOverlay
|
||||
anchors.fill: parent
|
||||
cached: true
|
||||
source: iconImage
|
||||
color: "transparent"
|
||||
opacity: 0.6
|
||||
}
|
||||
|
||||
// ColorOverlay doesn't work correctly with hidden windows so commenting it out for now
|
||||
// ColorOverlay {
|
||||
// id: colorOverlay
|
||||
// anchors.fill: parent
|
||||
// cached: true
|
||||
// source: iconImage
|
||||
// color: "#00000000"
|
||||
// opacity: 0.6
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -37,5 +37,6 @@ IconGizmo {
|
||||
: "qrc:///qtquickplugin/mockfiles/images/point_light_gradient.png"
|
||||
: "qrc:///qtquickplugin/mockfiles/images/point_light_gradient.png"
|
||||
|
||||
overlayColor: targetNode ? targetNode.color : "transparent"
|
||||
// ColorOverlay doesn't work correctly with hidden windows so commenting it out for now
|
||||
//overlayColor: targetNode ? targetNode.color : "transparent"
|
||||
}
|
||||
|
@@ -48,7 +48,7 @@
|
||||
namespace QmlDesigner {
|
||||
namespace Internal {
|
||||
|
||||
const QString globalStateId = QStringLiteral("@GTS"); // global tool state
|
||||
const QString _globalStateId = QStringLiteral("@GTS"); // global tool state
|
||||
|
||||
GeneralHelper::GeneralHelper()
|
||||
: QObject()
|
||||
@@ -260,37 +260,42 @@ void GeneralHelper::initToolStates(const QString &sceneId, const QVariantMap &to
|
||||
m_toolStates[sceneId] = toolStates;
|
||||
}
|
||||
|
||||
void GeneralHelper::storeWindowState(QQuickWindow *w)
|
||||
void GeneralHelper::storeWindowState()
|
||||
{
|
||||
if (!m_edit3DWindow)
|
||||
return;
|
||||
|
||||
QVariantMap windowState;
|
||||
const QRect geometry = w->geometry();
|
||||
const bool maximized = w->windowState() == Qt::WindowMaximized;
|
||||
const QRect geometry = m_edit3DWindow->geometry();
|
||||
const bool maximized = m_edit3DWindow->windowState() == Qt::WindowMaximized;
|
||||
windowState.insert("maximized", maximized);
|
||||
windowState.insert("geometry", geometry);
|
||||
|
||||
storeToolState(globalStateId, "windowState", windowState, 500);
|
||||
storeToolState(globalStateId(), "windowState", windowState, 500);
|
||||
}
|
||||
|
||||
void GeneralHelper::restoreWindowState(QQuickWindow *w)
|
||||
void GeneralHelper::restoreWindowState()
|
||||
{
|
||||
if (m_toolStates.contains(globalStateId)) {
|
||||
const QVariantMap &globalStateMap = m_toolStates[globalStateId];
|
||||
if (!m_edit3DWindow)
|
||||
return;
|
||||
|
||||
if (m_toolStates.contains(globalStateId())) {
|
||||
const QVariantMap &globalStateMap = m_toolStates[globalStateId()];
|
||||
const QString stateKey = QStringLiteral("windowState");
|
||||
if (globalStateMap.contains(stateKey)) {
|
||||
QVariantMap windowState = globalStateMap[stateKey].value<QVariantMap>();
|
||||
|
||||
doRestoreWindowState(w, windowState);
|
||||
doRestoreWindowState(windowState);
|
||||
|
||||
// If the mouse cursor at puppet launch time is in a different screen than the one where the
|
||||
// view geometry was saved on, the initial position and size can be incorrect, but if
|
||||
// we reset the geometry again asynchronously, it should end up with correct geometry.
|
||||
QTimer::singleShot(0, [this, w, windowState]() {
|
||||
doRestoreWindowState(w, windowState);
|
||||
|
||||
QTimer::singleShot(0, [w]() {
|
||||
QTimer::singleShot(0, [this, windowState]() {
|
||||
doRestoreWindowState(windowState);
|
||||
QTimer::singleShot(0, [this]() {
|
||||
// Make sure that the window is at least partially visible on the screen
|
||||
QRect geo = w->geometry();
|
||||
QRect sRect = w->screen()->geometry();
|
||||
QRect geo = m_edit3DWindow->geometry();
|
||||
QRect sRect = m_edit3DWindow->screen()->geometry();
|
||||
if (geo.left() > sRect.right() - 150)
|
||||
geo.moveRight(sRect.right());
|
||||
if (geo.right() < sRect.left() + 150)
|
||||
@@ -303,7 +308,7 @@ void GeneralHelper::restoreWindowState(QQuickWindow *w)
|
||||
geo.setWidth(sRect.width());
|
||||
if (geo.height() > sRect.height())
|
||||
geo.setHeight(sRect.height());
|
||||
w->setGeometry(geo);
|
||||
m_edit3DWindow->setGeometry(geo);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -324,7 +329,17 @@ QVariantMap GeneralHelper::getToolStates(const QString &sceneId)
|
||||
return {};
|
||||
}
|
||||
|
||||
void GeneralHelper::doRestoreWindowState(QQuickWindow *w, const QVariantMap &windowState)
|
||||
void GeneralHelper::setEdit3DWindow(QQuickWindow *w)
|
||||
{
|
||||
m_edit3DWindow = w;
|
||||
}
|
||||
|
||||
QString GeneralHelper::globalStateId() const
|
||||
{
|
||||
return _globalStateId;
|
||||
}
|
||||
|
||||
void GeneralHelper::doRestoreWindowState(const QVariantMap &windowState)
|
||||
{
|
||||
const QString geoKey = QStringLiteral("geometry");
|
||||
if (windowState.contains(geoKey)) {
|
||||
@@ -335,9 +350,9 @@ void GeneralHelper::doRestoreWindowState(QQuickWindow *w, const QVariantMap &win
|
||||
|
||||
QRect rect = windowState[geoKey].value<QRect>();
|
||||
|
||||
w->setGeometry(rect);
|
||||
m_edit3DWindow->setGeometry(rect);
|
||||
if (maximized)
|
||||
w->showMaximized();
|
||||
m_edit3DWindow->showMaximized();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qpointer.h>
|
||||
#include <QtGui/qvector3d.h>
|
||||
#include <QtGui/qmatrix4x4.h>
|
||||
|
||||
@@ -74,10 +75,12 @@ public:
|
||||
Q_INVOKABLE void storeToolState(const QString &sceneId, const QString &tool,
|
||||
const QVariant &state, int delayEmit = 0);
|
||||
void initToolStates(const QString &sceneId, const QVariantMap &toolStates);
|
||||
Q_INVOKABLE void storeWindowState(QQuickWindow *w);
|
||||
void restoreWindowState(QQuickWindow *w);
|
||||
Q_INVOKABLE void storeWindowState();
|
||||
void restoreWindowState();
|
||||
Q_INVOKABLE void enableItemUpdate(QQuickItem *item, bool enable);
|
||||
Q_INVOKABLE QVariantMap getToolStates(const QString &sceneId);
|
||||
void setEdit3DWindow(QQuickWindow *w);
|
||||
QString globalStateId() const;
|
||||
|
||||
bool isMacOS() const;
|
||||
|
||||
@@ -86,7 +89,7 @@ signals:
|
||||
void toolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState);
|
||||
|
||||
private slots:
|
||||
void doRestoreWindowState(QQuickWindow *w, const QVariantMap &windowState);
|
||||
void doRestoreWindowState(const QVariantMap &windowState);
|
||||
|
||||
private:
|
||||
void handlePendingToolStateUpdate();
|
||||
@@ -95,6 +98,7 @@ private:
|
||||
QTimer m_toolStateUpdateTimer;
|
||||
QHash<QString, QVariantMap> m_toolStates;
|
||||
QHash<QString, QVariantMap> m_toolStatesPending;
|
||||
QPointer<QQuickWindow> m_edit3DWindow;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -68,6 +68,8 @@
|
||||
#include <removesharedmemorycommand.h>
|
||||
#include <changeselectioncommand.h>
|
||||
#include <drop3dlibraryitemcommand.h>
|
||||
#include <inputeventcommand.h>
|
||||
#include <view3dactioncommand.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QQmlEngine>
|
||||
@@ -336,10 +338,6 @@ void NodeInstanceServer::update3DViewState(const Update3dViewStateCommand &/*com
|
||||
{
|
||||
}
|
||||
|
||||
void NodeInstanceServer::enable3DView(const Enable3DViewCommand &/*command*/)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeInstanceServer::changeSelection(const ChangeSelectionCommand & /*command*/)
|
||||
{
|
||||
}
|
||||
@@ -1395,6 +1393,16 @@ QStringList NodeInstanceServer::dummyDataDirectories(const QString& directoryPat
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInstanceServer::inputEvent(const InputEventCommand &command)
|
||||
{
|
||||
Q_UNUSED(command)
|
||||
}
|
||||
|
||||
void NodeInstanceServer::view3DAction(const View3DActionCommand &command)
|
||||
{
|
||||
Q_UNUSED(command)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -102,7 +102,6 @@ public:
|
||||
void createScene(const CreateSceneCommand &command) override;
|
||||
void clearScene(const ClearSceneCommand &command) override;
|
||||
void update3DViewState(const Update3dViewStateCommand &command) override;
|
||||
void enable3DView(const Enable3DViewCommand &command) override;
|
||||
void removeInstances(const RemoveInstancesCommand &command) override;
|
||||
void removeProperties(const RemovePropertiesCommand &command) override;
|
||||
void reparentInstances(const ReparentInstancesCommand &command) override;
|
||||
@@ -112,6 +111,8 @@ public:
|
||||
void token(const TokenCommand &command) override;
|
||||
void removeSharedMemory(const RemoveSharedMemoryCommand &command) override;
|
||||
void changeSelection(const ChangeSelectionCommand &command) override;
|
||||
void inputEvent(const InputEventCommand &command) override;
|
||||
void view3DAction(const View3DActionCommand &command) override;
|
||||
|
||||
ServerNodeInstance instanceForId(qint32 id) const;
|
||||
bool hasInstanceForId(qint32 id) const;
|
||||
|
@@ -41,7 +41,6 @@
|
||||
#include "clearscenecommand.h"
|
||||
#include "reparentinstancescommand.h"
|
||||
#include "update3dviewstatecommand.h"
|
||||
#include "enable3dviewcommand.h"
|
||||
#include "changevaluescommand.h"
|
||||
#include "changebindingscommand.h"
|
||||
#include "changeidscommand.h"
|
||||
@@ -63,6 +62,8 @@
|
||||
#include "drop3dlibraryitemcommand.h"
|
||||
#include "puppettocreatorcommand.h"
|
||||
#include "view3dclosedcommand.h"
|
||||
#include "inputeventcommand.h"
|
||||
#include "view3dactioncommand.h"
|
||||
|
||||
#include "dummycontextobject.h"
|
||||
#include "../editor3d/generalhelper.h"
|
||||
@@ -73,6 +74,7 @@
|
||||
#include "../editor3d/linegeometry.h"
|
||||
|
||||
#include <designersupportdelegate.h>
|
||||
#include <qmlprivategate.h>
|
||||
|
||||
#include <QVector3D>
|
||||
#include <QQmlProperty>
|
||||
@@ -80,6 +82,8 @@
|
||||
#include <QQuickView>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
#include <QtGui/qevent.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
|
||||
#ifdef QUICK3D_MODULE
|
||||
#include <QtQuick3D/private/qquick3dnode_p.h>
|
||||
@@ -165,53 +169,72 @@ bool Qt5InformationNodeInstanceServer::dropAcceptable(QDragMoveEvent *event) con
|
||||
return canBeDropped == "true" || canBeDropped == "True";
|
||||
}
|
||||
|
||||
QObject *Qt5InformationNodeInstanceServer::createEditView3D(QQmlEngine *engine)
|
||||
void Qt5InformationNodeInstanceServer::createEditView3D()
|
||||
{
|
||||
#ifdef QUICK3D_MODULE
|
||||
auto helper = new QmlDesigner::Internal::GeneralHelper();
|
||||
QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::toolStateChanged,
|
||||
this, &Qt5InformationNodeInstanceServer::handleToolStateChanged);
|
||||
engine->rootContext()->setContextProperty("_generalHelper", helper);
|
||||
m_3dHelper = helper;
|
||||
static bool showEditView = qEnvironmentVariableIsSet("QMLDESIGNER_QUICK3D_SHOW_EDIT_WINDOW");
|
||||
|
||||
qmlRegisterType<QmlDesigner::Internal::MouseArea3D>("MouseArea3D", 1, 0, "MouseArea3D");
|
||||
qmlRegisterType<QmlDesigner::Internal::CameraGeometry>("CameraGeometry", 1, 0, "CameraGeometry");
|
||||
qmlRegisterType<QmlDesigner::Internal::GridGeometry>("GridGeometry", 1, 0, "GridGeometry");
|
||||
qmlRegisterType<QmlDesigner::Internal::SelectionBoxGeometry>("SelectionBoxGeometry", 1, 0, "SelectionBoxGeometry");
|
||||
qmlRegisterType<QmlDesigner::Internal::LineGeometry>("LineGeometry", 1, 0, "LineGeometry");
|
||||
#endif
|
||||
|
||||
QQmlComponent component(engine, QUrl("qrc:/qtquickplugin/mockfiles/EditView3D.qml"));
|
||||
auto helper = new QmlDesigner::Internal::GeneralHelper();
|
||||
QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::toolStateChanged,
|
||||
this, &Qt5InformationNodeInstanceServer::handleToolStateChanged);
|
||||
engine()->rootContext()->setContextProperty("_generalHelper", helper);
|
||||
m_3dHelper = helper;
|
||||
|
||||
QWindow *window = qobject_cast<QWindow *>(component.create());
|
||||
QQmlComponent component(engine());
|
||||
if (showEditView) {
|
||||
component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/EditWindow3D.qml"));
|
||||
m_editWindow3D = qobject_cast<QQuickWindow *>(component.create());
|
||||
m_editView3DRootItem = QQmlProperty::read(m_editWindow3D, "editViewRoot").value<QQuickItem *>();
|
||||
|
||||
if (!window) {
|
||||
qWarning() << "Could not create edit view 3D: " << component.errors();
|
||||
return nullptr;
|
||||
//For macOS we have to use the 4.1 core profile
|
||||
QSurfaceFormat surfaceFormat = m_editWindow3D->requestedFormat();
|
||||
surfaceFormat.setVersion(4, 1);
|
||||
surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
|
||||
m_editWindow3D->setFormat(surfaceFormat);
|
||||
} else {
|
||||
m_editView3D = new QQuickView(quickView()->engine(), quickView());
|
||||
m_editView3D->setFormat(quickView()->format());
|
||||
DesignerSupport::createOpenGLContext(m_editView3D.data());
|
||||
component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/EditView3D.qml"));
|
||||
m_editView3DRootItem = qobject_cast<QQuickItem *>(component.create());
|
||||
}
|
||||
|
||||
window->installEventFilter(this);
|
||||
QObject::connect(window, SIGNAL(selectionChanged(QVariant)),
|
||||
if (!m_editView3DRootItem) {
|
||||
qWarning() << "Could not create edit view 3D: " << component.errors();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!showEditView) {
|
||||
DesignerSupport::setRootItem(m_editView3D, m_editView3DRootItem);
|
||||
} else {
|
||||
m_editView3DRootItem->installEventFilter(this);
|
||||
QQmlProperty showButtonsProperty(m_editView3DRootItem, "showButtons", context());
|
||||
showButtonsProperty.write(QVariant(true));
|
||||
}
|
||||
|
||||
QObject::connect(m_editView3DRootItem, SIGNAL(selectionChanged(QVariant)),
|
||||
this, SLOT(handleSelectionChanged(QVariant)));
|
||||
QObject::connect(window, SIGNAL(commitObjectProperty(QVariant, QVariant)),
|
||||
QObject::connect(m_editView3DRootItem, SIGNAL(commitObjectProperty(QVariant, QVariant)),
|
||||
this, SLOT(handleObjectPropertyCommit(QVariant, QVariant)));
|
||||
QObject::connect(window, SIGNAL(changeObjectProperty(QVariant, QVariant)),
|
||||
QObject::connect(m_editView3DRootItem, SIGNAL(changeObjectProperty(QVariant, QVariant)),
|
||||
this, SLOT(handleObjectPropertyChange(QVariant, QVariant)));
|
||||
QObject::connect(&m_propertyChangeTimer, &QTimer::timeout,
|
||||
this, &Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout);
|
||||
QObject::connect(&m_selectionChangeTimer, &QTimer::timeout,
|
||||
this, &Qt5InformationNodeInstanceServer::handleSelectionChangeTimeout);
|
||||
QObject::connect(&m_renderTimer, &QTimer::timeout,
|
||||
this, &Qt5InformationNodeInstanceServer::doRender3DEditView);
|
||||
|
||||
//For macOS we have to use the 4.1 core profile
|
||||
QSurfaceFormat surfaceFormat = window->requestedFormat();
|
||||
surfaceFormat.setVersion(4, 1);
|
||||
surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
|
||||
window->setFormat(surfaceFormat);
|
||||
|
||||
#ifdef QUICK3D_MODULE
|
||||
helper->setParent(window);
|
||||
helper->setParent(m_editView3DRootItem);
|
||||
if (showEditView)
|
||||
helper->setEdit3DWindow(m_editWindow3D);
|
||||
#endif
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
// The selection has changed in the edit view 3D. Empty list indicates selection is cleared.
|
||||
@@ -357,10 +380,10 @@ void Qt5InformationNodeInstanceServer::handleNode3DDestroyed(QObject *obj)
|
||||
{
|
||||
#ifdef QUICK3D_MODULE
|
||||
if (qobject_cast<QQuick3DCamera *>(obj)) {
|
||||
QMetaObject::invokeMethod(m_editView3D, "releaseCameraGizmo",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "releaseCameraGizmo",
|
||||
Q_ARG(QVariant, objectToVariant(obj)));
|
||||
} else if (qobject_cast<QQuick3DAbstractLight *>(obj)) {
|
||||
QMetaObject::invokeMethod(m_editView3D, "releaseLightGizmo",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "releaseLightGizmo",
|
||||
Q_ARG(QVariant, objectToVariant(obj)));
|
||||
}
|
||||
removeNode3D(obj);
|
||||
@@ -376,29 +399,41 @@ void Qt5InformationNodeInstanceServer::updateView3DRect(QObject *view3D)
|
||||
viewPortrect = QRectF(0., 0., view3D->property("width").toDouble(),
|
||||
view3D->property("height").toDouble());
|
||||
}
|
||||
QQmlProperty viewPortProperty(m_editView3D, "viewPortRect", context());
|
||||
QQmlProperty viewPortProperty(m_editView3DRootItem, "viewPortRect", context());
|
||||
viewPortProperty.write(viewPortrect);
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D()
|
||||
{
|
||||
#ifdef QUICK3D_MODULE
|
||||
// Active scene change handling on qml side is async, so a deleted importScene would crash
|
||||
// editView when it updates next. Disable/enable edit view update synchronously to avoid this.
|
||||
QVariant activeSceneVar = objectToVariant(m_active3DScene);
|
||||
QMetaObject::invokeMethod(m_editView3D, "enableEditViewUpdate",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "enableEditViewUpdate",
|
||||
Q_ARG(QVariant, activeSceneVar));
|
||||
|
||||
ServerNodeInstance sceneInstance = active3DSceneInstance();
|
||||
QVariant sceneInstanceVar;
|
||||
QQmlProperty sceneIdProperty(m_editView3D, "sceneId", context());
|
||||
QVariant sceneIdVar;
|
||||
QQmlProperty sceneIdProperty(m_editView3DRootItem, "sceneId", context());
|
||||
const QString sceneId = sceneInstance.id();
|
||||
if (sceneInstance.isValid())
|
||||
sceneInstanceVar = QVariant::fromValue(sceneInstance.id());
|
||||
sceneIdProperty.write(sceneInstanceVar);
|
||||
sceneIdVar = QVariant::fromValue(sceneId);
|
||||
sceneIdProperty.write(sceneIdVar);
|
||||
|
||||
QQmlProperty sceneProperty(m_editView3D, "activeScene", context());
|
||||
QQmlProperty sceneProperty(m_editView3DRootItem, "activeScene", context());
|
||||
sceneProperty.write(activeSceneVar);
|
||||
|
||||
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
|
||||
QVariantMap toolStates;
|
||||
if (helper)
|
||||
toolStates = helper->getToolStates(sceneId);
|
||||
toolStates.insert("sceneInstanceId", QVariant::fromValue(sceneInstance.instanceId()));
|
||||
|
||||
nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::ActiveSceneChanged,
|
||||
toolStates});
|
||||
|
||||
updateView3DRect(m_active3DView);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::removeNode3D(QObject *node)
|
||||
@@ -439,11 +474,11 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots()
|
||||
|
||||
if (newRoot != oldRoot) {
|
||||
if (qobject_cast<QQuick3DCamera *>(node)) {
|
||||
QMetaObject::invokeMethod(m_editView3D, "updateCameraGizmoScene",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "updateCameraGizmoScene",
|
||||
Q_ARG(QVariant, objectToVariant(newRoot)),
|
||||
Q_ARG(QVariant, objectToVariant(node)));
|
||||
} else if (qobject_cast<QQuick3DAbstractLight *>(node)) {
|
||||
QMetaObject::invokeMethod(m_editView3D, "updateLightGizmoScene",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "updateLightGizmoScene",
|
||||
Q_ARG(QVariant, objectToVariant(newRoot)),
|
||||
Q_ARG(QVariant, objectToVariant(node)));
|
||||
}
|
||||
@@ -467,11 +502,65 @@ ServerNodeInstance Qt5InformationNodeInstanceServer::active3DSceneInstance() con
|
||||
return sceneInstance;
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::render3DEditView()
|
||||
{
|
||||
m_needRender = true;
|
||||
if (!m_renderTimer.isActive())
|
||||
m_renderTimer.start(0);
|
||||
}
|
||||
|
||||
// render the 3D edit view and send the result to creator process
|
||||
void Qt5InformationNodeInstanceServer::doRender3DEditView()
|
||||
{
|
||||
static bool showEditView = qEnvironmentVariableIsSet("QMLDESIGNER_QUICK3D_SHOW_EDIT_WINDOW");
|
||||
if (m_editView3DRootItem && !showEditView) {
|
||||
auto t = std::chrono::steady_clock::now();
|
||||
if (!m_editView3DContentItem) {
|
||||
m_editView3DContentItem = QQmlProperty::read(m_editView3DRootItem, "contentItem").value<QQuickItem *>();
|
||||
if (m_editView3DContentItem) {
|
||||
designerSupport()->refFromEffectItem(m_editView3DContentItem, false);
|
||||
QmlDesigner::Internal::QmlPrivateGate::disableNativeTextRendering(m_editView3DContentItem);
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void (QQuickItem *)> updateNodesRecursive;
|
||||
updateNodesRecursive = [&updateNodesRecursive](QQuickItem *item) {
|
||||
for (QQuickItem *childItem : item->childItems())
|
||||
updateNodesRecursive(childItem);
|
||||
DesignerSupport::updateDirtyNode(item);
|
||||
};
|
||||
updateNodesRecursive(m_editView3DContentItem);
|
||||
|
||||
QSizeF size = qobject_cast<QQuickItem *>(m_editView3DContentItem)->size();
|
||||
QRectF renderRect(QPointF(0., 0.), size);
|
||||
QImage renderImage = designerSupport()->renderImageForItem(m_editView3DContentItem,
|
||||
renderRect, size.toSize());
|
||||
|
||||
// There's no instance related to image, so instance id is -1.
|
||||
// Key number is selected so that it is unlikely to conflict other ImageContainer use.
|
||||
const qint32 edit3DKey = 2100000000;
|
||||
auto imgContainer = ImageContainer(-1, renderImage, edit3DKey);
|
||||
|
||||
// send the rendered image to creator process
|
||||
nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::Render3DView,
|
||||
QVariant::fromValue(imgContainer)});
|
||||
qDebug() << "\x1b[42m \x1b[1m" << __FUNCTION__
|
||||
<< ", t=" << std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now()-t).count()
|
||||
<< "\x1b[m";
|
||||
|
||||
if (m_needRender) {
|
||||
m_renderTimer.start(0);
|
||||
m_needRender = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) :
|
||||
Qt5NodeInstanceServer(nodeInstanceClient)
|
||||
{
|
||||
m_propertyChangeTimer.setInterval(100);
|
||||
m_selectionChangeTimer.setSingleShot(true);
|
||||
m_renderTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::sendTokenBack()
|
||||
@@ -523,7 +612,6 @@ bool Qt5InformationNodeInstanceServer::isDirtyRecursiveForParentInstances(QQuick
|
||||
return false;
|
||||
|
||||
return isDirtyRecursiveForParentInstances(parentItem);
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -549,12 +637,14 @@ QList<ServerNodeInstance> Qt5InformationNodeInstanceServer::createInstances(
|
||||
{
|
||||
const auto createdInstances = NodeInstanceServer::createInstances(container);
|
||||
|
||||
if (m_editView3D) {
|
||||
if (m_editView3DRootItem) {
|
||||
add3DViewPorts(createdInstances);
|
||||
add3DScenes(createdInstances);
|
||||
createCameraAndLightGizmos(createdInstances);
|
||||
}
|
||||
|
||||
render3DEditView();
|
||||
|
||||
return createdInstances;
|
||||
}
|
||||
|
||||
@@ -586,7 +676,7 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
|
||||
while (cameraIt != cameras.constEnd()) {
|
||||
const auto cameraObjs = cameraIt.value();
|
||||
for (auto &obj : cameraObjs) {
|
||||
QMetaObject::invokeMethod(m_editView3D, "addCameraGizmo",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "addCameraGizmo",
|
||||
Q_ARG(QVariant, objectToVariant(cameraIt.key())),
|
||||
Q_ARG(QVariant, objectToVariant(obj)));
|
||||
}
|
||||
@@ -596,7 +686,7 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
|
||||
while (lightIt != lights.constEnd()) {
|
||||
const auto lightObjs = lightIt.value();
|
||||
for (auto &obj : lightObjs) {
|
||||
QMetaObject::invokeMethod(m_editView3D, "addLightGizmo",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "addLightGizmo",
|
||||
Q_ARG(QVariant, objectToVariant(lightIt.key())),
|
||||
Q_ARG(QVariant, objectToVariant(obj)));
|
||||
}
|
||||
@@ -791,8 +881,8 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
|
||||
m_active3DView = findView3DForSceneRoot(m_active3DScene);
|
||||
}
|
||||
if (m_active3DScene) {
|
||||
m_editView3D = createEditView3D(engine());
|
||||
if (!m_editView3D) {
|
||||
createEditView3D();
|
||||
if (!m_editView3DRootItem) {
|
||||
m_active3DScene = nullptr;
|
||||
m_active3DView = nullptr;
|
||||
return;
|
||||
@@ -805,7 +895,11 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
|
||||
helper->initToolStates(it.key(), it.value());
|
||||
++it;
|
||||
}
|
||||
helper->restoreWindowState(qobject_cast<QQuickWindow *>(m_editView3D));
|
||||
helper->restoreWindowState();
|
||||
if (toolStates.contains(helper->globalStateId())
|
||||
&& toolStates[helper->globalStateId()].contains("rootSize")) {
|
||||
m_editView3DRootItem->setSize(toolStates[helper->globalStateId()]["rootSize"].value<QSize>());
|
||||
}
|
||||
}
|
||||
|
||||
updateActiveSceneToEditView3D();
|
||||
@@ -900,7 +994,7 @@ void Qt5InformationNodeInstanceServer::reparentInstances(const ReparentInstances
|
||||
|
||||
Qt5NodeInstanceServer::reparentInstances(command);
|
||||
|
||||
if (m_editView3D)
|
||||
if (m_editView3DRootItem)
|
||||
resolveSceneRoots();
|
||||
}
|
||||
|
||||
@@ -991,7 +1085,7 @@ void QmlDesigner::Qt5InformationNodeInstanceServer::removeSharedMemory(const Qml
|
||||
|
||||
void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionCommand &command)
|
||||
{
|
||||
if (!m_editView3D)
|
||||
if (!m_editView3DRootItem)
|
||||
return;
|
||||
|
||||
if (m_selectionChangeTimer.isActive()) {
|
||||
@@ -1033,16 +1127,18 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
|
||||
// Ensure the UI has enough selection box items. If it doesn't yet have them, which can be the
|
||||
// case when the first selection processed is a multiselection, we wait a bit as
|
||||
// using the new boxes immediately leads to visual glitches.
|
||||
int boxCount = m_editView3D->property("selectionBoxes").value<QVariantList>().size();
|
||||
int boxCount = m_editView3DRootItem->property("selectionBoxes").value<QVariantList>().size();
|
||||
if (boxCount < selectedObjs.size()) {
|
||||
QMetaObject::invokeMethod(m_editView3D, "ensureSelectionBoxes",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "ensureSelectionBoxes",
|
||||
Q_ARG(QVariant, QVariant::fromValue(selectedObjs.size())));
|
||||
m_pendingSelectionChangeCommand = command;
|
||||
m_selectionChangeTimer.start(100);
|
||||
} else {
|
||||
QMetaObject::invokeMethod(m_editView3D, "selectObjects",
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "selectObjects",
|
||||
Q_ARG(QVariant, QVariant::fromValue(selectedObjs)));
|
||||
}
|
||||
|
||||
render3DEditView();
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::changePropertyValues(const ChangeValuesCommand &command)
|
||||
@@ -1060,6 +1156,8 @@ void Qt5InformationNodeInstanceServer::changePropertyValues(const ChangeValuesCo
|
||||
refreshBindings();
|
||||
|
||||
startRenderTimer();
|
||||
|
||||
render3DEditView();
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::removeInstances(const RemoveInstancesCommand &command)
|
||||
@@ -1074,45 +1172,118 @@ void Qt5InformationNodeInstanceServer::removeInstances(const RemoveInstancesComm
|
||||
resolveSceneRoots();
|
||||
}
|
||||
|
||||
if (m_editView3D && (!m_active3DScene || !m_active3DView)) {
|
||||
if (m_editView3DRootItem && (!m_active3DScene || !m_active3DView)) {
|
||||
if (!m_active3DScene && !m_3DSceneMap.isEmpty())
|
||||
m_active3DScene = m_3DSceneMap.begin().key();
|
||||
m_active3DView = findView3DForSceneRoot(m_active3DScene);
|
||||
updateActiveSceneToEditView3D();
|
||||
}
|
||||
render3DEditView();
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::inputEvent(const InputEventCommand &command)
|
||||
{
|
||||
if (m_editView3D) {
|
||||
if (command.type() == QEvent::Wheel) {
|
||||
QWheelEvent *we
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
|
||||
= new QWheelEvent(command.pos(), command.pos(), {0, 0}, {0, command.angleDelta()},
|
||||
command.buttons(), command.modifiers(), Qt::NoScrollPhase,
|
||||
false);
|
||||
#else
|
||||
= new QWheelEvent(command.pos(), command.pos(), {0, 0}, {0, command.angleDelta()},
|
||||
0, Qt::Horizontal, command.buttons(), command.modifiers(),
|
||||
Qt::NoScrollPhase, Qt::MouseEventNotSynthesized);
|
||||
#endif
|
||||
|
||||
QGuiApplication::postEvent(m_editView3D, we);
|
||||
} else {
|
||||
auto me = new QMouseEvent(command.type(), command.pos(), command.button(),
|
||||
command.buttons(), command.modifiers());
|
||||
QGuiApplication::postEvent(m_editView3D, me);
|
||||
}
|
||||
|
||||
render3DEditView();
|
||||
}
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &command)
|
||||
{
|
||||
QVariantMap updatedState;
|
||||
|
||||
switch (command.type()) {
|
||||
case View3DActionCommand::MoveTool:
|
||||
updatedState.insert("groupTransform", 0);
|
||||
break;
|
||||
case View3DActionCommand::RotateTool:
|
||||
updatedState.insert("groupTransform", 1);
|
||||
break;
|
||||
case View3DActionCommand::ScaleTool:
|
||||
updatedState.insert("groupTransform", 2);
|
||||
break;
|
||||
case View3DActionCommand::FitToView:
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "fitToView");
|
||||
break;
|
||||
case View3DActionCommand::SelectionModeToggle:
|
||||
updatedState.insert("groupSelect", command.isEnabled() ? 0 : 1);
|
||||
break;
|
||||
case View3DActionCommand::CameraToggle:
|
||||
updatedState.insert("usePerspective", command.isEnabled());
|
||||
break;
|
||||
case View3DActionCommand::OrientationToggle:
|
||||
updatedState.insert("globalOrientation", command.isEnabled());
|
||||
break;
|
||||
case View3DActionCommand::EditLightToggle:
|
||||
updatedState.insert("showEditLight", command.isEnabled());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!updatedState.isEmpty()) {
|
||||
QMetaObject::invokeMethod(m_editView3DRootItem, "updateToolStates",
|
||||
Q_ARG(QVariant, updatedState),
|
||||
Q_ARG(QVariant, QVariant::fromValue(false)));
|
||||
}
|
||||
|
||||
render3DEditView();
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command)
|
||||
{
|
||||
Qt5NodeInstanceServer::changeAuxiliaryValues(command);
|
||||
render3DEditView();
|
||||
}
|
||||
|
||||
// update 3D view window state when the main app window state change
|
||||
void Qt5InformationNodeInstanceServer::update3DViewState(const Update3dViewStateCommand &command)
|
||||
{
|
||||
auto window = qobject_cast<QWindow *>(m_editView3D);
|
||||
if (window) {
|
||||
#ifdef QUICK3D_MODULE
|
||||
if (command.type() == Update3dViewStateCommand::SizeChange) {
|
||||
if (m_editView3DRootItem) {
|
||||
m_editView3DRootItem->setSize(command.size());
|
||||
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
|
||||
if (helper)
|
||||
helper->storeToolState(helper->globalStateId(), "rootSize", QVariant(command.size()), 0);
|
||||
render3DEditView();
|
||||
}
|
||||
} else if (m_editWindow3D) {
|
||||
if (command.type() == Update3dViewStateCommand::StateChange) {
|
||||
if (command.previousStates() & Qt::WindowMinimized) // main window expanded from minimize state
|
||||
window->show();
|
||||
m_editWindow3D->show();
|
||||
else if (command.currentStates() & Qt::WindowMinimized) // main window minimized
|
||||
window->hide();
|
||||
m_editWindow3D->hide();
|
||||
} else if (command.type() == Update3dViewStateCommand::ActiveChange) {
|
||||
window->setFlag(Qt::WindowStaysOnTopHint, command.isActive());
|
||||
m_editWindow3D->setFlag(Qt::WindowStaysOnTopHint, command.isActive());
|
||||
|
||||
// main window has a popup open, lower the edit view 3D so that the pop up is visible
|
||||
if (command.hasPopup())
|
||||
window->lower();
|
||||
m_editWindow3D->lower();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Qt5InformationNodeInstanceServer::enable3DView(const Enable3DViewCommand &command)
|
||||
{
|
||||
// TODO: this method is not currently in use as the 3D view is currently enabled by resetting the puppet.
|
||||
// It should however be implemented here.
|
||||
|
||||
auto window = qobject_cast<QWindow *>(m_editView3D);
|
||||
if (window && !command.isEnable()) {
|
||||
// TODO: remove the 3D view
|
||||
} else if (!window && command.isEnable()) {
|
||||
// TODO: create the 3D view
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(command)
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <QTimer>
|
||||
#include <QVariant>
|
||||
#include <QPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDragMoveEvent;
|
||||
@@ -49,7 +50,6 @@ public:
|
||||
void reparentInstances(const ReparentInstancesCommand &command) override;
|
||||
void clearScene(const ClearSceneCommand &command) override;
|
||||
void update3DViewState(const Update3dViewStateCommand &command) override;
|
||||
void enable3DView(const Enable3DViewCommand &command) override;
|
||||
void createScene(const CreateSceneCommand &command) override;
|
||||
void completeComponent(const CompleteComponentCommand &command) override;
|
||||
void token(const TokenCommand &command) override;
|
||||
@@ -57,6 +57,9 @@ public:
|
||||
void changeSelection(const ChangeSelectionCommand &command) override;
|
||||
void changePropertyValues(const ChangeValuesCommand &command) override;
|
||||
void removeInstances(const RemoveInstancesCommand &command) override;
|
||||
void inputEvent(const InputEventCommand &command) override;
|
||||
void view3DAction(const View3DActionCommand &command) override;
|
||||
void changeAuxiliaryValues(const ChangeAuxiliaryCommand &command) override;
|
||||
|
||||
private slots:
|
||||
void handleSelectionChanged(const QVariant &objs);
|
||||
@@ -82,7 +85,7 @@ protected:
|
||||
private:
|
||||
void handleObjectPropertyChangeTimeout();
|
||||
void handleSelectionChangeTimeout();
|
||||
QObject *createEditView3D(QQmlEngine *engine);
|
||||
void createEditView3D();
|
||||
void setup3DEditView(const QList<ServerNodeInstance> &instanceList,
|
||||
const QHash<QString, QVariantMap> &toolStates);
|
||||
void createCameraAndLightGizmos(const QList<ServerNodeInstance> &instanceList) const;
|
||||
@@ -104,8 +107,13 @@ private:
|
||||
void removeNode3D(QObject *node);
|
||||
void resolveSceneRoots();
|
||||
ServerNodeInstance active3DSceneInstance() const;
|
||||
void render3DEditView();
|
||||
void doRender3DEditView();
|
||||
|
||||
QObject *m_editView3D = nullptr;
|
||||
QPointer<QQuickView> m_editView3D;
|
||||
QPointer<QQuickWindow> m_editWindow3D;
|
||||
QQuickItem *m_editView3DRootItem = nullptr;
|
||||
QQuickItem *m_editView3DContentItem = nullptr;
|
||||
QSet<QObject *> m_view3Ds;
|
||||
QMultiHash<QObject *, QObject *> m_3DSceneMap; // key: scene root, value: node
|
||||
QObject *m_active3DView;
|
||||
@@ -115,10 +123,12 @@ private:
|
||||
QList<TokenCommand> m_tokenList;
|
||||
QTimer m_propertyChangeTimer;
|
||||
QTimer m_selectionChangeTimer;
|
||||
QTimer m_renderTimer;
|
||||
QVariant m_changedNode;
|
||||
PropertyName m_changedProperty;
|
||||
ChangeSelectionCommand m_pendingSelectionChangeCommand;
|
||||
QObject *m_3dHelper = nullptr;
|
||||
bool m_needRender = false;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -70,8 +70,10 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) :
|
||||
* because we want to be able to show the 3D Edit View
|
||||
* as a normal QQuickView.
|
||||
* The DesignerWindowManager prevents any window from actually being shown. */
|
||||
if (!qEnvironmentVariableIsSet("QMLDESIGNER_QUICK3D_MODE"))
|
||||
if (!qEnvironmentVariableIsSet("QMLDESIGNER_QUICK3D_MODE")
|
||||
|| !qEnvironmentVariableIsSet("QMLDESIGNER_QUICK3D_SHOW_EDIT_WINDOW")) {
|
||||
DesignerSupport::activateDesignerWindowManager();
|
||||
}
|
||||
setNodeInstanceServer(new Qt5InformationNodeInstanceServer(this));
|
||||
initializeSocket();
|
||||
} else if (QCoreApplication::arguments().at(2) == QLatin1String("rendermode")) {
|
||||
|
@@ -72,6 +72,7 @@ void Qt5NodeInstanceServer::initializeView()
|
||||
DesignerSupport::createOpenGLContext(m_quickView.data());
|
||||
|
||||
if (qEnvironmentVariableIsSet("QMLDESIGNER_QUICK3D_MODE")
|
||||
&& qEnvironmentVariableIsSet("QMLDESIGNER_QUICK3D_SHOW_EDIT_WINDOW")
|
||||
&& QCoreApplication::arguments().at(2) == "editormode") {
|
||||
/* In '3d editormode' we do not use the DesignerWindowManager
|
||||
* and since we do not show the QQuickView we have to manually create the OpenGL context */
|
||||
|
@@ -627,16 +627,18 @@ QList<QQuickItem *> ServerNodeInstance::allItemsRecursive() const
|
||||
|
||||
QString ServerNodeInstance::id() const
|
||||
{
|
||||
return m_nodeInstance->id();
|
||||
if (isValid())
|
||||
return m_nodeInstance->id();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
qint32 ServerNodeInstance::instanceId() const
|
||||
{
|
||||
if (isValid()) {
|
||||
if (isValid())
|
||||
return m_nodeInstance->instanceId();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
QList<ServerNodeInstance> ServerNodeInstance::stateInstances() const
|
||||
|
@@ -8,6 +8,7 @@
|
||||
<file>mockfiles/GenericBackend.qml</file>
|
||||
<file>mockfiles/Dialog.qml</file>
|
||||
<file>mockfiles/EditView3D.qml</file>
|
||||
<file>mockfiles/EditWindow3D.qml</file>
|
||||
<file>mockfiles/EditCameraController.qml</file>
|
||||
<file>mockfiles/Arrow.qml</file>
|
||||
<file>mockfiles/AutoScaleHelper.qml</file>
|
||||
|
@@ -146,6 +146,12 @@ Rectangle {
|
||||
tooltip: qsTr("Toggles whether this item is exported as an alias property of the root item.")
|
||||
}
|
||||
}
|
||||
Item { //dummy object to preserve layout in case of multiselection
|
||||
Layout.preferredWidth: 20
|
||||
Layout.preferredHeight: 20
|
||||
enabled: modelNodeBackend.multiSelection
|
||||
visible: enabled
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
@@ -154,13 +160,11 @@ Rectangle {
|
||||
|
||||
SecondColumnLayout {
|
||||
enabled: !modelNodeBackend.multiSelection
|
||||
visible: enabled
|
||||
spacing: 2
|
||||
|
||||
LineEdit {
|
||||
id: annotationEdit
|
||||
enabled: annotationEditor.hasAuxData
|
||||
visible: enabled
|
||||
visible: annotationEditor.hasAuxData
|
||||
|
||||
backendValue: backendValues.customId__AUX
|
||||
placeholderText: qsTr("customId")
|
||||
@@ -176,8 +180,8 @@ Rectangle {
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
id: editAnnotationButton
|
||||
enabled: annotationEditor.hasAuxData
|
||||
visible: enabled
|
||||
visible: annotationEditor.hasAuxData
|
||||
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
width: 22
|
||||
@@ -185,14 +189,13 @@ Rectangle {
|
||||
buttonIcon: StudioTheme.Constants.edit
|
||||
|
||||
onClicked: annotationEditor.showWidget()
|
||||
|
||||
onHoveredChanged: annotationEditor.checkAux()
|
||||
}
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
id: removeAnnotationButton
|
||||
enabled: annotationEditor.hasAuxData
|
||||
visible: enabled
|
||||
visible: annotationEditor.hasAuxData
|
||||
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
width: 22
|
||||
@@ -200,19 +203,18 @@ Rectangle {
|
||||
buttonIcon: StudioTheme.Constants.closeCross
|
||||
|
||||
onClicked: annotationEditor.removeFullAnnotation()
|
||||
|
||||
onHoveredChanged: annotationEditor.checkAux()
|
||||
}
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
id: addAnnotationButton
|
||||
enabled: !annotationEditor.hasAuxData
|
||||
visible: enabled
|
||||
visible: !annotationEditor.hasAuxData
|
||||
|
||||
buttonIcon: qsTr("Add Annotation")
|
||||
iconFont: StudioTheme.Constants.font
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 240
|
||||
width: 240
|
||||
|
||||
onClicked: annotationEditor.showWidget()
|
||||
|
||||
@@ -222,8 +224,7 @@ Rectangle {
|
||||
Item {
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
enabled: !annotationEditor.hasAuxData
|
||||
visible: enabled
|
||||
visible: !annotationEditor.hasAuxData
|
||||
}
|
||||
|
||||
AnnotationEditor {
|
||||
|
@@ -21,6 +21,7 @@ from PyQt5 import QtQuick
|
||||
@endif
|
||||
@endif
|
||||
|
||||
|
||||
@if '%{Base}'
|
||||
class %{Class}(%{Base}):
|
||||
@else
|
||||
@@ -29,11 +30,9 @@ class %{Class}:
|
||||
def __init__(self):
|
||||
@if '%{Base}' === 'QWidget'
|
||||
QtWidgets.QWidget.__init__(self)
|
||||
@endif
|
||||
@if '%{Base}' === 'QMainWindow'
|
||||
@elif '%{Base}' === 'QMainWindow'
|
||||
QtWidgets.QMainWindow.__init__(self)
|
||||
@if '%{Base}' === 'QQuickItem'
|
||||
@elif '%{Base}' === 'QQuickItem'
|
||||
QtQuick.QQuickItem.__init__(self)
|
||||
@endif
|
||||
pass
|
||||
|
||||
|
@@ -46,7 +46,7 @@
|
||||
"data":
|
||||
{
|
||||
"items": [ { "trKey": "<Custom>", "value": "" },
|
||||
"QObject", "QWidget", "QMainWindow", "QDeclarativeItem" ]
|
||||
"QObject", "QWidget", "QMainWindow", "QDeclarativeItem", "QQuickItem" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
|
||||
# if__name__ == "__main__":
|
||||
# if __name__ == "__main__":
|
||||
# pass
|
||||
|
@@ -83,9 +83,18 @@
|
||||
"persistenceKey": "QtQuick.minimumQtVersion",
|
||||
"data":
|
||||
{
|
||||
"index": 2,
|
||||
"index": 3,
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"trKey": "Qt 5.15",
|
||||
"value":
|
||||
{
|
||||
"QtQuickVersion": "2.15",
|
||||
"QtQuickWindowVersion": "2.15",
|
||||
"QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"trKey": "Qt 5.14",
|
||||
"value":
|
||||
|
@@ -85,9 +85,18 @@
|
||||
"persistenceKey": "QtQuick.minimumQtVersion",
|
||||
"data":
|
||||
{
|
||||
"index": 2,
|
||||
"index": 3,
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"trKey": "Qt 5.15",
|
||||
"value":
|
||||
{
|
||||
"QtQuickVersion": "2.15",
|
||||
"QtQuickControlsVersion": "2.15",
|
||||
"QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"trKey": "Qt 5.14",
|
||||
"value":
|
||||
|
@@ -85,9 +85,18 @@
|
||||
"persistenceKey": "QtQuick.minimumQtVersion",
|
||||
"data":
|
||||
{
|
||||
"index": 2,
|
||||
"index": 3,
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"trKey": "Qt 5.15",
|
||||
"value":
|
||||
{
|
||||
"QtQuickVersion": "2.15",
|
||||
"QtQuickControlsVersion": "2.15",
|
||||
"QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"trKey": "Qt 5.14",
|
||||
"value":
|
||||
|
@@ -85,9 +85,18 @@
|
||||
"persistenceKey": "QtQuick.minimumQtVersion",
|
||||
"data":
|
||||
{
|
||||
"index": 2,
|
||||
"index": 3,
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"trKey": "Qt 5.15",
|
||||
"value":
|
||||
{
|
||||
"QtQuickVersion": "2.15",
|
||||
"QtQuickControlsVersion": "2.15",
|
||||
"QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"trKey": "Qt 5.14",
|
||||
"value":
|
||||
|
@@ -40,9 +40,18 @@
|
||||
"type": "ComboBox",
|
||||
"data":
|
||||
{
|
||||
"index": 2,
|
||||
"index": 3,
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"trKey": "Qt 5.15",
|
||||
"value":
|
||||
{
|
||||
"QtQuickVersion": "2.15",
|
||||
"QtQuickWindowVersion": "2.15",
|
||||
"QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"trKey": "Qt 5.14",
|
||||
"value":
|
||||
|
@@ -546,6 +546,7 @@ int main(int argc, char **argv)
|
||||
#endif
|
||||
|
||||
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
app.setAttribute(Qt::AA_DisableWindowContextHelpButton);
|
||||
|
||||
PluginManager pluginManager;
|
||||
PluginManager::setPluginIID(QLatin1String("org.qt-project.Qt.QtCreatorPlugin"));
|
||||
|
@@ -1,5 +1,6 @@
|
||||
add_subdirectory(3rdparty)
|
||||
|
||||
add_subdirectory(advanceddockingsystem)
|
||||
add_subdirectory(aggregation)
|
||||
add_subdirectory(extensionsystem)
|
||||
add_subdirectory(utils)
|
||||
|
31
src/libs/advanceddockingsystem/CMakeLists.txt
Normal file
31
src/libs/advanceddockingsystem/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
add_qtc_library(AdvancedDockingSystem
|
||||
DEPENDS Qt5::Widgets Qt5::Core Qt5::Gui Utils
|
||||
SOURCES
|
||||
ads_globals.cpp ads_globals.h
|
||||
dockareatabbar.cpp dockareatabbar.h
|
||||
dockareatitlebar.cpp dockareatitlebar.h
|
||||
dockareawidget.cpp dockareawidget.h
|
||||
dockcomponentsfactory.cpp dockcomponentsfactory.h
|
||||
dockcontainerwidget.cpp dockcontainerwidget.h
|
||||
dockingstatereader.cpp dockingstatereader.h
|
||||
dockmanager.cpp dockmanager.h
|
||||
dockoverlay.cpp dockoverlay.h
|
||||
docksplitter.cpp docksplitter.h
|
||||
dockwidget.cpp dockwidget.h
|
||||
dockwidgettab.cpp dockwidgettab.h
|
||||
elidinglabel.cpp elidinglabel.h
|
||||
floatingdockcontainer.cpp floatingdockcontainer.h
|
||||
floatingdragpreview.cpp floatingdragpreview.h
|
||||
iconprovider.cpp iconprovider.h
|
||||
workspacedialog.cpp workspacedialog.h
|
||||
workspacemodel.cpp workspacemodel.h
|
||||
workspaceview.cpp workspaceview.h
|
||||
workspacedialog.ui
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
extend_qtc_target(AdvancedDockingSystem
|
||||
INCLUDES linux
|
||||
SOURCES
|
||||
linux/floatingwidgettitlebar.cpp linux/floatingwidgettitlebar.h
|
||||
)
|
514
src/libs/advanceddockingsystem/LICENSE.LGPLv21
Normal file
514
src/libs/advanceddockingsystem/LICENSE.LGPLv21
Normal file
@@ -0,0 +1,514 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
|
||||
The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
|
||||
Contact: http://www.qt.io/licensing/
|
||||
|
||||
You may use, distribute and copy the Qt Toolkit under the terms of
|
||||
GNU Lesser General Public License version 2.1, which is displayed below.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
120
src/libs/advanceddockingsystem/ads_globals.cpp
Normal file
120
src/libs/advanceddockingsystem/ads_globals.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
#include "dockmanager.h"
|
||||
#include "docksplitter.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QPainter>
|
||||
#include <QVariant>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
namespace internal {
|
||||
|
||||
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
|
||||
{
|
||||
int index = splitter->indexOf(from);
|
||||
from->setParent(nullptr);
|
||||
splitter->insertWidget(index, to);
|
||||
}
|
||||
|
||||
DockInsertParam dockAreaInsertParameters(DockWidgetArea area)
|
||||
{
|
||||
switch (area) {
|
||||
case TopDockWidgetArea:
|
||||
return DockInsertParam(Qt::Vertical, false);
|
||||
case RightDockWidgetArea:
|
||||
return DockInsertParam(Qt::Horizontal, true);
|
||||
case CenterDockWidgetArea:
|
||||
case BottomDockWidgetArea:
|
||||
return DockInsertParam(Qt::Vertical, true);
|
||||
case LeftDockWidgetArea:
|
||||
return DockInsertParam(Qt::Horizontal, false);
|
||||
default:
|
||||
DockInsertParam(Qt::Vertical, false);
|
||||
}
|
||||
|
||||
return DockInsertParam(Qt::Vertical, false);
|
||||
}
|
||||
|
||||
QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity)
|
||||
{
|
||||
QPixmap transparentPixmap(source.size());
|
||||
transparentPixmap.fill(Qt::transparent);
|
||||
QPainter painter(&transparentPixmap);
|
||||
painter.setOpacity(opacity);
|
||||
painter.drawPixmap(0, 0, source);
|
||||
return transparentPixmap;
|
||||
}
|
||||
|
||||
void hideEmptyParentSplitters(DockSplitter *splitter)
|
||||
{
|
||||
while (splitter && splitter->isVisible()) {
|
||||
if (!splitter->hasVisibleContent()) {
|
||||
splitter->hide();
|
||||
}
|
||||
splitter = internal::findParent<DockSplitter *>(splitter);
|
||||
}
|
||||
}
|
||||
|
||||
void setButtonIcon(QAbstractButton* button,
|
||||
QStyle::StandardPixmap standarPixmap,
|
||||
ADS::eIcon customIconId)
|
||||
{
|
||||
// First we try to use custom icons if available
|
||||
QIcon icon = DockManager::iconProvider().customIcon(customIconId);
|
||||
if (!icon.isNull()) {
|
||||
button->setIcon(icon);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utils::HostOsInfo::isLinuxHost()) {
|
||||
button->setIcon(button->style()->standardIcon(standarPixmap));
|
||||
} else {
|
||||
// The standard icons does not look good on high DPI screens so we create
|
||||
// our own "standard" icon here.
|
||||
QPixmap normalPixmap = button->style()->standardPixmap(standarPixmap, nullptr, button);
|
||||
icon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
|
||||
icon.addPixmap(normalPixmap, QIcon::Normal);
|
||||
button->setIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ADS
|
233
src/libs/advanceddockingsystem/ads_globals.h
Normal file
233
src/libs/advanceddockingsystem/ads_globals.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPair>
|
||||
#include <QPixmap>
|
||||
#include <QStyle>
|
||||
#include <QWidget>
|
||||
#include <QtCore/QtGlobal>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAbstractButton;
|
||||
class QSplitter;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#ifndef ADS_STATIC
|
||||
#ifdef ADVANCEDDOCKINGSYSTEM_LIBRARY
|
||||
#define ADS_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
#define ADS_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
#else
|
||||
#define ADS_EXPORT
|
||||
#endif
|
||||
|
||||
//#define ADS_DEBUG_PRINT
|
||||
|
||||
// Define ADS_DEBUG_PRINT to enable a lot of debug output
|
||||
#ifdef ADS_DEBUG_PRINT
|
||||
#define ADS_PRINT(s) qDebug() << s
|
||||
#else
|
||||
#define ADS_PRINT(s)
|
||||
#endif
|
||||
|
||||
// Set ADS_DEBUG_LEVEL to enable additional debug output and to enable layout
|
||||
// dumps to qDebug and std::cout after layout changes
|
||||
#define ADS_DEBUG_LEVEL 0
|
||||
|
||||
namespace ADS {
|
||||
|
||||
enum eStateFileVersion { InitialVerison = 0, Version1 = 1, CurrentVersion = Version1 };
|
||||
|
||||
class DockSplitter;
|
||||
|
||||
enum DockWidgetArea {
|
||||
NoDockWidgetArea = 0x00,
|
||||
LeftDockWidgetArea = 0x01,
|
||||
RightDockWidgetArea = 0x02,
|
||||
TopDockWidgetArea = 0x04,
|
||||
BottomDockWidgetArea = 0x08,
|
||||
CenterDockWidgetArea = 0x10,
|
||||
|
||||
InvalidDockWidgetArea = NoDockWidgetArea,
|
||||
OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea
|
||||
| BottomDockWidgetArea,
|
||||
AllDockAreas = OuterDockAreas | CenterDockWidgetArea
|
||||
};
|
||||
Q_DECLARE_FLAGS(DockWidgetAreas, DockWidgetArea)
|
||||
|
||||
enum eTitleBarButton { TitleBarButtonTabsMenu, TitleBarButtonUndock, TitleBarButtonClose };
|
||||
|
||||
/**
|
||||
* The different dragging states
|
||||
*/
|
||||
enum eDragState {
|
||||
DraggingInactive, //!< DraggingInactive
|
||||
DraggingMousePressed, //!< DraggingMousePressed
|
||||
DraggingTab, //!< DraggingTab
|
||||
DraggingFloatingWidget //!< DraggingFloatingWidget
|
||||
};
|
||||
|
||||
/**
|
||||
* The different icons used in the UI
|
||||
*/
|
||||
enum eIcon {
|
||||
TabCloseIcon, //!< TabCloseIcon
|
||||
DockAreaMenuIcon, //!< DockAreaMenuIcon
|
||||
DockAreaUndockIcon, //!< DockAreaUndockIcon
|
||||
DockAreaCloseIcon, //!< DockAreaCloseIcon
|
||||
|
||||
IconCount, //!< just a delimiter for range checks
|
||||
};
|
||||
|
||||
/**
|
||||
* For bitwise combination of dock wdget features
|
||||
*/
|
||||
enum eBitwiseOperator
|
||||
{
|
||||
BitwiseAnd,
|
||||
BitwiseOr
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
const bool restoreTesting = true;
|
||||
const bool restore = false;
|
||||
const char *const closedProperty = "close";
|
||||
const char *const dirtyProperty = "dirty";
|
||||
|
||||
/**
|
||||
* Replace the from widget in the given splitter with the To widget
|
||||
*/
|
||||
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to);
|
||||
|
||||
/**
|
||||
* This function walks the splitter tree upwards to hides all splitters
|
||||
* that do not have visible content
|
||||
*/
|
||||
void hideEmptyParentSplitters(DockSplitter *firstParentSplitter);
|
||||
|
||||
/**
|
||||
* Convenience class for QPair to provide better naming than first and
|
||||
* second
|
||||
*/
|
||||
class DockInsertParam : public QPair<Qt::Orientation, bool>
|
||||
{
|
||||
public:
|
||||
using QPair::QPair;
|
||||
Qt::Orientation orientation() const { return this->first; }
|
||||
bool append() const { return this->second; }
|
||||
int insertOffset() const { return append() ? 1 : 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the insertion parameters for the given dock area
|
||||
*/
|
||||
DockInsertParam dockAreaInsertParameters(DockWidgetArea area);
|
||||
|
||||
/**
|
||||
* Searches for the parent widget of the given type.
|
||||
* Returns the parent widget of the given widget or 0 if the widget is not
|
||||
* child of any widget of type T
|
||||
*
|
||||
* It is not safe to use this function in in DockWidget because only
|
||||
* the current dock widget has a parent. All dock widgets that are not the
|
||||
* current dock widget in a dock area have no parent.
|
||||
*/
|
||||
template<class T>
|
||||
T findParent(const QWidget *widget)
|
||||
{
|
||||
QWidget *parentWidget = widget->parentWidget();
|
||||
while (parentWidget) {
|
||||
T parentImpl = qobject_cast<T>(parentWidget);
|
||||
if (parentImpl) {
|
||||
return parentImpl;
|
||||
}
|
||||
parentWidget = parentWidget->parentWidget();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a semi transparent pixmap from the given pixmap Source.
|
||||
* The Opacity parameter defines the opacity from completely transparent (0.0)
|
||||
* to completely opaque (1.0)
|
||||
*/
|
||||
QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity);
|
||||
|
||||
/**
|
||||
* Helper function for settings flags in a QFlags instance.
|
||||
*/
|
||||
template<class T>
|
||||
void setFlag(T &flags, typename T::enum_type flag, bool on = true)
|
||||
{
|
||||
flags.setFlag(flag, on);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for settings tooltips without cluttering the code with
|
||||
* tests for preprocessor macros
|
||||
*/
|
||||
template <class QObjectPtr>
|
||||
void setToolTip(QObjectPtr obj, const QString &tip)
|
||||
{
|
||||
#ifndef QT_NO_TOOLTIP
|
||||
obj->setToolTip(tip);
|
||||
#else
|
||||
Q_UNUSED(obj);
|
||||
Q_UNUSED(tip);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the icon of a certain button.
|
||||
* Use this function to set the icons for the dock area and dock widget buttons.
|
||||
* The function first uses the CustomIconId to get an icon from the
|
||||
* IconProvider. You can register your custom icons with the icon provider, if
|
||||
* you do not want to use the default buttons and if you do not want to use
|
||||
* stylesheets.
|
||||
* If the IconProvider does not return a valid icon (icon is null), the function
|
||||
* fetches the given standard pixmap from the QStyle.
|
||||
* param[in] Button The button whose icons are to be set
|
||||
* param[in] StandardPixmap The standard pixmap to be used for the button
|
||||
* param[in] CustomIconId The identifier for the custom icon.
|
||||
*/
|
||||
void setButtonIcon(QAbstractButton *button, QStyle::StandardPixmap standarPixmap,
|
||||
ADS::eIcon CustomIconId);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ADS
|
58
src/libs/advanceddockingsystem/advanceddockingsystem-lib.pri
Normal file
58
src/libs/advanceddockingsystem/advanceddockingsystem-lib.pri
Normal file
@@ -0,0 +1,58 @@
|
||||
shared {
|
||||
DEFINES += ADVANCEDDOCKINGSYSTEM_LIBRARY
|
||||
} else {
|
||||
DEFINES += BUILD_ADVANCEDDOCKINGSYSTEM_STATIC_LIB
|
||||
}
|
||||
|
||||
## Input
|
||||
RESOURCES += \
|
||||
resources.qrc
|
||||
|
||||
HEADERS += \
|
||||
ads_globals.h \
|
||||
dockareatabbar.h \
|
||||
dockareatitlebar.h \
|
||||
dockareawidget.h \
|
||||
dockcomponentsfactory.h \
|
||||
dockcontainerwidget.h \
|
||||
dockingstatereader.h \
|
||||
dockmanager.h \
|
||||
dockoverlay.h \
|
||||
docksplitter.h \
|
||||
dockwidget.h \
|
||||
dockwidgettab.h \
|
||||
elidinglabel.h \
|
||||
floatingdockcontainer.h \
|
||||
floatingdragpreview.h \
|
||||
iconprovider.h \
|
||||
workspacedialog.h \
|
||||
workspacemodel.h \
|
||||
workspaceview.h
|
||||
|
||||
SOURCES += \
|
||||
ads_globals.cpp \
|
||||
dockareatabbar.cpp \
|
||||
dockareatitlebar.cpp \
|
||||
dockareawidget.cpp \
|
||||
dockcomponentsfactory.cpp \
|
||||
dockcontainerwidget.cpp \
|
||||
dockingstatereader.cpp \
|
||||
dockmanager.cpp \
|
||||
dockoverlay.cpp \
|
||||
docksplitter.cpp \
|
||||
dockwidget.cpp \
|
||||
dockwidgettab.cpp \
|
||||
elidinglabel.cpp \
|
||||
floatingdockcontainer.cpp \
|
||||
floatingdragpreview.cpp \
|
||||
iconprovider.cpp \
|
||||
workspacedialog.cpp \
|
||||
workspacemodel.cpp \
|
||||
workspaceview.cpp
|
||||
|
||||
FORMS += \
|
||||
workspacedialog.ui
|
||||
|
||||
include(linux/linux.pri)
|
||||
|
||||
DISTFILES += advanceddockingsystem.pri
|
6
src/libs/advanceddockingsystem/advanceddockingsystem.pro
Normal file
6
src/libs/advanceddockingsystem/advanceddockingsystem.pro
Normal file
@@ -0,0 +1,6 @@
|
||||
unix:QMAKE_CXXFLAGS_DEBUG += -O3
|
||||
|
||||
INCLUDEPATH += $$PWD $$PWD/linux
|
||||
|
||||
include(../../qtcreatorlibrary.pri)
|
||||
include(advanceddockingsystem-lib.pri)
|
48
src/libs/advanceddockingsystem/advanceddockingsystem.qbs
Normal file
48
src/libs/advanceddockingsystem/advanceddockingsystem.qbs
Normal file
@@ -0,0 +1,48 @@
|
||||
import qbs 1.0
|
||||
|
||||
QtcLibrary {
|
||||
name: "AdvancedDockingSystem"
|
||||
|
||||
cpp.optimization: "fast"
|
||||
cpp.defines: base.concat("ADVANCEDDOCKINGSYSTEM_LIBRARY")
|
||||
cpp.includePaths: base.concat([".", linux.prefix])
|
||||
|
||||
Depends { name: "Qt"; submodules: ["widgets", "core", "gui"] }
|
||||
Depends { name: "Utils" }
|
||||
|
||||
Group {
|
||||
name: "General"
|
||||
files: [
|
||||
"ads_globals.cpp", "ads_globals.h",
|
||||
"dockareatabbar.cpp", "dockareatabbar.h",
|
||||
"dockareatitlebar.cpp", "dockareatitlebar.h",
|
||||
"dockareawidget.cpp", "dockareawidget.h",
|
||||
"dockcomponentsfactory.cpp", "dockcomponentsfactory.h",
|
||||
"dockcontainerwidget.cpp", "dockcontainerwidget.h",
|
||||
"dockingstatereader.cpp", "dockingstatereader.h",
|
||||
"dockmanager.cpp", "dockmanager.h",
|
||||
"dockoverlay.cpp", "dockoverlay.h",
|
||||
"docksplitter.cpp", "docksplitter.h",
|
||||
"dockwidget.cpp", "dockwidget.h",
|
||||
"dockwidgettab.cpp", "dockwidgettab.h",
|
||||
"elidinglabel.cpp", "elidinglabel.h",
|
||||
"floatingdockcontainer.cpp", "floatingdockcontainer.h",
|
||||
"floatingdragpreview.cpp", "floatingdragpreview.h",
|
||||
"iconprovider.cpp", "iconprovider.h",
|
||||
"workspacedialog.cpp", "workspacedialog.h",
|
||||
"workspacemodel.cpp", "workspacemodel.h",
|
||||
"workspaceview.cpp", "workspaceview.h",
|
||||
"workspacedialog.ui",
|
||||
"resources.qrc"
|
||||
]
|
||||
}
|
||||
|
||||
Group {
|
||||
name: "Linux"
|
||||
id: linux
|
||||
prefix: "linux/"
|
||||
files: [
|
||||
"floatingwidgettitlebar.cpp", "floatingwidgettitlebar.h"
|
||||
]
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
QTC_LIB_NAME = AdvancedDockingSystem
|
||||
QTC_LIB_DEPENDS += utils
|
||||
INCLUDEPATH *= $$IDE_SOURCE_TREE/src/libs/advanceddockingsystem
|
402
src/libs/advanceddockingsystem/dockareatabbar.cpp
Normal file
402
src/libs/advanceddockingsystem/dockareatabbar.cpp
Normal file
@@ -0,0 +1,402 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dockareatabbar.h"
|
||||
|
||||
#include "dockareawidget.h"
|
||||
#include "dockmanager.h"
|
||||
#include "dockoverlay.h"
|
||||
#include "dockwidget.h"
|
||||
#include "dockwidgettab.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
#include "floatingdragpreview.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBoxLayout>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
|
||||
|
||||
namespace ADS
|
||||
{
|
||||
/**
|
||||
* Private data class of DockAreaTabBar class (pimpl)
|
||||
*/
|
||||
struct DockAreaTabBarPrivate
|
||||
{
|
||||
DockAreaTabBar *q;
|
||||
DockAreaWidget *m_dockArea;
|
||||
QWidget *m_tabsContainerWidget;
|
||||
QBoxLayout *m_tabsLayout;
|
||||
int m_currentIndex = -1;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockAreaTabBarPrivate(DockAreaTabBar *parent);
|
||||
|
||||
/**
|
||||
* Update tabs after current index changed or when tabs are removed.
|
||||
* The function reassigns the stylesheet to update the tabs
|
||||
*/
|
||||
void updateTabs();
|
||||
|
||||
/**
|
||||
* Convenience function to access first tab
|
||||
*/
|
||||
DockWidgetTab *firstTab() const {return q->tab(0);}
|
||||
|
||||
/**
|
||||
* Convenience function to access last tab
|
||||
*/
|
||||
DockWidgetTab *lastTab() const {return q->tab(q->count() - 1);}
|
||||
};
|
||||
// struct DockAreaTabBarPrivate
|
||||
|
||||
DockAreaTabBarPrivate::DockAreaTabBarPrivate(DockAreaTabBar *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
void DockAreaTabBarPrivate::updateTabs()
|
||||
{
|
||||
// Set active TAB and update all other tabs to be inactive
|
||||
for (int i = 0; i < q->count(); ++i) {
|
||||
auto tabWidget = q->tab(i);
|
||||
if (!tabWidget)
|
||||
continue;
|
||||
|
||||
if (i == m_currentIndex) {
|
||||
tabWidget->show();
|
||||
tabWidget->setActiveTab(true);
|
||||
q->ensureWidgetVisible(tabWidget);
|
||||
} else {
|
||||
tabWidget->setActiveTab(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DockAreaTabBar::DockAreaTabBar(DockAreaWidget *parent)
|
||||
: QScrollArea(parent)
|
||||
, d(new DockAreaTabBarPrivate(this))
|
||||
{
|
||||
d->m_dockArea = parent;
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
setWidgetResizable(true);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
d->m_tabsContainerWidget = new QWidget();
|
||||
d->m_tabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
d->m_tabsContainerWidget->setObjectName("tabsContainerWidget");
|
||||
d->m_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
d->m_tabsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_tabsLayout->setSpacing(0);
|
||||
d->m_tabsLayout->addStretch(1);
|
||||
d->m_tabsContainerWidget->setLayout(d->m_tabsLayout);
|
||||
setWidget(d->m_tabsContainerWidget);
|
||||
}
|
||||
|
||||
DockAreaTabBar::~DockAreaTabBar() { delete d; }
|
||||
|
||||
void DockAreaTabBar::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
const int direction = event->angleDelta().y();
|
||||
if (direction < 0) {
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
|
||||
} else {
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTabBar::setCurrentIndex(int index)
|
||||
{
|
||||
if (index == d->m_currentIndex)
|
||||
return;
|
||||
|
||||
if (index < -1 || index > (count() - 1)) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
emit currentChanging(index);
|
||||
d->m_currentIndex = index;
|
||||
d->updateTabs();
|
||||
updateGeometry();
|
||||
emit currentChanged(index);
|
||||
}
|
||||
|
||||
int DockAreaTabBar::count() const
|
||||
{
|
||||
// The tab bar contains a stretch item as last item
|
||||
return d->m_tabsLayout->count() - 1;
|
||||
}
|
||||
|
||||
void DockAreaTabBar::insertTab(int index, DockWidgetTab *dockWidgetTab)
|
||||
{
|
||||
d->m_tabsLayout->insertWidget(index, dockWidgetTab);
|
||||
connect(dockWidgetTab, &DockWidgetTab::clicked, this, &DockAreaTabBar::onTabClicked);
|
||||
connect(dockWidgetTab,
|
||||
&DockWidgetTab::closeRequested,
|
||||
this,
|
||||
&DockAreaTabBar::onTabCloseRequested);
|
||||
connect(dockWidgetTab,
|
||||
&DockWidgetTab::closeOtherTabsRequested,
|
||||
this,
|
||||
&DockAreaTabBar::onCloseOtherTabsRequested);
|
||||
connect(dockWidgetTab, &DockWidgetTab::moved, this, &DockAreaTabBar::onTabWidgetMoved);
|
||||
connect(dockWidgetTab,
|
||||
&DockWidgetTab::elidedChanged,
|
||||
this,
|
||||
&DockAreaTabBar::elidedChanged);
|
||||
dockWidgetTab->installEventFilter(this);
|
||||
emit tabInserted(index);
|
||||
if (index <= d->m_currentIndex || d->m_currentIndex == -1) {
|
||||
setCurrentIndex(d->m_currentIndex + 1);
|
||||
}
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void DockAreaTabBar::removeTab(DockWidgetTab *dockWidgetTab)
|
||||
{
|
||||
if (!count())
|
||||
return;
|
||||
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
int newCurrentIndex = currentIndex();
|
||||
int removeIndex = d->m_tabsLayout->indexOf(dockWidgetTab);
|
||||
if (count() == 1)
|
||||
newCurrentIndex = -1;
|
||||
|
||||
if (newCurrentIndex > removeIndex) {
|
||||
newCurrentIndex--;
|
||||
} else if (newCurrentIndex == removeIndex) {
|
||||
newCurrentIndex = -1;
|
||||
// First we walk to the right to search for the next visible tab
|
||||
for (int i = (removeIndex + 1); i < count(); ++i) {
|
||||
if (tab(i)->isVisibleTo(this)) {
|
||||
newCurrentIndex = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no visible tab right to this tab then we walk to
|
||||
// the left to find a visible tab
|
||||
if (newCurrentIndex < 0) {
|
||||
for (int i = (removeIndex - 1); i >= 0; --i) {
|
||||
if (tab(i)->isVisibleTo(this)) {
|
||||
newCurrentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit removingTab(removeIndex);
|
||||
d->m_tabsLayout->removeWidget(dockWidgetTab);
|
||||
dockWidgetTab->disconnect(this);
|
||||
dockWidgetTab->removeEventFilter(this);
|
||||
qCInfo(adsLog) << "NewCurrentIndex " << newCurrentIndex;
|
||||
if (newCurrentIndex != d->m_currentIndex) {
|
||||
setCurrentIndex(newCurrentIndex);
|
||||
} else {
|
||||
d->updateTabs();
|
||||
}
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
int DockAreaTabBar::currentIndex() const { return d->m_currentIndex; }
|
||||
|
||||
DockWidgetTab *DockAreaTabBar::currentTab() const
|
||||
{
|
||||
if (d->m_currentIndex < 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return qobject_cast<DockWidgetTab *>(
|
||||
d->m_tabsLayout->itemAt(d->m_currentIndex)->widget());
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabClicked()
|
||||
{
|
||||
DockWidgetTab *tab = qobject_cast<DockWidgetTab *>(sender());
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
int index = d->m_tabsLayout->indexOf(tab);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
setCurrentIndex(index);
|
||||
emit tabBarClicked(index);
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabCloseRequested()
|
||||
{
|
||||
DockWidgetTab *tab = qobject_cast<DockWidgetTab *>(sender());
|
||||
int index = d->m_tabsLayout->indexOf(tab);
|
||||
closeTab(index);
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onCloseOtherTabsRequested()
|
||||
{
|
||||
auto senderTab = qobject_cast<DockWidgetTab *>(sender());
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
auto currentTab = tab(i);
|
||||
if (currentTab->isClosable() && !currentTab->isHidden() && currentTab != senderTab) {
|
||||
// If the dock widget is deleted with the closeTab() call, its tab it will no longer
|
||||
// be in the layout, and thus the index needs to be updated to not skip any tabs
|
||||
int offset = currentTab->dockWidget()->features().testFlag(
|
||||
DockWidget::DockWidgetDeleteOnClose)
|
||||
? 1
|
||||
: 0;
|
||||
closeTab(i);
|
||||
// If the the dock widget blocks closing, i.e. if the flag
|
||||
// CustomCloseHandling is set, and the dock widget is still open,
|
||||
// then we do not need to correct the index
|
||||
if (currentTab->dockWidget()->isClosed()) {
|
||||
i -= offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DockWidgetTab *DockAreaTabBar::tab(int index) const
|
||||
{
|
||||
if (index >= count() || index < 0)
|
||||
return nullptr;
|
||||
|
||||
return qobject_cast<DockWidgetTab *>(d->m_tabsLayout->itemAt(index)->widget());
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabWidgetMoved(const QPoint &globalPosition)
|
||||
{
|
||||
DockWidgetTab *movingTab = qobject_cast<DockWidgetTab *>(sender());
|
||||
if (!movingTab)
|
||||
return;
|
||||
|
||||
int fromIndex = d->m_tabsLayout->indexOf(movingTab);
|
||||
auto mousePos = mapFromGlobal(globalPosition);
|
||||
mousePos.rx() = qMax(d->firstTab()->geometry().left(), mousePos.x());
|
||||
mousePos.rx() = qMin(d->lastTab()->geometry().right(), mousePos.x());
|
||||
int toIndex = -1;
|
||||
// Find tab under mouse
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
DockWidgetTab *dropTab = tab(i);
|
||||
if (dropTab == movingTab || !dropTab->isVisibleTo(this)
|
||||
|| !dropTab->geometry().contains(mousePos))
|
||||
continue;
|
||||
|
||||
toIndex = d->m_tabsLayout->indexOf(dropTab);
|
||||
if (toIndex == fromIndex)
|
||||
toIndex = -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (toIndex > -1) {
|
||||
d->m_tabsLayout->removeWidget(movingTab);
|
||||
d->m_tabsLayout->insertWidget(toIndex, movingTab);
|
||||
qCInfo(adsLog) << "tabMoved from" << fromIndex << "to" << toIndex;
|
||||
emit tabMoved(fromIndex, toIndex);
|
||||
setCurrentIndex(toIndex);
|
||||
} else {
|
||||
// Ensure that the moved tab is reset to its start position
|
||||
d->m_tabsLayout->update();
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTabBar::closeTab(int index)
|
||||
{
|
||||
if (index < 0 || index >= count())
|
||||
return;
|
||||
|
||||
auto dockWidgetTab = tab(index);
|
||||
if (dockWidgetTab->isHidden())
|
||||
return;
|
||||
|
||||
emit tabCloseRequested(index);
|
||||
}
|
||||
|
||||
bool DockAreaTabBar::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
bool result = Super::eventFilter(watched, event);
|
||||
DockWidgetTab *dockWidgetTab = qobject_cast<DockWidgetTab *>(watched);
|
||||
if (!dockWidgetTab)
|
||||
return result;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::Hide:
|
||||
emit tabClosed(d->m_tabsLayout->indexOf(dockWidgetTab));
|
||||
updateGeometry();
|
||||
break;
|
||||
case QEvent::Show:
|
||||
emit tabOpened(d->m_tabsLayout->indexOf(dockWidgetTab));
|
||||
updateGeometry();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DockAreaTabBar::isTabOpen(int index) const
|
||||
{
|
||||
if (index < 0 || index >= count())
|
||||
return false;
|
||||
|
||||
return !tab(index)->isHidden();
|
||||
}
|
||||
|
||||
QSize DockAreaTabBar::minimumSizeHint() const
|
||||
{
|
||||
QSize size = sizeHint();
|
||||
size.setWidth(10);
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize DockAreaTabBar::sizeHint() const
|
||||
{
|
||||
return d->m_tabsContainerWidget->sizeHint();
|
||||
}
|
||||
|
||||
} // namespace ADS
|
217
src/libs/advanceddockingsystem/dockareatabbar.h
Normal file
217
src/libs/advanceddockingsystem/dockareatabbar.h
Normal file
@@ -0,0 +1,217 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
#include <QScrollArea>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
class DockAreaWidget;
|
||||
class DockWidgetTab;
|
||||
struct DockAreaTabBarPrivate;
|
||||
class DockAreaTitleBar;
|
||||
class FloatingDockContainer;
|
||||
class AbstractFloatingWidget;
|
||||
|
||||
/**
|
||||
* Custom tabbar implementation for tab area that is shown on top of a
|
||||
* dock area widget.
|
||||
* The tabbar displays the tab widgets of the contained dock widgets.
|
||||
* We cannot use QTabBar here because it does a lot of fancy animations
|
||||
* that will crash the application if a tab is removed while the animation
|
||||
* has not finished. And we need to remove a tab, if the user drags a
|
||||
* a dock widget out of a group of tabbed widgets
|
||||
*/
|
||||
class ADS_EXPORT DockAreaTabBar : public QScrollArea
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockAreaTabBarPrivate *d; ///< private data (pimpl)
|
||||
friend struct DockAreaTabBarPrivate;
|
||||
friend class DockAreaTitleBar;
|
||||
|
||||
void onTabClicked();
|
||||
void onTabCloseRequested();
|
||||
void onCloseOtherTabsRequested();
|
||||
void onTabWidgetMoved(const QPoint &globalPos);
|
||||
|
||||
protected:
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
public:
|
||||
using Super = QScrollArea;
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
DockAreaTabBar(DockAreaWidget *parent);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~DockAreaTabBar() override;
|
||||
|
||||
/**
|
||||
* Inserts the given dock widget tab at the given position.
|
||||
* Inserting a new tab at an index less than or equal to the current index
|
||||
* will increment the current index, but keep the current tab.
|
||||
*/
|
||||
void insertTab(int Index, DockWidgetTab *tab);
|
||||
|
||||
/**
|
||||
* Removes the given DockWidgetTab from the tabbar
|
||||
*/
|
||||
void removeTab(DockWidgetTab *tab);
|
||||
|
||||
/**
|
||||
* Returns the number of tabs in this tabbar
|
||||
*/
|
||||
int count() const;
|
||||
|
||||
/**
|
||||
* Returns the current index or -1 if no tab is selected
|
||||
*/
|
||||
int currentIndex() const;
|
||||
|
||||
/**
|
||||
* Returns the current tab or a nullptr if no tab is selected.
|
||||
*/
|
||||
DockWidgetTab *currentTab() const;
|
||||
|
||||
/**
|
||||
* Returns the tab with the given index
|
||||
*/
|
||||
DockWidgetTab *tab(int index) const;
|
||||
|
||||
/**
|
||||
* Filters the tab widget events
|
||||
*/
|
||||
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
/**
|
||||
* This function returns true if the tab is open, that means if it is
|
||||
* visible to the user. If the function returns false, the tab is
|
||||
* closed
|
||||
*/
|
||||
bool isTabOpen(int index) const;
|
||||
|
||||
/**
|
||||
* Overrides the minimumSizeHint() function of QScrollArea
|
||||
* The minimumSizeHint() is bigger than the sizeHint () for the scroll
|
||||
* area because even if the scrollbars are invisible, the required speace
|
||||
* is reserved in the minimumSizeHint(). This override simply returns
|
||||
* sizeHint();
|
||||
*/
|
||||
virtual QSize minimumSizeHint() const override;
|
||||
|
||||
/**
|
||||
* The function provides a sizeHint that matches the height of the
|
||||
* internal viewport.
|
||||
*/
|
||||
virtual QSize sizeHint() const override;
|
||||
|
||||
/**
|
||||
* This property sets the index of the tab bar's visible tab
|
||||
*/
|
||||
void setCurrentIndex(int index);
|
||||
|
||||
/**
|
||||
* This function will close the tab given in Index param.
|
||||
* Closing a tab means, the tab will be hidden, it will not be removed
|
||||
*/
|
||||
void closeTab(int index);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted when the tab bar's current tab is about to be changed. The new
|
||||
* current has the given index, or -1 if there isn't a new one.
|
||||
*/
|
||||
void currentChanging(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the tab bar's current tab changes. The new
|
||||
* current has the given index, or -1 if there isn't a new one
|
||||
*/
|
||||
void currentChanged(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted when user clicks on a tab
|
||||
*/
|
||||
void tabBarClicked(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the close button on a tab is clicked.
|
||||
* The index is the index that should be closed.
|
||||
*/
|
||||
void tabCloseRequested(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted if a tab has been closed
|
||||
*/
|
||||
void tabClosed(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted if a tab has been opened.
|
||||
* A tab is opened if it has been made visible
|
||||
*/
|
||||
void tabOpened(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the tab has moved the tab at index position
|
||||
* from to index position to.
|
||||
*/
|
||||
void tabMoved(int from, int to);
|
||||
|
||||
/**
|
||||
* This signal is emitted, just before the tab with the given index is
|
||||
* removed
|
||||
*/
|
||||
void removingTab(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted if a tab has been inserted
|
||||
*/
|
||||
void tabInserted(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted when a tab title elide state has been changed
|
||||
*/
|
||||
void elidedChanged(bool elided);
|
||||
}; // class DockAreaTabBar
|
||||
|
||||
} // namespace ADS
|
577
src/libs/advanceddockingsystem/dockareatitlebar.cpp
Normal file
577
src/libs/advanceddockingsystem/dockareatitlebar.cpp
Normal file
@@ -0,0 +1,577 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dockareatitlebar.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "dockareatabbar.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockmanager.h"
|
||||
#include "dockoverlay.h"
|
||||
#include "dockwidget.h"
|
||||
#include "dockwidgettab.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
#include "floatingdragpreview.h"
|
||||
#include "iconprovider.h"
|
||||
#include "dockcomponentsfactory.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QStyle>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
|
||||
|
||||
namespace ADS
|
||||
{
|
||||
/**
|
||||
* Private data class of DockAreaTitleBar class (pimpl)
|
||||
*/
|
||||
struct DockAreaTitleBarPrivate
|
||||
{
|
||||
DockAreaTitleBar *q;
|
||||
QPointer<TitleBarButtonType> m_tabsMenuButton;
|
||||
QPointer<TitleBarButtonType> m_undockButton;
|
||||
QPointer<TitleBarButtonType> m_closeButton;
|
||||
QBoxLayout *m_layout;
|
||||
DockAreaWidget *m_dockArea;
|
||||
DockAreaTabBar *m_tabBar;
|
||||
bool m_menuOutdated = true;
|
||||
QMenu *m_tabsMenu;
|
||||
QList<TitleBarButtonType *> m_dockWidgetActionsButtons;
|
||||
|
||||
QPoint m_dragStartMousePos;
|
||||
eDragState m_dragState = DraggingInactive;
|
||||
AbstractFloatingWidget *m_floatingWidget = nullptr;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockAreaTitleBarPrivate(DockAreaTitleBar *parent);
|
||||
|
||||
/**
|
||||
* Creates the title bar close and menu buttons
|
||||
*/
|
||||
void createButtons();
|
||||
|
||||
/**
|
||||
* Creates the internal TabBar
|
||||
*/
|
||||
void createTabBar();
|
||||
|
||||
/**
|
||||
* Convenience function for DockManager access
|
||||
*/
|
||||
DockManager *dockManager() const { return m_dockArea->dockManager(); }
|
||||
|
||||
/**
|
||||
* Returns true if the given config flag is set
|
||||
*/
|
||||
static bool testConfigFlag(DockManager::eConfigFlag flag)
|
||||
{
|
||||
return DockManager::configFlags().testFlag(flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test function for current drag state
|
||||
*/
|
||||
bool isDraggingState(eDragState dragState) const { return this->m_dragState == dragState; }
|
||||
|
||||
|
||||
/**
|
||||
* Starts floating
|
||||
*/
|
||||
void startFloating(const QPoint &offset);
|
||||
|
||||
/**
|
||||
* Makes the dock area floating
|
||||
*/
|
||||
AbstractFloatingWidget *makeAreaFloating(const QPoint &offset, eDragState dragState);
|
||||
}; // struct DockAreaTitleBarPrivate
|
||||
|
||||
|
||||
DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(DockAreaTitleBar *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
void DockAreaTitleBarPrivate::createButtons()
|
||||
{
|
||||
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
// Tabs menu button
|
||||
m_tabsMenuButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasTabsMenuButton));
|
||||
m_tabsMenuButton->setObjectName("tabsMenuButton");
|
||||
m_tabsMenuButton->setAutoRaise(true);
|
||||
m_tabsMenuButton->setPopupMode(QToolButton::InstantPopup);
|
||||
internal::setButtonIcon(m_tabsMenuButton,
|
||||
QStyle::SP_TitleBarUnshadeButton,
|
||||
ADS::DockAreaMenuIcon);
|
||||
QMenu *tabsMenu = new QMenu(m_tabsMenuButton);
|
||||
#ifndef QT_NO_TOOLTIP
|
||||
tabsMenu->setToolTipsVisible(true);
|
||||
#endif
|
||||
QObject::connect(tabsMenu, &QMenu::aboutToShow, q, &DockAreaTitleBar::onTabsMenuAboutToShow);
|
||||
m_tabsMenuButton->setMenu(tabsMenu);
|
||||
internal::setToolTip(m_tabsMenuButton, QObject::tr("List All Tabs"));
|
||||
m_tabsMenuButton->setSizePolicy(sizePolicy);
|
||||
m_layout->addWidget(m_tabsMenuButton, 0);
|
||||
QObject::connect(m_tabsMenuButton->menu(),
|
||||
&QMenu::triggered,
|
||||
q,
|
||||
&DockAreaTitleBar::onTabsMenuActionTriggered);
|
||||
|
||||
// Undock button
|
||||
m_undockButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasUndockButton));
|
||||
m_undockButton->setObjectName("undockButton");
|
||||
m_undockButton->setAutoRaise(true);
|
||||
internal::setToolTip(m_undockButton, QObject::tr("Detach Group"));
|
||||
internal::setButtonIcon(m_undockButton,
|
||||
QStyle::SP_TitleBarNormalButton,
|
||||
ADS::DockAreaUndockIcon);
|
||||
m_undockButton->setSizePolicy(sizePolicy);
|
||||
m_layout->addWidget(m_undockButton, 0);
|
||||
QObject::connect(m_undockButton,
|
||||
&QToolButton::clicked,
|
||||
q,
|
||||
&DockAreaTitleBar::onUndockButtonClicked);
|
||||
|
||||
// Close button
|
||||
m_closeButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasCloseButton));
|
||||
m_closeButton->setObjectName("closeButton");
|
||||
m_closeButton->setAutoRaise(true);
|
||||
internal::setButtonIcon(m_closeButton,
|
||||
QStyle::SP_TitleBarCloseButton,
|
||||
ADS::DockAreaCloseIcon);
|
||||
if (testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
|
||||
internal::setToolTip(m_closeButton, QObject::tr("Close Active Tab"));
|
||||
} else {
|
||||
internal::setToolTip(m_closeButton, QObject::tr("Close Group"));
|
||||
}
|
||||
m_closeButton->setSizePolicy(sizePolicy);
|
||||
m_closeButton->setIconSize(QSize(16, 16));
|
||||
m_layout->addWidget(m_closeButton, 0);
|
||||
QObject::connect(m_closeButton,
|
||||
&QToolButton::clicked,
|
||||
q,
|
||||
&DockAreaTitleBar::onCloseButtonClicked);
|
||||
}
|
||||
|
||||
void DockAreaTitleBarPrivate::createTabBar()
|
||||
{
|
||||
m_tabBar = componentsFactory()->createDockAreaTabBar(m_dockArea);
|
||||
m_tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
||||
m_layout->addWidget(m_tabBar);
|
||||
QObject::connect(m_tabBar,
|
||||
&DockAreaTabBar::tabClosed,
|
||||
q,
|
||||
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||
QObject::connect(m_tabBar,
|
||||
&DockAreaTabBar::tabOpened,
|
||||
q,
|
||||
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||
QObject::connect(m_tabBar,
|
||||
&DockAreaTabBar::tabInserted,
|
||||
q,
|
||||
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||
QObject::connect(m_tabBar,
|
||||
&DockAreaTabBar::removingTab,
|
||||
q,
|
||||
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||
QObject::connect(m_tabBar,
|
||||
&DockAreaTabBar::tabMoved,
|
||||
q,
|
||||
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||
QObject::connect(m_tabBar,
|
||||
&DockAreaTabBar::currentChanged,
|
||||
q,
|
||||
&DockAreaTitleBar::onCurrentTabChanged);
|
||||
QObject::connect(m_tabBar,
|
||||
&DockAreaTabBar::tabBarClicked,
|
||||
q,
|
||||
&DockAreaTitleBar::tabBarClicked);
|
||||
QObject::connect(m_tabBar,
|
||||
&DockAreaTabBar::elidedChanged,
|
||||
q,
|
||||
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||
}
|
||||
|
||||
AbstractFloatingWidget *DockAreaTitleBarPrivate::makeAreaFloating(const QPoint &offset,
|
||||
eDragState dragState)
|
||||
{
|
||||
QSize size = m_dockArea->size();
|
||||
m_dragState = dragState;
|
||||
bool opaqueUndocking = DockManager::configFlags().testFlag(DockManager::OpaqueUndocking)
|
||||
|| (DraggingFloatingWidget != dragState);
|
||||
FloatingDockContainer *floatingDockContainer = nullptr;
|
||||
AbstractFloatingWidget *floatingWidget;
|
||||
if (opaqueUndocking) {
|
||||
floatingWidget = floatingDockContainer = new FloatingDockContainer(m_dockArea);
|
||||
} else {
|
||||
auto w = new FloatingDragPreview(m_dockArea);
|
||||
QObject::connect(w, &FloatingDragPreview::draggingCanceled, [=]() {
|
||||
m_dragState = DraggingInactive;
|
||||
});
|
||||
floatingWidget = w;
|
||||
}
|
||||
|
||||
floatingWidget->startFloating(offset, size, dragState, nullptr);
|
||||
if (floatingDockContainer) {
|
||||
auto topLevelDockWidget = floatingDockContainer->topLevelDockWidget();
|
||||
if (topLevelDockWidget) {
|
||||
topLevelDockWidget->emitTopLevelChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
return floatingWidget;
|
||||
}
|
||||
|
||||
void DockAreaTitleBarPrivate::startFloating(const QPoint &offset)
|
||||
{
|
||||
m_floatingWidget = makeAreaFloating(offset, DraggingFloatingWidget);
|
||||
}
|
||||
|
||||
TitleBarButton::TitleBarButton(bool visible, QWidget *parent)
|
||||
: TitleBarButtonType(parent),
|
||||
m_visible(visible),
|
||||
m_hideWhenDisabled(DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaHideDisabledButtons))
|
||||
{}
|
||||
|
||||
void TitleBarButton::setVisible(bool visible)
|
||||
{
|
||||
// 'visible' can stay 'true' if and only if this button is configured to generaly visible:
|
||||
visible = visible && m_visible;
|
||||
|
||||
// 'visible' can stay 'true' unless: this button is configured to be invisible when it
|
||||
// is disabled and it is currently disabled:
|
||||
if (visible && m_hideWhenDisabled) {
|
||||
visible = isEnabled();
|
||||
}
|
||||
|
||||
Super::setVisible(visible);
|
||||
}
|
||||
|
||||
bool TitleBarButton::event(QEvent *event)
|
||||
{
|
||||
if (QEvent::EnabledChange == event->type() && m_hideWhenDisabled) {
|
||||
// force setVisible() call
|
||||
// Calling setVisible() directly here doesn't work well when button is expected to be shown first time
|
||||
QMetaObject::invokeMethod(this, "setVisible", Qt::QueuedConnection, Q_ARG(bool, isEnabled()));
|
||||
}
|
||||
|
||||
return Super::event(event);
|
||||
}
|
||||
|
||||
SpacerWidget::SpacerWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setStyleSheet("border: none; background: none;");
|
||||
}
|
||||
|
||||
DockAreaTitleBar::DockAreaTitleBar(DockAreaWidget *parent)
|
||||
: QFrame(parent)
|
||||
, d(new DockAreaTitleBarPrivate(this))
|
||||
{
|
||||
d->m_dockArea = parent;
|
||||
|
||||
setObjectName("dockAreaTitleBar");
|
||||
d->m_layout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
d->m_layout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_layout->setSpacing(0);
|
||||
setLayout(d->m_layout);
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
|
||||
d->createTabBar();
|
||||
d->m_layout->addWidget(new SpacerWidget(this));
|
||||
d->createButtons();
|
||||
}
|
||||
|
||||
DockAreaTitleBar::~DockAreaTitleBar() {
|
||||
if (!d->m_closeButton.isNull())
|
||||
delete d->m_closeButton;
|
||||
|
||||
if (!d->m_tabsMenuButton.isNull())
|
||||
delete d->m_tabsMenuButton;
|
||||
|
||||
if (!d->m_undockButton.isNull())
|
||||
delete d->m_undockButton;
|
||||
|
||||
delete d;
|
||||
}
|
||||
|
||||
DockAreaTabBar *DockAreaTitleBar::tabBar() const { return d->m_tabBar; }
|
||||
|
||||
void DockAreaTitleBar::markTabsMenuOutdated() {
|
||||
if (DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaDynamicTabsMenuButtonVisibility)) {
|
||||
bool hasElidedTabTitle = false;
|
||||
for (int i = 0; i < d->m_tabBar->count(); ++i) {
|
||||
if (!d->m_tabBar->isTabOpen(i))
|
||||
continue;
|
||||
|
||||
DockWidgetTab* tab = d->m_tabBar->tab(i);
|
||||
if (tab->isTitleElided()) {
|
||||
hasElidedTabTitle = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool visible = (hasElidedTabTitle && (d->m_tabBar->count() > 1));
|
||||
QMetaObject::invokeMethod(d->m_tabsMenuButton, "setVisible", Qt::QueuedConnection, Q_ARG(bool, visible));
|
||||
}
|
||||
d->m_menuOutdated = true;
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::onTabsMenuAboutToShow()
|
||||
{
|
||||
if (!d->m_menuOutdated) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu *menu = d->m_tabsMenuButton->menu();
|
||||
menu->clear();
|
||||
for (int i = 0; i < d->m_tabBar->count(); ++i) {
|
||||
if (!d->m_tabBar->isTabOpen(i))
|
||||
continue;
|
||||
|
||||
auto tab = d->m_tabBar->tab(i);
|
||||
QAction *action = menu->addAction(tab->icon(), tab->text());
|
||||
internal::setToolTip(action, tab->toolTip());
|
||||
action->setData(i);
|
||||
}
|
||||
|
||||
d->m_menuOutdated = false;
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::onCloseButtonClicked()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
if (d->testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
|
||||
d->m_tabBar->closeTab(d->m_tabBar->currentIndex());
|
||||
} else {
|
||||
d->m_dockArea->closeArea();
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::onUndockButtonClicked()
|
||||
{
|
||||
if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) {
|
||||
d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive);
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::onTabsMenuActionTriggered(QAction *action)
|
||||
{
|
||||
int index = action->data().toInt();
|
||||
d->m_tabBar->setCurrentIndex(index);
|
||||
emit tabBarClicked(index);
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::updateDockWidgetActionsButtons()
|
||||
{
|
||||
DockWidget* dockWidget = d->m_tabBar->currentTab()->dockWidget();
|
||||
if (!d->m_dockWidgetActionsButtons.isEmpty()) {
|
||||
for (auto button : d->m_dockWidgetActionsButtons) {
|
||||
d->m_layout->removeWidget(button);
|
||||
delete button;
|
||||
}
|
||||
d->m_dockWidgetActionsButtons.clear();
|
||||
}
|
||||
|
||||
auto actions = dockWidget->titleBarActions();
|
||||
if (actions.isEmpty())
|
||||
return;
|
||||
|
||||
int insertIndex = indexOf(d->m_tabsMenuButton);
|
||||
for (auto action : actions) {
|
||||
auto button = new TitleBarButton(true, this);
|
||||
button->setDefaultAction(action);
|
||||
button->setAutoRaise(true);
|
||||
button->setPopupMode(QToolButton::InstantPopup);
|
||||
button->setObjectName(action->objectName());
|
||||
d->m_layout->insertWidget(insertIndex++, button, 0);
|
||||
d->m_dockWidgetActionsButtons.append(button);
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::onCurrentTabChanged(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
if (d->testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
|
||||
DockWidget *dockWidget = d->m_tabBar->tab(index)->dockWidget();
|
||||
d->m_closeButton->setEnabled(
|
||||
dockWidget->features().testFlag(DockWidget::DockWidgetClosable));
|
||||
}
|
||||
|
||||
updateDockWidgetActionsButtons();
|
||||
}
|
||||
|
||||
QAbstractButton *DockAreaTitleBar::button(eTitleBarButton which) const
|
||||
{
|
||||
switch (which) {
|
||||
case TitleBarButtonTabsMenu:
|
||||
return d->m_tabsMenuButton;
|
||||
case TitleBarButtonUndock:
|
||||
return d->m_undockButton;
|
||||
case TitleBarButtonClose:
|
||||
return d->m_closeButton;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::setVisible(bool visible)
|
||||
{
|
||||
Super::setVisible(visible);
|
||||
markTabsMenuOutdated();
|
||||
}
|
||||
|
||||
|
||||
void DockAreaTitleBar::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
event->accept();
|
||||
d->m_dragStartMousePos = event->pos();
|
||||
d->m_dragState = DraggingMousePressed;
|
||||
return;
|
||||
}
|
||||
Super::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
event->accept();
|
||||
auto CurrentDragState = d->m_dragState;
|
||||
d->m_dragStartMousePos = QPoint();
|
||||
d->m_dragState = DraggingInactive;
|
||||
if (DraggingFloatingWidget == CurrentDragState)
|
||||
d->m_floatingWidget->finishDragging();
|
||||
|
||||
return;
|
||||
}
|
||||
Super::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
Super::mouseMoveEvent(event);
|
||||
if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) {
|
||||
d->m_dragState = DraggingInactive;
|
||||
return;
|
||||
}
|
||||
|
||||
// move floating window
|
||||
if (d->isDraggingState(DraggingFloatingWidget)) {
|
||||
d->m_floatingWidget->moveFloating();
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is the last dock area in a dock container it does not make
|
||||
// sense to move it to a new floating widget and leave this one empty
|
||||
if (d->m_dockArea->dockContainer()->isFloating()
|
||||
&& d->m_dockArea->dockContainer()->visibleDockAreaCount() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If one single dock widget in this area is not floatable then the whole
|
||||
// area is not floatable
|
||||
// If we do non opaque undocking, then we can create the floating drag
|
||||
// preview if the dock widget is movable
|
||||
auto features = d->m_dockArea->features();
|
||||
if (!features.testFlag(DockWidget::DockWidgetFloatable)
|
||||
&& !(features.testFlag(DockWidget::DockWidgetMovable)
|
||||
&& !DockManager::testConfigFlag(DockManager::OpaqueUndocking))) {
|
||||
return;
|
||||
}
|
||||
|
||||
int dragDistance = (d->m_dragStartMousePos - event->pos()).manhattanLength();
|
||||
if (dragDistance >= DockManager::startDragDistance()) {
|
||||
qCInfo(adsLog) << "TabsScrollArea::startFloating";
|
||||
d->startFloating(d->m_dragStartMousePos);
|
||||
auto overlay = d->m_dockArea->dockManager()->containerOverlay();
|
||||
overlay->setAllowedAreas(OuterDockAreas);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
// If this is the last dock area in a dock container it does not make
|
||||
// sense to move it to a new floating widget and leave this one empty
|
||||
if (d->m_dockArea->dockContainer()->isFloating()
|
||||
&& d->m_dockArea->dockContainer()->dockAreaCount() == 1)
|
||||
return;
|
||||
|
||||
if (!d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
|
||||
return;
|
||||
|
||||
d->makeAreaFloating(event->pos(), DraggingInactive);
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
if (d->isDraggingState(DraggingFloatingWidget))
|
||||
return;
|
||||
|
||||
QMenu menu(this);
|
||||
auto action = menu.addAction(tr("Detach Area"),
|
||||
this,
|
||||
&DockAreaTitleBar::onUndockButtonClicked);
|
||||
action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable));
|
||||
menu.addSeparator();
|
||||
action = menu.addAction(tr("Close Area"), this, &DockAreaTitleBar::onCloseButtonClicked);
|
||||
action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable));
|
||||
menu.addAction(tr("Close Other Areas"), d->m_dockArea, &DockAreaWidget::closeOtherAreas);
|
||||
menu.exec(event->globalPos());
|
||||
}
|
||||
|
||||
void DockAreaTitleBar::insertWidget(int index, QWidget *widget)
|
||||
{
|
||||
d->m_layout->insertWidget(index, widget);
|
||||
}
|
||||
|
||||
int DockAreaTitleBar::indexOf(QWidget *widget) const
|
||||
{
|
||||
return d->m_layout->indexOf(widget);
|
||||
}
|
||||
|
||||
} // namespace ADS
|
209
src/libs/advanceddockingsystem/dockareatitlebar.h
Normal file
209
src/libs/advanceddockingsystem/dockareatitlebar.h
Normal file
@@ -0,0 +1,209 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
#include <QFrame>
|
||||
#include <QToolButton>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAbstractButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace ADS {
|
||||
|
||||
class DockAreaTabBar;
|
||||
class DockAreaWidget;
|
||||
struct DockAreaTitleBarPrivate;
|
||||
|
||||
using TitleBarButtonType = QToolButton;
|
||||
|
||||
/**
|
||||
* Title bar button of a dock area that customizes TitleBarButtonType appearance/behavior
|
||||
* according to various config flags such as:
|
||||
* DockManager::DockAreaHas_xxx_Button - if set to 'false' keeps the button always invisible
|
||||
* DockManager::DockAreaHideDisabledButtons - if set to 'true' hides button when it is disabled
|
||||
*/
|
||||
class TitleBarButton : public TitleBarButtonType
|
||||
{
|
||||
Q_OBJECT
|
||||
bool m_visible = true;
|
||||
bool m_hideWhenDisabled = false;
|
||||
public:
|
||||
using Super = TitleBarButtonType;
|
||||
TitleBarButton(bool visible = true, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Adjust this visibility change request with our internal settings:
|
||||
*/
|
||||
virtual void setVisible(bool visible) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Handle EnabledChanged signal to set button invisible if the configured
|
||||
*/
|
||||
bool event(QEvent *event) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* This spacer widget is here because of the following problem.
|
||||
* The dock area title bar handles mouse dragging and moving the floating widget.
|
||||
* The problem is, that if the title bar becomes invisible, i.e. if the dock
|
||||
* area contains only one single dock widget and the dock area is moved
|
||||
* into a floating widget, then mouse events are not handled anymore and dragging
|
||||
* of the floating widget stops.
|
||||
*/
|
||||
class SpacerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SpacerWidget(QWidget *parent = nullptr);
|
||||
virtual QSize sizeHint() const override {return QSize(0, 0);}
|
||||
virtual QSize minimumSizeHint() const override {return QSize(0, 0);}
|
||||
};
|
||||
|
||||
/**
|
||||
* Title bar of a dock area.
|
||||
* The title bar contains a tabbar with all tabs for a dock widget group and
|
||||
* with a tabs menu button, a undock button and a close button.
|
||||
*/
|
||||
class ADS_EXPORT DockAreaTitleBar : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockAreaTitleBarPrivate *d; ///< private data (pimpl)
|
||||
friend struct DockAreaTitleBarPrivate;
|
||||
|
||||
void onTabsMenuAboutToShow();
|
||||
void onCloseButtonClicked();
|
||||
void onUndockButtonClicked();
|
||||
void onTabsMenuActionTriggered(QAction *action);
|
||||
void onCurrentTabChanged(int index);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Stores mouse position to detect dragging
|
||||
*/
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
/**
|
||||
* Stores mouse position to detect dragging
|
||||
*/
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
/**
|
||||
* Starts floating the complete docking area including all dock widgets,
|
||||
* if it is not the last dock area in a floating widget
|
||||
*/
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
/**
|
||||
* Double clicking the title bar also starts floating of the complete area
|
||||
*/
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
|
||||
/**
|
||||
* Show context menu
|
||||
*/
|
||||
virtual void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Call this slot to tell the title bar that it should update the tabs menu
|
||||
* the next time it is shown.
|
||||
*/
|
||||
void markTabsMenuOutdated();
|
||||
|
||||
using Super = QFrame;
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
DockAreaTitleBar(DockAreaWidget *parent);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~DockAreaTitleBar() override;
|
||||
|
||||
/**
|
||||
* Returns the pointer to the tabBar()
|
||||
*/
|
||||
DockAreaTabBar *tabBar() const;
|
||||
|
||||
/**
|
||||
* Returns the button corresponding to the given title bar button identifier
|
||||
*/
|
||||
QAbstractButton *button(eTitleBarButton which) const;
|
||||
|
||||
/**
|
||||
* Updates the visibility of the dock widget actions in the title bar
|
||||
*/
|
||||
void updateDockWidgetActionsButtons();
|
||||
|
||||
/**
|
||||
* Marks the tabs menu outdated before it calls its base class
|
||||
* implementation
|
||||
*/
|
||||
virtual void setVisible(bool visible) override;
|
||||
|
||||
/**
|
||||
* Inserts a custom widget at position index into this title bar.
|
||||
* If index is negative, the widget is added at the end.
|
||||
* You can use this function to insert custom widgets into the title bar.
|
||||
*/
|
||||
void insertWidget(int index, QWidget *widget);
|
||||
|
||||
/**
|
||||
* Searches for widget widget in this title bar.
|
||||
* You can use this function, to get the position of the default
|
||||
* widget in the tile bar.
|
||||
* \code
|
||||
* int tabBarIndex = TitleBar->indexOf(TitleBar->tabBar());
|
||||
* int closeButtonIndex = TitleBar->indexOf(TitleBar->button(TitleBarButtonClose));
|
||||
* \endcode
|
||||
*/
|
||||
int indexOf(QWidget *widget) const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if a tab in the tab bar is clicked by the user
|
||||
* or if the user clicks on a tab item in the title bar tab menu.
|
||||
*/
|
||||
void tabBarClicked(int index);
|
||||
}; // class DockAreaTitleBar
|
||||
|
||||
} // namespace ADS
|
686
src/libs/advanceddockingsystem/dockareawidget.cpp
Normal file
686
src/libs/advanceddockingsystem/dockareawidget.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dockareawidget.h"
|
||||
|
||||
#include "dockareatabbar.h"
|
||||
#include "dockareatitlebar.h"
|
||||
#include "dockcomponentsfactory.h"
|
||||
#include "dockcontainerwidget.h"
|
||||
#include "dockmanager.h"
|
||||
#include "dockoverlay.h"
|
||||
#include "docksplitter.h"
|
||||
#include "dockwidget.h"
|
||||
#include "dockwidgettab.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMenu>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QSplitter>
|
||||
#include <QStackedLayout>
|
||||
#include <QStyle>
|
||||
#include <QVector>
|
||||
#include <QWheelEvent>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
|
||||
|
||||
namespace ADS
|
||||
{
|
||||
static const char *const INDEX_PROPERTY = "index";
|
||||
static const char *const ACTION_PROPERTY = "action";
|
||||
|
||||
/**
|
||||
* Internal dock area layout mimics stack layout but only inserts the current
|
||||
* widget into the internal QLayout object.
|
||||
* \warning Only the current widget has a parent. All other widgets
|
||||
* do not have a parent. That means, a widget that is in this layout may
|
||||
* return nullptr for its parent() function if it is not the current widget.
|
||||
*/
|
||||
class DockAreaLayout
|
||||
{
|
||||
private:
|
||||
QBoxLayout *m_parentLayout;
|
||||
QList<QWidget *> m_widgets;
|
||||
int m_currentIndex = -1;
|
||||
QWidget *m_currentWidget = nullptr;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an instance with the given parent layout
|
||||
*/
|
||||
DockAreaLayout(QBoxLayout *parentLayout)
|
||||
: m_parentLayout(parentLayout)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Returns the number of widgets in this layout
|
||||
*/
|
||||
int count() const { return m_widgets.count(); }
|
||||
|
||||
/**
|
||||
* Inserts the widget at the given index position into the internal widget
|
||||
* list
|
||||
*/
|
||||
void insertWidget(int index, QWidget *widget)
|
||||
{
|
||||
widget->setParent(nullptr);
|
||||
if (index < 0) {
|
||||
index = m_widgets.count();
|
||||
}
|
||||
m_widgets.insert(index, widget);
|
||||
if (m_currentIndex < 0) {
|
||||
setCurrentIndex(index);
|
||||
} else {
|
||||
if (index <= m_currentIndex) {
|
||||
++m_currentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given widget from the layout
|
||||
*/
|
||||
void removeWidget(QWidget *widget)
|
||||
{
|
||||
if (currentWidget() == widget) {
|
||||
auto layoutItem = m_parentLayout->takeAt(1);
|
||||
if (layoutItem) {
|
||||
layoutItem->widget()->setParent(nullptr);
|
||||
}
|
||||
m_currentWidget = nullptr;
|
||||
m_currentIndex = -1;
|
||||
}
|
||||
m_widgets.removeOne(widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current selected widget
|
||||
*/
|
||||
QWidget *currentWidget() const { return m_currentWidget; }
|
||||
|
||||
/**
|
||||
* Activates the widget with the give index.
|
||||
*/
|
||||
void setCurrentIndex(int index)
|
||||
{
|
||||
QWidget *prev = currentWidget();
|
||||
QWidget *next = widget(index);
|
||||
if (!next || (next == prev && !m_currentWidget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool reenableUpdates = false;
|
||||
QWidget *parent = m_parentLayout->parentWidget();
|
||||
|
||||
if (parent && parent->updatesEnabled()) {
|
||||
reenableUpdates = true;
|
||||
parent->setUpdatesEnabled(false);
|
||||
}
|
||||
|
||||
// TODO
|
||||
auto layoutItem = m_parentLayout->takeAt(1);
|
||||
if (layoutItem) {
|
||||
layoutItem->widget()->setParent(nullptr);
|
||||
}
|
||||
|
||||
m_parentLayout->addWidget(next);
|
||||
if (prev) {
|
||||
prev->hide();
|
||||
}
|
||||
m_currentIndex = index;
|
||||
m_currentWidget = next;
|
||||
|
||||
if (reenableUpdates) {
|
||||
parent->setUpdatesEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the current active widget
|
||||
*/
|
||||
int currentIndex() const { return m_currentIndex; }
|
||||
|
||||
/**
|
||||
* Returns true if there are no widgets in the layout
|
||||
*/
|
||||
bool isEmpty() const { return m_widgets.empty(); }
|
||||
|
||||
/**
|
||||
* Returns the index of the given widget
|
||||
*/
|
||||
int indexOf(QWidget *widget) const { return m_widgets.indexOf(widget); }
|
||||
|
||||
/**
|
||||
* Returns the widget for the given index
|
||||
*/
|
||||
QWidget *widget(int index) const
|
||||
{
|
||||
return (index < m_widgets.size()) ? m_widgets.at(index) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the geometry of the current active widget
|
||||
*/
|
||||
QRect geometry() const { return m_widgets.empty() ? QRect() : currentWidget()->geometry(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Private data class of DockAreaWidget class (pimpl)
|
||||
*/
|
||||
struct DockAreaWidgetPrivate
|
||||
{
|
||||
DockAreaWidget *q = nullptr;
|
||||
QBoxLayout *m_layout = nullptr;
|
||||
DockAreaLayout *m_contentsLayout = nullptr;
|
||||
DockAreaTitleBar *m_titleBar = nullptr;
|
||||
DockManager *m_dockManager = nullptr;
|
||||
bool m_updateTitleBarButtons = false;
|
||||
DockWidgetAreas m_allowedAreas = AllDockAreas;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockAreaWidgetPrivate(DockAreaWidget *parent);
|
||||
|
||||
/**
|
||||
* Creates the layout for top area with tabs and close button
|
||||
*/
|
||||
void createTitleBar();
|
||||
|
||||
/**
|
||||
* Returns the dock widget with the given index
|
||||
*/
|
||||
DockWidget *dockWidgetAt(int index)
|
||||
{
|
||||
return qobject_cast<DockWidget *>(m_contentsLayout->widget(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to ease title widget access by index
|
||||
*/
|
||||
DockWidgetTab *tabWidgetAt(int index) { return dockWidgetAt(index)->tabWidget(); }
|
||||
|
||||
/**
|
||||
* Returns the tab action of the given dock widget
|
||||
*/
|
||||
QAction *dockWidgetTabAction(DockWidget *dockWidget) const
|
||||
{
|
||||
return qvariant_cast<QAction *>(dockWidget->property(ACTION_PROPERTY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the given dock widget
|
||||
*/
|
||||
int dockWidgetIndex(DockWidget *dockWidget) const
|
||||
{
|
||||
return dockWidget->property(INDEX_PROPERTY).toInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for tabbar access
|
||||
*/
|
||||
DockAreaTabBar *tabBar() const { return m_titleBar->tabBar(); }
|
||||
|
||||
/**
|
||||
* Udpates the enable state of the close and detach button
|
||||
*/
|
||||
void updateTitleBarButtonStates();
|
||||
};
|
||||
// struct DockAreaWidgetPrivate
|
||||
|
||||
DockAreaWidgetPrivate::DockAreaWidgetPrivate(DockAreaWidget *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
void DockAreaWidgetPrivate::createTitleBar()
|
||||
{
|
||||
m_titleBar = componentsFactory()->createDockAreaTitleBar(q);
|
||||
m_layout->addWidget(m_titleBar);
|
||||
QObject::connect(tabBar(),
|
||||
&DockAreaTabBar::tabCloseRequested,
|
||||
q,
|
||||
&DockAreaWidget::onTabCloseRequested);
|
||||
QObject::connect(m_titleBar,
|
||||
&DockAreaTitleBar::tabBarClicked,
|
||||
q,
|
||||
&DockAreaWidget::setCurrentIndex);
|
||||
QObject::connect(tabBar(), &DockAreaTabBar::tabMoved, q, &DockAreaWidget::reorderDockWidget);
|
||||
}
|
||||
|
||||
void DockAreaWidgetPrivate::updateTitleBarButtonStates()
|
||||
{
|
||||
if (q->isHidden()) {
|
||||
m_updateTitleBarButtons = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_titleBar->button(TitleBarButtonClose)
|
||||
->setEnabled(q->features().testFlag(DockWidget::DockWidgetClosable));
|
||||
m_titleBar->button(TitleBarButtonUndock)
|
||||
->setEnabled(q->features().testFlag(DockWidget::DockWidgetFloatable));
|
||||
m_titleBar->updateDockWidgetActionsButtons();
|
||||
m_updateTitleBarButtons = false;
|
||||
}
|
||||
|
||||
DockAreaWidget::DockAreaWidget(DockManager *dockManager, DockContainerWidget *parent)
|
||||
: QFrame(parent)
|
||||
, d(new DockAreaWidgetPrivate(this))
|
||||
{
|
||||
d->m_dockManager = dockManager;
|
||||
d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
d->m_layout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_layout->setSpacing(0);
|
||||
setLayout(d->m_layout);
|
||||
|
||||
d->createTitleBar();
|
||||
d->m_contentsLayout = new DockAreaLayout(d->m_layout);
|
||||
if (d->m_dockManager) {
|
||||
emit d->m_dockManager->dockAreaCreated(this);
|
||||
}
|
||||
}
|
||||
|
||||
DockAreaWidget::~DockAreaWidget()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
delete d->m_contentsLayout;
|
||||
delete d;
|
||||
}
|
||||
|
||||
DockManager *DockAreaWidget::dockManager() const { return d->m_dockManager; }
|
||||
|
||||
DockContainerWidget *DockAreaWidget::dockContainer() const
|
||||
{
|
||||
return internal::findParent<DockContainerWidget *>(this);
|
||||
}
|
||||
|
||||
void DockAreaWidget::addDockWidget(DockWidget *dockWidget)
|
||||
{
|
||||
insertDockWidget(d->m_contentsLayout->count(), dockWidget);
|
||||
}
|
||||
|
||||
void DockAreaWidget::insertDockWidget(int index, DockWidget *dockWidget, bool activate)
|
||||
{
|
||||
d->m_contentsLayout->insertWidget(index, dockWidget);
|
||||
dockWidget->tabWidget()->setDockAreaWidget(this);
|
||||
auto tabWidget = dockWidget->tabWidget();
|
||||
// Inserting the tab will change the current index which in turn will
|
||||
// make the tab widget visible in the slot
|
||||
d->tabBar()->blockSignals(true);
|
||||
d->tabBar()->insertTab(index, tabWidget);
|
||||
d->tabBar()->blockSignals(false);
|
||||
tabWidget->setVisible(!dockWidget->isClosed());
|
||||
dockWidget->setProperty(INDEX_PROPERTY, index);
|
||||
if (activate) {
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
dockWidget->setDockArea(this);
|
||||
d->updateTitleBarButtonStates();
|
||||
}
|
||||
|
||||
void DockAreaWidget::removeDockWidget(DockWidget *dockWidget)
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
auto nextOpen = nextOpenDockWidget(dockWidget);
|
||||
|
||||
d->m_contentsLayout->removeWidget(dockWidget);
|
||||
auto tabWidget = dockWidget->tabWidget();
|
||||
tabWidget->hide();
|
||||
d->tabBar()->removeTab(tabWidget);
|
||||
DockContainerWidget *dockContainerWidget = dockContainer();
|
||||
if (nextOpen) {
|
||||
setCurrentDockWidget(nextOpen);
|
||||
} else if (d->m_contentsLayout->isEmpty() && dockContainerWidget->dockAreaCount() > 1) {
|
||||
qCInfo(adsLog) << "Dock Area empty";
|
||||
dockContainerWidget->removeDockArea(this);
|
||||
this->deleteLater();
|
||||
} else {
|
||||
// if contents layout is not empty but there are no more open dock
|
||||
// widgets, then we need to hide the dock area because it does not
|
||||
// contain any visible content
|
||||
hideAreaWithNoVisibleContent();
|
||||
}
|
||||
|
||||
d->updateTitleBarButtonStates();
|
||||
updateTitleBarVisibility();
|
||||
auto topLevelDockWidget = dockContainerWidget->topLevelDockWidget();
|
||||
if (topLevelDockWidget) {
|
||||
topLevelDockWidget->emitTopLevelChanged(true);
|
||||
}
|
||||
|
||||
#if (ADS_DEBUG_LEVEL > 0)
|
||||
dockContainerWidget->dumpLayout();
|
||||
#endif
|
||||
}
|
||||
|
||||
void DockAreaWidget::hideAreaWithNoVisibleContent()
|
||||
{
|
||||
this->toggleView(false);
|
||||
|
||||
// Hide empty parent splitters
|
||||
auto splitter = internal::findParent<DockSplitter *>(this);
|
||||
internal::hideEmptyParentSplitters(splitter);
|
||||
|
||||
//Hide empty floating widget
|
||||
DockContainerWidget *container = this->dockContainer();
|
||||
if (!container->isFloating()) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateTitleBarVisibility();
|
||||
auto topLevelWidget = container->topLevelDockWidget();
|
||||
auto floatingWidget = container->floatingWidget();
|
||||
if (topLevelWidget) {
|
||||
floatingWidget->updateWindowTitle();
|
||||
DockWidget::emitTopLevelEventForWidget(topLevelWidget, true);
|
||||
} else if (container->openedDockAreas().isEmpty()) {
|
||||
floatingWidget->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaWidget::onTabCloseRequested(int index)
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO << "index" << index;
|
||||
auto *currentDockWidget = dockWidget(index);
|
||||
if (currentDockWidget->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
|
||||
currentDockWidget->closeDockWidgetInternal();
|
||||
} else {
|
||||
currentDockWidget->toggleView(false);
|
||||
}
|
||||
}
|
||||
|
||||
DockWidget *DockAreaWidget::currentDockWidget() const
|
||||
{
|
||||
int currentIdx = currentIndex();
|
||||
if (currentIdx < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dockWidget(currentIdx);
|
||||
}
|
||||
|
||||
void DockAreaWidget::setCurrentDockWidget(DockWidget *dockWidget)
|
||||
{
|
||||
if (dockManager()->isRestoringState()) {
|
||||
return;
|
||||
}
|
||||
|
||||
internalSetCurrentDockWidget(dockWidget);
|
||||
}
|
||||
|
||||
void DockAreaWidget::internalSetCurrentDockWidget(DockWidget *dockWidget)
|
||||
{
|
||||
int index = indexOf(dockWidget);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void DockAreaWidget::setCurrentIndex(int index)
|
||||
{
|
||||
auto currentTabBar = d->tabBar();
|
||||
if (index < 0 || index > (currentTabBar->count() - 1)) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
auto cw = d->m_contentsLayout->currentWidget();
|
||||
auto nw = d->m_contentsLayout->widget(index);
|
||||
if (cw == nw && !nw->isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit currentChanging(index);
|
||||
currentTabBar->setCurrentIndex(index);
|
||||
d->m_contentsLayout->setCurrentIndex(index);
|
||||
d->m_contentsLayout->currentWidget()->show();
|
||||
emit currentChanged(index);
|
||||
}
|
||||
|
||||
int DockAreaWidget::currentIndex() const { return d->m_contentsLayout->currentIndex(); }
|
||||
|
||||
QRect DockAreaWidget::titleBarGeometry() const { return d->m_titleBar->geometry(); }
|
||||
|
||||
QRect DockAreaWidget::contentAreaGeometry() const { return d->m_contentsLayout->geometry(); }
|
||||
|
||||
int DockAreaWidget::indexOf(DockWidget *dockWidget)
|
||||
{
|
||||
return d->m_contentsLayout->indexOf(dockWidget);
|
||||
}
|
||||
|
||||
QList<DockWidget *> DockAreaWidget::dockWidgets() const
|
||||
{
|
||||
QList<DockWidget *> dockWidgetList;
|
||||
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||
dockWidgetList.append(dockWidget(i));
|
||||
}
|
||||
return dockWidgetList;
|
||||
}
|
||||
|
||||
int DockAreaWidget::openDockWidgetsCount() const
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||
if (!dockWidget(i)->isClosed()) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
QList<DockWidget *> DockAreaWidget::openedDockWidgets() const
|
||||
{
|
||||
QList<DockWidget *> dockWidgetList;
|
||||
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||
DockWidget *currentDockWidget = dockWidget(i);
|
||||
if (!currentDockWidget->isClosed()) {
|
||||
dockWidgetList.append(dockWidget(i));
|
||||
}
|
||||
}
|
||||
return dockWidgetList;
|
||||
}
|
||||
|
||||
int DockAreaWidget::indexOfFirstOpenDockWidget() const
|
||||
{
|
||||
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||
if (!dockWidget(i)->isClosed()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int DockAreaWidget::dockWidgetsCount() const { return d->m_contentsLayout->count(); }
|
||||
|
||||
DockWidget *DockAreaWidget::dockWidget(int index) const
|
||||
{
|
||||
return qobject_cast<DockWidget *>(d->m_contentsLayout->widget(index));
|
||||
}
|
||||
|
||||
void DockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
if (fromIndex >= d->m_contentsLayout->count() || fromIndex < 0
|
||||
|| toIndex >= d->m_contentsLayout->count() || toIndex < 0 || fromIndex == toIndex) {
|
||||
qCInfo(adsLog) << "Invalid index for tab movement" << fromIndex << toIndex;
|
||||
return;
|
||||
}
|
||||
|
||||
auto widget = d->m_contentsLayout->widget(fromIndex);
|
||||
d->m_contentsLayout->removeWidget(widget);
|
||||
d->m_contentsLayout->insertWidget(toIndex, widget);
|
||||
setCurrentIndex(toIndex);
|
||||
}
|
||||
|
||||
void DockAreaWidget::toggleDockWidgetView(DockWidget *dockWidget, bool open)
|
||||
{
|
||||
Q_UNUSED(dockWidget)
|
||||
Q_UNUSED(open)
|
||||
updateTitleBarVisibility();
|
||||
}
|
||||
|
||||
void DockAreaWidget::updateTitleBarVisibility()
|
||||
{
|
||||
DockContainerWidget *container = dockContainer();
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DockManager::configFlags().testFlag(DockManager::AlwaysShowTabs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->m_titleBar) {
|
||||
d->m_titleBar->setVisible(!container->isFloating() || !container->hasTopLevelDockWidget());
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaWidget::markTitleBarMenuOutdated()
|
||||
{
|
||||
if (d->m_titleBar) {
|
||||
d->m_titleBar->markTabsMenuOutdated();
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaWidget::saveState(QXmlStreamWriter &stream) const
|
||||
{
|
||||
stream.writeStartElement("area");
|
||||
stream.writeAttribute("tabs", QString::number(d->m_contentsLayout->count()));
|
||||
auto localDockWidget = currentDockWidget();
|
||||
QString name = localDockWidget ? localDockWidget->objectName() : "";
|
||||
stream.writeAttribute("current", name);
|
||||
qCInfo(adsLog) << Q_FUNC_INFO << "TabCount: " << d->m_contentsLayout->count()
|
||||
<< " Current: " << name;
|
||||
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||
dockWidget(i)->saveState(stream);
|
||||
}
|
||||
stream.writeEndElement();
|
||||
}
|
||||
|
||||
DockWidget *DockAreaWidget::nextOpenDockWidget(DockWidget *dockWidget) const
|
||||
{
|
||||
auto openDockWidgets = openedDockWidgets();
|
||||
if (openDockWidgets.count() > 1
|
||||
|| (openDockWidgets.count() == 1 && openDockWidgets[0] != dockWidget)) {
|
||||
DockWidget *nextDockWidget;
|
||||
if (openDockWidgets.last() == dockWidget) {
|
||||
nextDockWidget = openDockWidgets[openDockWidgets.count() - 2];
|
||||
} else {
|
||||
int nextIndex = openDockWidgets.indexOf(dockWidget) + 1;
|
||||
nextDockWidget = openDockWidgets[nextIndex];
|
||||
}
|
||||
|
||||
return nextDockWidget;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DockWidget::DockWidgetFeatures DockAreaWidget::features(eBitwiseOperator mode) const
|
||||
{
|
||||
if (BitwiseAnd == mode) {
|
||||
DockWidget::DockWidgetFeatures features(DockWidget::AllDockWidgetFeatures);
|
||||
for (const auto dockWidget : dockWidgets()) {
|
||||
features &= dockWidget->features();
|
||||
}
|
||||
return features;
|
||||
} else {
|
||||
DockWidget::DockWidgetFeatures features(DockWidget::NoDockWidgetFeatures);
|
||||
for (const auto dockWidget : dockWidgets()) {
|
||||
features |= dockWidget->features();
|
||||
}
|
||||
return features;
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaWidget::toggleView(bool open)
|
||||
{
|
||||
setVisible(open);
|
||||
|
||||
emit viewToggled(open);
|
||||
}
|
||||
|
||||
void DockAreaWidget::setVisible(bool visible)
|
||||
{
|
||||
Super::setVisible(visible);
|
||||
if (d->m_updateTitleBarButtons) {
|
||||
d->updateTitleBarButtonStates();
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaWidget::setAllowedAreas(DockWidgetAreas areas)
|
||||
{
|
||||
d->m_allowedAreas = areas;
|
||||
}
|
||||
|
||||
DockWidgetAreas DockAreaWidget::allowedAreas() const
|
||||
{
|
||||
return d->m_allowedAreas;
|
||||
}
|
||||
|
||||
QAbstractButton *DockAreaWidget::titleBarButton(eTitleBarButton which) const
|
||||
{
|
||||
return d->m_titleBar->button(which);
|
||||
}
|
||||
|
||||
void DockAreaWidget::closeArea()
|
||||
{
|
||||
// If there is only one single dock widget and this widget has the
|
||||
// DeleteOnClose feature, then we delete the dock widget now
|
||||
auto openDockWidgets = openedDockWidgets();
|
||||
if (openDockWidgets.count() == 1
|
||||
&& openDockWidgets[0]->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
|
||||
openDockWidgets[0]->closeDockWidgetInternal();
|
||||
} else {
|
||||
for (auto dockWidget : openedDockWidgets()) {
|
||||
dockWidget->toggleView(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaWidget::closeOtherAreas() { dockContainer()->closeOtherAreas(this); }
|
||||
|
||||
DockAreaTitleBar *DockAreaWidget::titleBar() const { return d->m_titleBar; }
|
||||
|
||||
} // namespace ADS
|
321
src/libs/advanceddockingsystem/dockareawidget.h
Normal file
321
src/libs/advanceddockingsystem/dockareawidget.h
Normal file
@@ -0,0 +1,321 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "dockwidget.h"
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAbstractButton;
|
||||
class QXmlStreamWriter;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace ADS {
|
||||
|
||||
struct DockAreaWidgetPrivate;
|
||||
class DockManager;
|
||||
class DockContainerWidget;
|
||||
class DockContainerWidgetPrivate;
|
||||
class DockAreaTitleBar;
|
||||
|
||||
/**
|
||||
* DockAreaWidget manages multiple instances of DockWidgets.
|
||||
* It displays a title tab, which is clickable and will switch to
|
||||
* the contents associated to the title when clicked.
|
||||
*/
|
||||
class ADS_EXPORT DockAreaWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockAreaWidgetPrivate *d; ///< private data (pimpl)
|
||||
friend struct DockAreaWidgetPrivate;
|
||||
friend class DockContainerWidget;
|
||||
friend class DockContainerWidgetPrivate;
|
||||
friend class DockWidgetTab;
|
||||
friend struct DockWidgetPrivate;
|
||||
friend class DockWidget;
|
||||
friend struct DockManagerPrivate;
|
||||
friend class DockManager;
|
||||
|
||||
void onTabCloseRequested(int index);
|
||||
|
||||
/**
|
||||
* Reorder the index position of DockWidget at fromIndx to toIndex
|
||||
* if a tab in the tabbar is dragged from one index to another one
|
||||
*/
|
||||
void reorderDockWidget(int fromIndex, int toIndex);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Inserts a dock widget into dock area.
|
||||
* All dockwidgets in the dock area tabified in a stacked layout with tabs.
|
||||
* The index indicates the index of the new dockwidget in the tabbar and
|
||||
* in the stacked layout. If the Activate parameter is true, the new
|
||||
* DockWidget will be the active one in the stacked layout
|
||||
*/
|
||||
void insertDockWidget(int index, DockWidget *dockWidget, bool activate = true);
|
||||
|
||||
/**
|
||||
* Add a new dock widget to dock area.
|
||||
* All dockwidgets in the dock area tabified in a stacked layout with tabs
|
||||
*/
|
||||
void addDockWidget(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Removes the given dock widget from the dock area
|
||||
*/
|
||||
void removeDockWidget(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Called from dock widget if it is opened or closed
|
||||
*/
|
||||
void toggleDockWidgetView(DockWidget *dockWidget, bool open);
|
||||
|
||||
/**
|
||||
* This is a helper function to get the next open dock widget to activate
|
||||
* if the given DockWidget will be closed or removed.
|
||||
* The function returns the next widget that should be activated or
|
||||
* nullptr in case there are no more open widgets in this area.
|
||||
*/
|
||||
DockWidget *nextOpenDockWidget(DockWidget *dockWidget) const;
|
||||
|
||||
/**
|
||||
* Returns the index of the given DockWidget in the internal layout
|
||||
*/
|
||||
int indexOf(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Call this function, if you already know, that the dock does not
|
||||
* contain any visible content (any open dock widgets).
|
||||
*/
|
||||
void hideAreaWithNoVisibleContent();
|
||||
|
||||
/**
|
||||
* Updates the dock area layout and components visibility
|
||||
*/
|
||||
void updateTitleBarVisibility();
|
||||
|
||||
/**
|
||||
* This is the internal private function for setting the current widget.
|
||||
* This function is called by the public setCurrentDockWidget() function
|
||||
* and by the dock manager when restoring the state
|
||||
*/
|
||||
void internalSetCurrentDockWidget(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Marks tabs menu to update
|
||||
*/
|
||||
void markTitleBarMenuOutdated();
|
||||
|
||||
void toggleView(bool open);
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
DockAreaWidget(DockManager *dockManager, DockContainerWidget *parent);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~DockAreaWidget() override;
|
||||
|
||||
/**
|
||||
* Returns the dock manager object this dock area belongs to
|
||||
*/
|
||||
DockManager *dockManager() const;
|
||||
|
||||
/**
|
||||
* Returns the dock container widget this dock area widget belongs to or 0
|
||||
* if there is no
|
||||
*/
|
||||
DockContainerWidget *dockContainer() const;
|
||||
|
||||
/**
|
||||
* Returns the rectangle of the title area
|
||||
*/
|
||||
QRect titleBarGeometry() const;
|
||||
|
||||
/**
|
||||
* Returns the rectangle of the content
|
||||
*/
|
||||
QRect contentAreaGeometry() const;
|
||||
|
||||
/**
|
||||
* Returns the number of dock widgets in this area
|
||||
*/
|
||||
int dockWidgetsCount() const;
|
||||
|
||||
/**
|
||||
* Returns a list of all dock widgets in this dock area.
|
||||
* This list contains open and closed dock widgets.
|
||||
*/
|
||||
QList<DockWidget *> dockWidgets() const;
|
||||
|
||||
/**
|
||||
* Returns the number of open dock widgets in this area
|
||||
*/
|
||||
int openDockWidgetsCount() const;
|
||||
|
||||
/**
|
||||
* Returns a list of dock widgets that are not closed.
|
||||
*/
|
||||
QList<DockWidget *> openedDockWidgets() const;
|
||||
|
||||
/**
|
||||
* Returns a dock widget by its index
|
||||
*/
|
||||
DockWidget *dockWidget(int indexOf) const;
|
||||
|
||||
/**
|
||||
* Returns the index of the current active dock widget or -1 if there
|
||||
* are is no active dock widget (ie.e if all dock widgets are closed)
|
||||
*/
|
||||
int currentIndex() const;
|
||||
|
||||
/**
|
||||
* Returns the index of the first open dock widgets in the list of
|
||||
* dock widgets.
|
||||
* This function is here for performance reasons. Normally it would
|
||||
* be possible to take the first dock widget from the list returned by
|
||||
* openedDockWidgets() function. But that function enumerates all
|
||||
* dock widgets while this functions stops after the first open dock widget.
|
||||
* If there are no open dock widgets, the function returns -1.
|
||||
*/
|
||||
int indexOfFirstOpenDockWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the current active dock widget or a nullptr if there is no
|
||||
* active dock widget (i.e. if all dock widgets are closed)
|
||||
*/
|
||||
DockWidget *currentDockWidget() const;
|
||||
|
||||
/**
|
||||
* Shows the tab with the given dock widget
|
||||
*/
|
||||
void setCurrentDockWidget(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Saves the state into the given stream
|
||||
*/
|
||||
void saveState(QXmlStreamWriter &stream) const;
|
||||
|
||||
/**
|
||||
* This functions returns the dock widget features of all dock widget in
|
||||
* this area.
|
||||
* A bitwise and is used to combine the flags of all dock widgets. That
|
||||
* means, if only one single dock widget does not support a certain flag,
|
||||
* the whole dock are does not support the flag. I.e. if one single
|
||||
* dock widget in this area is not closable, the whole dock are is not
|
||||
* closable.
|
||||
*/
|
||||
DockWidget::DockWidgetFeatures features(eBitwiseOperator mode = BitwiseAnd) const;
|
||||
|
||||
/**
|
||||
* Returns the title bar button corresponding to the given title bar
|
||||
* button identifier
|
||||
*/
|
||||
QAbstractButton *titleBarButton(eTitleBarButton which) const;
|
||||
|
||||
/**
|
||||
* Update the close button if visibility changed
|
||||
*/
|
||||
virtual void setVisible(bool visible) override;
|
||||
|
||||
/**
|
||||
* Configures the areas of this particular dock area that are allowed for docking
|
||||
*/
|
||||
void setAllowedAreas(DockWidgetAreas areas);
|
||||
|
||||
/**
|
||||
* Returns flags with all allowed drop areas of this particular dock area
|
||||
*/
|
||||
DockWidgetAreas allowedAreas() const;
|
||||
|
||||
/**
|
||||
* Returns the title bar of this dock area
|
||||
*/
|
||||
DockAreaTitleBar *titleBar() const;
|
||||
|
||||
/**
|
||||
* This activates the tab for the given tab index.
|
||||
* If the dock widget for the given tab is not visible, the this function
|
||||
* call will make it visible.
|
||||
*/
|
||||
void setCurrentIndex(int indexOf);
|
||||
|
||||
/**
|
||||
* Closes the dock area and all dock widgets in this area
|
||||
*/
|
||||
void closeArea();
|
||||
|
||||
/**
|
||||
* This function closes all other areas except of this area
|
||||
*/
|
||||
void closeOtherAreas();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted when user clicks on a tab at an index.
|
||||
*/
|
||||
void tabBarClicked(int indexOf);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the tab bar's current tab is about to be changed. The new
|
||||
* current has the given index, or -1 if there isn't a new one.
|
||||
* @param index
|
||||
*/
|
||||
void currentChanging(int indexOf);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the tab bar's current tab changes. The new
|
||||
* current has the given index, or -1 if there isn't a new one
|
||||
* @param index
|
||||
*/
|
||||
void currentChanged(int indexOf);
|
||||
|
||||
/**
|
||||
* This signal is emitted if the visibility of this dock area is toggled
|
||||
* via toggle view function
|
||||
*/
|
||||
void viewToggled(bool open);
|
||||
}; // class DockAreaWidget
|
||||
|
||||
} // namespace ADS
|
80
src/libs/advanceddockingsystem/dockcomponentsfactory.cpp
Normal file
80
src/libs/advanceddockingsystem/dockcomponentsfactory.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dockcomponentsfactory.h"
|
||||
|
||||
#include "dockwidgettab.h"
|
||||
#include "dockareatabbar.h"
|
||||
#include "dockareatitlebar.h"
|
||||
#include "dockwidget.h"
|
||||
#include "dockareawidget.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ADS
|
||||
{
|
||||
static std::unique_ptr<DockComponentsFactory> g_defaultFactory(new DockComponentsFactory());
|
||||
|
||||
DockWidgetTab *DockComponentsFactory::createDockWidgetTab(DockWidget *dockWidget) const
|
||||
{
|
||||
return new DockWidgetTab(dockWidget);
|
||||
}
|
||||
|
||||
DockAreaTabBar *DockComponentsFactory::createDockAreaTabBar(DockAreaWidget *dockArea) const
|
||||
{
|
||||
return new DockAreaTabBar(dockArea);
|
||||
}
|
||||
|
||||
DockAreaTitleBar *DockComponentsFactory::createDockAreaTitleBar(DockAreaWidget *dockArea) const
|
||||
{
|
||||
return new DockAreaTitleBar(dockArea);
|
||||
}
|
||||
|
||||
const DockComponentsFactory *DockComponentsFactory::factory()
|
||||
{
|
||||
return g_defaultFactory.get();
|
||||
}
|
||||
|
||||
void DockComponentsFactory::setFactory(DockComponentsFactory *factory)
|
||||
{
|
||||
g_defaultFactory.reset(factory);
|
||||
}
|
||||
|
||||
void DockComponentsFactory::resetDefaultFactory()
|
||||
{
|
||||
g_defaultFactory.reset(new DockComponentsFactory());
|
||||
}
|
||||
|
||||
} // namespace ADS
|
109
src/libs/advanceddockingsystem/dockcomponentsfactory.h
Normal file
109
src/libs/advanceddockingsystem/dockcomponentsfactory.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
namespace ADS {
|
||||
|
||||
class DockWidgetTab;
|
||||
class DockAreaTitleBar;
|
||||
class DockAreaTabBar;
|
||||
class DockAreaWidget;
|
||||
class DockWidget;
|
||||
|
||||
/**
|
||||
* Factory for creation of certain GUI elements for the docking framework.
|
||||
* A default unique instance provided by DockComponentsFactory is used for
|
||||
* creation of all supported components. To inject your custom components,
|
||||
* you can create your own derived dock components factory and register
|
||||
* it via setDefaultFactory() function.
|
||||
* \code
|
||||
* CDockComponentsFactory::setDefaultFactory(new MyComponentsFactory()));
|
||||
* \endcode
|
||||
*/
|
||||
class ADS_EXPORT DockComponentsFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Force virtual destructor
|
||||
*/
|
||||
virtual ~DockComponentsFactory() {}
|
||||
|
||||
/**
|
||||
* This default implementation just creates a dock widget tab with
|
||||
* new DockWidgetTab(dockWidget).
|
||||
*/
|
||||
virtual DockWidgetTab *createDockWidgetTab(DockWidget *dockWidget) const;
|
||||
|
||||
/**
|
||||
* This default implementation just creates a dock area tab bar with
|
||||
* new DockAreaTabBar(dockArea).
|
||||
*/
|
||||
virtual DockAreaTabBar *createDockAreaTabBar(DockAreaWidget *dockArea) const;
|
||||
|
||||
/**
|
||||
* This default implementation just creates a dock area title bar with
|
||||
* new DockAreaTitleBar(dockArea).
|
||||
*/
|
||||
virtual DockAreaTitleBar *createDockAreaTitleBar(DockAreaWidget *dockArea) const;
|
||||
|
||||
/**
|
||||
* Returns the default components factory
|
||||
*/
|
||||
static const DockComponentsFactory *factory();
|
||||
|
||||
/**
|
||||
* Sets a new default factory for creation of GUI elements.
|
||||
* This function takes ownership of the given Factory.
|
||||
*/
|
||||
static void setFactory(DockComponentsFactory* factory);
|
||||
|
||||
/**
|
||||
* Resets the current factory to the
|
||||
*/
|
||||
static void resetDefaultFactory();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience function to ease factory instance access
|
||||
*/
|
||||
inline const DockComponentsFactory *componentsFactory()
|
||||
{
|
||||
return DockComponentsFactory::factory();
|
||||
}
|
||||
|
||||
} // namespace ADS
|
1459
src/libs/advanceddockingsystem/dockcontainerwidget.cpp
Normal file
1459
src/libs/advanceddockingsystem/dockcontainerwidget.cpp
Normal file
File diff suppressed because it is too large
Load Diff
292
src/libs/advanceddockingsystem/dockcontainerwidget.h
Normal file
292
src/libs/advanceddockingsystem/dockcontainerwidget.h
Normal file
@@ -0,0 +1,292 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "dockwidget.h"
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QXmlStreamWriter;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace ADS {
|
||||
|
||||
class DockContainerWidgetPrivate;
|
||||
class DockAreaWidget;
|
||||
class DockWidget;
|
||||
class DockManager;
|
||||
struct DockManagerPrivate;
|
||||
class FloatingDockContainer;
|
||||
struct FloatingDockContainerPrivate;
|
||||
class FloatingDragPreview;
|
||||
struct FloatingDragPreviewPrivate;
|
||||
class DockingStateReader;
|
||||
|
||||
/**
|
||||
* Container that manages a number of dock areas with single dock widgets
|
||||
* or tabyfied dock widgets in each area.
|
||||
* Each window that support docking has a DockContainerWidget. That means
|
||||
* the main application window and all floating windows contain
|
||||
* a DockContainerWidget.
|
||||
*/
|
||||
class ADS_EXPORT DockContainerWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockContainerWidgetPrivate *d; ///< private data (pimpl)
|
||||
friend class DockContainerWidgetPrivate;
|
||||
friend class DockManager;
|
||||
friend struct DockManagerPrivate;
|
||||
friend class DockAreaWidget;
|
||||
friend struct DockAreaWidgetPrivate;
|
||||
friend class FloatingDockContainer;
|
||||
friend struct FloatingDockContainerPrivate;
|
||||
friend class DockWidget;
|
||||
friend class FloatingDragPreview;
|
||||
friend struct FloatingDragPreviewPrivate;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Handles activation events to update zOrderIndex
|
||||
*/
|
||||
virtual bool event(QEvent *event) override;
|
||||
|
||||
public: // TODO temporary
|
||||
/**
|
||||
* Access function for the internal root splitter
|
||||
*/
|
||||
QSplitter *rootSplitter() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Helper function for creation of the root splitter
|
||||
*/
|
||||
void createRootSplitter();
|
||||
|
||||
/**
|
||||
* Drop floating widget into the container
|
||||
*/
|
||||
void dropFloatingWidget(FloatingDockContainer *floatingWidget, const QPoint &targetPos);
|
||||
|
||||
/**
|
||||
* Drop a dock area or a dock widget given in widget parameter
|
||||
*/
|
||||
void dropWidget(QWidget *widget, const QPoint &targetPos);
|
||||
|
||||
/**
|
||||
* Adds the given dock area to this container widget
|
||||
*/
|
||||
void addDockArea(DockAreaWidget *dockAreaWidget, DockWidgetArea area = CenterDockWidgetArea);
|
||||
|
||||
/**
|
||||
* Removes the given dock area from this container
|
||||
*/
|
||||
void removeDockArea(DockAreaWidget *area);
|
||||
|
||||
/**
|
||||
* This function replaces the goto construct. Still need to write a good description.
|
||||
*/
|
||||
void emitAndExit() const; // TODO rename
|
||||
|
||||
/**
|
||||
* Saves the state into the given stream
|
||||
*/
|
||||
void saveState(QXmlStreamWriter &stream) const;
|
||||
|
||||
/**
|
||||
* Restores the state from given stream.
|
||||
* If Testing is true, the function only parses the data from the given
|
||||
* stream but does not restore anything. You can use this check for
|
||||
* faulty files before you start restoring the state
|
||||
*/
|
||||
bool restoreState(DockingStateReader &stream, bool testing);
|
||||
|
||||
/**
|
||||
* This function returns the last added dock area widget for the given
|
||||
* area identifier or 0 if no dock area widget has been added for the given
|
||||
* area
|
||||
*/
|
||||
DockAreaWidget *lastAddedDockAreaWidget(DockWidgetArea area) const;
|
||||
|
||||
/**
|
||||
* If hasSingleVisibleDockWidget() returns true, this function returns the
|
||||
* one and only visible dock widget. Otherwise it returns a nullptr.
|
||||
*/
|
||||
DockWidget *topLevelDockWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the top level dock area.
|
||||
*/
|
||||
DockAreaWidget *topLevelDockArea() const;
|
||||
|
||||
/**
|
||||
* This function returns a list of all dock widgets in this floating widget.
|
||||
* It may be possible, depending on the implementation, that dock widgets,
|
||||
* that are not visible to the user have no parent widget. Therefore simply
|
||||
* calling findChildren() would not work here. Therefore this function
|
||||
* iterates over all dock areas and creates a list that contains all
|
||||
* dock widgets returned from all dock areas.
|
||||
*/
|
||||
QList<DockWidget *> dockWidgets() const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
DockContainerWidget(DockManager *dockManager, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~DockContainerWidget() override;
|
||||
|
||||
/**
|
||||
* Adds dockwidget into the given area.
|
||||
* If DockAreaWidget is not null, then the area parameter indicates the area
|
||||
* into the DockAreaWidget. If DockAreaWidget is null, the Dockwidget will
|
||||
* be dropped into the container.
|
||||
* \return Returns the dock area widget that contains the new DockWidget
|
||||
*/
|
||||
DockAreaWidget *addDockWidget(DockWidgetArea area,
|
||||
DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget = nullptr);
|
||||
|
||||
/**
|
||||
* Removes dockwidget
|
||||
*/
|
||||
void removeDockWidget(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Returns the current zOrderIndex
|
||||
*/
|
||||
virtual unsigned int zOrderIndex() const;
|
||||
|
||||
/**
|
||||
* This function returns true if this container widgets z order index is
|
||||
* higher than the index of the container widget given in Other parameter
|
||||
*/
|
||||
bool isInFrontOf(DockContainerWidget *other) const;
|
||||
|
||||
/**
|
||||
* Returns the dock area at the given global position or 0 if there is no
|
||||
* dock area at this position
|
||||
*/
|
||||
DockAreaWidget *dockAreaAt(const QPoint &globalPos) const;
|
||||
|
||||
/**
|
||||
* Returns the dock area at the given Index or 0 if the index is out of
|
||||
* range
|
||||
*/
|
||||
DockAreaWidget *dockArea(int index) const;
|
||||
|
||||
/**
|
||||
* Returns the list of dock areas that are not closed
|
||||
* If all dock widgets in a dock area are closed, the dock area will be closed
|
||||
*/
|
||||
QList<DockAreaWidget *> openedDockAreas() const;
|
||||
|
||||
/**
|
||||
* This function returns true if this dock area has only one single
|
||||
* visible dock widget.
|
||||
* A top level widget is a real floating widget. Only the isFloating()
|
||||
* function of top level widgets may returns true.
|
||||
*/
|
||||
bool hasTopLevelDockWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the number of dock areas in this container
|
||||
*/
|
||||
int dockAreaCount() const;
|
||||
|
||||
/**
|
||||
* Returns the number of visible dock areas
|
||||
*/
|
||||
int visibleDockAreaCount() const;
|
||||
|
||||
/**
|
||||
* This function returns true, if this container is in a floating widget
|
||||
*/
|
||||
bool isFloating() const;
|
||||
|
||||
/**
|
||||
* Dumps the layout for debugging purposes
|
||||
*/
|
||||
void dumpLayout() const;
|
||||
|
||||
/**
|
||||
* This functions returns the dock widget features of all dock widget in
|
||||
* this container.
|
||||
* A bitwise and is used to combine the flags of all dock widgets. That
|
||||
* means, if only dock widget does not support a certain flag, the whole
|
||||
* dock are does not support the flag.
|
||||
*/
|
||||
DockWidget::DockWidgetFeatures features() const;
|
||||
|
||||
/**
|
||||
* If this dock container is in a floating widget, this function returns
|
||||
* the floating widget.
|
||||
* Else, it returns a nullptr.
|
||||
*/
|
||||
FloatingDockContainer *floatingWidget() const;
|
||||
|
||||
/**
|
||||
* Call this function to close all dock areas except the KeepOpenArea
|
||||
*/
|
||||
void closeOtherAreas(DockAreaWidget *keepOpenArea);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if one or multiple dock areas has been added to
|
||||
* the internal list of dock areas.
|
||||
* If multiple dock areas are inserted, this signal is emitted only once
|
||||
*/
|
||||
void dockAreasAdded();
|
||||
|
||||
/**
|
||||
* This signal is emitted if one or multiple dock areas has been removed
|
||||
*/
|
||||
void dockAreasRemoved();
|
||||
|
||||
/**
|
||||
* This signal is emitted if a dock area is opened or closed via
|
||||
* toggleView() function
|
||||
*/
|
||||
void dockAreaViewToggled(DockAreaWidget *dockArea, bool open);
|
||||
}; // class DockContainerWidget
|
||||
|
||||
} // namespace ADS
|
50
src/libs/advanceddockingsystem/dockingstatereader.cpp
Normal file
50
src/libs/advanceddockingsystem/dockingstatereader.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dockingstatereader.h"
|
||||
|
||||
namespace ADS {
|
||||
|
||||
void DockingStateReader::setFileVersion(int fileVersion)
|
||||
{
|
||||
m_fileVersion = fileVersion;
|
||||
}
|
||||
|
||||
int DockingStateReader::fileVersion() const
|
||||
{
|
||||
return m_fileVersion;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
64
src/libs/advanceddockingsystem/dockingstatereader.h
Normal file
64
src/libs/advanceddockingsystem/dockingstatereader.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
/**
|
||||
* Extends QXmlStreamReader with file version information
|
||||
*/
|
||||
class DockingStateReader : public QXmlStreamReader
|
||||
{
|
||||
private:
|
||||
int m_fileVersion;
|
||||
|
||||
public:
|
||||
using QXmlStreamReader::QXmlStreamReader;
|
||||
|
||||
/**
|
||||
* Set the file version for this state reader
|
||||
*/
|
||||
void setFileVersion(int fileVersion);
|
||||
|
||||
/**
|
||||
* Returns the file version set via setFileVersion
|
||||
*/
|
||||
int fileVersion() const;
|
||||
};
|
||||
|
||||
} // namespace ADS
|
820
src/libs/advanceddockingsystem/dockmanager.cpp
Normal file
820
src/libs/advanceddockingsystem/dockmanager.cpp
Normal file
@@ -0,0 +1,820 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dockmanager.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockingstatereader.h"
|
||||
#include "dockoverlay.h"
|
||||
#include "dockwidget.h"
|
||||
#include "dockwidgettab.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
#include "workspacedialog.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QList>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMainWindow>
|
||||
#include <QMap>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QVariant>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
|
||||
|
||||
namespace ADS
|
||||
{
|
||||
static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig;
|
||||
|
||||
/**
|
||||
* Private data class of DockManager class (pimpl)
|
||||
*/
|
||||
struct DockManagerPrivate
|
||||
{
|
||||
DockManager *q;
|
||||
QList<FloatingDockContainer *> m_floatingWidgets;
|
||||
QList<DockContainerWidget *> m_containers;
|
||||
DockOverlay *m_containerOverlay;
|
||||
DockOverlay *m_dockAreaOverlay;
|
||||
QMap<QString, DockWidget *> m_dockWidgetsMap;
|
||||
bool m_restoringState = false;
|
||||
QVector<FloatingDockContainer *> m_uninitializedFloatingWidgets;
|
||||
|
||||
QString m_workspaceName;
|
||||
bool m_workspaceListDirty = true;
|
||||
QStringList m_workspaces;
|
||||
QHash<QString, QDateTime> m_workspaceDateTimes;
|
||||
QString m_workspaceToRestoreAtStartup;
|
||||
bool m_autorestoreLastWorkspace; // This option is set in the Workspace Manager!
|
||||
QSettings *m_settings;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockManagerPrivate(DockManager *parent);
|
||||
|
||||
/**
|
||||
* Checks if the given data stream is a valid docking system state
|
||||
* file.
|
||||
*/
|
||||
bool checkFormat(const QByteArray &state, int version);
|
||||
|
||||
/**
|
||||
* Restores the state
|
||||
*/
|
||||
bool restoreStateFromXml(const QByteArray &state,
|
||||
int version,
|
||||
bool testing = internal::restore);
|
||||
|
||||
/**
|
||||
* Restore state
|
||||
*/
|
||||
bool restoreState(const QByteArray &state, int version);
|
||||
|
||||
void restoreDockWidgetsOpenState();
|
||||
void restoreDockAreasIndices();
|
||||
void emitTopLevelEvents();
|
||||
|
||||
void hideFloatingWidgets()
|
||||
{
|
||||
// Hide updates of floating widgets from user
|
||||
for (auto floatingWidget : m_floatingWidgets) { // TODO qAsConst()
|
||||
floatingWidget->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void markDockWidgetsDirty()
|
||||
{
|
||||
for (auto dockWidget : m_dockWidgetsMap) { // TODO qAsConst()
|
||||
dockWidget->setProperty("dirty", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the container with the given index
|
||||
*/
|
||||
bool restoreContainer(int index, DockingStateReader &stream, bool testing);
|
||||
|
||||
void workspaceLoadingProgress();
|
||||
};
|
||||
// struct DockManagerPrivate
|
||||
|
||||
DockManagerPrivate::DockManagerPrivate(DockManager *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
bool DockManagerPrivate::restoreContainer(int index, DockingStateReader &stream, bool testing)
|
||||
{
|
||||
if (testing) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if (index >= m_containers.count()) {
|
||||
FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
|
||||
result = floatingWidget->restoreState(stream, testing);
|
||||
} else {
|
||||
qCInfo(adsLog) << "d->m_containers[i]->restoreState ";
|
||||
auto container = m_containers[index];
|
||||
if (container->isFloating()) {
|
||||
result = container->floatingWidget()->restoreState(stream, testing);
|
||||
} else {
|
||||
result = container->restoreState(stream, testing);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DockManagerPrivate::checkFormat(const QByteArray &state, int version)
|
||||
{
|
||||
return restoreStateFromXml(state, version, internal::restoreTesting);
|
||||
}
|
||||
|
||||
bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int version, bool testing)
|
||||
{
|
||||
Q_UNUSED(version) // TODO version is not needed, why is it in here in the first place?
|
||||
|
||||
if (state.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
DockingStateReader stateReader(state);
|
||||
stateReader.readNextStartElement();
|
||||
if (stateReader.name() != "QtAdvancedDockingSystem") {
|
||||
return false;
|
||||
}
|
||||
qCInfo(adsLog) << stateReader.attributes().value("version");
|
||||
bool ok;
|
||||
int v = stateReader.attributes().value("version").toInt(&ok);
|
||||
if (!ok || v > CurrentVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stateReader.setFileVersion(v);
|
||||
bool result = true;
|
||||
#ifdef ADS_DEBUG_PRINT
|
||||
int dockContainers = stateReader.attributes().value("containers").toInt();
|
||||
qCInfo(adsLog) << dockContainers;
|
||||
#endif
|
||||
int dockContainerCount = 0;
|
||||
while (stateReader.readNextStartElement()) {
|
||||
if (stateReader.name() == "container") {
|
||||
result = restoreContainer(dockContainerCount, stateReader, testing);
|
||||
if (!result) {
|
||||
break;
|
||||
}
|
||||
dockContainerCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!testing) {
|
||||
// Delete remaining empty floating widgets
|
||||
int floatingWidgetIndex = dockContainerCount - 1;
|
||||
int deleteCount = m_floatingWidgets.count() - floatingWidgetIndex;
|
||||
for (int i = 0; i < deleteCount; ++i) {
|
||||
m_floatingWidgets[floatingWidgetIndex + i]->deleteLater();
|
||||
q->removeDockContainer(m_floatingWidgets[floatingWidgetIndex + i]->dockContainer());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DockManagerPrivate::restoreDockWidgetsOpenState()
|
||||
{
|
||||
// All dock widgets, that have not been processed in the restore state
|
||||
// function are invisible to the user now and have no assigned dock area
|
||||
// They do not belong to any dock container, until the user toggles the
|
||||
// toggle view action the next time
|
||||
for (auto dockWidget : m_dockWidgetsMap) {
|
||||
if (dockWidget->property(internal::dirtyProperty).toBool()) {
|
||||
dockWidget->flagAsUnassigned();
|
||||
emit dockWidget->viewToggled(false);
|
||||
} else {
|
||||
dockWidget->toggleViewInternal(
|
||||
!dockWidget->property(internal::closedProperty).toBool());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockManagerPrivate::restoreDockAreasIndices()
|
||||
{
|
||||
// Now all dock areas are properly restored and we setup the index of
|
||||
// The dock areas because the previous toggleView() action has changed
|
||||
// the dock area index
|
||||
int count = 0;
|
||||
for (auto dockContainer : m_containers) {
|
||||
count++;
|
||||
for (int i = 0; i < dockContainer->dockAreaCount(); ++i) {
|
||||
DockAreaWidget *dockArea = dockContainer->dockArea(i);
|
||||
QString dockWidgetName = dockArea->property("currentDockWidget").toString();
|
||||
DockWidget *dockWidget = nullptr;
|
||||
if (!dockWidgetName.isEmpty()) {
|
||||
dockWidget = q->findDockWidget(dockWidgetName);
|
||||
}
|
||||
|
||||
if (!dockWidget || dockWidget->isClosed()) {
|
||||
int index = dockArea->indexOfFirstOpenDockWidget();
|
||||
if (index < 0) {
|
||||
continue;
|
||||
}
|
||||
dockArea->setCurrentIndex(index);
|
||||
} else {
|
||||
dockArea->internalSetCurrentDockWidget(dockWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockManagerPrivate::emitTopLevelEvents()
|
||||
{
|
||||
// Finally we need to send the topLevelChanged() signals for all dock
|
||||
// widgets if top level changed
|
||||
for (auto dockContainer : m_containers) {
|
||||
DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget();
|
||||
if (topLevelDockWidget) {
|
||||
topLevelDockWidget->emitTopLevelChanged(true);
|
||||
} else {
|
||||
for (int i = 0; i < dockContainer->dockAreaCount(); ++i) {
|
||||
auto dockArea = dockContainer->dockArea(i);
|
||||
for (auto dockWidget : dockArea->dockWidgets()) {
|
||||
dockWidget->emitTopLevelChanged(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
|
||||
{
|
||||
QByteArray currentState = state.startsWith("<?xml") ? state : qUncompress(state);
|
||||
if (!checkFormat(currentState, version)) {
|
||||
qCInfo(adsLog) << "checkFormat: Error checking format!!!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide updates of floating widgets from use
|
||||
hideFloatingWidgets();
|
||||
markDockWidgetsDirty();
|
||||
|
||||
if (!restoreStateFromXml(currentState, version)) {
|
||||
qCInfo(adsLog) << "restoreState: Error restoring state!!!";
|
||||
return false;
|
||||
}
|
||||
|
||||
restoreDockWidgetsOpenState();
|
||||
restoreDockAreasIndices();
|
||||
emitTopLevelEvents();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DockManager::DockManager(QWidget *parent)
|
||||
: DockContainerWidget(this, parent)
|
||||
, d(new DockManagerPrivate(this))
|
||||
{
|
||||
connect(this, &DockManager::workspaceListChanged, this, [=] {
|
||||
d->m_workspaceListDirty = true;
|
||||
});
|
||||
|
||||
createRootSplitter();
|
||||
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent);
|
||||
if (mainWindow) {
|
||||
mainWindow->setCentralWidget(this);
|
||||
}
|
||||
|
||||
d->m_dockAreaOverlay = new DockOverlay(this, DockOverlay::ModeDockAreaOverlay);
|
||||
d->m_containerOverlay = new DockOverlay(this, DockOverlay::ModeContainerOverlay);
|
||||
d->m_containers.append(this);
|
||||
//d->loadStylesheet();
|
||||
}
|
||||
|
||||
DockManager::~DockManager()
|
||||
{
|
||||
// If the factory default workspace is still loaded, create a default workspace just in case
|
||||
// the layout changed as there is no tracking of layout changes.
|
||||
if (isFactoryDefaultWorkspace(d->m_workspaceName)
|
||||
&& !isDefaultWorkspace(d->m_workspaceName)) {
|
||||
createWorkspace(Constants::DEFAULT_NAME);
|
||||
openWorkspace(Constants::DEFAULT_NAME);
|
||||
}
|
||||
|
||||
emit aboutToUnloadWorkspace(d->m_workspaceName);
|
||||
save();
|
||||
|
||||
for (auto floatingWidget : d->m_floatingWidgets) {
|
||||
delete floatingWidget;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
DockManager::ConfigFlags DockManager::configFlags() { return g_staticConfigFlags; }
|
||||
|
||||
void DockManager::setConfigFlags(const ConfigFlags flags) { g_staticConfigFlags = flags; }
|
||||
|
||||
void DockManager::setConfigFlag(eConfigFlag flag, bool on)
|
||||
{
|
||||
internal::setFlag(g_staticConfigFlags, flag, on);
|
||||
}
|
||||
|
||||
bool DockManager::testConfigFlag(eConfigFlag flag)
|
||||
{
|
||||
return configFlags().testFlag(flag);
|
||||
}
|
||||
|
||||
IconProvider &DockManager::iconProvider()
|
||||
{
|
||||
static IconProvider instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
int DockManager::startDragDistance()
|
||||
{
|
||||
return static_cast<int>(QApplication::startDragDistance() * 1.5);
|
||||
}
|
||||
|
||||
void DockManager::setSettings(QSettings *settings) { d->m_settings = settings; }
|
||||
|
||||
DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area,
|
||||
DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget)
|
||||
{
|
||||
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
||||
return DockContainerWidget::addDockWidget(area, dockWidget, dockAreaWidget);
|
||||
}
|
||||
|
||||
DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget)
|
||||
{
|
||||
DockAreaWidget *areaWidget = lastAddedDockAreaWidget(area);
|
||||
if (areaWidget) {
|
||||
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget);
|
||||
} else if (!openedDockAreas().isEmpty()) {
|
||||
return addDockWidget(area, dockWidget, openedDockAreas().last());
|
||||
} else {
|
||||
return addDockWidget(area, dockWidget, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget)
|
||||
{
|
||||
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, dockAreaWidget);
|
||||
}
|
||||
|
||||
FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget)
|
||||
{
|
||||
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
||||
DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget();
|
||||
if (oldDockArea) {
|
||||
oldDockArea->removeDockWidget(dockWidget);
|
||||
}
|
||||
|
||||
dockWidget->setDockManager(this);
|
||||
FloatingDockContainer *floatingWidget = new FloatingDockContainer(dockWidget);
|
||||
floatingWidget->resize(dockWidget->size());
|
||||
if (isVisible()) {
|
||||
floatingWidget->show();
|
||||
} else {
|
||||
d->m_uninitializedFloatingWidgets.append(floatingWidget);
|
||||
}
|
||||
return floatingWidget;
|
||||
}
|
||||
|
||||
void DockManager::registerFloatingWidget(FloatingDockContainer *floatingWidget)
|
||||
{
|
||||
d->m_floatingWidgets.append(floatingWidget);
|
||||
emit floatingWidgetCreated(floatingWidget);
|
||||
qCInfo(adsLog) << "d->FloatingWidgets.count() " << d->m_floatingWidgets.count();
|
||||
}
|
||||
|
||||
void DockManager::removeFloatingWidget(FloatingDockContainer *floatingWidget)
|
||||
{
|
||||
d->m_floatingWidgets.removeAll(floatingWidget);
|
||||
}
|
||||
|
||||
void DockManager::registerDockContainer(DockContainerWidget *dockContainer)
|
||||
{
|
||||
d->m_containers.append(dockContainer);
|
||||
}
|
||||
|
||||
void DockManager::removeDockContainer(DockContainerWidget *dockContainer)
|
||||
{
|
||||
if (this != dockContainer) {
|
||||
d->m_containers.removeAll(dockContainer);
|
||||
}
|
||||
}
|
||||
|
||||
DockOverlay *DockManager::containerOverlay() const { return d->m_containerOverlay; }
|
||||
|
||||
DockOverlay *DockManager::dockAreaOverlay() const { return d->m_dockAreaOverlay; }
|
||||
|
||||
const QList<DockContainerWidget *> DockManager::dockContainers() const
|
||||
{
|
||||
return d->m_containers;
|
||||
}
|
||||
|
||||
const QList<FloatingDockContainer *> DockManager::floatingWidgets() const
|
||||
{
|
||||
return d->m_floatingWidgets;
|
||||
}
|
||||
|
||||
unsigned int DockManager::zOrderIndex() const { return 0; }
|
||||
|
||||
QByteArray DockManager::saveState(int version) const
|
||||
{
|
||||
QByteArray xmlData;
|
||||
QXmlStreamWriter stream(&xmlData);
|
||||
auto configFlags = DockManager::configFlags();
|
||||
stream.setAutoFormatting(configFlags.testFlag(XmlAutoFormattingEnabled));
|
||||
stream.writeStartDocument();
|
||||
stream.writeStartElement("QtAdvancedDockingSystem");
|
||||
stream.writeAttribute("version", QString::number(version));
|
||||
stream.writeAttribute("containers", QString::number(d->m_containers.count()));
|
||||
for (auto container : d->m_containers) {
|
||||
container->saveState(stream);
|
||||
}
|
||||
|
||||
stream.writeEndElement();
|
||||
stream.writeEndDocument();
|
||||
return xmlData;
|
||||
}
|
||||
|
||||
bool DockManager::restoreState(const QByteArray &state, int version)
|
||||
{
|
||||
// Prevent multiple calls as long as state is not restore. This may
|
||||
// happen, if QApplication::processEvents() is called somewhere
|
||||
if (d->m_restoringState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We hide the complete dock manager here. Restoring the state means
|
||||
// that DockWidgets are removed from the DockArea internal stack layout
|
||||
// which in turn means, that each time a widget is removed the stack
|
||||
// will show and raise the next available widget which in turn
|
||||
// triggers show events for the dock widgets. To avoid this we hide the
|
||||
// dock manager. Because there will be no processing of application
|
||||
// events until this function is finished, the user will not see this
|
||||
// hiding
|
||||
bool isHidden = this->isHidden();
|
||||
if (!isHidden) {
|
||||
hide();
|
||||
}
|
||||
d->m_restoringState = true;
|
||||
emit restoringState();
|
||||
bool result = d->restoreState(state, version);
|
||||
d->m_restoringState = false;
|
||||
emit stateRestored();
|
||||
if (!isHidden) {
|
||||
show();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DockManager::showEvent(QShowEvent *event)
|
||||
{
|
||||
Super::showEvent(event);
|
||||
if (d->m_uninitializedFloatingWidgets.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto floatingWidget : d->m_uninitializedFloatingWidgets) {
|
||||
floatingWidget->show();
|
||||
}
|
||||
d->m_uninitializedFloatingWidgets.clear();
|
||||
}
|
||||
|
||||
DockWidget *DockManager::findDockWidget(const QString &objectName) const
|
||||
{
|
||||
return d->m_dockWidgetsMap.value(objectName, nullptr);
|
||||
}
|
||||
|
||||
void DockManager::removeDockWidget(DockWidget *dockWidget)
|
||||
{
|
||||
emit dockWidgetAboutToBeRemoved(dockWidget);
|
||||
d->m_dockWidgetsMap.remove(dockWidget->objectName());
|
||||
DockContainerWidget::removeDockWidget(dockWidget);
|
||||
emit dockWidgetRemoved(dockWidget);
|
||||
}
|
||||
|
||||
QMap<QString, DockWidget *> DockManager::dockWidgetsMap() const { return d->m_dockWidgetsMap; }
|
||||
|
||||
bool DockManager::isRestoringState() const { return d->m_restoringState; }
|
||||
|
||||
void DockManager::showWorkspaceMananger()
|
||||
{
|
||||
// Save current workspace
|
||||
save();
|
||||
|
||||
WorkspaceDialog workspaceDialog(this, parentWidget());
|
||||
workspaceDialog.setAutoLoadWorkspace(autoRestorLastWorkspace());
|
||||
workspaceDialog.exec();
|
||||
|
||||
QTC_ASSERT(d->m_settings, return );
|
||||
d->m_settings->setValue(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY,
|
||||
workspaceDialog.autoLoadWorkspace());
|
||||
}
|
||||
|
||||
bool DockManager::isFactoryDefaultWorkspace(const QString &workspace) const
|
||||
{
|
||||
return workspace == QLatin1String(Constants::FACTORY_DEFAULT_NAME);
|
||||
}
|
||||
|
||||
bool DockManager::isDefaultWorkspace(const QString &workspace) const
|
||||
{
|
||||
return workspace == QLatin1String(Constants::DEFAULT_NAME);
|
||||
}
|
||||
|
||||
bool DockManager::save()
|
||||
{
|
||||
if (isFactoryDefaultWorkspace(activeWorkspace()))
|
||||
return true;
|
||||
|
||||
emit aboutToSaveWorkspace();
|
||||
|
||||
bool result = write(saveState(), parentWidget());
|
||||
if (result) {
|
||||
d->m_workspaceDateTimes.insert(activeWorkspace(), QDateTime::currentDateTime());
|
||||
} else {
|
||||
QMessageBox::warning(parentWidget(),
|
||||
tr("Cannot Save Session"),
|
||||
tr("Could not save session to file %1")
|
||||
.arg(workspaceNameToFileName(d->m_workspaceName)
|
||||
.toUserOutput()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString DockManager::activeWorkspace() const { return d->m_workspaceName; }
|
||||
|
||||
QString DockManager::lastWorkspace() const
|
||||
{
|
||||
QTC_ASSERT(d->m_settings, return {});
|
||||
return d->m_settings->value(Constants::STARTUP_WORKSPACE_SETTINGS_KEY).toString();
|
||||
}
|
||||
|
||||
bool DockManager::autoRestorLastWorkspace() const
|
||||
{
|
||||
QTC_ASSERT(d->m_settings, return false);
|
||||
return d->m_settings->value(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY).toBool();
|
||||
}
|
||||
|
||||
const QString m_dirName = QLatin1String("workspaces");
|
||||
const QString m_fileExt = QLatin1String(".wrk"); // TODO
|
||||
|
||||
QStringList DockManager::workspaces()
|
||||
{
|
||||
if (d->m_workspaces.isEmpty() || d->m_workspaceListDirty) {
|
||||
auto tmp = Utils::toSet(d->m_workspaces);
|
||||
|
||||
QTC_ASSERT(d->m_settings, return {});
|
||||
QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/')
|
||||
+ m_dirName);
|
||||
QFileInfoList workspaceFiles
|
||||
= workspaceDir.entryInfoList(QStringList() << QLatin1String("*.wrk"),
|
||||
QDir::NoFilter,
|
||||
QDir::Time); // TODO Choose different extension
|
||||
for (const QFileInfo &fileInfo : workspaceFiles) {
|
||||
QString filename = fileInfo.completeBaseName();
|
||||
filename.replace("_", " ");
|
||||
d->m_workspaceDateTimes.insert(filename, fileInfo.lastModified());
|
||||
//if (name != QLatin1String(Constants::DEFAULT_NAME))
|
||||
tmp.insert(filename);
|
||||
}
|
||||
//d->m_workspaces.prepend(QLatin1String(Constants::DEFAULT_NAME));
|
||||
|
||||
d->m_workspaceListDirty = false;
|
||||
d->m_workspaces = Utils::toList(tmp);
|
||||
}
|
||||
return d->m_workspaces;
|
||||
}
|
||||
|
||||
QDateTime DockManager::workspaceDateTime(const QString &workspace) const
|
||||
{
|
||||
return d->m_workspaceDateTimes.value(workspace);
|
||||
}
|
||||
|
||||
Utils::FilePath DockManager::workspaceNameToFileName(const QString &workspaceName) const
|
||||
{
|
||||
QTC_ASSERT(d->m_settings, return {});
|
||||
QString workspaceNameCopy = workspaceName;
|
||||
return Utils::FilePath::fromString(
|
||||
QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') + m_dirName
|
||||
+ QLatin1Char('/') + workspaceNameCopy.replace(" ", "_") + QLatin1String(".wrk"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates \a workspace, but does not actually create the file.
|
||||
*/
|
||||
bool DockManager::createWorkspace(const QString &workspace)
|
||||
{
|
||||
if (workspaces().contains(workspace))
|
||||
return false;
|
||||
d->m_workspaces.insert(1, workspace);
|
||||
d->m_workspaceDateTimes.insert(workspace, QDateTime::currentDateTime());
|
||||
|
||||
emit workspaceListChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DockManager::openWorkspace(const QString &workspace)
|
||||
{
|
||||
// Do nothing if we have that workspace already loaded, exception if the
|
||||
// workspace is the default virgin workspace we still want to be able to
|
||||
// load the default workspace.
|
||||
if (workspace == d->m_workspaceName) // && !isFactoryDefaultWorkspace(workspace))
|
||||
return true;
|
||||
|
||||
if (!workspaces().contains(workspace))
|
||||
return false;
|
||||
|
||||
// Check if the currently active workspace isn't empty and try to save it
|
||||
if (!d->m_workspaceName.isEmpty()) {
|
||||
// Allow everyone to set something in the workspace and before saving
|
||||
emit aboutToUnloadWorkspace(d->m_workspaceName);
|
||||
if (!save()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Try loading the file
|
||||
QByteArray data;
|
||||
Utils::FilePath fileName = workspaceNameToFileName(workspace);
|
||||
if (fileName.exists()) {
|
||||
QFile file(fileName.toString());
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox::warning(parentWidget(),
|
||||
tr("Cannot Restore Workspace"),
|
||||
tr("Could not restore workspace %1")
|
||||
.arg(fileName.toUserOutput()));
|
||||
return false;
|
||||
}
|
||||
data = file.readAll();
|
||||
file.close();
|
||||
}
|
||||
|
||||
emit openingWorkspace(workspace);
|
||||
// If data was loaded from file try to restore its state
|
||||
if (!data.isNull() && !restoreState(data)) {
|
||||
return false;
|
||||
}
|
||||
d->m_workspaceName = workspace;
|
||||
emit workspaceLoaded(workspace);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Shows a dialog asking the user to confirm deleting the workspace \p workspace
|
||||
*/
|
||||
bool DockManager::confirmWorkspaceDelete(const QStringList &workspace)
|
||||
{
|
||||
const QString title = workspace.size() == 1 ? tr("Delete Workspace")
|
||||
: tr("Delete Workspaces");
|
||||
const QString question = workspace.size() == 1
|
||||
? tr("Delete workspace %1?").arg(workspace.first())
|
||||
: tr("Delete these workspaces?\n %1")
|
||||
.arg(workspace.join("\n "));
|
||||
return QMessageBox::question(parentWidget(),
|
||||
title,
|
||||
question,
|
||||
QMessageBox::Yes | QMessageBox::No)
|
||||
== QMessageBox::Yes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes \a workspace name from workspace list and the file from disk.
|
||||
*/
|
||||
bool DockManager::deleteWorkspace(const QString &workspace)
|
||||
{
|
||||
// Remove workspace from internal list
|
||||
if (!d->m_workspaces.contains(workspace))
|
||||
return false;
|
||||
d->m_workspaces.removeOne(workspace);
|
||||
|
||||
emit workspacesRemoved();
|
||||
emit workspaceListChanged();
|
||||
|
||||
// Remove corresponding workspace file
|
||||
QFile fi(workspaceNameToFileName(workspace).toString());
|
||||
if (fi.exists())
|
||||
return fi.remove();
|
||||
|
||||
return false; // TODO If we allow temporary workspaces without writing them to file
|
||||
// directly, this needs to be true otherwise in all those cases it will return false.
|
||||
}
|
||||
|
||||
void DockManager::deleteWorkspaces(const QStringList &workspaces)
|
||||
{
|
||||
for (const QString &workspace : workspaces)
|
||||
deleteWorkspace(workspace);
|
||||
}
|
||||
|
||||
bool DockManager::cloneWorkspace(const QString &original, const QString &clone)
|
||||
{
|
||||
if (!d->m_workspaces.contains(original))
|
||||
return false;
|
||||
|
||||
QFile fi(workspaceNameToFileName(original).toString());
|
||||
// If the file does not exist, we can still clone
|
||||
if (!fi.exists() || fi.copy(workspaceNameToFileName(clone).toString())) {
|
||||
d->m_workspaces.insert(1, clone);
|
||||
d->m_workspaceDateTimes
|
||||
.insert(clone, workspaceNameToFileName(clone).toFileInfo().lastModified());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DockManager::renameWorkspace(const QString &original, const QString &newName)
|
||||
{
|
||||
if (!cloneWorkspace(original, newName))
|
||||
return false;
|
||||
if (original == activeWorkspace())
|
||||
openWorkspace(newName);
|
||||
return deleteWorkspace(original);
|
||||
}
|
||||
|
||||
bool DockManager::write(const QByteArray &data, QString *errorString) const
|
||||
{
|
||||
Utils::FilePath fileName = workspaceNameToFileName(activeWorkspace());
|
||||
|
||||
QDir tmp;
|
||||
tmp.mkpath(fileName.toFileInfo().path());
|
||||
Utils::FileSaver fileSaver(fileName.toString(), QIODevice::Text);
|
||||
if (!fileSaver.hasError()) {
|
||||
fileSaver.write(data);
|
||||
}
|
||||
bool ok = fileSaver.finalize();
|
||||
|
||||
if (!ok && errorString) {
|
||||
*errorString = fileSaver.errorString();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
bool DockManager::write(const QByteArray &data, QWidget *parent) const
|
||||
{
|
||||
QString errorString;
|
||||
const bool success = write(data, &errorString);
|
||||
if (!success)
|
||||
QMessageBox::critical(parent,
|
||||
QCoreApplication::translate("Utils::FileSaverBase", "File Error"),
|
||||
errorString);
|
||||
return success;
|
||||
}
|
||||
#endif // QT_GUI_LIB
|
||||
|
||||
} // namespace ADS
|
480
src/libs/advanceddockingsystem/dockmanager.h
Normal file
480
src/libs/advanceddockingsystem/dockmanager.h
Normal file
@@ -0,0 +1,480 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "dockcontainerwidget.h"
|
||||
#include "dockwidget.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
|
||||
#include <utils/persistentsettings.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QFlags>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QtGui/QIcon>
|
||||
#include <qobjectdefs.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QMenu;
|
||||
class QSettings;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace ADS {
|
||||
|
||||
namespace Constants {
|
||||
const char FACTORY_DEFAULT_NAME[] = "factorydefault";
|
||||
const char DEFAULT_NAME[] = "default";
|
||||
const char STARTUP_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/StartupWorkspace";
|
||||
const char AUTO_RESTORE_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/AutoRestoreLastWorkspace";
|
||||
} // namespace Constants
|
||||
|
||||
struct DockManagerPrivate;
|
||||
class FloatingDockContainer;
|
||||
struct FloatingDockContainerPrivate;
|
||||
class DockComponentsFactory;
|
||||
class DockContainerWidget;
|
||||
class DockContainerWidgetPrivate;
|
||||
class DockOverlay;
|
||||
class DockAreaTabBar;
|
||||
class DockWidgetTab;
|
||||
struct DockWidgetTabPrivate;
|
||||
struct DockAreaWidgetPrivate;
|
||||
class IconProvider;
|
||||
|
||||
/**
|
||||
* The central dock manager that maintains the complete docking system.
|
||||
* With the configuration flags you can globally control the functionality
|
||||
* of the docking system. The dock manager uses an internal stylesheet to
|
||||
* style its components like splitters, tabs and buttons. If you want to
|
||||
* disable this stylesheet because your application uses its own,
|
||||
* just call the function for settings the stylesheet with an empty
|
||||
* string.
|
||||
* \code
|
||||
* dockManager->setStyleSheet("");
|
||||
* \endcode
|
||||
**/
|
||||
class ADS_EXPORT DockManager : public DockContainerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockManagerPrivate *d; ///< private data (pimpl)
|
||||
friend struct DockManagerPrivate;
|
||||
friend class FloatingDockContainer;
|
||||
friend struct FloatingDockContainerPrivate;
|
||||
friend class DockContainerWidget;
|
||||
friend class DockContainerWidgetPrivate;
|
||||
friend class DockAreaTabBar;
|
||||
friend class DockWidgetTab;
|
||||
friend struct DockAreaWidgetPrivate;
|
||||
friend struct DockWidgetTabPrivate;
|
||||
friend class FloatingDragPreview;
|
||||
friend struct FloatingDragPreviewPrivate;
|
||||
friend class DockAreaTitleBar;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Registers the given floating widget in the internal list of
|
||||
* floating widgets
|
||||
*/
|
||||
void registerFloatingWidget(FloatingDockContainer *floatingWidget);
|
||||
|
||||
/**
|
||||
* Remove the given floating widget from the list of registered floating
|
||||
* widgets
|
||||
*/
|
||||
void removeFloatingWidget(FloatingDockContainer *floatingWidget);
|
||||
|
||||
/**
|
||||
* Registers the given dock container widget
|
||||
*/
|
||||
void registerDockContainer(DockContainerWidget *dockContainer);
|
||||
|
||||
/**
|
||||
* Remove dock container from the internal list of registered dock
|
||||
* containers
|
||||
*/
|
||||
void removeDockContainer(DockContainerWidget *dockContainer);
|
||||
|
||||
/**
|
||||
* Overlay for containers
|
||||
*/
|
||||
DockOverlay *containerOverlay() const;
|
||||
|
||||
/**
|
||||
* Overlay for dock areas
|
||||
*/
|
||||
DockOverlay *dockAreaOverlay() const;
|
||||
|
||||
/**
|
||||
* Show the floating widgets that has been created floating
|
||||
*/
|
||||
virtual void showEvent(QShowEvent *event) override;
|
||||
|
||||
public:
|
||||
using Super = DockContainerWidget;
|
||||
|
||||
/**
|
||||
* These global configuration flags configure some global dock manager
|
||||
* settings.
|
||||
*/
|
||||
enum eConfigFlag {
|
||||
ActiveTabHasCloseButton
|
||||
= 0x0001, //!< If this flag is set, the active tab in a tab area has a close button
|
||||
DockAreaHasCloseButton
|
||||
= 0x0002, //!< If the flag is set each dock area has a close button
|
||||
DockAreaCloseButtonClosesTab
|
||||
= 0x0004, //!< If the flag is set, the dock area close button closes the active tab, if not set, it closes the complete dock area
|
||||
OpaqueSplitterResize
|
||||
= 0x0008, //!< See QSplitter::setOpaqueResize() documentation
|
||||
XmlAutoFormattingEnabled
|
||||
= 0x0010, //!< If enabled, the XML writer automatically adds line-breaks and indentation to empty sections between elements (ignorable whitespace).
|
||||
XmlCompressionEnabled
|
||||
= 0x0020, //!< If enabled, the XML output will be compressed and is not human readable anymore
|
||||
TabCloseButtonIsToolButton
|
||||
= 0x0040, //! If enabled the tab close buttons will be QToolButtons instead of QPushButtons - disabled by default
|
||||
AllTabsHaveCloseButton
|
||||
= 0x0080, //!< if this flag is set, then all tabs that are closable show a close button
|
||||
RetainTabSizeWhenCloseButtonHidden
|
||||
= 0x0100, //!< if this flag is set, the space for the close button is reserved even if the close button is not visible
|
||||
OpaqueUndocking
|
||||
= 0x0200, ///< If enabled, the widgets are immediately undocked into floating widgets, if disabled, only a draw preview is undocked and the real undocking is deferred until the mouse is released
|
||||
DragPreviewIsDynamic
|
||||
= 0x0400, ///< If opaque undocking is disabled, this flag defines the behavior of the drag preview window, if this flag is enabled, the preview will be adjusted dynamically to the drop area
|
||||
DragPreviewShowsContentPixmap
|
||||
= 0x0800, ///< If opaque undocking is disabled, the created drag preview window shows a copy of the content of the dock widget / dock are that is dragged
|
||||
DragPreviewHasWindowFrame
|
||||
= 0x1000, ///< If opaque undocking is disabled, then this flag configures if the drag preview is frameless or looks like a real window
|
||||
AlwaysShowTabs
|
||||
= 0x2000, ///< If this option is enabled, the tab of a dock widget is always displayed - even if it is the only visible dock widget in a floating widget.
|
||||
DockAreaHasUndockButton
|
||||
= 0x4000, //!< If the flag is set each dock area has an undock button
|
||||
DockAreaHasTabsMenuButton
|
||||
= 0x8000, //!< If the flag is set each dock area has a tabs menu button
|
||||
DockAreaHideDisabledButtons
|
||||
= 0x10000, //!< If the flag is set disabled dock area buttons will not appear on the tollbar at all (enabling them will bring them back)
|
||||
DockAreaDynamicTabsMenuButtonVisibility
|
||||
= 0x20000, //!< If the flag is set dock area will disable a tabs menu button when there is only one tab in the area
|
||||
FloatingContainerHasWidgetTitle
|
||||
= 0x40000,
|
||||
FloatingContainerHasWidgetIcon
|
||||
= 0x80000,
|
||||
|
||||
DefaultDockAreaButtons = DockAreaHasCloseButton
|
||||
| DockAreaHasUndockButton
|
||||
| DockAreaHasTabsMenuButton,///< default configuration of dock area title bar buttons
|
||||
|
||||
DefaultBaseConfig = DefaultDockAreaButtons
|
||||
| ActiveTabHasCloseButton
|
||||
| XmlCompressionEnabled
|
||||
| FloatingContainerHasWidgetTitle,///< default base configuration settings
|
||||
|
||||
DefaultOpaqueConfig = DefaultBaseConfig
|
||||
| OpaqueSplitterResize
|
||||
| OpaqueUndocking, ///< the default configuration with opaque operations - this may cause issues if ActiveX or Qt 3D windows are involved
|
||||
|
||||
DefaultNonOpaqueConfig = DefaultBaseConfig
|
||||
| DragPreviewShowsContentPixmap, ///< the default configuration for non opaque operations
|
||||
|
||||
NonOpaqueWithWindowFrame = DefaultNonOpaqueConfig
|
||||
| DragPreviewHasWindowFrame ///< the default configuration for non opaque operations that show a real window with frame
|
||||
};
|
||||
Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag)
|
||||
|
||||
/**
|
||||
* Default Constructor.
|
||||
* If the given parent is a QMainWindow, the dock manager sets itself as the
|
||||
* central widget.
|
||||
* Before you create any dock widgets, you should properly setup the
|
||||
* configuration flags via setConfigFlags().
|
||||
*/
|
||||
DockManager(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~DockManager() override;
|
||||
|
||||
/**
|
||||
* This function returns the global configuration flags
|
||||
*/
|
||||
static ConfigFlags configFlags();
|
||||
|
||||
/**
|
||||
* Sets the global configuration flags for the whole docking system.
|
||||
* Call this function before you create your first dock widget.
|
||||
*/
|
||||
static void setConfigFlags(const ConfigFlags flags);
|
||||
|
||||
/**
|
||||
* Set a certain config flag
|
||||
*/
|
||||
static void setConfigFlag(eConfigFlag flag, bool on = true);
|
||||
|
||||
/**
|
||||
* Returns true if the given config flag is set
|
||||
*/
|
||||
static bool testConfigFlag(eConfigFlag flag);
|
||||
|
||||
/**
|
||||
* Returns the global icon provider.
|
||||
* The icon provider enables the use of custom icons in case using
|
||||
* styleheets for icons is not an option.
|
||||
*/
|
||||
static IconProvider &iconProvider();
|
||||
|
||||
/**
|
||||
* The distance the user needs to move the mouse with the left button
|
||||
* hold down before a dock widget start floating
|
||||
*/
|
||||
static int startDragDistance();
|
||||
|
||||
/**
|
||||
* Set the QtCreator settings.
|
||||
*/
|
||||
void setSettings(QSettings *settings);
|
||||
|
||||
/**
|
||||
* Adds dockwidget into the given area.
|
||||
* If DockAreaWidget is not null, then the area parameter indicates the area
|
||||
* into the DockAreaWidget. If DockAreaWidget is null, the Dockwidget will
|
||||
* be dropped into the container. If you would like to add a dock widget
|
||||
* tabified, then you need to add it to an existing dock area object
|
||||
* into the CenterDockWidgetArea. The following code shows this:
|
||||
* \code
|
||||
* DockManager->addDockWidget(ads::CenterDockWidgetArea, NewDockWidget,
|
||||
* ExisitingDockArea);
|
||||
* \endcode
|
||||
* \return Returns the dock area widget that contains the new DockWidget
|
||||
*/
|
||||
DockAreaWidget *addDockWidget(DockWidgetArea area,
|
||||
DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget = nullptr);
|
||||
|
||||
/**
|
||||
* This function will add the given Dockwidget to the given dock area as
|
||||
* a new tab.
|
||||
* If no dock area widget exists for the given area identifier, a new
|
||||
* dock area widget is created.
|
||||
*/
|
||||
DockAreaWidget *addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* This function will add the given Dockwidget to the given DockAreaWidget
|
||||
* as a new tab.
|
||||
*/
|
||||
DockAreaWidget *addDockWidgetTabToArea(DockWidget *dockWidget, DockAreaWidget *dockAreaWidget);
|
||||
|
||||
/**
|
||||
* Adds the given DockWidget floating and returns the created
|
||||
* CFloatingDockContainer instance.
|
||||
*/
|
||||
FloatingDockContainer *addDockWidgetFloating(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Searches for a registered doc widget with the given ObjectName
|
||||
* \return Return the found dock widget or nullptr if a dock widget with the
|
||||
* given name is not registered
|
||||
*/
|
||||
DockWidget *findDockWidget(const QString &objectName) const;
|
||||
|
||||
/**
|
||||
* Remove the given Dock from the dock manager
|
||||
*/
|
||||
void removeDockWidget(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* This function returns a readable reference to the internal dock
|
||||
* widgets map so that it is possible to iterate over all dock widgets
|
||||
*/
|
||||
QMap<QString, DockWidget *> dockWidgetsMap() const;
|
||||
|
||||
/**
|
||||
* Returns the list of all active and visible dock containers
|
||||
* Dock containers are the main dock manager and all floating widgets
|
||||
*/
|
||||
const QList<DockContainerWidget *> dockContainers() const;
|
||||
|
||||
/**
|
||||
* Returns the list of all floating widgets
|
||||
*/
|
||||
const QList<FloatingDockContainer *> floatingWidgets() const;
|
||||
|
||||
/**
|
||||
* This function always return 0 because the main window is always behind
|
||||
* any floating widget
|
||||
*/
|
||||
virtual unsigned int zOrderIndex() const override;
|
||||
|
||||
/**
|
||||
* Saves the current state of the dockmanger and all its dock widgets
|
||||
* into the returned QByteArray.
|
||||
* The XmlMode enables / disables the auto formatting for the XmlStreamWriter.
|
||||
* If auto formatting is enabled, the output is intended and line wrapped.
|
||||
* The XmlMode XmlAutoFormattingDisabled is better if you would like to have
|
||||
* a more compact XML output - i.e. for storage in ini files.
|
||||
*/
|
||||
QByteArray saveState(int version = Version1) const;
|
||||
|
||||
/**
|
||||
* Restores the state of this dockmanagers dockwidgets.
|
||||
* The version number is compared with that stored in state. If they do
|
||||
* not match, the dockmanager's state is left unchanged, and this function
|
||||
* returns false; otherwise, the state is restored, and this function
|
||||
* returns true.
|
||||
*/
|
||||
bool restoreState(const QByteArray &state, int version = Version1);
|
||||
|
||||
/**
|
||||
* This function returns true between the restoringState() and
|
||||
* stateRestored() signals.
|
||||
*/
|
||||
bool isRestoringState() const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if the list of perspectives changed
|
||||
*/
|
||||
void workspaceListChanged();
|
||||
|
||||
/**
|
||||
* This signal is emitted if perspectives have been removed
|
||||
*/
|
||||
void workspacesRemoved();
|
||||
|
||||
/**
|
||||
* This signal is emitted, if the restore function is called, just before
|
||||
* the dock manager starts restoring the state.
|
||||
* If this function is called, nothing has changed yet
|
||||
*/
|
||||
void restoringState();
|
||||
|
||||
/**
|
||||
* This signal is emitted if the state changed in restoreState.
|
||||
* The signal is emitted if the restoreState() function is called or
|
||||
* if the openWorkspace() function is called
|
||||
*/
|
||||
void stateRestored();
|
||||
|
||||
/**
|
||||
* This signal is emitted, if the dock manager starts opening a
|
||||
* perspective.
|
||||
* Opening a perspective may take more than a second if there are
|
||||
* many complex widgets. The application may use this signal
|
||||
* to show some progress indicator or to change the mouse cursor
|
||||
* into a busy cursor.
|
||||
*/
|
||||
void openingWorkspace(const QString &workspaceName);
|
||||
|
||||
/**
|
||||
* This signal is emitted if the dock manager finished opening a
|
||||
* perspective
|
||||
*/
|
||||
void workspaceOpened(const QString &workspaceName);
|
||||
|
||||
/**
|
||||
* This signal is emitted, if a new floating widget has been created.
|
||||
* An application can use this signal to e.g. subscribe to events of
|
||||
* the newly created window.
|
||||
*/
|
||||
void floatingWidgetCreated(FloatingDockContainer *floatingWidget);
|
||||
|
||||
/**
|
||||
* This signal is emitted, if a new DockArea has been created.
|
||||
* An application can use this signal to set custom icons or custom
|
||||
* tooltips for the DockArea buttons.
|
||||
*/
|
||||
void dockAreaCreated(DockAreaWidget *dockArea);
|
||||
|
||||
/**
|
||||
* This signal is emitted just before the given dock widget is removed
|
||||
* from the
|
||||
*/
|
||||
void dockWidgetAboutToBeRemoved(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* This signal is emitted if a dock widget has been removed with the remove
|
||||
* removeDockWidget() function.
|
||||
* If this signal is emitted, the dock widget has been removed from the
|
||||
* docking system but it is not deleted yet.
|
||||
*/
|
||||
void dockWidgetRemoved(DockWidget *dockWidget);
|
||||
|
||||
public:
|
||||
void showWorkspaceMananger();
|
||||
|
||||
// higher level workspace management
|
||||
QString activeWorkspace() const;
|
||||
QString lastWorkspace() const;
|
||||
bool autoRestorLastWorkspace() const;
|
||||
QStringList workspaces();
|
||||
QDateTime workspaceDateTime(const QString &workspace) const;
|
||||
Utils::FilePath workspaceNameToFileName(const QString &workspaceName) const;
|
||||
|
||||
bool createWorkspace(const QString &workspace);
|
||||
|
||||
bool openWorkspace(const QString &workspace);
|
||||
|
||||
bool confirmWorkspaceDelete(const QStringList &workspaces);
|
||||
bool deleteWorkspace(const QString &workspace);
|
||||
void deleteWorkspaces(const QStringList &workspaces);
|
||||
|
||||
bool cloneWorkspace(const QString &original, const QString &clone);
|
||||
bool renameWorkspace(const QString &original, const QString &newName);
|
||||
|
||||
bool save();
|
||||
|
||||
bool isFactoryDefaultWorkspace(const QString &workspace) const;
|
||||
bool isDefaultWorkspace(const QString &workspace) const;
|
||||
|
||||
signals:
|
||||
void aboutToUnloadWorkspace(QString workspaceName);
|
||||
void aboutToLoadWorkspace(QString workspaceName);
|
||||
void workspaceLoaded(QString workspaceName);
|
||||
void aboutToSaveWorkspace();
|
||||
|
||||
private:
|
||||
bool write(const QByteArray &data, QString *errorString) const;
|
||||
#ifdef QT_GUI_LIB
|
||||
bool write(const QByteArray &data, QWidget *parent) const;
|
||||
#endif
|
||||
}; // class DockManager
|
||||
|
||||
} // namespace ADS
|
773
src/libs/advanceddockingsystem/dockoverlay.cpp
Normal file
773
src/libs/advanceddockingsystem/dockoverlay.cpp
Normal file
@@ -0,0 +1,773 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dockoverlay.h"
|
||||
|
||||
#include "dockareawidget.h"
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <QCursor>
|
||||
#include <QGridLayout>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QMoveEvent>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QPointer>
|
||||
#include <QResizeEvent>
|
||||
#include <QWindow>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
/**
|
||||
* Private data class of DockOverlay
|
||||
*/
|
||||
struct DockOverlayPrivate
|
||||
{
|
||||
DockOverlay *q;
|
||||
DockWidgetAreas m_allowedAreas = InvalidDockWidgetArea;
|
||||
DockOverlayCross *m_cross;
|
||||
QPointer<QWidget> m_targetWidget;
|
||||
DockWidgetArea m_lastLocation = InvalidDockWidgetArea;
|
||||
bool m_dropPreviewEnabled = true;
|
||||
DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay;
|
||||
QRect m_dropAreaRect;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockOverlayPrivate(DockOverlay *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* Private data of DockOverlayCross class
|
||||
*/
|
||||
struct DockOverlayCrossPrivate
|
||||
{
|
||||
DockOverlayCross *q;
|
||||
DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay;
|
||||
DockOverlay *m_dockOverlay;
|
||||
QHash<DockWidgetArea, QWidget *> m_dropIndicatorWidgets;
|
||||
QGridLayout *m_gridLayout;
|
||||
QColor m_iconColors[5];
|
||||
bool m_updateRequired = false;
|
||||
double m_lastDevicePixelRatio = 0.1;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockOverlayCrossPrivate(DockOverlayCross *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @param area
|
||||
* @return
|
||||
*/
|
||||
QPoint areaGridPosition(const DockWidgetArea area);
|
||||
|
||||
/**
|
||||
* Palette based default icon colors
|
||||
*/
|
||||
QColor defaultIconColor(DockOverlayCross::eIconColor colorIndex)
|
||||
{
|
||||
QPalette palette = q->palette();
|
||||
switch (colorIndex) {
|
||||
case DockOverlayCross::FrameColor:
|
||||
return palette.color(QPalette::Active, QPalette::Highlight);
|
||||
case DockOverlayCross::WindowBackgroundColor:
|
||||
return palette.color(QPalette::Active, QPalette::Base);
|
||||
case DockOverlayCross::OverlayColor: {
|
||||
QColor color = palette.color(QPalette::Active, QPalette::Highlight);
|
||||
color.setAlpha(64);
|
||||
return color;
|
||||
}
|
||||
case DockOverlayCross::ArrowColor:
|
||||
return palette.color(QPalette::Active, QPalette::Base);
|
||||
case DockOverlayCross::ShadowColor:
|
||||
return QColor(0, 0, 0, 64);
|
||||
}
|
||||
|
||||
return QColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stylehseet based icon colors
|
||||
*/
|
||||
QColor iconColor(DockOverlayCross::eIconColor colorIndex)
|
||||
{
|
||||
QColor color = m_iconColors[colorIndex];
|
||||
if (!color.isValid()) {
|
||||
color = defaultIconColor(colorIndex);
|
||||
m_iconColors[colorIndex] = color;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that returns the drop indicator width depending on the
|
||||
* operating system
|
||||
*/
|
||||
qreal dropIndicatiorWidth(QLabel *label) const
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
Q_UNUSED(label)
|
||||
return 40;
|
||||
#else
|
||||
return static_cast<qreal>(label->fontMetrics().height()) * 3.f;
|
||||
#endif
|
||||
}
|
||||
|
||||
QWidget *createDropIndicatorWidget(DockWidgetArea dockWidgetArea, DockOverlay::eMode mode)
|
||||
{
|
||||
QLabel *label = new QLabel();
|
||||
label->setObjectName("DockWidgetAreaLabel");
|
||||
|
||||
const qreal metric = dropIndicatiorWidth(label);
|
||||
const QSizeF size(metric, metric);
|
||||
|
||||
label->setPixmap(createHighDpiDropIndicatorPixmap(size, dockWidgetArea, mode));
|
||||
label->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
label->setAttribute(Qt::WA_TranslucentBackground);
|
||||
label->setProperty("dockWidgetArea", dockWidgetArea);
|
||||
return label;
|
||||
}
|
||||
|
||||
void updateDropIndicatorIcon(QWidget *dropIndicatorWidget)
|
||||
{
|
||||
QLabel *label = qobject_cast<QLabel *>(dropIndicatorWidget);
|
||||
const qreal metric = dropIndicatiorWidth(label);
|
||||
const QSizeF size(metric, metric);
|
||||
|
||||
int area = label->property("dockWidgetArea").toInt();
|
||||
label->setPixmap(createHighDpiDropIndicatorPixmap(size,
|
||||
static_cast<DockWidgetArea>(area),
|
||||
m_mode)); // TODO
|
||||
}
|
||||
|
||||
QPixmap createHighDpiDropIndicatorPixmap(const QSizeF &size,
|
||||
DockWidgetArea dockWidgetArea,
|
||||
DockOverlay::eMode mode)
|
||||
{
|
||||
QColor borderColor = iconColor(DockOverlayCross::FrameColor);
|
||||
QColor backgroundColor = iconColor(DockOverlayCross::WindowBackgroundColor);
|
||||
double devicePixelRatio = q->window()->devicePixelRatioF();
|
||||
QSizeF pixmapSize = size * devicePixelRatio;
|
||||
QPixmap pixmap(pixmapSize.toSize());
|
||||
pixmap.fill(QColor(0, 0, 0, 0));
|
||||
|
||||
QPainter painter(&pixmap);
|
||||
QPen pen = painter.pen();
|
||||
QRectF shadowRect(pixmap.rect());
|
||||
QRectF baseRect;
|
||||
baseRect.setSize(shadowRect.size() * 0.7);
|
||||
baseRect.moveCenter(shadowRect.center());
|
||||
|
||||
// Fill
|
||||
QColor shadowColor = iconColor(DockOverlayCross::ShadowColor);
|
||||
if (shadowColor.alpha() == 255) {
|
||||
shadowColor.setAlpha(64);
|
||||
}
|
||||
painter.fillRect(shadowRect, shadowColor);
|
||||
|
||||
// Drop area rect.
|
||||
painter.save();
|
||||
QRectF areaRect;
|
||||
QLineF areaLine;
|
||||
QRectF nonAreaRect;
|
||||
switch (dockWidgetArea) {
|
||||
case TopDockWidgetArea:
|
||||
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * 0.5);
|
||||
nonAreaRect = QRectF(baseRect.x(),
|
||||
shadowRect.height() * 0.5,
|
||||
baseRect.width(),
|
||||
baseRect.height() * 0.5);
|
||||
areaLine = QLineF(areaRect.bottomLeft(), areaRect.bottomRight());
|
||||
break;
|
||||
case RightDockWidgetArea:
|
||||
areaRect = QRectF(shadowRect.width() * 0.5,
|
||||
baseRect.y(),
|
||||
baseRect.width() * 0.5,
|
||||
baseRect.height());
|
||||
nonAreaRect = QRectF(baseRect.x(),
|
||||
baseRect.y(),
|
||||
baseRect.width() * 0.5,
|
||||
baseRect.height());
|
||||
areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft());
|
||||
break;
|
||||
case BottomDockWidgetArea:
|
||||
areaRect = QRectF(baseRect.x(),
|
||||
shadowRect.height() * 0.5,
|
||||
baseRect.width(),
|
||||
baseRect.height() * 0.5);
|
||||
nonAreaRect = QRectF(baseRect.x(),
|
||||
baseRect.y(),
|
||||
baseRect.width(),
|
||||
baseRect.height() * 0.5);
|
||||
areaLine = QLineF(areaRect.topLeft(), areaRect.topRight());
|
||||
break;
|
||||
case LeftDockWidgetArea:
|
||||
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * 0.5, baseRect.height());
|
||||
nonAreaRect = QRectF(shadowRect.width() * 0.5,
|
||||
baseRect.y(),
|
||||
baseRect.width() * 0.5,
|
||||
baseRect.height());
|
||||
areaLine = QLineF(areaRect.topRight(), areaRect.bottomRight());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QSizeF baseSize = baseRect.size();
|
||||
if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) {
|
||||
baseRect = areaRect;
|
||||
}
|
||||
|
||||
painter.fillRect(baseRect, backgroundColor);
|
||||
if (areaRect.isValid()) {
|
||||
pen = painter.pen();
|
||||
pen.setColor(borderColor);
|
||||
QColor color = iconColor(DockOverlayCross::OverlayColor);
|
||||
if (color.alpha() == 255) {
|
||||
color.setAlpha(64);
|
||||
}
|
||||
painter.setBrush(color);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(areaRect);
|
||||
|
||||
pen = painter.pen();
|
||||
pen.setWidth(1);
|
||||
pen.setColor(borderColor);
|
||||
pen.setStyle(Qt::DashLine);
|
||||
painter.setPen(pen);
|
||||
painter.drawLine(areaLine);
|
||||
}
|
||||
painter.restore();
|
||||
|
||||
painter.save();
|
||||
// Draw outer border
|
||||
pen = painter.pen();
|
||||
pen.setColor(borderColor);
|
||||
pen.setWidth(1);
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(pen);
|
||||
painter.drawRect(baseRect);
|
||||
|
||||
// draw window title bar
|
||||
painter.setBrush(borderColor);
|
||||
QRectF frameRect(baseRect.topLeft(), QSizeF(baseRect.width(), baseSize.height() / 10));
|
||||
painter.drawRect(frameRect);
|
||||
painter.restore();
|
||||
|
||||
// Draw arrow for outer container drop indicators
|
||||
if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) {
|
||||
QRectF arrowRect;
|
||||
arrowRect.setSize(baseSize);
|
||||
arrowRect.setWidth(arrowRect.width() / 4.6);
|
||||
arrowRect.setHeight(arrowRect.height() / 2);
|
||||
arrowRect.moveCenter(QPointF(0, 0));
|
||||
QPolygonF arrow;
|
||||
arrow << arrowRect.topLeft() << QPointF(arrowRect.right(), arrowRect.center().y())
|
||||
<< arrowRect.bottomLeft();
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(iconColor(DockOverlayCross::ArrowColor));
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
painter.translate(nonAreaRect.center().x(), nonAreaRect.center().y());
|
||||
|
||||
switch (dockWidgetArea) {
|
||||
case TopDockWidgetArea:
|
||||
painter.rotate(-90);
|
||||
break;
|
||||
case RightDockWidgetArea:
|
||||
break;
|
||||
case BottomDockWidgetArea:
|
||||
painter.rotate(90);
|
||||
break;
|
||||
case LeftDockWidgetArea:
|
||||
painter.rotate(180);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
painter.drawPolygon(arrow);
|
||||
}
|
||||
|
||||
pixmap.setDevicePixelRatio(devicePixelRatio);
|
||||
return pixmap;
|
||||
}
|
||||
};
|
||||
|
||||
DockOverlay::DockOverlay(QWidget *parent, eMode mode)
|
||||
: QFrame(parent)
|
||||
, d(new DockOverlayPrivate(this))
|
||||
{
|
||||
d->m_mode = mode;
|
||||
d->m_cross = new DockOverlayCross(this);
|
||||
|
||||
if (Utils::HostOsInfo::isLinuxHost())
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
|
||||
| Qt::X11BypassWindowManagerHint);
|
||||
else
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
|
||||
setWindowOpacity(1);
|
||||
setWindowTitle("DockOverlay");
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
d->m_cross->setVisible(false);
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
DockOverlay::~DockOverlay()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void DockOverlay::setAllowedAreas(DockWidgetAreas areas)
|
||||
{
|
||||
if (areas == d->m_allowedAreas)
|
||||
return;
|
||||
d->m_allowedAreas = areas;
|
||||
d->m_cross->reset();
|
||||
}
|
||||
|
||||
DockWidgetAreas DockOverlay::allowedAreas() const
|
||||
{
|
||||
return d->m_allowedAreas;
|
||||
}
|
||||
|
||||
DockWidgetArea DockOverlay::dropAreaUnderCursor() const
|
||||
{
|
||||
DockWidgetArea result = d->m_cross->cursorLocation();
|
||||
if (result != InvalidDockWidgetArea) {
|
||||
return result;
|
||||
}
|
||||
|
||||
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(d->m_targetWidget.data());
|
||||
if (!dockArea) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (dockArea->allowedAreas().testFlag(CenterDockWidgetArea)
|
||||
&& dockArea->titleBarGeometry().contains(dockArea->mapFromGlobal(QCursor::pos()))) {
|
||||
return CenterDockWidgetArea;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DockWidgetArea DockOverlay::showOverlay(QWidget *target)
|
||||
{
|
||||
if (d->m_targetWidget == target) {
|
||||
// Hint: We could update geometry of overlay here.
|
||||
DockWidgetArea dockWidgetArea = dropAreaUnderCursor();
|
||||
if (dockWidgetArea != d->m_lastLocation) {
|
||||
repaint();
|
||||
d->m_lastLocation = dockWidgetArea;
|
||||
}
|
||||
return dockWidgetArea;
|
||||
}
|
||||
|
||||
d->m_targetWidget = target;
|
||||
d->m_lastLocation = InvalidDockWidgetArea;
|
||||
|
||||
// Move it over the target.
|
||||
resize(target->size());
|
||||
QPoint topLeft = target->mapToGlobal(target->rect().topLeft());
|
||||
move(topLeft);
|
||||
show();
|
||||
d->m_cross->updatePosition();
|
||||
d->m_cross->updateOverlayIcons();
|
||||
return dropAreaUnderCursor();
|
||||
}
|
||||
|
||||
void DockOverlay::hideOverlay()
|
||||
{
|
||||
hide();
|
||||
d->m_targetWidget.clear();
|
||||
d->m_lastLocation = InvalidDockWidgetArea;
|
||||
d->m_dropAreaRect = QRect();
|
||||
}
|
||||
|
||||
void DockOverlay::enableDropPreview(bool enable)
|
||||
{
|
||||
d->m_dropPreviewEnabled = enable;
|
||||
update();
|
||||
}
|
||||
|
||||
bool DockOverlay::dropPreviewEnabled() const
|
||||
{
|
||||
return d->m_dropPreviewEnabled;
|
||||
}
|
||||
|
||||
void DockOverlay::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
// Draw rect based on location
|
||||
if (!d->m_dropPreviewEnabled) {
|
||||
d->m_dropAreaRect = QRect();
|
||||
return;
|
||||
}
|
||||
|
||||
QRect rectangle = rect();
|
||||
const DockWidgetArea dockWidgetArea = dropAreaUnderCursor();
|
||||
double factor = (DockOverlay::ModeContainerOverlay == d->m_mode) ? 3 : 2;
|
||||
|
||||
switch (dockWidgetArea) {
|
||||
case TopDockWidgetArea:
|
||||
rectangle.setHeight(static_cast<int>(rectangle.height() / factor));
|
||||
break;
|
||||
case RightDockWidgetArea:
|
||||
rectangle.setX(static_cast<int>(rectangle.width() * (1 - 1 / factor)));
|
||||
break;
|
||||
case BottomDockWidgetArea:
|
||||
rectangle.setY(static_cast<int>(rectangle.height() * (1 - 1 / factor)));
|
||||
break;
|
||||
case LeftDockWidgetArea:
|
||||
rectangle.setWidth(static_cast<int>(rectangle.width() / factor));
|
||||
break;
|
||||
case CenterDockWidgetArea:
|
||||
rectangle = rect();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
QPainter painter(this);
|
||||
QColor color = palette().color(QPalette::Active, QPalette::Highlight);
|
||||
QPen pen = painter.pen();
|
||||
pen.setColor(color.darker(120));
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
pen.setWidth(1);
|
||||
pen.setCosmetic(true);
|
||||
painter.setPen(pen);
|
||||
color = color.lighter(130);
|
||||
color.setAlpha(64);
|
||||
painter.setBrush(color);
|
||||
painter.drawRect(rectangle.adjusted(0, 0, -1, -1));
|
||||
d->m_dropAreaRect = rectangle;
|
||||
}
|
||||
|
||||
QRect DockOverlay::dropOverlayRect() const
|
||||
{
|
||||
return d->m_dropAreaRect;
|
||||
}
|
||||
|
||||
void DockOverlay::showEvent(QShowEvent *event)
|
||||
{
|
||||
d->m_cross->show();
|
||||
QFrame::showEvent(event);
|
||||
}
|
||||
|
||||
void DockOverlay::hideEvent(QHideEvent *event)
|
||||
{
|
||||
d->m_cross->hide();
|
||||
QFrame::hideEvent(event);
|
||||
}
|
||||
|
||||
bool DockOverlay::event(QEvent *event)
|
||||
{
|
||||
bool result = Super::event(event);
|
||||
if (event->type() == QEvent::Polish) {
|
||||
d->m_cross->setupOverlayCross(d->m_mode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int areaAlignment(const DockWidgetArea area)
|
||||
{
|
||||
switch (area) {
|
||||
case TopDockWidgetArea:
|
||||
return Qt::AlignHCenter | Qt::AlignBottom;
|
||||
case RightDockWidgetArea:
|
||||
return Qt::AlignLeft | Qt::AlignVCenter;
|
||||
case BottomDockWidgetArea:
|
||||
return Qt::AlignHCenter | Qt::AlignTop;
|
||||
case LeftDockWidgetArea:
|
||||
return Qt::AlignRight | Qt::AlignVCenter;
|
||||
case CenterDockWidgetArea:
|
||||
return Qt::AlignCenter;
|
||||
default:
|
||||
return Qt::AlignCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// DockOverlayCrossPrivate
|
||||
QPoint DockOverlayCrossPrivate::areaGridPosition(const DockWidgetArea area)
|
||||
{
|
||||
if (DockOverlay::ModeDockAreaOverlay == m_mode) {
|
||||
switch (area) {
|
||||
case TopDockWidgetArea:
|
||||
return QPoint(1, 2);
|
||||
case RightDockWidgetArea:
|
||||
return QPoint(2, 3);
|
||||
case BottomDockWidgetArea:
|
||||
return QPoint(3, 2);
|
||||
case LeftDockWidgetArea:
|
||||
return QPoint(2, 1);
|
||||
case CenterDockWidgetArea:
|
||||
return QPoint(2, 2);
|
||||
default:
|
||||
return QPoint();
|
||||
}
|
||||
} else {
|
||||
switch (area) {
|
||||
case TopDockWidgetArea:
|
||||
return QPoint(0, 2);
|
||||
case RightDockWidgetArea:
|
||||
return QPoint(2, 4);
|
||||
case BottomDockWidgetArea:
|
||||
return QPoint(4, 2);
|
||||
case LeftDockWidgetArea:
|
||||
return QPoint(2, 0);
|
||||
case CenterDockWidgetArea:
|
||||
return QPoint(2, 2);
|
||||
default:
|
||||
return QPoint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DockOverlayCross::DockOverlayCross(DockOverlay *overlay)
|
||||
: QWidget(overlay->parentWidget())
|
||||
, d(new DockOverlayCrossPrivate(this))
|
||||
{
|
||||
d->m_dockOverlay = overlay;
|
||||
|
||||
if (Utils::HostOsInfo::isLinuxHost())
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
|
||||
| Qt::X11BypassWindowManagerHint);
|
||||
else
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
|
||||
setWindowTitle("DockOverlayCross");
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
d->m_gridLayout = new QGridLayout();
|
||||
d->m_gridLayout->setSpacing(0);
|
||||
setLayout(d->m_gridLayout);
|
||||
}
|
||||
|
||||
DockOverlayCross::~DockOverlayCross()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void DockOverlayCross::setupOverlayCross(DockOverlay::eMode mode)
|
||||
{
|
||||
d->m_mode = mode;
|
||||
|
||||
QHash<DockWidgetArea, QWidget *> areaWidgets;
|
||||
areaWidgets.insert(TopDockWidgetArea, d->createDropIndicatorWidget(TopDockWidgetArea, mode));
|
||||
areaWidgets.insert(RightDockWidgetArea, d->createDropIndicatorWidget(RightDockWidgetArea, mode));
|
||||
areaWidgets.insert(BottomDockWidgetArea,
|
||||
d->createDropIndicatorWidget(BottomDockWidgetArea, mode));
|
||||
areaWidgets.insert(LeftDockWidgetArea, d->createDropIndicatorWidget(LeftDockWidgetArea, mode));
|
||||
areaWidgets.insert(CenterDockWidgetArea,
|
||||
d->createDropIndicatorWidget(CenterDockWidgetArea, mode));
|
||||
d->m_lastDevicePixelRatio = devicePixelRatioF();
|
||||
setAreaWidgets(areaWidgets);
|
||||
d->m_updateRequired = false;
|
||||
}
|
||||
|
||||
void DockOverlayCross::updateOverlayIcons()
|
||||
{
|
||||
if (windowHandle()->devicePixelRatio() == d->m_lastDevicePixelRatio) { // TODO
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto Widget : d->m_dropIndicatorWidgets) {
|
||||
d->updateDropIndicatorIcon(Widget);
|
||||
}
|
||||
d->m_lastDevicePixelRatio = devicePixelRatioF();
|
||||
}
|
||||
|
||||
void DockOverlayCross::setIconColor(eIconColor colorIndex, const QColor &color)
|
||||
{
|
||||
d->m_iconColors[colorIndex] = color;
|
||||
d->m_updateRequired = true;
|
||||
}
|
||||
|
||||
QColor DockOverlayCross::iconColor(eIconColor colorIndex) const
|
||||
{
|
||||
return d->m_iconColors[colorIndex];
|
||||
}
|
||||
|
||||
void DockOverlayCross::setAreaWidgets(const QHash<DockWidgetArea, QWidget *> &widgets)
|
||||
{
|
||||
// Delete old widgets.
|
||||
const auto values = d->m_dropIndicatorWidgets.values();
|
||||
for (auto widget : values) {
|
||||
d->m_gridLayout->removeWidget(widget);
|
||||
delete widget;
|
||||
}
|
||||
d->m_dropIndicatorWidgets.clear();
|
||||
|
||||
// Insert new widgets into grid.
|
||||
d->m_dropIndicatorWidgets = widgets;
|
||||
|
||||
const QHash<DockWidgetArea, QWidget *> areas = d->m_dropIndicatorWidgets;
|
||||
QHash<DockWidgetArea, QWidget *>::const_iterator constIt;
|
||||
for (constIt = areas.begin(); constIt != areas.end(); ++constIt) {
|
||||
const DockWidgetArea area = constIt.key();
|
||||
QWidget *widget = constIt.value();
|
||||
QPoint position = d->areaGridPosition(area);
|
||||
d->m_gridLayout->addWidget(widget,
|
||||
position.x(),
|
||||
position.y(),
|
||||
static_cast<Qt::Alignment>(areaAlignment(area)));
|
||||
}
|
||||
|
||||
if (DockOverlay::ModeDockAreaOverlay == d->m_mode) {
|
||||
d->m_gridLayout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_gridLayout->setRowStretch(0, 1);
|
||||
d->m_gridLayout->setRowStretch(1, 0);
|
||||
d->m_gridLayout->setRowStretch(2, 0);
|
||||
d->m_gridLayout->setRowStretch(3, 0);
|
||||
d->m_gridLayout->setRowStretch(4, 1);
|
||||
|
||||
d->m_gridLayout->setColumnStretch(0, 1);
|
||||
d->m_gridLayout->setColumnStretch(1, 0);
|
||||
d->m_gridLayout->setColumnStretch(2, 0);
|
||||
d->m_gridLayout->setColumnStretch(3, 0);
|
||||
d->m_gridLayout->setColumnStretch(4, 1);
|
||||
} else {
|
||||
d->m_gridLayout->setContentsMargins(4, 4, 4, 4);
|
||||
d->m_gridLayout->setRowStretch(0, 0);
|
||||
d->m_gridLayout->setRowStretch(1, 1);
|
||||
d->m_gridLayout->setRowStretch(2, 1);
|
||||
d->m_gridLayout->setRowStretch(3, 1);
|
||||
d->m_gridLayout->setRowStretch(4, 0);
|
||||
|
||||
d->m_gridLayout->setColumnStretch(0, 0);
|
||||
d->m_gridLayout->setColumnStretch(1, 1);
|
||||
d->m_gridLayout->setColumnStretch(2, 1);
|
||||
d->m_gridLayout->setColumnStretch(3, 1);
|
||||
d->m_gridLayout->setColumnStretch(4, 0);
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
DockWidgetArea DockOverlayCross::cursorLocation() const
|
||||
{
|
||||
const QPoint position = mapFromGlobal(QCursor::pos());
|
||||
|
||||
const QHash<DockWidgetArea, QWidget *> areas = d->m_dropIndicatorWidgets;
|
||||
QHash<DockWidgetArea, QWidget *>::const_iterator constIt;
|
||||
for (constIt = areas.begin(); constIt != areas.end(); ++constIt)
|
||||
{
|
||||
if (d->m_dockOverlay->allowedAreas().testFlag(constIt.key()) && constIt.value()
|
||||
&& constIt.value()->isVisible() && constIt.value()->geometry().contains(position)) {
|
||||
return constIt.key();
|
||||
}
|
||||
}
|
||||
|
||||
return InvalidDockWidgetArea;
|
||||
}
|
||||
|
||||
void DockOverlayCross::showEvent(QShowEvent *)
|
||||
{
|
||||
if (d->m_updateRequired) {
|
||||
setupOverlayCross(d->m_mode);
|
||||
}
|
||||
this->updatePosition();
|
||||
}
|
||||
|
||||
void DockOverlayCross::updatePosition()
|
||||
{
|
||||
resize(d->m_dockOverlay->size());
|
||||
QPoint topLeft = d->m_dockOverlay->pos();
|
||||
QPoint offest((this->width() - d->m_dockOverlay->width()) / 2,
|
||||
(this->height() - d->m_dockOverlay->height()) / 2);
|
||||
QPoint crossTopLeft = topLeft - offest;
|
||||
move(crossTopLeft);
|
||||
}
|
||||
|
||||
void DockOverlayCross::reset()
|
||||
{
|
||||
const QList<DockWidgetArea> allAreas{TopDockWidgetArea,
|
||||
RightDockWidgetArea,
|
||||
BottomDockWidgetArea,
|
||||
LeftDockWidgetArea,
|
||||
CenterDockWidgetArea};
|
||||
const DockWidgetAreas allowedAreas = d->m_dockOverlay->allowedAreas();
|
||||
|
||||
// Update visibility of area widgets based on allowedAreas.
|
||||
for (auto area : allAreas) {
|
||||
QPoint position = d->areaGridPosition(area);
|
||||
QLayoutItem *item = d->m_gridLayout->itemAtPosition(position.x(), position.y());
|
||||
QWidget *widget = nullptr;
|
||||
if (item && (widget = item->widget()) != nullptr) {
|
||||
widget->setVisible(allowedAreas.testFlag(area));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockOverlayCross::setIconColors(const QString &colors)
|
||||
{
|
||||
static const QMap<QString, int>
|
||||
colorCompenentStringMap{{"Frame", DockOverlayCross::FrameColor},
|
||||
{"Background", DockOverlayCross::WindowBackgroundColor},
|
||||
{"Overlay", DockOverlayCross::OverlayColor},
|
||||
{"Arrow", DockOverlayCross::ArrowColor},
|
||||
{"Shadow", DockOverlayCross::ShadowColor}};
|
||||
|
||||
auto colorList = colors.split(' ', QString::SkipEmptyParts);
|
||||
for (const auto &colorListEntry : colorList) {
|
||||
auto componentColor = colorListEntry.split('=', QString::SkipEmptyParts);
|
||||
int component = colorCompenentStringMap.value(componentColor[0], -1);
|
||||
if (component < 0) {
|
||||
continue;
|
||||
}
|
||||
d->m_iconColors[component] = QColor(componentColor[1]);
|
||||
}
|
||||
|
||||
d->m_updateRequired = true;
|
||||
}
|
||||
|
||||
QString DockOverlayCross::iconColors() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
} // namespace ADS
|
265
src/libs/advanceddockingsystem/dockoverlay.h
Normal file
265
src/libs/advanceddockingsystem/dockoverlay.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
#include <QFrame>
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QRect>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QGridLayout;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace ADS {
|
||||
|
||||
struct DockOverlayPrivate;
|
||||
class DockOverlayCross;
|
||||
|
||||
/**
|
||||
* DockOverlay paints a translucent rectangle over another widget. The geometry
|
||||
* of the rectangle is based on the mouse location.
|
||||
*/
|
||||
class ADS_EXPORT DockOverlay : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockOverlayPrivate *d; //< private data class
|
||||
friend struct DockOverlayPrivate;
|
||||
friend class DockOverlayCross;
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
|
||||
enum eMode { ModeDockAreaOverlay, ModeContainerOverlay };
|
||||
|
||||
/**
|
||||
* Creates a dock overlay
|
||||
*/
|
||||
DockOverlay(QWidget *parent, eMode Mode = ModeDockAreaOverlay);
|
||||
|
||||
/**
|
||||
* Virtual destructor
|
||||
*/
|
||||
virtual ~DockOverlay() override;
|
||||
|
||||
/**
|
||||
* Configures the areas that are allowed for docking
|
||||
*/
|
||||
void setAllowedAreas(DockWidgetAreas areas);
|
||||
|
||||
/**
|
||||
* Returns flags with all allowed drop areas
|
||||
*/
|
||||
DockWidgetAreas allowedAreas() const;
|
||||
|
||||
/**
|
||||
* Returns the drop area under the current cursor location
|
||||
*/
|
||||
DockWidgetArea dropAreaUnderCursor() const;
|
||||
|
||||
/**
|
||||
* Show the drop overly for the given target widget
|
||||
*/
|
||||
DockWidgetArea showOverlay(QWidget *target);
|
||||
|
||||
/**
|
||||
* Hides the overlay
|
||||
*/
|
||||
void hideOverlay();
|
||||
|
||||
/**
|
||||
* Enables / disables the semi transparent overlay rectangle that represents
|
||||
* the future area of the dropped widget
|
||||
*/
|
||||
void enableDropPreview(bool enable);
|
||||
|
||||
/**
|
||||
* Returns true if drop preview is enabled
|
||||
*/
|
||||
bool dropPreviewEnabled() const;
|
||||
|
||||
/**
|
||||
* The drop overlay rectangle for the target area
|
||||
*/
|
||||
QRect dropOverlayRect() const;
|
||||
|
||||
/**
|
||||
* Handle polish events
|
||||
*/
|
||||
virtual bool event(QEvent *event) override;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
virtual void showEvent(QShowEvent *event) override;
|
||||
virtual void hideEvent(QHideEvent *event) override;
|
||||
};
|
||||
|
||||
struct DockOverlayCrossPrivate;
|
||||
/**
|
||||
* DockOverlayCross shows a cross with 5 different drop area possibilities.
|
||||
* I could have handled everything inside DockOverlay, but because of some
|
||||
* styling issues it's better to have a separate class for the cross.
|
||||
* You can style the cross icon using the property system.
|
||||
* \code
|
||||
* ADS--DockOverlayCross
|
||||
{
|
||||
qproperty-iconFrameColor: palette(highlight);
|
||||
qproperty-iconBackgroundColor: palette(base);
|
||||
qproperty-iconOverlayColor: palette(highlight);
|
||||
qproperty-iconArrowColor: rgb(227, 227, 227);
|
||||
qproperty-iconShadowColor: rgb(0, 0, 0);
|
||||
}
|
||||
* \endcode
|
||||
* Or you can use the iconColors property to pass in AARRGGBB values as
|
||||
* hex string like shown in the example below.
|
||||
* \code
|
||||
* ADS--DockOverlayCross
|
||||
* {
|
||||
* qproperty-iconColors: "Frame=#ff3d3d3d Background=#ff929292 Overlay=#1f3d3d3d Arrow=#ffb4b4b4 Shadow=#40474747";
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
class DockOverlayCross : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString iconColors READ iconColors WRITE setIconColors)
|
||||
Q_PROPERTY(QColor iconFrameColor READ iconColor WRITE setIconFrameColor)
|
||||
Q_PROPERTY(QColor iconBackgroundColor READ iconColor WRITE setIconBackgroundColor)
|
||||
Q_PROPERTY(QColor iconOverlayColor READ iconColor WRITE setIconOverlayColor)
|
||||
Q_PROPERTY(QColor iconArrowColor READ iconColor WRITE setIconArrowColor)
|
||||
Q_PROPERTY(QColor iconShadowColor READ iconColor WRITE setIconShadowColor)
|
||||
|
||||
public:
|
||||
enum eIconColor {
|
||||
FrameColor, ///< the color of the frame of the small window icon
|
||||
WindowBackgroundColor, ///< the background color of the small window in the icon
|
||||
OverlayColor, ///< the color that shows the overlay (the dock side) in the icon
|
||||
ArrowColor, ///< the arrow that points into the direction
|
||||
ShadowColor ///< the color of the shadow rectangle that is painted below the icons
|
||||
};
|
||||
|
||||
private:
|
||||
DockOverlayCrossPrivate *d;
|
||||
friend struct DockOverlayCrossPrivate;
|
||||
friend class DockOverlay;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* This function returns an empty string and is only here to silence
|
||||
* moc
|
||||
*/
|
||||
QString iconColors() const;
|
||||
|
||||
/**
|
||||
* This is a dummy function for the property system
|
||||
*/
|
||||
QColor iconColor() const { return QColor(); }
|
||||
void setIconFrameColor(const QColor &color) { setIconColor(FrameColor, color); }
|
||||
void setIconBackgroundColor(const QColor &color) { setIconColor(WindowBackgroundColor, color); }
|
||||
void setIconOverlayColor(const QColor &color) { setIconColor(OverlayColor, color); }
|
||||
void setIconArrowColor(const QColor &color) { setIconColor(ArrowColor, color); }
|
||||
void setIconShadowColor(const QColor &color) { setIconColor(ShadowColor, color); }
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an overlay cross for the given overlay
|
||||
*/
|
||||
DockOverlayCross(DockOverlay *overlay);
|
||||
|
||||
/**
|
||||
* Virtual destructor
|
||||
*/
|
||||
virtual ~DockOverlayCross() override;
|
||||
|
||||
/**
|
||||
* Sets a certain icon color
|
||||
*/
|
||||
void setIconColor(eIconColor colorIndex, const QColor &color);
|
||||
|
||||
/**
|
||||
* Returns the icon color given by ColorIndex
|
||||
*/
|
||||
QColor iconColor(eIconColor colorIndex) const;
|
||||
|
||||
/**
|
||||
* Returns the dock widget area depending on the current cursor location.
|
||||
* The function checks, if the mouse cursor is inside of any drop indicator
|
||||
* widget and returns the corresponding DockWidgetArea.
|
||||
*/
|
||||
DockWidgetArea cursorLocation() const;
|
||||
|
||||
/**
|
||||
* Sets up the overlay cross for the given overlay mode
|
||||
*/
|
||||
void setupOverlayCross(DockOverlay::eMode mode);
|
||||
|
||||
/**
|
||||
* Recreates the overlay icons.
|
||||
*/
|
||||
void updateOverlayIcons();
|
||||
|
||||
/**
|
||||
* Resets and updates the
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Updates the current position
|
||||
*/
|
||||
void updatePosition();
|
||||
|
||||
/**
|
||||
* A string with all icon colors to set.
|
||||
* You can use this property to style the overly icon via CSS stylesheet
|
||||
* file. The colors are set via a color identifier and a hex AARRGGBB value like
|
||||
* in the example below.
|
||||
* \code
|
||||
* ADS--DockOverlayCross
|
||||
* {
|
||||
* qproperty-iconColors: "Frame=#ff3d3d3d Background=#ff929292 Overlay=#1f3d3d3d Arrow=#ffb4b4b4 Shadow=#40474747";
|
||||
* }
|
||||
*/
|
||||
void setIconColors(const QString &colors);
|
||||
|
||||
protected:
|
||||
virtual void showEvent(QShowEvent *event) override;
|
||||
void setAreaWidgets(const QHash<DockWidgetArea, QWidget *> &widgets);
|
||||
}; // DockOverlayCross
|
||||
|
||||
} // namespace ADS
|
92
src/libs/advanceddockingsystem/docksplitter.cpp
Normal file
92
src/libs/advanceddockingsystem/docksplitter.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "docksplitter.h"
|
||||
|
||||
#include "dockareawidget.h"
|
||||
|
||||
#include <QChildEvent>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
|
||||
|
||||
namespace ADS
|
||||
{
|
||||
/**
|
||||
* Private dock splitter data
|
||||
*/
|
||||
struct DockSplitterPrivate
|
||||
{
|
||||
DockSplitter *q;
|
||||
int m_visibleContentCount = 0;
|
||||
|
||||
DockSplitterPrivate(DockSplitter *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
};
|
||||
|
||||
DockSplitter::DockSplitter(QWidget *parent)
|
||||
: QSplitter(parent)
|
||||
, d(new DockSplitterPrivate(this))
|
||||
{
|
||||
//setProperty("ads-splitter", true); // TODO
|
||||
setProperty("minisplitter", true);
|
||||
setChildrenCollapsible(false);
|
||||
}
|
||||
|
||||
DockSplitter::DockSplitter(Qt::Orientation orientation, QWidget *parent)
|
||||
: QSplitter(orientation, parent)
|
||||
, d(new DockSplitterPrivate(this))
|
||||
{}
|
||||
|
||||
DockSplitter::~DockSplitter()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool DockSplitter::hasVisibleContent() const
|
||||
{
|
||||
// TODO Cache or precalculate this to speed up
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
if (!widget(i)->isHidden()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
72
src/libs/advanceddockingsystem/docksplitter.h
Normal file
72
src/libs/advanceddockingsystem/docksplitter.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
#include <QSplitter>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
struct DockSplitterPrivate;
|
||||
|
||||
/**
|
||||
* Splitter used internally instead of QSplitter with some additional
|
||||
* fuctionality.
|
||||
*/
|
||||
class ADS_EXPORT DockSplitter : public QSplitter
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockSplitterPrivate *d;
|
||||
friend struct DockSplitterPrivate;
|
||||
|
||||
public:
|
||||
DockSplitter(QWidget *parent = nullptr);
|
||||
DockSplitter(Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Prints debug info
|
||||
*/
|
||||
virtual ~DockSplitter() override;
|
||||
|
||||
/**
|
||||
* Returns true, if any of the internal widgets is visible
|
||||
*/
|
||||
bool hasVisibleContent() const;
|
||||
}; // class DockSplitter
|
||||
|
||||
} // namespace ADS
|
625
src/libs/advanceddockingsystem/dockwidget.cpp
Normal file
625
src/libs/advanceddockingsystem/dockwidget.cpp
Normal file
@@ -0,0 +1,625 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 Uwe Kindler
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or (at your option) any later version.
|
||||
** The licenses are as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||
** of this file. Please review the following information to ensure
|
||||
** the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 or (at your option) any later version
|
||||
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "dockwidget.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockcomponentsfactory.h"
|
||||
#include "dockcontainerwidget.h"
|
||||
#include "dockmanager.h"
|
||||
#include "docksplitter.h"
|
||||
#include "dockwidgettab.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QBoxLayout>
|
||||
#include <QEvent>
|
||||
#include <QLoggingCategory>
|
||||
#include <QPointer>
|
||||
#include <QScrollArea>
|
||||
#include <QSplitter>
|
||||
#include <QStack>
|
||||
#include <QTextStream>
|
||||
#include <QToolBar>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
|
||||
|
||||
namespace ADS
|
||||
{
|
||||
/**
|
||||
* Private data class of DockWidget class (pimpl)
|
||||
*/
|
||||
struct DockWidgetPrivate
|
||||
{
|
||||
DockWidget *q = nullptr;
|
||||
QBoxLayout *m_layout = nullptr;
|
||||
QWidget *m_widget = nullptr;
|
||||
DockWidgetTab *m_tabWidget = nullptr;
|
||||
DockWidget::DockWidgetFeatures m_features = DockWidget::DefaultDockWidgetFeatures;
|
||||
DockManager *m_dockManager = nullptr;
|
||||
DockAreaWidget *m_dockArea = nullptr;
|
||||
QAction *m_toggleViewAction = nullptr;
|
||||
bool m_closed = false;
|
||||
QScrollArea *m_scrollArea = nullptr;
|
||||
QToolBar *m_toolBar = nullptr;
|
||||
Qt::ToolButtonStyle m_toolBarStyleDocked = Qt::ToolButtonIconOnly;
|
||||
Qt::ToolButtonStyle m_toolBarStyleFloating = Qt::ToolButtonTextUnderIcon;
|
||||
QSize m_toolBarIconSizeDocked = QSize(16, 16);
|
||||
QSize m_toolBarIconSizeFloating = QSize(24, 24);
|
||||
bool m_isFloatingTopLevel = false;
|
||||
QList<QAction *> m_titleBarActions;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockWidgetPrivate(DockWidget *parent);
|
||||
|
||||
/**
|
||||
* Show dock widget
|
||||
*/
|
||||
void showDockWidget();
|
||||
|
||||
/**
|
||||
* Hide dock widget.
|
||||
*/
|
||||
void hideDockWidget();
|
||||
|
||||
/**
|
||||
* Hides a dock area if all dock widgets in the area are closed.
|
||||
* This function updates the current selected tab and hides the parent
|
||||
* dock area if it is empty
|
||||
*/
|
||||
void updateParentDockArea();
|
||||
|
||||
/**
|
||||
* Setup the top tool bar
|
||||
*/
|
||||
void setupToolBar();
|
||||
|
||||
/**
|
||||
* Setup the main scroll area
|
||||
*/
|
||||
void setupScrollArea();
|
||||
};
|
||||
// struct DockWidgetPrivate
|
||||
|
||||
DockWidgetPrivate::DockWidgetPrivate(DockWidget *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
void DockWidgetPrivate::showDockWidget()
|
||||
{
|
||||
if (!m_dockArea) {
|
||||
FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
|
||||
floatingWidget->resize(q->size());
|
||||
floatingWidget->show();
|
||||
} else {
|
||||
m_dockArea->setCurrentDockWidget(q);
|
||||
m_dockArea->toggleView(true);
|
||||
m_tabWidget->show();
|
||||
QSplitter *splitter = internal::findParent<QSplitter *>(m_dockArea);
|
||||
while (splitter && !splitter->isVisible()) {
|
||||
splitter->show();
|
||||
splitter = internal::findParent<QSplitter *>(splitter);
|
||||
}
|
||||
|
||||
DockContainerWidget *container = m_dockArea->dockContainer();
|
||||
if (container->isFloating()) {
|
||||
FloatingDockContainer *floatingWidget
|
||||
= internal::findParent<FloatingDockContainer *>(container);
|
||||
floatingWidget->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetPrivate::hideDockWidget()
|
||||
{
|
||||
m_tabWidget->hide();
|
||||
updateParentDockArea();
|
||||
}
|
||||
|
||||
void DockWidgetPrivate::updateParentDockArea()
|
||||
{
|
||||
if (!m_dockArea) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto nextDockWidget = m_dockArea->nextOpenDockWidget(q);
|
||||
if (nextDockWidget) {
|
||||
m_dockArea->setCurrentDockWidget(nextDockWidget);
|
||||
} else {
|
||||
m_dockArea->hideAreaWithNoVisibleContent();
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetPrivate::setupToolBar()
|
||||
{
|
||||
m_toolBar = new QToolBar(q);
|
||||
m_toolBar->setObjectName("dockWidgetToolBar");
|
||||
m_layout->insertWidget(0, m_toolBar);
|
||||
m_toolBar->setIconSize(QSize(16, 16));
|
||||
m_toolBar->toggleViewAction()->setEnabled(false);
|
||||
m_toolBar->toggleViewAction()->setVisible(false);
|
||||
QObject::connect(q, &DockWidget::topLevelChanged, q, &DockWidget::setToolbarFloatingStyle);
|
||||
}
|
||||
|
||||
void DockWidgetPrivate::setupScrollArea()
|
||||
{
|
||||
m_scrollArea = new QScrollArea(q);
|
||||
m_scrollArea->setObjectName("dockWidgetScrollArea");
|
||||
m_scrollArea->setWidgetResizable(true);
|
||||
m_layout->addWidget(m_scrollArea);
|
||||
}
|
||||
|
||||
DockWidget::DockWidget(const QString &uniqueId, QWidget *parent)
|
||||
: QFrame(parent)
|
||||
, d(new DockWidgetPrivate(this))
|
||||
{
|
||||
d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
d->m_layout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_layout->setSpacing(0);
|
||||
setLayout(d->m_layout);
|
||||
setWindowTitle(uniqueId); // temporarily use unique id as title
|
||||
setObjectName(uniqueId);
|
||||
|
||||
d->m_tabWidget = componentsFactory()->createDockWidgetTab(this);
|
||||
d->m_toggleViewAction = new QAction(uniqueId, this);
|
||||
d->m_toggleViewAction->setCheckable(true);
|
||||
connect(d->m_toggleViewAction, &QAction::triggered, this, &DockWidget::toggleView);
|
||||
setToolbarFloatingStyle(false);
|
||||
}
|
||||
|
||||
DockWidget::~DockWidget()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
delete d;
|
||||
}
|
||||
|
||||
void DockWidget::setToggleViewActionChecked(bool checked)
|
||||
{
|
||||
QAction *action = d->m_toggleViewAction;
|
||||
//action->blockSignals(true);
|
||||
action->setChecked(checked);
|
||||
//action->blockSignals(false);
|
||||
}
|
||||
|
||||
void DockWidget::setWidget(QWidget *widget, eInsertMode insertMode)
|
||||
{
|
||||
QScrollArea *scrollAreaWidget = qobject_cast<QScrollArea *>(widget);
|
||||
if (scrollAreaWidget || ForceNoScrollArea == insertMode) {
|
||||
d->m_layout->addWidget(widget);
|
||||
if (scrollAreaWidget && scrollAreaWidget->viewport()) {
|
||||
scrollAreaWidget->viewport()->setProperty("dockWidgetContent", true);
|
||||
}
|
||||
} else {
|
||||
d->setupScrollArea();
|
||||
d->m_scrollArea->setWidget(widget);
|
||||
}
|
||||
|
||||
d->m_widget = widget;
|
||||
d->m_widget->setProperty("dockWidgetContent", true);
|
||||
}
|
||||
|
||||
QWidget *DockWidget::takeWidget()
|
||||
{
|
||||
// TODO Shouldn't m_widget being set to nullptr?!
|
||||
d->m_scrollArea->takeWidget();
|
||||
d->m_layout->removeWidget(d->m_widget);
|
||||
d->m_widget->setParent(nullptr);
|
||||
return d->m_widget;
|
||||
}
|
||||
|
||||
QWidget *DockWidget::widget() const { return d->m_widget; }
|
||||
|
||||
DockWidgetTab *DockWidget::tabWidget() const { return d->m_tabWidget; }
|
||||
|
||||
void DockWidget::setFeatures(DockWidgetFeatures features)
|
||||
{
|
||||
if (d->m_features == features) {
|
||||
return;
|
||||
}
|
||||
d->m_features = features;
|
||||
emit featuresChanged(d->m_features);
|
||||
d->m_tabWidget->onDockWidgetFeaturesChanged();
|
||||
}
|
||||
|
||||
void DockWidget::setFeature(DockWidgetFeature flag, bool on)
|
||||
{
|
||||
auto currentFeatures = features();
|
||||
internal::setFlag(currentFeatures, flag, on);
|
||||
setFeatures(currentFeatures);
|
||||
}
|
||||
|
||||
DockWidget::DockWidgetFeatures DockWidget::features() const { return d->m_features; }
|
||||
|
||||
DockManager *DockWidget::dockManager() const { return d->m_dockManager; }
|
||||
|
||||
void DockWidget::setDockManager(DockManager *dockManager) { d->m_dockManager = dockManager; }
|
||||
|
||||
DockContainerWidget *DockWidget::dockContainer() const
|
||||
{
|
||||
if (d->m_dockArea) {
|
||||
return d->m_dockArea->dockContainer();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DockAreaWidget *DockWidget::dockAreaWidget() const { return d->m_dockArea; }
|
||||
|
||||
bool DockWidget::isFloating() const
|
||||
{
|
||||
if (!isInFloatingContainer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return dockContainer()->topLevelDockWidget() == this;
|
||||
}
|
||||
|
||||
bool DockWidget::isInFloatingContainer() const
|
||||
{
|
||||
auto container = dockContainer();
|
||||
if (!container) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!container->isFloating()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DockWidget::isClosed() const { return d->m_closed; }
|
||||
|
||||
QAction *DockWidget::toggleViewAction() const { return d->m_toggleViewAction; }
|
||||
|
||||
void DockWidget::setToggleViewActionMode(eToggleViewActionMode mode)
|
||||
{
|
||||
if (ActionModeToggle == mode) {
|
||||
d->m_toggleViewAction->setCheckable(true);
|
||||
d->m_toggleViewAction->setIcon(QIcon());
|
||||
} else {
|
||||
d->m_toggleViewAction->setCheckable(false);
|
||||
d->m_toggleViewAction->setIcon(d->m_tabWidget->icon());
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::toggleView(bool open)
|
||||
{
|
||||
// If the toggle view action mode is ActionModeShow, then Open is always
|
||||
// true if the sender is the toggle view action
|
||||
QAction *action = qobject_cast<QAction *>(sender());
|
||||
if (action == d->m_toggleViewAction && !d->m_toggleViewAction->isCheckable()) {
|
||||
open = true;
|
||||
}
|
||||
// If the dock widget state is different, then we really need to toggle
|
||||
// the state. If we are in the right state, then we simply make this
|
||||
// dock widget the current dock widget
|
||||
if (d->m_closed != !open) {
|
||||
toggleViewInternal(open);
|
||||
} else if (open && d->m_dockArea) {
|
||||
d->m_dockArea->setCurrentDockWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::toggleViewInternal(bool open)
|
||||
{
|
||||
DockContainerWidget *dockContainerWidget = dockContainer();
|
||||
DockWidget *topLevelDockWidgetBefore = dockContainerWidget
|
||||
? dockContainerWidget->topLevelDockWidget()
|
||||
: nullptr;
|
||||
|
||||
if (open) {
|
||||
d->showDockWidget();
|
||||
} else {
|
||||
d->hideDockWidget();
|
||||
}
|
||||
d->m_closed = !open;
|
||||
//d->m_toggleViewAction->blockSignals(true);
|
||||
d->m_toggleViewAction->setChecked(open);
|
||||
//d->m_toggleViewAction->blockSignals(false);
|
||||
if (d->m_dockArea) {
|
||||
d->m_dockArea->toggleDockWidgetView(this, open);
|
||||
}
|
||||
|
||||
if (open && topLevelDockWidgetBefore) {
|
||||
DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetBefore, false);
|
||||
}
|
||||
|
||||
// Here we need to call the dockContainer() function again, because if
|
||||
// this dock widget was unassigned before the call to showDockWidget() then
|
||||
// it has a dock container now
|
||||
dockContainerWidget = dockContainer();
|
||||
DockWidget *topLevelDockWidgetAfter = dockContainerWidget
|
||||
? dockContainerWidget->topLevelDockWidget()
|
||||
: nullptr;
|
||||
DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetAfter, true);
|
||||
FloatingDockContainer *floatingContainer = dockContainerWidget->floatingWidget();
|
||||
if (floatingContainer) {
|
||||
floatingContainer->updateWindowTitle();
|
||||
}
|
||||
|
||||
if (!open) {
|
||||
emit closed();
|
||||
}
|
||||
emit viewToggled(open);
|
||||
}
|
||||
|
||||
void DockWidget::setDockArea(DockAreaWidget *dockArea)
|
||||
{
|
||||
d->m_dockArea = dockArea;
|
||||
d->m_toggleViewAction->setChecked(dockArea != nullptr && !this->isClosed());
|
||||
}
|
||||
|
||||
void DockWidget::saveState(QXmlStreamWriter &stream) const
|
||||
{
|
||||
stream.writeStartElement("widget");
|
||||
stream.writeAttribute("name", objectName());
|
||||
stream.writeAttribute("closed", QVariant::fromValue(d->m_closed).toString());
|
||||
stream.writeEndElement();
|
||||
}
|
||||
|
||||
void DockWidget::flagAsUnassigned()
|
||||
{
|
||||
d->m_closed = true;
|
||||
setParent(d->m_dockManager);
|
||||
setVisible(false);
|
||||
setDockArea(nullptr);
|
||||
tabWidget()->setParent(this);
|
||||
}
|
||||
|
||||
bool DockWidget::event(QEvent *event)
|
||||
{
|
||||
switch (event->type()) {
|
||||
case QEvent::Hide:
|
||||
emit visibilityChanged(false);
|
||||
break;
|
||||
|
||||
case QEvent::Show:
|
||||
emit visibilityChanged(geometry().right() >= 0 && geometry().bottom() >= 0);
|
||||
break;
|
||||
|
||||
case QEvent::WindowTitleChange :
|
||||
{
|
||||
const auto title = windowTitle();
|
||||
if (d->m_tabWidget) {
|
||||
d->m_tabWidget->setText(title);
|
||||
}
|
||||
if (d->m_toggleViewAction) {
|
||||
d->m_toggleViewAction->setText(title);
|
||||
}
|
||||
if (d->m_dockArea) {
|
||||
d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu
|
||||
}
|
||||
emit titleChanged(title);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Super::event(event);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_TOOLTIP
|
||||
void DockWidget::setTabToolTip(const QString &text)
|
||||
{
|
||||
if (d->m_tabWidget) {
|
||||
d->m_tabWidget->setToolTip(text);
|
||||
}
|
||||
if (d->m_toggleViewAction) {
|
||||
d->m_toggleViewAction->setToolTip(text);
|
||||
}
|
||||
if (d->m_dockArea) {
|
||||
d->m_dockArea->markTitleBarMenuOutdated(); //update tabs menu
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void DockWidget::setIcon(const QIcon &icon)
|
||||
{
|
||||
d->m_tabWidget->setIcon(icon);
|
||||
if (!d->m_toggleViewAction->isCheckable()) {
|
||||
d->m_toggleViewAction->setIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
QIcon DockWidget::icon() const { return d->m_tabWidget->icon(); }
|
||||
|
||||
QToolBar *DockWidget::toolBar() const { return d->m_toolBar; }
|
||||
|
||||
QToolBar *DockWidget::createDefaultToolBar()
|
||||
{
|
||||
if (!d->m_toolBar) {
|
||||
d->setupToolBar();
|
||||
}
|
||||
|
||||
return d->m_toolBar;
|
||||
}
|
||||
|
||||
void DockWidget::setToolBar(QToolBar *toolBar)
|
||||
{
|
||||
if (d->m_toolBar) {
|
||||
delete d->m_toolBar;
|
||||
}
|
||||
|
||||
d->m_toolBar = toolBar;
|
||||
d->m_layout->insertWidget(0, d->m_toolBar);
|
||||
connect(this, &DockWidget::topLevelChanged, this, &DockWidget::setToolbarFloatingStyle);
|
||||
setToolbarFloatingStyle(isFloating());
|
||||
}
|
||||
|
||||
void DockWidget::setToolBarStyle(Qt::ToolButtonStyle style, eState state)
|
||||
{
|
||||
if (StateFloating == state) {
|
||||
d->m_toolBarStyleFloating = style;
|
||||
} else {
|
||||
d->m_toolBarStyleDocked = style;
|
||||
}
|
||||
|
||||
setToolbarFloatingStyle(isFloating());
|
||||
}
|
||||
|
||||
Qt::ToolButtonStyle DockWidget::toolBarStyle(eState state) const
|
||||
{
|
||||
if (StateFloating == state) {
|
||||
return d->m_toolBarStyleFloating;
|
||||
} else {
|
||||
return d->m_toolBarStyleDocked;
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::setToolBarIconSize(const QSize &iconSize, eState state)
|
||||
{
|
||||
if (StateFloating == state) {
|
||||
d->m_toolBarIconSizeFloating = iconSize;
|
||||
} else {
|
||||
d->m_toolBarIconSizeDocked = iconSize;
|
||||
}
|
||||
|
||||
setToolbarFloatingStyle(isFloating());
|
||||
}
|
||||
|
||||
QSize DockWidget::toolBarIconSize(eState state) const
|
||||
{
|
||||
if (StateFloating == state) {
|
||||
return d->m_toolBarIconSizeFloating;
|
||||
} else {
|
||||
return d->m_toolBarIconSizeDocked;
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::setToolbarFloatingStyle(bool floating)
|
||||
{
|
||||
if (!d->m_toolBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto iconSize = floating ? d->m_toolBarIconSizeFloating : d->m_toolBarIconSizeDocked;
|
||||
if (iconSize != d->m_toolBar->iconSize()) {
|
||||
d->m_toolBar->setIconSize(iconSize);
|
||||
}
|
||||
|
||||
auto buttonStyle = floating ? d->m_toolBarStyleFloating : d->m_toolBarStyleDocked;
|
||||
if (buttonStyle != d->m_toolBar->toolButtonStyle()) {
|
||||
d->m_toolBar->setToolButtonStyle(buttonStyle);
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating)
|
||||
{
|
||||
if (topLevelDockWidget) {
|
||||
topLevelDockWidget->dockAreaWidget()->updateTitleBarVisibility();
|
||||
topLevelDockWidget->emitTopLevelChanged(floating);
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::emitTopLevelChanged(bool floating)
|
||||
{
|
||||
if (floating != d->m_isFloatingTopLevel) {
|
||||
d->m_isFloatingTopLevel = floating;
|
||||
emit topLevelChanged(d->m_isFloatingTopLevel);
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidget::setClosedState(bool closed) { d->m_closed = closed; }
|
||||
|
||||
QSize DockWidget::minimumSizeHint() const { return QSize(60, 40); }
|
||||
|
||||
void DockWidget::setFloating()
|
||||
{
|
||||
if (isClosed()) {
|
||||
return;
|
||||
}
|
||||
d->m_tabWidget->detachDockWidget();
|
||||
}
|
||||
|
||||
void DockWidget::deleteDockWidget()
|
||||
{
|
||||
dockManager()->removeDockWidget(this);
|
||||
deleteLater();
|
||||
d->m_closed = true;
|
||||
}
|
||||
|
||||
void DockWidget::closeDockWidget()
|
||||
{
|
||||
closeDockWidgetInternal(true);
|
||||
}
|
||||
|
||||
bool DockWidget::closeDockWidgetInternal(bool forceClose)
|
||||
{
|
||||
if (!forceClose) {
|
||||
emit closeRequested();
|
||||
}
|
||||
|
||||
if (!forceClose && features().testFlag(DockWidget::CustomCloseHandling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
|
||||
// If the dock widget is floating, then we check if we also need to
|
||||
// delete the floating widget
|
||||
if (isFloating()) {
|
||||
FloatingDockContainer* floatingWidget = internal::findParent<
|
||||
FloatingDockContainer *>(this);
|
||||
if (floatingWidget->dockWidgets().count() == 1) {
|
||||
floatingWidget->deleteLater();
|
||||
} else {
|
||||
floatingWidget->hide();
|
||||
}
|
||||
}
|
||||
deleteDockWidget();
|
||||
} else {
|
||||
toggleView(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DockWidget::setTitleBarActions(QList<QAction *> actions)
|
||||
{
|
||||
d->m_titleBarActions = actions;
|
||||
}
|
||||
|
||||
QList<QAction *> DockWidget::titleBarActions() const
|
||||
{
|
||||
return d->m_titleBarActions;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user