Merge remote-tracking branch 'origin/master' into feature/missions

# Conflicts:
#	data/assets/scene/solarsystem/missions/newhorizons/newhorizons.asset
#	data/assets/scene/solarsystem/missions/osirisrex/osirisrex.asset
#	data/assets/scene/solarsystem/missions/osirisrex/osirisrex.mission
#	data/assets/scene/solarsystem/missions/perseverance/perseverance.asset
#	include/openspace/mission/missionmanager.h
#	modules/server/src/connection.cpp
#	src/mission/missionmanager.cpp
#	src/mission/missionmanager_lua.inl
This commit is contained in:
Ylva Selling
2023-03-21 12:03:10 -04:00
3829 changed files with 169534 additions and 143316 deletions

View File

@@ -9,7 +9,3 @@ charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
# overwrite default settings here, for instance like this:
# [*.cpp]
# indent_style = tabs

13
.gitattributes vendored
View File

@@ -1,5 +1,18 @@
* text=auto
# Correct GitHub's language detection shenanigans
# Asset files are not Unity, but Lua instead
*.asset linguist-language=Lua
# We have some SPICE frame kernels that get misclassified as code
*.tf -linguist-detectable
# We do not want to index the GDAL csv and xml files
modules/globebrowsing/gdal_data/* linguist-vendored
# No need to index any external files
*/ext/* linguist-vendored
# No C allowed
*.h linguist-language=C++
# GitHub files
ATTRIBUTION text
AUTHORS text

8
.gitignore vendored
View File

@@ -1,8 +1,5 @@
# Build and editor thing
/build/
/build-ninja/
/build-xcode/
/build-make/
/build*/
*~
*.dir
*.idea/
@@ -40,3 +37,6 @@ customization.lua
# The COMMIT info is generated everytime CMake is run
COMMIT.md
*_codegen.cpp
# SkyBrowser Module downloaded data
/modules/skybrowser/wwtimagedata
doc

2
.gitmodules vendored
View File

@@ -9,7 +9,7 @@
url = https://github.com/OpenSpace/Spice.git
[submodule "modules/touch/ext/libTUIO11"]
path = modules/touch/ext/libTUIO11
url = https://github.com/mkalten/TUIO11_CPP
url = https://github.com/OpenSpace/TUIO11_CPP.git
[submodule "apps/OpenSpace-MinVR/ext/minvr"]
path = apps/OpenSpace-MinVR/ext/minvr
url = https://github.com/OpenSpace/minvr

73
CITATION.cff Normal file
View File

@@ -0,0 +1,73 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Bock"
given-names: "Alexander"
orcid: "https://orcid.org/0000-0002-2849-6146"
- family-names: "Axelsson"
given-names: "Emil"
- family-names: "Costa"
given-names: "Jonathas"
orcid: "https://orcid.org/0000-0002-5008-5685"
- family-names: "Payne"
given-names: "Gene"
orcid: "https://orcid.org/0000-0001-8022-4781"
- family-names: "Acinapura"
given-names: "Micah"
- family-names: "Trakinski"
given-names: "Vivian"
- family-names: "Emmart"
given-names: "Carter"
- family-names: "Silva"
given-names: "Claudio"
orcid: "https://orcid.org/0000-0003-2452-2295"
- family-names: "Hansen"
given-names: "Charles"
orcid: "https://orcid.org/0000-0002-8480-2152"
- family-names: "Ynnerman"
given-names: "Anders"
orcid: "https://orcid.org/0000-0002-9466-9826"
title: "OpenSpace"
version: 0.18.2
doi: 10.1109/TVCG.2019.2934259
date-released: 2022-12-24
url: "https://github.com/OpenSpace/OpenSpace"
preferred-citation:
scope: "If you use this software, please cite it as below"
type: article
authors:
- family-names: "Bock"
given-names: "Alexander"
orcid: "https://orcid.org/0000-0002-2849-6146"
- family-names: "Axelsson"
given-names: "Emil"
- family-names: "Costa"
given-names: "Jonathas"
orcid: "https://orcid.org/0000-0002-5008-5685"
- family-names: "Payne"
given-names: "Gene"
orcid: "https://orcid.org/0000-0001-8022-4781"
- family-names: "Acinapura"
given-names: "Micah"
- family-names: "Trakinski"
given-names: "Vivian"
- family-names: "Emmart"
given-names: "Carter"
- family-names: "Silva"
given-names: "Claudio"
orcid: "https://orcid.org/0000-0003-2452-2295"
- family-names: "Hansen"
given-names: "Charles"
orcid: "https://orcid.org/0000-0002-8480-2152"
- family-names: "Ynnerman"
given-names: "Anders"
orcid: "https://orcid.org/0000-0002-9466-9826"
doi: 10.1109/TVCG.2019.2934259
journal: "IEEE Transactions on Visualization and Computer Graphics"
month: 1
start: 633
end: 642
title: "OpenSpace: A System for Astrographics"
issue: 1
volume: 26
year: 2020

View File

@@ -2,7 +2,7 @@
# #
# OpenSpace #
# #
# Copyright (c) 2014-2021 #
# Copyright (c) 2014-2023 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #
@@ -22,28 +22,22 @@
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################################
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
cmake_minimum_required(VERSION 3.25 FATAL_ERROR)
cmake_policy(VERSION 3.25)
project(OpenSpace)
set(OPENSPACE_VERSION_MAJOR 0)
set(OPENSPACE_VERSION_MINOR 16)
set(OPENSPACE_VERSION_MINOR 19)
set(OPENSPACE_VERSION_PATCH 0)
set(OPENSPACE_VERSION_STRING "Beta-8")
set(OPENSPACE_VERSION_STRING "<dev>")
set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}")
set(OPENSPACE_CMAKE_EXT_DIR "${OPENSPACE_BASE_DIR}/support/cmake")
set(GHOUL_BASE_DIR "${OPENSPACE_BASE_DIR}/ext/ghoul")
include(${OPENSPACE_CMAKE_EXT_DIR}/module_common.cmake)
include(${OPENSPACE_CMAKE_EXT_DIR}/global_variables.cmake)
include(${GHOUL_BASE_DIR}/support/cmake/copy_shared_libraries.cmake)
include(${GHOUL_BASE_DIR}/support/cmake/handle_external_library.cmake)
include(${GHOUL_BASE_DIR}/support/cmake/message_macros.cmake)
include(${PROJECT_SOURCE_DIR}/support/cmake/module_common.cmake)
include(${PROJECT_SOURCE_DIR}/ext/ghoul/support/cmake/message_macros.cmake)
begin_header("Configuring OpenSpace project")
# Bail out if the user tries to generate a 32 bit project.
# Bail out if the user tries to generate a 32 bit project
if (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
message(FATAL_ERROR "OpenSpace can only be generated for 64 bit architectures.")
endif ()
@@ -51,9 +45,7 @@ endif ()
##########################################################################################
# Cleanup project #
##########################################################################################
set(OPENSPACE_APPS_DIR "${OPENSPACE_BASE_DIR}/apps")
if (NOT EXISTS "${OPENSPACE_BASE_DIR}/ext/ghoul/CMakeLists.txt")
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/ext/ghoul/CMakeLists.txt")
message(FATAL_ERROR "Git submodules are missing. Please run "
"git submodule update --init --recursive to download the missing dependencies."
)
@@ -68,11 +60,13 @@ mark_as_advanced(CMAKE_BACKWARDS_COMPATIBILITY CMAKE_BUILD_TYPE CMAKE_DEBUG_POST
)
# Set build output directories
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_CMAKE_EXT_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/support/cmake)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
# "OpenSpace Helper" is not a valid CMake target name under OLD
cmake_policy(SET CMP0037 NEW)
if (MSVC)
# Force all builds to be multi-threaded and increase number of sections in obj files
add_definitions(/MP /bigobj)
endif ()
##########################################################################################
# Main #
@@ -106,9 +100,9 @@ else ()
set(OPENSPACE_GIT_STATUS "")
endif ()
option(OPENSPACE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
if (MSVC)
option(OPENSPACE_BREAK_ON_FLOATING_POINT_EXCEPTION "Raise exceptions when encountering Inf's or Nan's in floating point numbers" OFF)
option(OPENSPACE_OPTIMIZATION_ENABLE_AVX "Enable AVX instruction set for compilation" OFF)
option(OPENSPACE_OPTIMIZATION_ENABLE_AVX2 "Enable AVX2 instruction set for compilation" OFF)
option(OPENSPACE_OPTIMIZATION_ENABLE_AVX512 "Enable AVX2 instruction set for compilation" OFF)
@@ -132,25 +126,30 @@ if (MSVC)
set(GHOUL_OPTIMIZATION_ENABLE_OTHER_OPTIMIZATIONS ${OPENSPACE_OPTIMIZATION_ENABLE_OTHER_OPTIMIZATIONS} CACHE BOOL "" FORCE)
endif ()
option(OPENSPACE_WITH_ABUFFER_RENDERER "Compile ABuffer Renderer" OFF)
if (UNIX)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++ -lc++abi")
else ()
if(NOT CMAKE_BUILD_TYPE)
#Can set to "RelWithDebInfo" or "Debug" also, but problems occur if this is blank by default
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Default build type" FORCE)
endif()
if (UNIX AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if (NOT CMAKE_BUILD_TYPE)
# Can set to "RelWithDebInfo" or "Debug" also, but problems occur if this is blank by default
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Default build type" FORCE)
endif ()
if (NOT DEFINED CMAKE_CXX_FLAGS OR CMAKE_CXX_FLAGS MATCHES "")
set(CMAKE_CXX_FLAGS " ")
endif ()
STRING(FIND ${CMAKE_CXX_FLAGS} "GLM_ENABLE_EXPERIMENTAL" GLM_FLAG_POS)
if (${GLM_FLAG_POS} EQUAL -1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGLM_ENABLE_EXPERIMENTAL" CACHE STRING "" FORCE)
set(OpenGL_GL_PREFERENCE "GLVND" CACHE STRING "OpenGL Preference setting necessary for linux" FORCE)
set(ASSIMP_BUILD_MINIZIP ON CACHE BOOL "Set to have assimp build minizip" FORCE)
endif ()
set(OpenGL_GL_PREFERENCE "GLVND" CACHE STRING "OpenGL Preference setting necessary for linux" FORCE)
# Fix for GCC tolerating space in target name
if (NOT DEFINED CMAKE_C_FLAGS OR CMAKE_C_FLAGS MATCHES "")
set(CMAKE_C_FLAGS " ")
endif ()
STRING(FIND ${CMAKE_C_FLAGS} "_GNU_SOURCE" GNUSOURCE_FLAG_POS)
if (${GNUSOURCE_FLAG_POS} EQUAL -1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE" CACHE STRING "" FORCE)
endif ()
endif ()
add_subdirectory(ext)
add_subdirectory(src)
add_subdirectory(support/coding/codegen)
@@ -164,12 +163,13 @@ add_custom_target(
add_dependencies(run_codegen codegen)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/__codegen.h"
COMMAND codegen ARGS "${OPENSPACE_BASE_DIR}/modules" "${OPENSPACE_BASE_DIR}/src"
COMMAND codegen ARGS "modules" "src"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
VERBATIM
)
set_folder_location(codegen-lib "support")
set_folder_location(codegen "support")
set_folder_location(run_codegen "support")
set_target_properties(codegen-lib PROPERTIES FOLDER "support")
set_target_properties(codegen PROPERTIES FOLDER "support")
set_target_properties(run_codegen PROPERTIES FOLDER "support")
# Qt
@@ -185,6 +185,7 @@ if (APPLE)
"~/Qt/5.11/clang_64/lib/cmake"
"~/Qt/5.12/clang_64/lib/cmake"
"~/Qt/5.15.1/clang_64/lib/cmake"
"~/Qt/6.2.3/macos/lib/cmake"
)
endif ()
@@ -205,24 +206,24 @@ end_header("End: Configuring Modules")
add_subdirectory(support/coding/codegen/tests)
set_folder_location(run_test_codegen "Unit Tests/support")
set_folder_location(codegentest "Unit Tests")
set_target_properties(run_test_codegen PROPERTIES FOLDER "Unit Tests/support")
set_target_properties(codegentest PROPERTIES FOLDER "Unit Tests")
begin_header("Configuring Applications")
add_subdirectory("${OPENSPACE_APPS_DIR}")
add_subdirectory(apps)
end_header("End: Configuring Applications")
if (MSVC AND TARGET OpenSpace)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT OpenSpace)
endif()
endif ()
option(OPENSPACE_HAVE_TESTS "Activate the OpenSpace unit tests" ON)
if (OPENSPACE_HAVE_TESTS)
begin_header("Generating OpenSpace unit test")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")
add_subdirectory(tests)
end_header()
endif (OPENSPACE_HAVE_TESTS)
@@ -237,7 +238,7 @@ if (OPENSPACE_MODULE_WEBBROWSER AND CEF_ROOT)
set(PROJECT_ARCH "x86_64")
if (WIN32)
set(RESOURCE_FILE ${OPENSPACE_APPS_DIR}/OpenSpace/openspace.rc)
set(RESOURCE_FILE openspace.rc)
endif ()
# Add the CEF binary distribution's cmake/ directory to the module path and
@@ -253,6 +254,6 @@ endif ()
##########################################################################################
# Manage the CPack packaging
include(${OPENSPACE_CMAKE_EXT_DIR}/packaging.cmake)
include(${PROJECT_SOURCE_DIR}/support/cmake/packaging.cmake)
end_header("End: Configuring OpenSpace project")

View File

@@ -27,7 +27,7 @@ Project maintainers have the right and responsibility to remove, edit, or reject
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at alexander.bock@me.com or vivian@amnh.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at alex@openspaceproject.com or vivian@amnh.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

View File

@@ -1,20 +1,20 @@
# Core Team
Alexander Bock
Emma Broman
Emil Axelsson
Gene Payne
Kalle Bladin
Jonathas Costa
Gene Payne
Malin Ejdbo
Elon Olsson
Micah Acinapura
Jonas Strandstedt
Michal Marcinkowski
Elon Olsson
Emma Broman
Micah Acinapura
Joakim Kilby
Lovisa Hassler
Mikael Petterson
Erik Sundén
Stefan Lindblad
Malin Ejdbo
Corrie Roe
Eric Myers
@@ -23,6 +23,8 @@ Erik Broberg
Jonathan Bosson
Michael Nilsson
Jonathan Franzen
ChristianAdamsson
Emilie Ho
Karin Reidarman
Hans-Christian Helltegen
Anton Arbring
@@ -39,15 +41,16 @@ Michael Sjöström
Michael Novén
Christoffer Särevall
# Community Support
Anteige
arfon
DavidLaidlaw
ethanejohnsons
johnriedel
mik3caprio
mingenuity
nbartzokas
nealmcb
noahdasanaike
PTrottier
sa5bke

2679
Doxyfile

File diff suppressed because it is too large Load Diff

272
Jenkinsfile vendored
View File

@@ -6,6 +6,12 @@ library('sharedSpace'); // jenkins-pipeline-lib
def url = 'https://github.com/OpenSpace/OpenSpace';
def branch = env.BRANCH_NAME;
// The CHANGE_BRANCH only exists if we are building a PR branch in which case it returns
// the original branch
if (env.CHANGE_BRANCH) {
branch = env.CHANGE_BRANCH;
}
@NonCPS
def readDir() {
def dirsl = [];
@@ -29,42 +35,6 @@ def moduleCMakeFlags() {
modules = bat(returnStdout: true, script: '@dir modules /b /ad /on').trim().split('\r\n');
}
// def dirs = readDir();
// def currentDir = new File('.')
// def dirs = []
// currentDir.eachFile FileType.DIRECTORIES, {
// dirs << it.name
// }
// def moduleFlags = [
// 'atmosphere',
// 'base',
// // 'cefwebgui',
// 'debugging',
// 'digitaluniverse',
// 'fieldlines',
// 'fieldlinessequence',
// 'fitsfilereader',
// 'gaia',
// 'galaxy',
// 'globebrowsing',
// 'imgui',
// 'iswa',
// 'kameleon',
// 'kameleonvolume',
// 'multiresvolume',
// 'server',
// 'space',
// 'spacecraftinstruments',
// 'space',
// 'spout',
// 'sync',
// 'touch',
// 'toyvolume',
// 'volume',
// // 'webbrowser',
// // 'webgui'
// ];
def flags = '';
for (module in modules) {
flags += "-DOPENSPACE_MODULE_${module.toUpperCase()}=ON "
@@ -72,8 +42,6 @@ def moduleCMakeFlags() {
return flags;
}
// echo flags
//
// Pipeline start
//
@@ -100,22 +68,45 @@ parallel tools: {
},
linux_gcc_make: {
if (env.USE_BUILD_OS_LINUX == 'true') {
node('linux' && 'gcc') {
node('linux-gcc') {
stage('linux-gcc-make/scm') {
deleteDir();
gitHelper.checkoutGit(url, branch);
}
stage('linux-gcc-make/build') {
def cmakeCompileOptions = moduleCMakeFlags();
cmakeCompileOptions += ' -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS:STRING="-DGLM_ENABLE_EXPERIMENTAL"';
cmakeCompileOptions += ' -DOpenGL_GL_PREFERENCE:STRING=GLVND -DASSIMP_BUILD_MINIZIP=1';
// Not sure why the linking of OpenSpaceTest takes so long
compileHelper.build(compileHelper.Make(), compileHelper.Gcc(), cmakeCompileOptions, 'OpenSpace', 'build-make');
compileHelper.build(compileHelper.Make(), compileHelper.Gcc(), cmakeCompileOptions, '', 'build-make');
compileHelper.recordCompileIssues(compileHelper.Gcc());
}
stage('linux-gcc-make/test') {
// testHelper.runUnitTests('build/OpenSpaceTest');
// testHelper.runUnitTests('bin/codegentest')
if (env.RUN_UNIT_TESTS == 'true') {
stage('linux-gcc-make/test-codegen') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/codegentest');
}
}
stage('linux-gcc-make/test-sgct') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/SGCTTest');
}
}
stage('linux-gcc-make/test-ghoul') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/GhoulTest');
}
}
stage('linux-gcc-make/test-openspace') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/OpenSpaceTest');
}
}
}
cleanWs()
} // node('linux')
@@ -123,20 +114,43 @@ linux_gcc_make: {
},
linux_gcc_ninja: {
if (env.USE_BUILD_OS_LINUX == 'true') {
node('linux' && 'gcc') {
node('linux-gcc') {
stage('linux-gcc-ninja/scm') {
deleteDir();
gitHelper.checkoutGit(url, branch);
}
stage('linux-gcc-ninja/build') {
def cmakeCompileOptions = moduleCMakeFlags();
cmakeCompileOptions += '-DMAKE_BUILD_TYPE=Release';
// Not sure why the linking of OpenSpaceTest takes so long
compileHelper.build(compileHelper.Ninja(), compileHelper.Gcc(), cmakeCompileOptions, 'OpenSpace', 'build-ninja');
compileHelper.build(compileHelper.Ninja(), compileHelper.Gcc(), cmakeCompileOptions, '', 'build-ninja');
}
stage('linux-gcc-ninja/test') {
// testHelper.runUnitTests('build/OpenSpaceTest');
// testHelper.runUnitTests('bin/codegentest')
if (env.RUN_UNIT_TESTS == 'true') {
stage('linux-gcc-ninja/test-codegen') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/codegentest');
}
}
stage('linux-gcc-ninja/test-sgct') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/SGCTTest');
}
}
stage('linux-gcc-ninja/test-ghoul') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/GhoulTest');
}
}
stage('linux-gcc-ninja/test-openspace') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/OpenSpaceTest');
}
}
}
cleanWs()
} // node('linux')
@@ -144,21 +158,44 @@ linux_gcc_ninja: {
},
linux_clang_make: {
if (env.USE_BUILD_OS_LINUX == 'true') {
node('linux' && 'clang') {
node('linux-clang') {
stage('linux-clang-make/scm') {
deleteDir()
gitHelper.checkoutGit(url, branch);
}
stage('linux-clang-make/build') {
def cmakeCompileOptions = moduleCMakeFlags()
cmakeCompileOptions += ' -DMAKE_BUILD_TYPE=Release'
// Not sure why the linking of OpenSpaceTest takes so long
compileHelper.build(compileHelper.Make(), compileHelper.Clang(), cmakeCompileOptions, 'OpenSpace', 'build-make');
compileHelper.build(compileHelper.Make(), compileHelper.Clang(), cmakeCompileOptions, '', 'build-make');
compileHelper.recordCompileIssues(compileHelper.Clang());
}
stage('linux-clang-make/test') {
// testHelper.runUnitTests('build/OpenSpaceTest');
// testHelper.runUnitTests('bin/codegentest')
if (env.RUN_UNIT_TESTS == 'true') {
stage('linux-clang-make/test-codegen') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/codegentest');
}
}
stage('linux-clang-make/test-sgct') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/SGCTTest');
}
}
stage('linux-clang-make/test-ghoul') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/GhoulTest');
}
}
stage('linux-clang-make/test-openspace') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/OpenSpaceTest');
}
}
}
cleanWs()
} // node('linux')
@@ -166,20 +203,43 @@ linux_clang_make: {
},
linux_clang_ninja: {
if (env.USE_BUILD_OS_LINUX == 'true') {
node('linux' && 'clang') {
node('linux-clang') {
stage('linux-clang-ninja/scm') {
deleteDir()
gitHelper.checkoutGit(url, branch);
}
stage('linux-clang-ninja/build') {
def cmakeCompileOptions = moduleCMakeFlags()
cmakeCompileOptions += '-DMAKE_BUILD_TYPE=Release'
// Not sure why the linking of OpenSpaceTest takes so long
compileHelper.build(compileHelper.Ninja(), compileHelper.Clang(), cmakeCompileOptions, 'OpenSpace', 'build-ninja');
compileHelper.build(compileHelper.Ninja(), compileHelper.Clang(), cmakeCompileOptions, '', 'build-ninja');
}
stage('linux-clang-ninja/test') {
// testHelper.runUnitTests('build/OpenSpaceTest');
// testHelper.runUnitTests('bin/codegentest')
if (env.RUN_UNIT_TESTS == 'true') {
stage('linux-clang-ninja/test-codegen') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/codegentest');
}
}
stage('linux-clang-ninja/test-sgct') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/SGCTTest');
}
}
stage('linux-clang-ninja/test-ghoul') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/GhoulTest');
}
}
stage('linux-clang-ninja/test-openspace') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/OpenSpaceTest');
}
}
}
cleanWs()
} // node('linux')
@@ -192,15 +252,37 @@ windows_msvc: {
deleteDir();
gitHelper.checkoutGit(url, branch);
}
stage('windows-msvc/build') {
compileHelper.build(compileHelper.VisualStudio(), compileHelper.VisualStudio(), moduleCMakeFlags(), '', 'build-msvc');
compileHelper.recordCompileIssues(compileHelper.VisualStudio());
}
stage('windows-msvc/test') {
// Currently, the unit tests are failing on Windows
// testHelper.runUnitTests('bin\\Debug\\OpenSpaceTest')
testHelper.runUnitTests('bin\\Debug\\codegentest')
}
if (env.RUN_UNIT_TESTS == 'true') {
stage('windows-msvc/test-codegen') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin\\Debug\\codegentest');
}
}
stage('windows-msvc/test-sgct') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin\\Debug\\SGCTTest');
}
}
stage('windows-msvc/test-ghoul') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin\\Debug\\GhoulTest');
}
}
stage('windows-msvc/test-openspace') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin\\Debug\\OpenSpaceTest');
}
}
}
cleanWs()
} // node('windows')
}
@@ -232,13 +314,36 @@ macos_make: {
deleteDir();
gitHelper.checkoutGit(url, branch);
}
stage('macos-make/build') {
compileHelper.build(compileHelper.Make(), compileHelper.Clang(), moduleCMakeFlags(), '', 'build-make');
}
stage('macos-make/test') {
// Currently, the unit tests are crashing on OS X
// testHelper.runUnitTests('build/Debug/OpenSpaceTest')
}
if (env.RUN_UNIT_TESTS == 'true') {
stage('macos-make/test-codegen') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/Debug/codegentest');\
}
}
stage('macos-make/test-sgct') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/Debug\\SGCTTest');
}
}
stage('macos-make/test-ghoul') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/Debug\\GhoulTest');
}
}
stage('macos-make/test-openspace') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/Debug/OpenSpaceTest');
}
}
}
cleanWs()
} // node('macos')
}
@@ -250,14 +355,37 @@ macos_xcode: {
deleteDir();
gitHelper.checkoutGit(url, branch);
}
stage('macos-xcode/build') {
compileHelper.build(compileHelper.Xcode(), compileHelper.Xcode(), moduleCMakeFlags(), '', 'build-xcode');
}
stage('macos-xcode/test') {
// Currently, the unit tests are crashing on OS X
// testHelper.runUnitTests('build/Debug/OpenSpaceTest')
}
cleanWs()
if (env.RUN_UNIT_TESTS == 'true') {
stage('macos-xcode/test-codegen') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/Debug/codegentest');
}
}
stage('macos-xcode/test-sgct') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/Debug\\SGCTTest');
}
}
stage('macos-xcode/test-ghoul') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/Debug\\GhoulTest');
}
}
stage('macos-xcode/test-openspace') {
timeout(time: 2, unit: 'MINUTES') {
testHelper.runUnitTests('bin/Debug/OpenSpaceTest');
}
}
}
cleanWs()
} // node('macos')
}
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2014-2021
Copyright (c) 2014-2023
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
@@ -15,4 +15,4 @@ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,13 +1,50 @@
[OpenSpace](http://openspaceproject.com) is an open source, non-commercial, and freely available interactive data visualization software designed to visualize the entire known universe and portray our ongoing efforts to investigate the cosmos. Bringing the latest techniques from data visualization research to the general public, OpenSpace supports interactive presentation of dynamic data from observations, simulations, and space mission planning and operations. The software works on multiple operating systems (Windows, Linux, MacOS) with an extensible architecture powering high resolution tiled displays and planetarium domes, making use of the latest graphic card technologies for rapid data throughput. In addition, OpenSpace enables simultaneous connections across the globe creating opportunity for shared experiences among audiences worldwide.
![OpenSpace Logo](/data/openspace-horiz-logo-crop.png)
[OpenSpace](http://openspaceproject.com) is an open source, non-commercial, and freely available interactive data visualization software designed to visualize the entire known universe and portray our ongoing efforts to investigate the cosmos. Bringing the latest techniques from data visualization research to the general public, OpenSpace supports interactive presentation of dynamic data from observations, simulations, and space mission planning and operations. The software works on multiple operating systems (Windows, Linux, MacOS) with an extensible architecture capable of powering both personal computers and also high resolution tiled displays and planetarium domes. In addition, OpenSpace enables simultaneous connections across the globe creating opportunity for shared experiences among audiences worldwide. The target audience of the software reaches from the general public who wishes to explore our universe, enthusiasts interested in hacking the underlying components in OpenSpace to create unique experiences, informal science institutions wishing to create a low-cost, yet powerful exhibition piece, but also scientists desiring to visualize their datasets in a contextualized, powerful software.
The project stems from the same academic collaboration between Swedens [Linköping University](https://www.liu.se) (LiU) and the [American Museum of Natural History](https://www.amnh.org) (AMNH) that led to the creation of Uniview and its parent company [SCISS](http://sciss.se). Development of the software began several years ago through a close collaboration with NASA Goddards [Community Coordinated Modeling Center](https://ccmc.gsfc.nasa.gov) (CCMC) to model space weather forecasting and continued with visualizations of NASAs New Horizons mission to Pluto and ESAs Rosetta mission. This promising set of preliminary work provided a foundation for recent NASA funding, which has extended the collaboration to include the University of Utahs [Scientific Computing and Imaging](https://www.sci.utah.edu) (SCI) Institute, [New York University](https://www.nyu.edu)s Tandon School of Engineering, multiple informal science institutions across the United States, and multiple, international vendors. Current areas of focus within OpenSpace include:
[![License](https://img.shields.io/badge/License-MIT-purple.svg?style=flat-square)](LICENSE)
[![Download](https://img.shields.io/github/v/tag/OpenSpace/OpenSpace?label=Version&color=maroon&style=flat-square)](https://www.openspaceproject.com/installation)
![Size](https://img.shields.io/github/repo-size/OpenSpace/OpenSpace?style=flat-square&color=red)
- Visualization of dynamic simulations via interactive volumetric rendering, as a priority for communicating research in astrophysics.
- Utilization of NASAs SPICE observational geometry system with its Planetary Data Service (PDS) to enable space mission visualizations that reveal how missions are designed to gather science.
- Globe browsing techniques across spatial and temporal scales to examine scientific campaigns on multiple planets, including close up surface exploration.
[![System Paper](https://img.shields.io/badge/System%20Paper-10.1109%2FTVCG.2019.2934259-blue?style=flat-square)](https://doi.org/10.1109/TVCG.2019.2934259)
[![GlobeBrowsing Paper](https://img.shields.io/badge/GlobeBrowsing%20Paper-https%3A%2F%2Fdoi.org%2F10.1109%2FTVCG.2017.2743958-blue?style=flat-square)](https://doi.org/10.1109/TVCG.2017.2743958)
OpenSpace requires graphics support for [OpenGL](https://www.opengl.org/) version 3.3.
![Contributors](https://img.shields.io/github/contributors/OpenSpace/OpenSpace?style=flat-square)
![Commits](https://img.shields.io/github/commit-activity/m/OpenSpace/OpenSpace?color=green&style=flat-square)
This repository contains the source code and example scenes for OpenSpace, but does not contain any data. To build and install the client, we refer to the [OpenSpace Wiki](http://wiki.openspaceproject.com/), specifically [building](http://wiki.openspaceproject.com/docs/developers/compiling/general) for [Windows](http://wiki.openspaceproject.com/docs/developers/compiling/windows), [Linux (Ubuntu)](http://wiki.openspaceproject.com/docs/developers/compiling/ubuntu), and [MacOS](http://wiki.openspaceproject.com/docs/developers/compiling/macos). Required preexisting dependencies are: [Boost](http://www.boost.org/) and [Qt](http://www.qt.io/download). Feel free to create issues for missing features, bug reports, or compile problems or contact us via [email](mailto:alexander.bock@me.com?subject=OpenSpace:).
![Image](https://github.com/OpenSpace/openspace.github.io/raw/master/assets/images/collection.jpg)
Regarding any issues, you are very welcome on our [Slack support channel](https://openspacesupport.slack.com) to which you can freely [sign-up](https://join.slack.com/t/openspacesupport/shared_invite/zt-37niq6y9-T0JaCIk4UoFLI4VF5U9Vsw).
# Background
OpenSpace started as a collaboration between Sweden's [Linköping University](https://scivis.github.io) (LiU) and the [American Museum of Natural History](https://www.amnh.org) (AMNH). Development of the software began several years ago through a close collaboration with NASA Goddard's [Community Coordinated Modeling Center](https://ccmc.gsfc.nasa.gov) (CCMC) to model space weather forecasting and continued with visualizations of NASA's New Horizons mission to Pluto and ESA's Rosetta mission to 67P/Churyumov-Gerasimenko. This promising set of preliminary work provided a foundation for continued funding from NASA, the Swedish eScience Research Centre, and the Knut and Alice Wallenberg foundation, which has extended the collaboration to include the University of Utah's [Scientific Computing and Imaging](https://www.sci.utah.edu) (SCI) Institute, [New York University](https://www.nyu.edu)'s Tandon School of Engineering, multiple informal science institutions across the world, and multiple, international vendors.
![Image](https://github.com/OpenSpace/openspace.github.io/raw/master/assets/images/presentation.jpg)
# Features
Some of the high-level features supported in OpenSpace are:
- AMNH's Digital Universe catalog of extrasolar datasets (stars, galaxies, quasars, ...)
- High-resolution planetary images for major objects in the solar system (Earth, Moon, Mars, Venus, ...)
- Animated 3D models representing space missions (ISS, New Horizons, JWST, ...)
- Support for custom profiles with arbitrary user-defined content
- Ability to drive any type of display environment (flat screen, multi-projector, planetariums, ...)
- Lua and JavaScript interface into the engine allowing highly customized controls
- Native support to export an interactive sessions as individual frames for video export
- much much more (see our [Changelog](http://wiki.openspaceproject.com/docs/general/releases))
OpenSpace requires at least support for [OpenGL](https://www.opengl.org/) version 3.3, some custom components require at least version 4.2.
![Image](https://github.com/OpenSpace/openspace.github.io/raw/master/assets/images/display-systems.jpg)
# Getting Started
This repository contains the source code and example profiles for OpenSpace, but does not contain any data. To build and install the application, please check out the [GitHub Wiki](https://github.com/OpenSpace/OpenSpace/wiki). Here, you will find two pages, a [build instruction](https://github.com/OpenSpace/OpenSpace/wiki/Compiling) for all operating systems and then additional instructions for [Windows](https://github.com/OpenSpace/OpenSpace/wiki/Compiling-Windows), [Linux (Ubuntu)](https://github.com/OpenSpace/OpenSpace/wiki/Compiling-Ubuntu), and [MacOS](https://github.com/OpenSpace/OpenSpace/wiki/Compiling-MacOS). Please note that the Apple Silicon series of chips do not support OpenGL natively and Metal 2 does not support `double` precision accuracy (see [here](https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf) Section 2.1), therefore only the Intel processors for MacOS are supported and maintained.
Requirements for compiling are:
- CMake version 3.25 or above
- C++ compiler supporting C++20 (MSVC 19.31, GCC11, Clang14, AppleClang 13.1.6)
- [Boost](http://www.boost.org/)
- [Qt](http://www.qt.io/download)
Feel free to create issues for missing features, bug reports, or compile problems or contact us via [email](mailto:openspace@amnh.org?subject=OpenSpace:). Regarding any issues, you are very welcome on our [Slack support channel](https://openspacesupport.slack.com) to which you can freely [sign-up](https://join.slack.com/t/openspacesupport/shared_invite/zt-37niq6y9-T0JaCIk4UoFLI4VF5U9Vsw).
![Image](https://github.com/OpenSpace/openspace.github.io/raw/master/assets/images/himalaya-nkpg-dome.jpg)
# License
The contents of this repository is under an [MIT license](https://github.com/OpenSpace/OpenSpace/blob/master/LICENSE.md).

View File

@@ -2,7 +2,7 @@
# #
# OpenSpace #
# #
# Copyright (c) 2014-2021 #
# Copyright (c) 2014-2023 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #

View File

@@ -2,7 +2,7 @@
# #
# OpenSpace #
# #
# Copyright (c) 2014-2021 #
# Copyright (c) 2014-2022 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #
@@ -22,9 +22,9 @@
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################################
include(${GHOUL_BASE_DIR}/support/cmake/copy_shared_libraries.cmake)
include(${OPENSPACE_CMAKE_EXT_DIR}/application_definition.cmake)
include(${OPENSPACE_CMAKE_EXT_DIR}/global_variables.cmake)
include(${PROJECT_SOURCE_DIR}/ext/ghoul/support/cmake/copy_shared_libraries.cmake)
include(${PROJECT_SOURCE_DIR}/support/cmake/application_definition.cmake)
include(${PROJECT_SOURCE_DIR}/support/cmake/global_variables.cmake)
set(MACOSX_BUNDLE_ICON_FILE openspace.icns)
@@ -50,14 +50,14 @@ target_include_directories(OpenSpace-MinVR PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/ex
target_include_directories(OpenSpace-MinVR PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/ext/minvr/external/GLFW/src/include)
target_link_libraries(OpenSpace-MinVR openspace-core MinVR)
target_link_libraries(OpenSpace-MinVR PUBLIC openspace-core MinVR)
# Web Browser and Web gui
# Why not put these in the module's path? Because they do not have access to the
# target as of July 2017, which is needed.
if (OPENSPACE_MODULE_WEBBROWSER AND CEF_ROOT)
if (WIN32)
set(RESOURCE_FILE ${OPENSPACE_APPS_DIR}/OpenSpace-MinVR/openspace.rc)
set(RESOURCE_FILE openspace.rc)
endif ()
# Add the CEF binary distribution's cmake/ directory to the module path and
@@ -71,16 +71,7 @@ elseif (OPENSPACE_MODULE_WEBBROWSER)
message(WARNING "Web configured to be included, but no CEF_ROOT was found, please try configuring CMake again.")
endif ()
if (OPENSPACE_MODULE_WEBGUI AND WEBGUI_MODULE_PATH)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${WEBGUI_MODULE_PATH}/cmake")
include(webgui_helpers)
build_webgui_source(OpenSpace-MinVR)
elseif(OPENSPACE_MODULE_WEBGUI)
message(WARNING "WebGui is configured to be included, but the web source could not be found. Try configuring CMake again.")
endif()
# End Web Browser and Web gui
if (MSVC)
# This library is used for being able to output the callstack if an exception escapes
target_link_libraries(OpenSpace-MinVR Dbghelp.lib)
endif()
target_link_libraries(OpenSpace-MinVR PUBLIC Dbghelp.lib)
endif ()

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -60,7 +60,7 @@ public:
void appendNewInputEventsSinceLastCall(VRDataQueue* queue) override;
};
constexpr const char* _loggerCat = "main_minvr";
constexpr std::string_view _loggerCat = "main_minvr";
VRMain engine;
Handler handler;
@@ -86,7 +86,7 @@ struct {
bool HasInitializedGL = false;
std::array<float, 30> LastFrametimes = { 1.f / 60.f }; // we can be optimistic here
constexpr const char* MasterNode = "/MinVR/Desktop1";
constexpr std::string_view MasterNode = "/MinVR/Desktop1";
bool IsMasterNode = false;
uint64_t FrameNumber = 0;
std::chrono::time_point<std::chrono::high_resolution_clock> lastFrameTime;

View File

@@ -2,7 +2,7 @@
# #
# OpenSpace #
# #
# Copyright (c) 2014-2021 #
# Copyright (c) 2014-2023 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #
@@ -22,10 +22,8 @@
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################################
include(${GHOUL_BASE_DIR}/support/cmake/copy_shared_libraries.cmake)
include(${GHOUL_BASE_DIR}/support/cmake/message_macros.cmake)
include(${OPENSPACE_CMAKE_EXT_DIR}/application_definition.cmake)
include(${OPENSPACE_CMAKE_EXT_DIR}/global_variables.cmake)
include(${PROJECT_SOURCE_DIR}/ext/ghoul/support/cmake/message_macros.cmake)
include(${PROJECT_SOURCE_DIR}/support/cmake/application_definition.cmake)
# We are getting all_enabled_modules from the handle_applications.cmake file which gets
# it from the main CMakeLists file
@@ -44,14 +42,14 @@ if (OPENSPACE_OPENVR_SUPPORT)
if (WIN32)
find_path(SGCT_OPENVR_INCLUDE_DIRECTORY
NAMES SGCTOpenVR.h
PATHS ${OPENSPACE_BASE_DIR}/ext/sgct/additional_includes/openvr NO_DEFAULT_PATH
PATHS ${PROJECT_SOURCE_DIR}/ext/sgct/additional_includes/openvr NO_DEFAULT_PATH
REQUIRED
)
else ()
find_path(SGCT_OPENVR_INCLUDE_DIRECTORY
NAMES SGCTOpenVR.h
PATH_SUFFIXES SGCTOpenVR
PATHS ${OPENSPACE_BASE_DIR}/ext/sgct/additional_includes/openvr
PATHS ${PROJECT_SOURCE_DIR}/ext/sgct/additional_includes/openvr
REQUIRED
)
endif ()
@@ -106,23 +104,24 @@ target_compile_definitions(OpenSpace PRIVATE
${SPOUT_DEFINITIONS}
)
if (OPENSPACE_BREAK_ON_FLOATING_POINT_EXCEPTION)
target_compile_definitions(OpenSpace PRIVATE "OPENSPACE_BREAK_ON_FLOATING_POINT_EXCEPTION")
endif ()
begin_header("Dependency: SGCT")
set(SGCT_TEXT OFF CACHE BOOL "" FORCE)
set(SGCT_DEP_INCLUDE_FREETYPE OFF CACHE BOOL "" FORCE)
set(SGCT_DEP_INCLUDE_FMT OFF CACHE BOOL "" FORCE)
set(SGCT_DEP_INCLUDE_SCN OFF CACHE BOOL "" FORCE)
set(SGCT_DEP_INCLUDE_CATCH2 OFF CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/sgct)
target_link_libraries(OpenSpace PRIVATE sgct)
set_folder_location(sgct "External")
set_folder_location(glfw "External/SGCT")
set_folder_location(miniziplibstatic "External/SGCT")
set_folder_location(png16_static "External/SGCT")
set_folder_location(quat "External/SGCT")
set_folder_location(tinyxml2static "External/SGCT")
set_folder_location(vrpn "External/SGCT")
set_folder_location(zlibstatic "External/SGCT")
set_target_properties(sgct PROPERTIES FOLDER "External")
set_target_properties(glfw PROPERTIES FOLDER "External")
set_target_properties(SGCTTest PROPERTIES FOLDER "External")
if (UNIX AND (NOT APPLE))
target_link_libraries(OpenSpace PRIVATE Xcursor Xinerama X11)
@@ -135,6 +134,18 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/launcher)
target_link_libraries(OpenSpace PRIVATE openspace-ui-launcher)
end_header("Dependency: Profile Editor")
if (WIN32)
# Find the windeployqt application
get_target_property(_qmake_executable Qt6::qmake IMPORTED_LOCATION)
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${_qt_bin_dir}")
add_custom_command(
TARGET OpenSpace POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E env PATH="${_qt_bin_dir}" "${WINDEPLOYQT_EXECUTABLE}" --verbose 0 --no-compiler-runtime \"$<TARGET_FILE:OpenSpace>\"
COMMENT "Deploying Qt libraries"
)
endif ()
# Web Browser and Web gui
# Why not put these in the module's path? Because they do not have access to the
@@ -144,7 +155,7 @@ if (OPENSPACE_MODULE_WEBBROWSER AND CEF_ROOT)
set(PROJECT_ARCH "x86_64")
if (WIN32)
set(RESOURCE_FILE ${OPENSPACE_APPS_DIR}/OpenSpace/openspace.rc)
set(RESOURCE_FILE openspace.rc)
endif ()
# Add the CEF binary distribution's cmake/ directory to the module path and
@@ -155,7 +166,7 @@ if (OPENSPACE_MODULE_WEBBROWSER AND CEF_ROOT)
set_cef_targets("${CEF_ROOT}" OpenSpace)
run_cef_platform_config("${CEF_ROOT}" "${CEF_TARGET}" "${WEBBROWSER_MODULE_PATH}")
elseif ()
message(WARNING "Web configured to be included, but no CEF_ROOT was found, please try configuring CMake again.")
message(WARNING "Web configured to be included, but no CEF_ROOT was found, please try configuring CMake again")
endif ()
if (MSVC)
@@ -168,3 +179,10 @@ endif ()
if (OPENSPACE_NVTOOLS_ENABLED)
target_link_libraries(OpenSpace PRIVATE "${OPENSPACE_NVTOOLS_PATH}/lib/x64/nvToolsExt64_1.lib")
endif ()
if (WIN32)
add_custom_command(TARGET OpenSpace POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:OpenSpace> $<TARGET_FILE_DIR:OpenSpace>
COMMAND_EXPAND_LISTS
)
endif ()

View File

@@ -2,7 +2,7 @@
# #
# OpenSpace #
# #
# Copyright (c) 2014-2021 #
# Copyright (c) 2014-2023 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #
@@ -22,18 +22,20 @@
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################################
include(${OPENSPACE_CMAKE_EXT_DIR}/set_openspace_compile_settings.cmake)
include(${PROJECT_SOURCE_DIR}/support/cmake/set_openspace_compile_settings.cmake)
set(HEADER_FILES
include/filesystemaccess.h
include/launcherwindow.h
include/profile/actiondialog.h
include/profile/additionalscriptsdialog.h
include/profile/assetsdialog.h
include/profile/assetedit.h
include/profile/assettreeitem.h
include/profile/assettreemodel.h
include/profile/cameradialog.h
include/profile/deltatimesdialog.h
include/profile/keybindingsdialog.h
include/profile/horizonsdialog.h
include/profile/scriptlogdialog.h
include/profile/line.h
include/profile/marknodesdialog.h
@@ -42,18 +44,26 @@ set(HEADER_FILES
include/profile/timedialog.h
include/profile/profileedit.h
include/profile/propertiesdialog.h
include/sgctedit/displaywindowunion.h
include/sgctedit/monitorbox.h
include/sgctedit/orientationdialog.h
include/sgctedit/settingswidget.h
include/sgctedit/sgctedit.h
include/sgctedit/windowcontrol.h
)
set(SOURCE_FILES
src/launcherwindow.cpp
src/filesystemaccess.cpp
src/profile/actiondialog.cpp
src/profile/additionalscriptsdialog.cpp
src/profile/assetsdialog.cpp
src/profile/assetedit.cpp
src/profile/assettreeitem.cpp
src/profile/assettreemodel.cpp
src/profile/cameradialog.cpp
src/profile/deltatimesdialog.cpp
src/profile/keybindingsdialog.cpp
src/profile/horizonsdialog.cpp
src/profile/scriptlogdialog.cpp
src/profile/line.cpp
src/profile/marknodesdialog.cpp
@@ -62,42 +72,80 @@ set(SOURCE_FILES
src/profile/timedialog.cpp
src/profile/profileedit.cpp
src/profile/propertiesdialog.cpp
src/sgctedit/sgctedit.cpp
src/sgctedit/displaywindowunion.cpp
src/sgctedit/monitorbox.cpp
src/sgctedit/orientationdialog.cpp
src/sgctedit/settingswidget.cpp
src/sgctedit/windowcontrol.cpp
)
find_package(Qt5 COMPONENTS Widgets REQUIRED)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Widgets Network REQUIRED)
set(MOC_FILES "")
qt5_wrap_cpp(
MOC_FILES
include/launcherwindow.h
include/profile/additionalscriptsdialog.h
include/profile/assetsdialog.h
include/profile/assettreemodel.h
include/profile/cameradialog.h
include/profile/deltatimesdialog.h
include/profile/keybindingsdialog.h
include/profile/scriptlogdialog.h
include/profile/marknodesdialog.h
include/profile/metadialog.h
include/profile/modulesdialog.h
include/profile/timedialog.h
include/profile/profileedit.h
include/profile/propertiesdialog.h
)
set(RESOURCE_FILES "")
qt5_add_resources(RESOURCE_FILES resources/resources.qrc)
set(LIBRARIES "")
if (${QT_VERSION_MAJOR} EQUAL 5)
find_package(Qt5 COMPONENTS Core Widgets Network)
qt5_wrap_cpp(
MOC_FILES
${HEADER_FILES}
)
qt5_add_resources(RESOURCE_FILES resources/resources.qrc)
set(LIBRARIES )
elseif (${QT_VERSION_MAJOR} EQUAL 6)
find_package(Qt6 COMPONENTS Core Widgets Network REQUIRED)
qt6_wrap_cpp(
MOC_FILES
${HEADER_FILES}
)
qt6_add_resources(RESOURCE_FILES resources/resources.qrc)
elseif (NOT DEFINED QT_VERSION_MAJOR)
message(FATAL_ERROR "Unable to find Qt version")
else ()
message(FATAL_ERROR "Unsupported Qt version")
endif()
add_library(openspace-ui-launcher STATIC ${HEADER_FILES} ${SOURCE_FILES} ${MOC_FILES} ${RESOURCE_FILES})
set_openspace_compile_settings(openspace-ui-launcher)
target_include_directories(openspace-ui-launcher PUBLIC include)
target_link_libraries(openspace-ui-launcher PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets openspace-core)
target_include_directories(
openspace-ui-launcher
PUBLIC
include
# @TODO: This should be handled in a better way
../sgct/include
../sgct/sgct/ext/glm
)
target_link_libraries(
openspace-ui-launcher
PUBLIC
openspace-core
openspace-module-collection
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Network
)
target_precompile_headers(openspace-ui-launcher PRIVATE
<openspace/scene/profile.h>
<ghoul/glm.h>
<QAbstractItemDelegate>
<QAbstractItemModel>
<QDialog>
<QLineEdit>
<QObject>
<QStyleOption>
<QTextCursor>
<QWidget>
<filesystem>
)
if (MSVC)
set(MSVC_WARNINGS
"/wd4619" # #pragma warning: there is no warning number (raised by Qt headers)
"/wd4946" # reinterpret_cast used between related classes:
)
target_compile_options(openspace-ui-launcher INTERFACE ${MSVC_WARNINGS})
set(MSVC_WARNINGS
"/wd4619" # #pragma warning: there is no warning number (raised by Qt headers)
"/wd4946" # reinterpret_cast used between related classes:
)
target_compile_options(openspace-ui-launcher INTERFACE ${MSVC_WARNINGS})
endif ()

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -34,10 +34,6 @@ public:
*
* \param fileExtension string that defines the filter used to find files. Only
* files with this extension will be recognized (e.g. '.xml')
* \param approvedPaths vector or strings containing directory names to be included
* in the search. These are directories at the base level of
* the starting point of the search. Any sub-directories within
* these directories will be included.
* \param hideFileExtensions if true then file extensions will be removed from the
* listed files in the output
* \param useCheckboxes if true then the text output format will contain a '0' as
@@ -45,8 +41,7 @@ public:
* used to represent checked ('1'), uncheck ('0') or doesn't
* exist in filesystem ('x') states.
*/
FileSystemAccess(std::string fileExtension,
std::vector<std::string> approvedPaths, bool hideFileExtensions,
FileSystemAccess(std::string fileExtension, bool hideFileExtensions,
bool useCheckboxes);
/**
@@ -54,7 +49,8 @@ public:
*
* \param dir The directory from which to start the search from
*/
std::string useQtFileSystemModelToTraverseDir(std::string dir, bool usersAssets = false);
std::string useQtFileSystemModelToTraverseDir(std::string dir,
bool usersAssets = false);
private:
void parseChildDirElements(QFileInfo item, std::string space, int level,
@@ -62,7 +58,6 @@ private:
bool userAssets);
void parseChildFile(std::string res, bool& hasDirHeaderBeenAdded,
std::vector<std::string>& dirNames, std::vector<std::string>& output);
bool isApprovedPath(std::string path);
QFileSystemModel _filesystemModel;
QDir::Filters _fileFilterOptions = QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot;

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -27,7 +27,9 @@
#include <QMainWindow>
#include "sgctedit/sgctedit.h"
#include <openspace/scene/profile.h>
#include <QApplication>
#include <optional>
namespace openspace::configuration { struct Configuration; }
@@ -35,7 +37,7 @@ namespace openspace::configuration { struct Configuration; }
class QComboBox;
class QLabel;
class LauncherWindow : public QMainWindow {
class LauncherWindow final : public QMainWindow {
Q_OBJECT
public:
/**
@@ -82,6 +84,7 @@ private:
void setBackgroundImage(const std::string& syncPath);
void openProfileEditor(const std::string& profile, bool isUserProfile);
void openWindowEditor();
void populateProfilesList(std::string preset);
void populateWindowConfigsList(std::string preset);
@@ -96,6 +99,7 @@ private:
bool _shouldLaunch = false;
int _userAssetCount = 0;
int _userConfigCount = 0;
const std::string _sgctConfigName;
QComboBox* _profileBox = nullptr;
QComboBox* _windowConfigBox = nullptr;

View File

@@ -0,0 +1,109 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___ACTIONDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___ACTIONDIALOG___H__
#include <QDialog>
#include <openspace/scene/profile.h>
class QCheckBox;
class QComboBox;
class QDialogButtonBox;
class QGridLayout;
class QLabel;
class QLineEdit;
class QListWidget;
class QPushButton;
class QTextEdit;
class ActionDialog final : public QDialog {
Q_OBJECT
public:
ActionDialog(QWidget* parent, std::vector<openspace::Profile::Action>* actions,
std::vector<openspace::Profile::Keybinding>* keybindings);
private:
void createWidgets();
void createActionWidgets(QGridLayout* layout);
void createKeyboardWidgets(QGridLayout* layout);
void applyChanges();
openspace::Profile::Action* selectedAction();
void actionAdd();
void actionRemove();
void actionSelected();
void actionSaved();
void clearActionFields();
void actionRejected();
void chooseScripts();
void appendScriptsToTextfield(std::vector<std::string> scripts);
openspace::Profile::Keybinding* selectedKeybinding();
void keybindingAdd();
void keybindingRemove();
void keybindingSelected();
void keybindingActionSelected(int);
void keybindingSaved();
void clearKeybindingFields();
void keybindingRejected();
std::vector<openspace::Profile::Action>* _actions = nullptr;
std::vector<openspace::Profile::Action> _actionData;
std::vector<openspace::Profile::Keybinding>* _keybindings = nullptr;
std::vector<openspace::Profile::Keybinding> _keybindingsData;
struct {
QListWidget* list = nullptr;
QLineEdit* identifier = nullptr;
QLabel* infoText = nullptr;
QLineEdit* name = nullptr;
QLineEdit* guiPath = nullptr;
QLineEdit* documentation = nullptr;
QCheckBox* isLocal = nullptr;
QPushButton* chooseScripts = nullptr;
QTextEdit* script = nullptr;
QPushButton* addButton = nullptr;
QPushButton* removeButton = nullptr;
QDialogButtonBox* saveButtons = nullptr;
} _actionWidgets;
struct {
QListWidget* list = nullptr;
QCheckBox* shiftModifier = nullptr;
QCheckBox* ctrlModifier = nullptr;
QCheckBox* altModifier = nullptr;
QComboBox* key = nullptr;
QComboBox* action = nullptr;
QLineEdit* actionText = nullptr;
QPushButton* addButton = nullptr;
QPushButton* removeButton = nullptr;
QDialogButtonBox* saveButtons = nullptr;
} _keybindingWidgets;
QDialogButtonBox* _mainButton = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___ACTIONDIALOG___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,16 +22,14 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___ADDITIONALSCRIPTS___H__
#define __OPENSPACE_UI_LAUNCHER___ADDITIONALSCRIPTS___H__
#ifndef __OPENSPACE_UI_LAUNCHER___ADDITIONALSCRIPTSDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___ADDITIONALSCRIPTSDIALOG___H__
#include <QDialog>
namespace openspace { class Profile; }
class QTextEdit;
class AdditionalScriptsDialog : public QDialog {
class AdditionalScriptsDialog final : public QDialog {
Q_OBJECT
public:
/**
@@ -41,9 +39,11 @@ public:
* new or imported profile.
* \param parent Pointer to parent Qt widget
*/
AdditionalScriptsDialog(openspace::Profile& profile, QWidget* parent);
AdditionalScriptsDialog(QWidget* parent, std::vector<std::string>* scripts);
private:
void createWidgets();
private slots:
void parseScript();
void chooseScripts();
@@ -52,14 +52,12 @@ private slots:
*
* \param scripts #std::string scripts to be appended
*/
void appendScriptsToTextfield(std::string scripts);
void appendScriptsToTextfield(std::vector<std::string> scripts);
private:
void createWidgets();
openspace::Profile& _profile;
std::vector<std::string>* _scripts = nullptr;
std::vector<std::string> _scriptsData;
QTextEdit* _textScripts = nullptr;
QPushButton* _chooseScriptsButton = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___ADDITIONALSCRIPTS___H__
#endif // __OPENSPACE_UI_LAUNCHER___ADDITIONALSCRIPTSDIALOG___H__

View File

@@ -0,0 +1,55 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___ASSETEDIT___H__
#define __OPENSPACE_UI_LAUNCHER___ASSETEDIT___H__
#include <QDialog>
namespace openspace { class Asset; }
class QBoxLayout;
class QComboBox;
class QLabel;
class QLineEdit;
class QWidget;
class AssetEdit final : public QDialog {
Q_OBJECT
public:
AssetEdit(QWidget* parent);
private:
void createWidgets();
void openHorizons();
void approved();
QBoxLayout* _layout = nullptr;
QLineEdit* _nameEdit = nullptr;
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___ASSETEDIT___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,19 +22,17 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___ASSETS___H__
#define __OPENSPACE_UI_LAUNCHER___ASSETS___H__
#ifndef __OPENSPACE_UI_LAUNCHER___ASSETSDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___ASSETSDIALOG___H__
#include <QDialog>
#include "assettreemodel.h"
namespace openspace { class Profile; }
class QTextEdit;
class QTreeView;
class AssetsDialog : public QDialog {
class AssetsDialog final : public QDialog {
Q_OBJECT
public:
/**
@@ -43,29 +41,27 @@ public:
* \param profile The #openspace::Profile object containing all data of the
* new or imported profile.
* \param assetBasePath The path to the folder in which all of the assets are living
* \param userAssetBasePath The path to the folder in which the users' assets are living
* \param userAssetBasePath The path to the folder in which the users' assets are
* living
* \param parent Pointer to parent Qt widget
*/
AssetsDialog(openspace::Profile& profile, const std::string& assetBasePath,
const std::string& userAssetBasePath, QWidget* parent);
private slots:
void parseSelections();
void selected(const QModelIndex&);
AssetsDialog(QWidget* parent, openspace::Profile* profile,
const std::string& assetBasePath, const std::string& userAssetBasePath);
private:
void createWidgets();
/**
* Creates a text summary of all assets and their paths
*
* \return the #std::string summary
*/
QString createTextSummary();
openspace::Profile& _profile;
void parseSelections();
void selected(const QModelIndex&);
/// Creates a text summary of all assets and their paths
QString createTextSummary();
void openAssetEditor();
openspace::Profile* _profile = nullptr;
AssetTreeModel _assetTreeModel;
QTreeView* _assetTree = nullptr;
QTextEdit* _summary = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___ASSETS___H__
#endif // __OPENSPACE_UI_LAUNCHER___ASSETSDIALOG___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -30,20 +30,19 @@
class AssetTreeItem {
public:
static constexpr const int CheckboxColumn = 1;
static constexpr int CheckboxColumn = 1;
/**
* Constructor for assetTreeItem class
* Constructor for assetTreeItem class.
*
* \param data QVector containing the data contained in this tree view item
* \param parentItem The parent that contains this (and possibly other) children
* in the tree structure (optional).
*/
explicit AssetTreeItem(const std::vector<QVariant>& data,
AssetTreeItem* parentItem = nullptr);
AssetTreeItem(std::vector<QVariant> data, AssetTreeItem* parentItem = nullptr);
/**
* Destructor for assetTreeItem class
* Destructor for assetTreeItem class.
*/
~AssetTreeItem();
@@ -51,28 +50,25 @@ public:
* Returns pointer to this tree item's child at position \p row.
*
* \param row int of the row number of the child to get pointer
* \param parentItem The parent that contains this (and possibly other) children
* in the tree structure (optional).
* \return pointer to the child #assetTreeItem
*/
AssetTreeItem* child(int row);
AssetTreeItem* child(int row) const;
/**
* Returns the number of children this item has
* Returns the number of children this item has.
*
* \return The number of children
*/
int childCount() const;
/**
* Returns the number of data columns of this item
* Returns the number of data columns of this item.
*
* \return The number of data columns
*/
int columnCount() const;
/**
* Returns the data at column \p column of this item
* Returns the data at column \p column of this item.
*
* \param column Column number from which to retrieve data
* \return The data contained in the column
@@ -80,7 +76,7 @@ public:
QVariant data(int column) const;
/**
* Inserts children item(s) to the current item
* Inserts children item(s) to the current item.
*
* \param position where in this item's children to insert new children
* \param count number of children to insert
@@ -91,7 +87,7 @@ public:
bool insertChildren(int position, int count, int columns);
/**
* Inserts data column(s) in the current item
* Inserts data column(s) in the current item.
*
* \param position column number at which to insert column(s)
* \param columns number of columns to insert
@@ -100,21 +96,21 @@ public:
bool insertColumns(int position, int columns);
/**
* Returns a pointer to the current item's parent
* Returns a pointer to the current item's parent.
*
* \return pointer to the \p assetTreeItem parent
*/
AssetTreeItem* parent();
/**
* Returns the row number / child number of this item's parent
* Returns the row number / child number of this item's parent.
*
* \return The row number of this item's parent
*/
int row() const;
/**
* Returns the data at column \p column of this item
* Returns the data at column \p column of this item.
*
* \param position The position of the child(ren) to remove from the current
* item (which is parent)
@@ -124,13 +120,13 @@ public:
bool removeChildren(int position, int count);
/**
* Set data at column \p column
* Set data at column \p column.
*
* \param column The data column number to set
* \param value The #QVariant data element to store at column \p column
* \return true if the data set was successful
*/
bool setData(int column, const QVariant &value);
bool setData(int column, const QVariant& value);
/**
* Returns the checked state of this item. If an asset is selected to be included
@@ -141,7 +137,7 @@ public:
bool isChecked() const;
/**
* Sets the checked state of this item (whether or not it is selected for a profile)
* Sets the checked state of this item (whether or not it is selected for a profile).
*
* \param set bool for whether or not this is checked
*/
@@ -164,23 +160,23 @@ public:
bool isCategory() const;
/**
* Sets status of whether or not the asset exists in the current filesystem.
* It is possible that an imported profile lists an asset from another system
* that is not included on the current filesystem.
* Sets status of whether or not the asset exists in the current filesystem. It is
* possible that an imported profile lists an asset from another system that is not
* included on the current filesystem.
*
* \param set to true if the asset file exists in the current filesystem
*/
void setExistsInFilesystem(bool fileExists);
/**
* Returns bool for whether or not the asset exists in the current filesystem
* Returns bool for whether or not the asset exists in the current filesystem.
*
* \return true if the asset exists in the current filesystem
*/
bool doesExistInFilesystem() const;
/**
* Returns the asset name of the current item
* Returns the asset name of the current item.
*
* \return The asset name
*/

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -31,24 +31,23 @@
#include "openspace/scene/profile.h"
#include <memory>
class AssetTreeModel : public QAbstractItemModel {
class AssetTreeModel final : public QAbstractItemModel {
Q_OBJECT
public:
AssetTreeModel(QObject* parent = nullptr);
/**
* Returns the data contained at an index
* Returns the data contained at an index.
*
* \param index that defines where the item is located in the tree model
* \param role Qt-defined role that describes the reason Qt is calling the
* function (can be multiple times)
* \return QVariant data object
*/
QVariant data(const QModelIndex& index, int role) const override;
QVariant data(const QModelIndex& index, int role) const final;
/**
* Returns the header data of the tree view
* Returns the header data of the tree view.
*
* \param section of data to be obtained from header
* \param orientation of the query (e.g. Qt::horizontal)
@@ -57,10 +56,10 @@ public:
* \return QVariant data object in the header
*/
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
int role = Qt::DisplayRole) const final;
/**
* Returns the index of item in #QModelIndex object form
* Returns the index of item in #QModelIndex object form.
*
* \param row the row number
* \param column the column number
@@ -68,18 +67,18 @@ public:
* \return #QModelIndex index of the item at specified position
*/
QModelIndex index(int row, int column,
const QModelIndex& parent = QModelIndex()) const override;
const QModelIndex& parent = QModelIndex()) const final;
/**
* Returns the index of the parent of the item specified by input param
* Returns the index of the parent of the item specified by input param.
*
* \param index of item that is a child of the parent
* \return #QModelIndex index of the parent
*/
QModelIndex parent(const QModelIndex& index) const override;
QModelIndex parent(const QModelIndex& index) const final;
/**
* Returns the index of the parent of the item specified by the input params
* Returns the index of the parent of the item specified by the input params.
*
* \param row the row number
* \param column the column number
@@ -90,7 +89,7 @@ public:
const QModelIndex& parent = QModelIndex()) const;
/**
* Returns asset item at specified index
* Returns asset item at specified index.
*
* \param index of item that is a child of the parent
* \return #assetTreeItem pointer to the item at the provided index
@@ -98,32 +97,32 @@ public:
AssetTreeItem* assetItem(const QModelIndex& index);
/**
* Returns number of children/rows of the parent
* Returns number of children/rows of the parent.
*
* \param parent #QModelIndex of the parent item
* \return number of children/rows of this parent
*/
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const final;
/**
* Returns the number of columns of data in each item of the tree
* Returns the number of columns of data in each item of the tree.
*
* \param parent specified by the #QModelIndex index
* \return the number of data columns
*/
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const final;
/**
* Return the Qt flags of the item specified by index, which can include
* Qt::ItemIsEnabled, Qt::ItemIsSelectable
* Qt::ItemIsEnabled, Qt::ItemIsSelectable.
*
* \param index specified by the #QModelIndex index
* \return the Qt flags
*/
Qt::ItemFlags flags(const QModelIndex& index) const override;
Qt::ItemFlags flags(const QModelIndex& index) const final;
/**
* Set data at index \p index
* Set data at index \p index.
*
* \param index location of the item to set
* \param value The #QVariant data element to store at column \p column
@@ -131,15 +130,14 @@ public:
* \return true if the data set was successful
*/
bool setData(const QModelIndex& index, const QVariant& value,
int role = Qt::EditRole) override;
int role = Qt::EditRole) final;
/**
* Returns a vector of all #Assets selected in the tree view
* Returns a vector of all #Assets selected in the tree view.
*
* \param outputPaths vector of #openspace::Profile::Asset objects,
* each of which are selected
* \param outputItems vector of #assetTreeItem * objects,
* each of which are selected
* \param outputPaths vector of #openspace::Profile::Asset objects, each of which are
* selected
* \param outputItems vector of #assetTreeItem * objects, each of which are selected
*/
void getSelectedAssets(std::vector<std::string>& outputPaths,
std::vector<AssetTreeItem*>& outputItems);
@@ -154,7 +152,7 @@ public:
const std::string& userAssetBasePath);
/**
* Returns bool for if item is checked/selected
* Returns bool for if item is checked/selected.
*
* \param index location of the item to set
* \return true if the item is checked
@@ -162,7 +160,7 @@ public:
bool isChecked(QModelIndex& index) const;
/**
* Answers query about whether or not item is an asset
* Answers query about whether or not item is an asset.
*
* \param index location of the item to query
* \return true if the item is an asset (and not a directory)
@@ -170,7 +168,7 @@ public:
bool isAsset(QModelIndex& index) const;
/**
* Answers query about whether or not item is in the current filesystem
* Answers query about whether or not item is in the current filesystem.
*
* \param index location of the item to query
* \return true if the data is in the filesystem
@@ -178,7 +176,7 @@ public:
bool inFilesystem(QModelIndex& index) const;
/**
* Returns number of child items of referenced item
* Returns number of child items of referenced item.
*
* \param index location of the item to query
* \return number of child items
@@ -186,7 +184,7 @@ public:
int childCount(QModelIndex& index) const;
/**
* Returns a pointer to a child item of the current item
* Returns a pointer to a child item of the current item.
*
* \param row the child number of the current item
* \return assetTreeItem pointer to the child
@@ -194,7 +192,7 @@ public:
AssetTreeItem* child(int row) const;
/**
* Returns the asset name of the specified item
* Returns the asset name of the specified item.
*
* \param index location of the item to query
* \return the asset name of the item
@@ -202,7 +200,7 @@ public:
QString name(QModelIndex& index) const;
/**
* Set asset name at specified index
* Set asset name at specified index.
*
* \param index location of the item to set
* \param name the asset name to set
@@ -210,7 +208,7 @@ public:
void setName(QModelIndex& index, QString name);
/**
* Set state of checked/selected of an item
* Set state of checked/selected of an item.
*
* \param index location of the item to set
* \param checked true if item is checked/selected
@@ -218,7 +216,7 @@ public:
void setChecked(QModelIndex& index, bool checked);
/**
* Set state of whether or not asset exists in filesystem
* Set state of whether or not asset exists in filesystem.
*
* \param index location of the item to set
* \param fileExists true if asset exists in filesystem
@@ -226,7 +224,7 @@ public:
void setExistenceInFilesystem(QModelIndex& index, bool fileExists);
private:
AssetTreeItem* getItem(const QModelIndex& index) const;
AssetTreeItem* item(const QModelIndex& index) const;
std::unique_ptr<AssetTreeItem> _rootItem;
};

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -27,13 +27,14 @@
#include <QDialog>
namespace openspace { class Profile; }
#include <openspace/scene/profile.h>
#include <optional>
class QLabel;
class QLineEdit;
class QTabWidget;
class CameraDialog : public QDialog {
class CameraDialog final : public QDialog {
Q_OBJECT
public:
/**
@@ -43,7 +44,7 @@ public:
* new or imported profile.
* \param parent Pointer to parent Qt widget (optional)
*/
CameraDialog(openspace::Profile& profile, QWidget* parent);
CameraDialog(QWidget* parent, std::optional<openspace::Profile::CameraType>* camera);
private slots:
void approved();
@@ -57,7 +58,7 @@ private:
void addErrorMsg(QString errorDescription);
bool areRequiredFormsFilledAndValid();
openspace::Profile& _profile;
std::optional<openspace::Profile::CameraType>* _camera = nullptr;
QTabWidget* _tabWidget = nullptr;
struct {
QLineEdit* anchor = nullptr;

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,20 +22,18 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___DELTATIMES___H__
#define __OPENSPACE_UI_LAUNCHER___DELTATIMES___H__
#ifndef __OPENSPACE_UI_LAUNCHER___DELTATIMESDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___DELTATIMESDIALOG___H__
#include <QDialog>
namespace openspace { class Profile; }
class QDialogButtonBox;
class QLabel;
class QListWidget;
class QLineEdit;
class QPushButton;
class DeltaTimesDialog : public QDialog {
class DeltaTimesDialog final : public QDialog {
Q_OBJECT
public:
/**
@@ -45,7 +43,7 @@ public:
* new or imported profile.
* \param parent Pointer to parent Qt widget
*/
DeltaTimesDialog(openspace::Profile& profile, QWidget* parent);
DeltaTimesDialog(QWidget* parent, std::vector<double>* deltaTimes);
/**
* Returns a text summary of the delta time list for display purposes
@@ -63,8 +61,9 @@ public:
*/
virtual void keyPressEvent(QKeyEvent* evt) override;
private:
void createWidgets();
private slots:
void listItemSelected();
void valueChanged(const QString& text);
void saveDeltaTimeValue();
@@ -73,22 +72,19 @@ private slots:
void removeDeltaTimeValue();
void parseSelections();
private:
void createWidgets();
/**
* Called to transition to editing a particular dt value (gui settings)
*
* \param index index in dt list
* \param state \c true if the edit mode should be turned on, \c false otherwise
* \param state `true` if the edit mode should be turned on, `false` otherwise
*/
void transitionEditMode(int index, bool state);
void setLabelForKey(int index, bool editMode, std::string color);
bool isLineEmpty(int index);
openspace::Profile& _profile;
std::vector<double> _data;
std::vector<double>* _deltaTimes = nullptr;
std::vector<double> _deltaTimesData;
bool _editModeNewItem = false;
QListWidget* _listWidget = nullptr;
@@ -105,4 +101,4 @@ private:
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___DELTATIMES___H__
#endif // __OPENSPACE_UI_LAUNCHER___DELTATIMESDIALOG___H__

View File

@@ -0,0 +1,128 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___HORIZONSDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___HORIZONSDIALOG___H__
#include <QDialog>
#include <openspace/json.h>
#include <filesystem>
#include <string>
#ifdef OPENSPACE_MODULE_SPACE_ENABLED
#include <modules/space/horizonsfile.h>
#endif // OPENSPACE_MODULE_SPACE_ENABLED
class QComboBox;
class QDateTimeEdit;
class QLabel;
class QLineEdit;
class QNetworkAccessManager;
class QNetworkReply;
class QPlainTextEdit;
class QProgressBar;
class HorizonsDialog final : public QDialog {
Q_OBJECT
public:
explicit HorizonsDialog(QWidget* parent);
std::filesystem::path file() const;
private:
enum class LogLevel {
Error,
Warning,
Info,
None
};
enum class HTTPCodes {
Ok = 200,
BadRequest = 400,
MethodNotAllowed = 405,
InternalServerError = 500,
ServiceUnavailable = 503
};
void createWidgets();
void openSaveAs();
void typeOnChange(int index);
void downloadProgress(int value, int max);
void importTimeRange();
void approved();
bool isValidInput();
nlohmann::json sendRequest(const std::string& url);
nlohmann::json handleReply(QNetworkReply* reply);
bool checkHttpStatus(const QVariant& statusCode);
void appendLog(const std::string& message, LogLevel level);
#ifdef OPENSPACE_MODULE_SPACE_ENABLED
std::pair<std::string, std::string> readTimeRange();
bool handleRequest();
std::string constructUrl();
openspace::HorizonsFile handleAnswer(nlohmann::json& answer);
bool handleResult(openspace::HorizonsResultCode& result);
openspace::HorizonsFile _horizonsFile;
#endif // OPENSPACE_MODULE_SPACE_ENABLED
QNetworkAccessManager* _manager;
QLabel* _typeLabel = nullptr;
QComboBox* _typeCombo = nullptr;
QLabel* _fileLabel = nullptr;
QLineEdit* _fileEdit = nullptr;
QLabel* _targetLabel = nullptr;
QLineEdit* _targetEdit = nullptr;
QComboBox* _chooseTargetCombo = nullptr;
std::string _targetName;
QLabel* _centerLabel = nullptr;
QLineEdit* _centerEdit = nullptr;
QComboBox* _chooseObserverCombo = nullptr;
std::string _observerName;
QLabel* _startLabel = nullptr;
QDateTimeEdit* _startEdit = nullptr;
std::string _startTime;
QLabel* _endLabel = nullptr;
QDateTimeEdit* _endEdit = nullptr;
std::string _endTime;
std::pair<std::string, std::string> _validTimeRange;
QPushButton* _importTimeButton = nullptr;
QLabel* _stepLabel = nullptr;
QLineEdit* _stepEdit = nullptr;
QComboBox* _timeTypeCombo = nullptr;
QProgressBar* _downloadProgress = nullptr;
QLabel* _downloadLabel = nullptr;
QPlainTextEdit* _log = nullptr;
QLabel* _errorMsg = nullptr;
std::string _latestHorizonsError;
};
#endif // __OPENSPACE_UI_LAUNCHER___HORIZONSDIALOG___H__

View File

@@ -1,118 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___KEYBINDINGS___H__
#define __OPENSPACE_UI_LAUNCHER___KEYBINDINGS___H__
#include <QDialog>
#include <openspace/scene/profile.h>
#include <QWidget>
#include <QListWidgetItem>
class QComboBox;
class QCheckBox;
class QTextEdit;
class QDialogButtonBox;
class QListWidget;
class QLabel;
class QPushButton;
class KeybindingsDialog : public QDialog {
Q_OBJECT
public:
/**
* Constructor for keybindings class
*
* \param profile The #openspace::Profile object containing all data of the
* new or imported profile.
* \param parent Pointer to parent Qt widget (optional)
*/
KeybindingsDialog(openspace::Profile& profile, QWidget* parent);
/**
* Handles keypress while the Qt dialog window is open
*
* \param evt #QKeyEvent object for the key press event
*/
virtual void keyPressEvent(QKeyEvent* evt) override;
private slots:
void listItemSelected();
void listItemAdded();
void listItemRemove();
void listItemSave();
void listItemCancelSave();
void transitionToEditMode();
void parseSelections();
void chooseScripts();
void keySelected(int index);
/**
* Adds scripts to the _scriptEdit from outside dialogs
*
* \param scripts #std::string scripts to be appended
*/
void appendScriptsToKeybind(std::string scripts);
private:
void createWidgets();
void transitionFromEditMode();
void editBoxDisabled(bool disabled);
int indexInKeyMapping(std::vector<int>& mapVector, int keyInt);
bool areRequiredFormsFilled();
bool isLineEmpty(int index);
openspace::Profile& _profile;
std::vector<openspace::Profile::Keybinding> _data;
std::vector<int> _mapModKeyComboBoxIndexToKeyValue;
std::vector<int> _mapKeyComboBoxIndexToKeyValue;
bool _editModeNewItem = false;
QListWidget* _list = nullptr;
QLabel* _keyModLabel = nullptr;
QComboBox* _keyModCombo = nullptr;
QLabel* _keyLabel = nullptr;
QComboBox* _keyCombo = nullptr;
QLabel* _nameLabel = nullptr;
QLineEdit* _nameEdit = nullptr;
QLabel* _guiPathLabel = nullptr;
QLineEdit* _guiPathEdit = nullptr;
QLabel* _documentationLabel = nullptr;
QLineEdit* _documentationEdit = nullptr;
QCheckBox* _localCheck = nullptr;
QLabel* _scriptLabel = nullptr;
QTextEdit* _scriptEdit = nullptr;
QPushButton* _addButton = nullptr;
QPushButton* _removeButton = nullptr;
QPushButton* _chooseScriptsButton = nullptr;
QPushButton* _saveButton = nullptr;
QPushButton* _cancelButton = nullptr;
QDialogButtonBox* _buttonBox = nullptr;
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___KEYBINDINGS___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -27,7 +27,7 @@
#include <QFrame>
class Line : public QFrame {
class Line final : public QFrame {
public:
Line();
};

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,19 +22,17 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___MARKNODES___H__
#define __OPENSPACE_UI_LAUNCHER___MARKNODES___H__
#ifndef __OPENSPACE_UI_LAUNCHER___MARKNODESDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___MARKNODESDIALOG___H__
#include <QDialog>
namespace openspace { class Profile; }
class QLineEdit;
class QListWidget;
class QListWidgetItem;
class QPushButton;
class MarkNodesDialog : public QDialog {
class MarkNodesDialog final : public QDialog {
Q_OBJECT
public:
/**
@@ -44,7 +42,7 @@ public:
* new or imported profile.
* \param parent Pointer to parent Qt widget
*/
MarkNodesDialog(openspace::Profile& profile, QWidget* parent);
MarkNodesDialog(QWidget* parent, std::vector<std::string>* markedNodes);
/**
* Handles keypress while the Qt dialog window is open
@@ -53,23 +51,18 @@ public:
*/
void keyPressEvent(QKeyEvent* evt) override;
private slots:
void listItemSelected();
private:
void createWidgets();
void listItemAdded();
void listItemRemove();
void parseSelections();
private:
void createWidgets();
std::vector<QListWidgetItem*> _markedNodesListItems;
openspace::Profile& _profile;
std::vector<std::string> _data;
std::vector<std::string>* _markedNodes;
QListWidget* _list = nullptr;
QPushButton* _removeButton = nullptr;
QLineEdit* _newNode = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___MARKNODES___H__
#endif // __OPENSPACE_UI_LAUNCHER___MARKNODESDIALOG___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,35 +22,35 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___META___H__
#define __OPENSPACE_UI_LAUNCHER___META___H__
#ifndef __OPENSPACE_UI_LAUNCHER___METADIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___METADIALOG___H__
#include <QDialog>
namespace openspace { class Profile; }
#include <openspace/scene/profile.h>
#include <optional>
class QLineEdit;
class QTextEdit;
class MetaDialog : public QDialog {
class MetaDialog final : public QDialog {
Q_OBJECT
public:
/**
* Constructor for meta class
* Constructor for meta class.
*
* \param profile The #openspace::Profile object containing all data of the
* new or imported profile.
* \param parent Pointer to parent Qt widget
*/
MetaDialog(openspace::Profile& profile, QWidget* parent);
private slots:
void save();
MetaDialog(QWidget* parent, std::optional<openspace::Profile::Meta>* meta);
private:
void createWidgets();
openspace::Profile& _profile;
void save();
std::optional<openspace::Profile::Meta>* _meta = nullptr;
QLineEdit* _nameEdit = nullptr;
QLineEdit* _versionEdit = nullptr;
@@ -60,4 +60,4 @@ private:
QLineEdit* _licenseEdit = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___META___H__
#endif // __OPENSPACE_UI_LAUNCHER___METADIALOG___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,8 +22,8 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___MODULES___H__
#define __OPENSPACE_UI_LAUNCHER___MODULES___H__
#ifndef __OPENSPACE_UI_LAUNCHER___MODULESDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___MODULESDIALOG___H__
#include <QDialog>
@@ -35,7 +35,7 @@ class QLineEdit;
class QListWidget;
class QPushButton;
class ModulesDialog : public QDialog {
class ModulesDialog final : public QDialog {
Q_OBJECT
public:
/**
@@ -45,7 +45,7 @@ public:
* new or imported profile.
* \param parent Pointer to parent Qt widget
*/
ModulesDialog(openspace::Profile& profile, QWidget* parent);
ModulesDialog(QWidget* parent, std::vector<openspace::Profile::Module>* modules);
/**
* Handles keypress while the Qt dialog window is open
@@ -54,7 +54,9 @@ public:
*/
virtual void keyPressEvent(QKeyEvent* evt) override;
private slots:
private:
void createWidgets();
void listItemSelected();
void listItemAdded();
void listItemRemove();
@@ -63,16 +65,13 @@ private slots:
void transitionToEditMode();
void parseSelections();
private:
void createWidgets();
QString createOneLineSummary(openspace::Profile::Module m);
void transitionFromEditMode();
void editBoxDisabled(bool disabled);
bool isLineEmpty(int index) const;
openspace::Profile& _profile;
std::vector<openspace::Profile::Module> _data;
std::vector<openspace::Profile::Module>* _modules = nullptr;
std::vector<openspace::Profile::Module> _moduleData;
bool _editModeNewItem = false;
QListWidget* _list = nullptr;
@@ -82,7 +81,7 @@ private:
QLineEdit* _loadedEdit = nullptr;
QLabel* _notLoadedLabel = nullptr;
QLineEdit* _notLoadedEdit = nullptr;
QPushButton* _buttonAdd = nullptr;
QPushButton* _buttonRemove = nullptr;
QPushButton* _buttonSave = nullptr;
@@ -92,4 +91,4 @@ private:
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___MODULES___H__
#endif // __OPENSPACE_UI_LAUNCHER___MODULESDIALOG___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -36,7 +36,7 @@ class QLabel;
class QLineEdit;
class QTextEdit;
class ProfileEdit : public QDialog {
class ProfileEdit final : public QDialog {
Q_OBJECT
public:
/**

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,8 +22,8 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___PROPERTIES___H__
#define __OPENSPACE_UI_LAUNCHER___PROPERTIES___H__
#ifndef __OPENSPACE_UI_LAUNCHER___PROPERTIESDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___PROPERTIESDIALOG___H__
#include <QDialog>
@@ -36,7 +36,7 @@ class QLineEdit;
class QListWidget;
class QPushButton;
class PropertiesDialog : public QDialog {
class PropertiesDialog final : public QDialog {
Q_OBJECT
public:
/**
@@ -46,7 +46,8 @@ public:
* new or imported profile.
* \param parent Pointer to parent Qt widget
*/
PropertiesDialog(openspace::Profile& profile, QWidget* parent);
PropertiesDialog(QWidget* parent,
std::vector<openspace::Profile::Property>* properties);
/**
* Handles keypress while the Qt dialog window is open
@@ -55,7 +56,9 @@ public:
*/
virtual void keyPressEvent(QKeyEvent* evt) override;
private slots:
private:
void createWidgets();
void listItemSelected();
void listItemAdded();
void listItemRemove();
@@ -64,8 +67,7 @@ private slots:
void transitionToEditMode();
void parseSelections();
private:
void createWidgets();
void selectLineFromScriptLog();
QString createOneLineSummary(openspace::Profile::Property p);
void transitionFromEditMode();
@@ -73,13 +75,15 @@ private:
bool areRequiredFormsFilled();
bool isLineEmpty(int index);
openspace::Profile& _profile;
std::vector<openspace::Profile::Property> _data;
std::vector<openspace::Profile::Property>* _properties = nullptr;
std::vector<openspace::Profile::Property> _propertyData;
bool _editModeNewItem = false;
QListWidget* _list = nullptr;
QPushButton* _addButton = nullptr;
QPushButton* _removeButton = nullptr;
QPushButton* _fillFromScriptLog = nullptr;
QLabel* _commandLabel = nullptr;
QComboBox* _commandCombo = nullptr;
QLabel* _propertyLabel = nullptr;
@@ -93,4 +97,4 @@ private:
QLabel* _errorMsg = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___PROPERTIES___H__
#endif // __OPENSPACE_UI_LAUNCHER___PROPERTIESDIALOG___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,33 +22,38 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___SCRIPTLOG___H__
#define __OPENSPACE_UI_LAUNCHER___SCRIPTLOG___H__
#ifndef __OPENSPACE_UI_LAUNCHER___SCRIPTLOGDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___SCRIPTLOGDIALOG___H__
#include "profile/keybindingsdialog.h"
#include <QDialog>
#include <QListWidget>
class ScriptlogDialog : public QDialog {
class QLineEdit;
class QListWidget;
class QPushButton;
class ScriptlogDialog final : public QDialog {
Q_OBJECT
public:
/**
* Constructor for ScriptlogDialog class
*
* \param parent Pointer to parent Qt widget
*/
ScriptlogDialog(QWidget* parent);
ScriptlogDialog(QWidget* parent, std::string filter = "");
signals:
void scriptsSelected(std::string script);
private slots:
void saveChosenScripts();
void scriptsSelected(std::vector<std::string> script);
private:
void createWidgets();
void loadScriptFile();
void saveChosenScripts();
void updateScriptList();
QListWidget* _scriptlogList = nullptr;
QLineEdit* _filter = nullptr;
QPushButton* _reloadFile = nullptr;
std::string _scriptLogFile;
std::vector<std::string> _scripts;
std::string _fixedFilter;
};
#endif // __OPENSPACE_UI_LAUNCHER___SCRIPTLOG___H__
#endif // __OPENSPACE_UI_LAUNCHER___SCRIPTLOGDIALOG___H__

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -22,19 +22,20 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___OSTIME___H__
#define __OPENSPACE_UI_LAUNCHER___OSTIME___H__
#ifndef __OPENSPACE_UI_LAUNCHER___TIMEDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___TIMEDIALOG___H__
#include <QDialog>
#include <openspace/scene/profile.h>
class QCheckBox;
class QComboBox;
class QDateTimeEdit;
class QLabel;
class QLineEdit;
class TimeDialog : public QDialog {
class TimeDialog final : public QDialog {
Q_OBJECT
public:
/**
@@ -44,7 +45,7 @@ public:
* new or imported profile.
* \param parent Pointer to parent Qt widget
*/
TimeDialog(openspace::Profile& profile, QWidget* parent);
TimeDialog(QWidget* parent, std::optional<openspace::Profile::Time>* time);
private slots:
void enableAccordingToType(int);
@@ -54,8 +55,8 @@ private:
void createWidgets();
void enableFormatForAbsolute(bool enableAbs);
openspace::Profile& _profile;
openspace::Profile::Time _data;
std::optional<openspace::Profile::Time>* _time = nullptr;
openspace::Profile::Time _timeData;
bool _initializedAsAbsolute = true;
QComboBox* _typeCombo = nullptr;
@@ -63,6 +64,7 @@ private:
QDateTimeEdit* _absoluteEdit = nullptr;
QLabel* _relativeLabel = nullptr;
QLineEdit* _relativeEdit = nullptr;
QCheckBox* _startPaused = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___OSTIME___H__
#endif // __OPENSPACE_UI_LAUNCHER___TIMEDIALOG___H__

View File

@@ -0,0 +1,106 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___DISPLAYWINDOWUNION___H__
#define __OPENSPACE_UI_LAUNCHER___DISPLAYWINDOWUNION___H__
#include <QWidget>
#include <vector>
class QFrame;
class QPushButton;
class QVBoxLayout;
class WindowControl;
class DisplayWindowUnion final : public QWidget {
Q_OBJECT
public:
/**
* Constructor for DisplayWindowUnion class, which manages the overall control layout
* including monitorBox, multiple WindowControl columns, and additional controls
*
* \param monitorSizeList A vector containing QRect objects containing pixel dims
* of each monitor
* \param nMaxWindows The maximum number of windows allowed (depends on the number
* of monitors in the system)
* \param winColors An array of QColor objects for window colors. The indexing of
* this array matches the window indexing used elsewhere in the
* class. This allows for a unique color for each window.
* \param parent The parent to which this widget belongs
*/
DisplayWindowUnion(const std::vector<QRect>& monitorSizeList,
int nMaxWindows, const std::array<QColor, 4>& windowColors,
QWidget* parent = nullptr);
/**
* Returns a vector of pointers to the WindowControl objects for all visible windows.
*
* \return vector of pointers of WindowControl objects
*/
std::vector<WindowControl*> windowControls() const;
/**
* When called will add a new window to the set of windows, which will, in turn, send
* out all of the corresponding signals described below.
*/
void addWindow();
/**
* When called will remove the last window from the set of windows, which will, in
* turn, send out all of the corresponding signals described below.
*/
void removeWindow();
signals:
/**
* This signal is emitted when a windowhas changed.
*
* \param monitorIndex The 0-based index of the monitor to which the window belongs to
* \param windowIndex The 0-based index of the window that was changed
* \param newDimensions The pixel sizes of the window after the change
*/
void windowChanged(int monitorIndex, int windowIndex, const QRectF& newDimensions);
/**
* This signal is emitted when the total number of windows has changed.
*
* \param newCount The new total number of windows
*/
void nWindowsChanged(int newCount);
private:
void createWidgets(int nMaxWindows, std::vector<QRect> monitorResolutions,
std::array<QColor, 4> windowColors);
void showWindows();
unsigned int _nWindowsDisplayed = 0;
std::vector<WindowControl*> _windowControl;
QPushButton* _addWindowButton = nullptr;
QPushButton* _removeWindowButton = nullptr;
std::vector<QFrame*> _frameBorderLines;
};
#endif // __OPENSPACE_UI_LAUNCHER___DISPLAYWINDOWUNION___H__

View File

@@ -0,0 +1,94 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___MONITORBOX___H__
#define __OPENSPACE_UI_LAUNCHER___MONITORBOX___H__
#include <QWidget>
#include <QColor>
#include <array>
#include <vector>
class MonitorBox final : public QWidget {
Q_OBJECT
public:
/**
* Constructor for MonitorBox class, which displays the system's monitor(s),
* their relative position and size, and window(s) that they contain
*
* \param widgetDims The size of the display widget in pixels, stored in QRect
* \param monitorResolution A vector containing the monitor's maximum display
* size in pixels in a QRect object
* \param nWindows The current number of windows that has been selected by the user
* \param winColors An array of QColor objects for window colors. The indexing of
* this array matches the window indexing used elsewhere in the
* class. This allows for a unique color for each window.
* \param parent The parent which to which this MonitorBox belongs
*/
MonitorBox(QRect widgetDims, const std::vector<QRect>& monitorResolutions,
unsigned int nWindows, const std::array<QColor, 4>& windowColors,
QWidget* parent = nullptr);
/**
* Called when window dimensions or monitor location have changed, requiring redraw.
* This will also map the window resolution into the scaled resolution of the display
* widget.
*
* \param mIdx The zero-based monitor index (primary monitor is 0)
* \param wIdx The zero-based window index
* \param newDimensions Dimensions (pixels) of window to be mapped in QRect
*/
void windowDimensionsChanged(unsigned int mIdx, unsigned int wIdx,
const QRectF& newDimensions);
/**
* Called when the number of windows that should be displayed changes.
*
* \param newCount The new number of windows included
*/
void nWindowsDisplayedChanged(int newCount);
protected:
void paintEvent(QPaintEvent* event) override;
private:
std::vector<QSizeF> computeScaledResolutionLandscape(QRectF arrangement,
const std::vector<QRect>& resolutions);
std::vector<QSizeF> computeScaledResolutionPortrait(QRectF arrangement,
const std::vector<QRect>& resolutions);
std::vector<QRectF> _monitorDimensionsScaled;
std::array<QRectF, 4> _windowRendering = {
QRectF(0.f, 0.f, 0.f, 0.f),
QRectF(0.f, 0.f, 0.f, 0.f),
QRectF(0.f, 0.f, 0.f, 0.f),
QRectF(0.f, 0.f, 0.f, 0.f)
};
int _nWindows = 1;
const std::array<QColor, 4> _colorsForWindows;
float _monitorScaleFactor = 1.0;
};
#endif // __OPENSPACE_UI_LAUNCHER___MONITORBOX___H__

View File

@@ -0,0 +1,56 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___ORIENTATIONDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___ORIENTATIONDIALOG___H__
#include <QDialog>
#include <sgct/math.h>
class QLineEdit;
class QWidget;
class OrientationDialog final : public QDialog {
Q_OBJECT
public:
/**
* Constructor for OrientationDialog object which contains the input text boxes for
* orientation x,y,z values
*
* \param orientation x,y,z angles in degrees contained in sgct::quat object
* \param parent pointer to Qt QWidget parent object
*/
OrientationDialog(sgct::quat& orientation, QWidget* parent);
private:
void ok();
QLineEdit* _linePitch = nullptr;
QLineEdit* _lineRoll = nullptr;
QLineEdit* _lineYaw = nullptr;
sgct::quat& _orientationValue;
};
#endif // __OPENSPACE_UI_LAUNCHER___ORIENTATIONDIALOG___H__

View File

@@ -0,0 +1,71 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___SETTINGSWIDGET___H__
#define __OPENSPACE_UI_LAUNCHER___SETTINGSWIDGET___H__
#include <QWidget>
#include <sgct/math.h>
class QCheckBox;
class QLabel;
class SettingsWidget final : public QWidget {
Q_OBJECT
public:
/**
* Constructor for Orientation class, which manages the overall control layout
* including monitorBox, multiple WindowControl columns, and additional controls
*/
SettingsWidget(sgct::quat orientation, QWidget* parent = nullptr);
/**
* Gets the user-provided x,y,z orientation values (degrees)
*
* \return the orientation angles provided in sgct::quat object
*/
sgct::quat orientation() const;
/**
* Gets the value for if VSync is enabled
*
* \return true if the VSync option is checked/enabled
*/
bool vsync() const;
/**
* Gets whether the UI should be restricted to the first window
*
* \return true if the UI should only be on the first window
*/
bool showUiOnFirstWindow() const;
private:
sgct::quat _orientationValue = sgct::quat(0.f, 0.f, 0.f, 0.f);
QCheckBox* _checkBoxVsync = nullptr;
QCheckBox* _showUiOnFirstWindow = nullptr;
};
#endif // __OPENSPACE_UI_LAUNCHER___SETTINGSWIDGET___H__

View File

@@ -0,0 +1,93 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___SGCTEDIT___H__
#define __OPENSPACE_UI_LAUNCHER___SGCTEDIT___H__
#include <QDialog>
#include <sgct/config.h>
#include <QColor>
#include <array>
#include <filesystem>
#include <string>
class DisplayWindowUnion;
class SettingsWidget;
class QBoxLayout;
class QWidget;
class SgctEdit final : public QDialog {
Q_OBJECT
public:
/**
* Constructor for SgctEdit class, the underlying class for the full window
* configuration editor
*
* \param parent The Qt QWidget parent object
* \param userConfigPath A string containing the file path of the user config
* directory where all window configs are stored
*/
SgctEdit(QWidget* parent, std::string userConfigPath);
/**
* Returns the saved filename
*
* \return saved filename in std::string
*/
std::filesystem::path saveFilename() const;
/**
* Returns the generated Cluster object.
*
* \return The generated Cluster object
*/
sgct::config::Cluster cluster() const;
private:
void createWidgets(const std::vector<QRect>& monitorSizes);
sgct::config::Cluster generateConfiguration() const;
void save();
void apply();
DisplayWindowUnion* _displayWidget = nullptr;
SettingsWidget* _settingsWidget = nullptr;
sgct::config::Cluster _cluster;
const std::string _userConfigPath;
const std::array<QColor, 4> _colorsForWindows = {
QColor(0x2B, 0x9E, 0xC3),
QColor(0xFC, 0xAB, 0x10),
QColor(0x44, 0xAF, 0x69),
QColor(0xF8, 0x33, 0x3C)
};
QBoxLayout* _layoutButtonBox = nullptr;
QPushButton* _saveButton = nullptr;
QPushButton* _cancelButton = nullptr;
QPushButton* _applyButton = nullptr;
std::string _saveTarget;
};
#endif // __OPENSPACE_UI_LAUNCHER___SGCTEDIT___H__

View File

@@ -0,0 +1,155 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___WINDOWCONTROL___H__
#define __OPENSPACE_UI_LAUNCHER___WINDOWCONTROL___H__
#include <QWidget>
#include <sgct/config.h>
#include <QIcon>
#include <vector>
class QCheckBox;
class QComboBox;
class QDoubleSpinBox;
class QLabel;
class QLineEdit;
class QSpinBox;
class WindowControl final : public QWidget {
Q_OBJECT
public:
/**
* Constructor for WindowControl class, which contains settings and configuration
* for individual windows
*
* \param monitorIndex The zero-based index for monitor number that this window
* resides in
* \param windowIndex The zero-based window index
* \param monitorDims Vector of monitor dimensions in QRect form
* \param winColor A QColor object for this window's unique color
*/
WindowControl(int monitorIndex, int windowIndex,
const std::vector<QRect>& monitorDims, const QColor& winColor, QWidget* parent);
/**
* Makes the window label at top of a window control column visible
*
* \param bool Shows the window label if true
*/
void showWindowLabel(bool show);
/**
* Resets all controls for this window to default settings
*/
void resetToDefaults();
sgct::config::Window generateWindowInformation() const;
signals:
void windowChanged(int monitorIndex, int windowIndex, const QRectF& newDimensions);
private:
enum class ProjectionIndices {
Planar = 0,
Fisheye,
SphericalMirror,
Cylindrical,
Equirectangular
};
void createWidgets(const QColor& windowColor);
QWidget* createPlanarWidget();
QWidget* createFisheyeWidget();
QWidget* createSphericalMirrorWidget();
QWidget* createCylindricalWidget();
QWidget* createEquirectangularWidget();
void onSizeXChanged(int newValue);
void onSizeYChanged(int newValue);
void onOffsetXChanged(int newValue);
void onOffsetYChanged(int newValue);
void onProjectionChanged(int newSelection);
void onFullscreenClicked();
void onAspectRatioLockClicked();
void onFovLockClicked();
sgct::config::Projections generateProjectionInformation() const;
void updatePlanarLockedFov();
static constexpr float IdealAspectRatio = 16.f / 9.f;
float _aspectRatioSize = IdealAspectRatio;
const int _monitorIndexDefault = 0;
int _windowIndex = 0;
bool _aspectRatioLocked = false;
bool _fovLocked = true;
std::vector<QRect> _monitorResolutions;
QRectF _windowDimensions;
QLabel* _windowNumber = nullptr;
QLineEdit* _windowName = nullptr;
QComboBox* _monitor = nullptr;
QSpinBox* _sizeX = nullptr;
QSpinBox* _sizeY = nullptr;
QSpinBox* _offsetX = nullptr;
QSpinBox* _offsetY = nullptr;
QCheckBox* _windowDecoration = nullptr;
QComboBox* _projectionType = nullptr;
struct {
QWidget* widget = nullptr;
QDoubleSpinBox* fovH = nullptr;
QDoubleSpinBox* fovV = nullptr;
} _planar;
struct {
QWidget* widget = nullptr;
QComboBox* quality = nullptr;
QCheckBox* spoutOutput = nullptr;
} _fisheye;
struct {
QWidget* widget = nullptr;
QComboBox* quality = nullptr;
} _sphericalMirror;
struct {
QWidget* widget = nullptr;
QComboBox* quality = nullptr;
QDoubleSpinBox* heightOffset = nullptr;
} _cylindrical;
struct {
QWidget* widget = nullptr;
QComboBox* quality = nullptr;
QCheckBox* spoutOutput = nullptr;
} _equirectangular;
const QIcon _lockIcon;
const QIcon _unlockIcon;
};
#endif // __OPENSPACE_UI_LAUNCHER___WINDOWCONTROL___H__

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -6,7 +6,7 @@
}
QLabel#heading {
font-size: 12pt;
font-size: 12pt;
}
QLabel#error-message {
@@ -18,12 +18,12 @@ QLabel#error-message {
*/
LauncherWindow QLabel {
font-family: Segoe UI;
font-weight:600;
font-weight: 600;
}
LauncherWindow QLabel#label_choose, QLabel#label_options {
color: #ddd;
font-size:10pt;
color: #dddddd;
font-size: 10pt;
}
LauncherWindow QLabel#clear {
@@ -36,7 +36,7 @@ LauncherWindow QComboBox#config {
border-radius: 3px;
border-color: rgb(225, 225, 225);
padding: 1px 18px 1px 3px;
min-width: 6em;
min-width: 14em;
font-size: 10pt;
font-family: Segoe UI;
font-weight: bold;
@@ -115,15 +115,26 @@ PropertiesDialog QListWidget {
*/
AssetsDialog QTreeView {
min-width: 40em;
min-height: 40em;
}
/*
* Keybindings
* ActionDialog
*/
KeybindingsDialog QListWidget {
ActionDialog QListWidget {
min-width: 40em;
}
ActionDialog QPushButton#add-button, QPushButton#remove-button {
font-weight: bold;
padding: 0px 0px 0px 0px;
min-width: 1.5em;
max-width: 1.5em;
min-height: 1.5em;
max-height: 1.5em;
}
/*
* DeltaTimes
*/
@@ -134,6 +145,45 @@ DeltaTimesDialog QListWidget {
/*
* Camera
*/
CameraDialog QLabel#error-message {
CameraDialog QLabel#error-message {
min-width: 10em;
}
/*
* ScriptlogDialog
*/
ScriptlogDialog QListWidget {
min-width: 60em;
}
/*
* Horizons dialog
*/
HorizonsDialog QPlainTextEdit#log {
font-family: Courier;
}
HorizonsDialog QComboBox#mono {
font-family: Courier;
}
HorizonsDialog QLabel#thin {
font-weight: normal;
}
HorizonsDialog QLabel#error {
color: rgb(221, 17, 17);
}
HorizonsDialog QLabel#normal {
color: rgb(0, 0, 0);
}
/*
* SGCT
*/
WindowControl QLabel#info {
font-style: italic;
font-weight: normal;
font-size: 8pt;
}

View File

@@ -3,5 +3,7 @@
<file>qss/launcher.qss</file>
<file>images/openspace-horiz-logo-small.png</file>
<file>images/launcher-background.png</file>
<file>images/outline_locked.png</file>
<file>images/outline_unlocked.png</file>
</qresource>
</RCC>

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -25,15 +25,14 @@
#include "filesystemaccess.h"
FileSystemAccess::FileSystemAccess(std::string fileExtension,
std::vector<std::string> approvedPaths,
bool hideFileExtensions, bool useCheckboxes)
: _fileExtension(std::move(fileExtension))
, _approvedPaths(std::move(approvedPaths))
, _hideFileExtensions(hideFileExtensions)
, _useCheckboxes(useCheckboxes)
{}
std::string FileSystemAccess::useQtFileSystemModelToTraverseDir(std::string dir, bool userAssets) {
std::string FileSystemAccess::useQtFileSystemModelToTraverseDir(std::string dir,
bool userAssets) {
_filesystemModel.setRootPath(QString::fromStdString(dir));
QModelIndex index = _filesystemModel.index(_filesystemModel.rootPath());
QFileInfo fileInfo = _filesystemModel.fileInfo(index);
@@ -48,8 +47,10 @@ std::string FileSystemAccess::useQtFileSystemModelToTraverseDir(std::string dir,
}
void FileSystemAccess::parseChildDirElements(QFileInfo fileInfo, std::string space,
int level, std::vector<std::string>& dirNames,
std::vector<std::string>& output, bool userAssets)
int level,
std::vector<std::string>& dirNames,
std::vector<std::string>& output,
bool userAssets)
{
QDir dir(fileInfo.filePath());
bool hasDirHeaderBeenAdded = false;
@@ -62,10 +63,8 @@ void FileSystemAccess::parseChildDirElements(QFileInfo fileInfo, std::string spa
res = "${USER_ASSETS}/" + res;
}
if (fi.isDir()) {
if (level != 0 || (level == 0 && (isApprovedPath(res) || userAssets))) {
dirNames.push_back(res);
parseChildDirElements(fi, (space + " "), level + 1, dirNames, output, userAssets);
}
dirNames.push_back(res);
parseChildDirElements(fi, (space + " "), level + 1, dirNames, output, userAssets);
}
else {
parseChildFile(res, hasDirHeaderBeenAdded, dirNames, output);
@@ -77,19 +76,6 @@ void FileSystemAccess::parseChildDirElements(QFileInfo fileInfo, std::string spa
}
}
bool FileSystemAccess::isApprovedPath(std::string path) {
bool approvedMatch = false;
path.erase(0, path.find_first_not_of(" "));
for (const std::string& p : _approvedPaths) {
if (path.substr(0, p.length()).compare(p) == 0) {
approvedMatch = true;
break;
}
}
return approvedMatch;
}
void FileSystemAccess::parseChildFile(std::string filename, bool& hasDirHeaderBeenAdded,
std::vector<std::string>& dirNames,
std::vector<std::string>& output)
@@ -101,7 +87,7 @@ void FileSystemAccess::parseChildFile(std::string filename, bool& hasDirHeaderBe
else {
std::string extension = filename.substr(filename.length()
- _fileExtension.length());
if (extension.compare(_fileExtension) != 0) {
if (extension != _fileExtension) {
return;
}
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -27,54 +27,73 @@
#include "profile/profileedit.h"
#include <openspace/engine/configuration.h>
#include <openspace/openspace.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/fmt.h>
#include <ghoul/logging/logmanager.h>
#include <QComboBox>
#include <QFile>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QStandardItemModel>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <random>
#include <QStandardItemModel>
#include <sgct/readconfig.h>
using namespace openspace;
namespace {
constexpr const int ScreenWidth = 480;
constexpr const int ScreenHeight = 640;
constexpr int ScreenWidth = 480;
constexpr int ScreenHeight = 640;
constexpr const int LeftRuler = 40;
constexpr const int TopRuler = 80;
constexpr const int ItemWidth = 240;
constexpr const int ItemHeight = ItemWidth / 4;
constexpr const int SmallItemWidth = 100;
constexpr const int SmallItemHeight = SmallItemWidth / 4;
constexpr int LeftRuler = 40;
constexpr int TopRuler = 80;
constexpr int ItemWidth = 260;
constexpr int ItemHeight = ItemWidth / 4;
constexpr int SmallItemWidth = 100;
constexpr int SmallItemHeight = SmallItemWidth / 4;
namespace geometry {
constexpr const QRect BackgroundImage(0, 0, ScreenWidth, ScreenHeight);
constexpr const QRect LogoImage(LeftRuler, TopRuler, ItemWidth, ItemHeight);
constexpr const QRect ChooseLabel(LeftRuler, TopRuler + 80, 151, 24);
constexpr const QRect ProfileBox(
LeftRuler, TopRuler + 110, ItemWidth, ItemHeight
constexpr QRect BackgroundImage(0, 0, ScreenWidth, ScreenHeight);
constexpr QRect LogoImage(LeftRuler, TopRuler, ItemWidth, ItemHeight);
constexpr QRect ChooseLabel(LeftRuler, TopRuler + 80, 151, 24);
constexpr QRect ProfileBox(LeftRuler, TopRuler + 110, ItemWidth, ItemHeight);
constexpr QRect NewProfileButton(
LeftRuler + 160, TopRuler + 180, SmallItemWidth, SmallItemHeight
);
constexpr const QRect OptionsLabel(LeftRuler, TopRuler + 180, 151, 24);
constexpr const QRect WindowConfigBox(
LeftRuler, TopRuler + 210, ItemWidth, ItemHeight
constexpr QRect EditProfileButton(
LeftRuler, TopRuler + 180, SmallItemWidth, SmallItemHeight
);
constexpr const QRect StartButton(
LeftRuler, TopRuler + 290, ItemWidth, ItemHeight
constexpr QRect OptionsLabel(LeftRuler, TopRuler + 230, 151, 24);
constexpr QRect WindowConfigBox(LeftRuler, TopRuler + 260, ItemWidth, ItemHeight);
constexpr QRect NewWindowButton(
LeftRuler + 160, TopRuler + 330, SmallItemWidth, SmallItemHeight
);
constexpr const QRect NewButton(
LeftRuler + 140, TopRuler + 380, SmallItemWidth, SmallItemHeight
constexpr QRect EditWindowButton(
LeftRuler, TopRuler + 330, SmallItemWidth, SmallItemHeight
);
constexpr const QRect EditButton(
LeftRuler, TopRuler + 380, SmallItemWidth, SmallItemHeight
constexpr QRect StartButton(
LeftRuler, TopRuler + 400, ItemWidth, ItemHeight
);
} // geometry
std::optional<Profile> loadProfileFromFile(QWidget* parent, std::string filename) {
// Verify that the file actually exists
if (!std::filesystem::exists(filename)) {
QMessageBox::critical(
parent,
"Exception",
QString::fromStdString(fmt::format(
"Could not open profile file '{}'", filename
))
);
return std::nullopt;
}
std::ifstream inFile;
try {
inFile.open(filename, std::ifstream::in);
@@ -132,6 +151,33 @@ namespace {
);
}
}
void saveWindowConfig(QWidget* parent, const std::filesystem::path& path,
sgct::config::Cluster& cluster)
{
std::ofstream outFile;
try {
outFile.open(path, std::ofstream::out);
sgct::config::GeneratorVersion genEntry = sgct::config::GeneratorVersion{
"OpenSpace",
OPENSPACE_VERSION_MAJOR,
OPENSPACE_VERSION_MINOR
};
outFile << sgct::serializeConfig(
cluster,
genEntry
);
}
catch (const std::ofstream::failure& e) {
QMessageBox::critical(
parent,
"Exception",
QString::fromStdString(fmt::format(
"Error writing data to file: {} ({})", path, e.what()
))
);
}
}
} // namespace
using namespace openspace;
@@ -150,6 +196,7 @@ LauncherWindow::LauncherWindow(bool profileEnabled,
absPath(globalConfig.pathTokens.at("USER_PROFILES")).string() + '/'
)
, _readOnlyProfiles(globalConfig.readOnlyProfiles)
, _sgctConfigName(sgctConfigName)
{
Q_INIT_RESOURCE(resources);
@@ -177,7 +224,7 @@ LauncherWindow::LauncherWindow(bool profileEnabled,
populateProfilesList(globalConfig.profile);
_profileBox->setEnabled(profileEnabled);
populateWindowConfigsList(sgctConfigName);
populateWindowConfigsList(_sgctConfigName);
_windowConfigBox->setEnabled(sgctConfigEnabled);
@@ -245,30 +292,41 @@ QWidget* LauncherWindow::createCentralWidget() {
startButton->setGeometry(geometry::StartButton);
startButton->setCursor(Qt::PointingHandCursor);
QPushButton* newButton = new QPushButton("New", centralWidget);
QPushButton* newProfileButton = new QPushButton("New", centralWidget);
connect(
newButton, &QPushButton::released,
newProfileButton, &QPushButton::released,
[this]() {
openProfileEditor("", true);
}
);
newButton->setObjectName("small");
newButton->setGeometry(geometry::NewButton);
newButton->setCursor(Qt::PointingHandCursor);
newProfileButton->setObjectName("small");
newProfileButton->setGeometry(geometry::NewProfileButton);
newProfileButton->setCursor(Qt::PointingHandCursor);
QPushButton* editButton = new QPushButton("Edit", centralWidget);
QPushButton* editProfileButton = new QPushButton("Edit", centralWidget);
connect(
editButton, &QPushButton::released,
editProfileButton, &QPushButton::released,
[this]() {
const std::string selection = _profileBox->currentText().toStdString();
int selectedIndex = _profileBox->currentIndex();
bool isUserProfile = selectedIndex <= _userAssetCount;
bool isUserProfile = selectedIndex < _userAssetCount;
openProfileEditor(selection, isUserProfile);
}
);
editButton->setObjectName("small");
editButton->setGeometry(geometry::EditButton);
editButton->setCursor(Qt::PointingHandCursor);
editProfileButton->setObjectName("small");
editProfileButton->setGeometry(geometry::EditProfileButton);
editProfileButton->setCursor(Qt::PointingHandCursor);
QPushButton* newWindowButton = new QPushButton("New", centralWidget);
connect(
newWindowButton, &QPushButton::released,
[this]() {
openWindowEditor();
}
);
newWindowButton->setObjectName("small");
newWindowButton->setGeometry(geometry::NewWindowButton);
newWindowButton->setCursor(Qt::PointingHandCursor);
return centralWidget;
}
@@ -311,8 +369,24 @@ void LauncherWindow::setBackgroundImage(const std::string& syncPath) {
std::mt19937 g(rd());
std::shuffle(files.begin(), files.end(), g);
// We know there has to be at least one folder, so it's fine to just pick the first
std::string image = files.front();
_backgroundImage->setPixmap(QPixmap(QString::fromStdString(image)));
while (!files.empty()) {
std::string p = files.front();
if (std::filesystem::path(p).extension() == ".png") {
// If the top path starts with the png extension, we have found our candidate
break;
}
else {
// There shouldn't be any non-png images in here, but you never know. So we
// just remove non-image files here
files.erase(files.begin());
}
}
// There better be at least one file left, but just in in case
if (!files.empty()) {
std::string image = files.front();
_backgroundImage->setPixmap(QPixmap(QString::fromStdString(image)));
}
}
void LauncherWindow::populateProfilesList(std::string preset) {
@@ -330,18 +404,41 @@ void LauncherWindow::populateProfilesList(std::string preset) {
}
_profileBox->addItem(QString::fromStdString("--- User Profiles ---"));
const QStandardItemModel* model = qobject_cast<const QStandardItemModel*>(_profileBox->model());
const QStandardItemModel* model = qobject_cast<const QStandardItemModel*>(
_profileBox->model()
);
model->item(_userAssetCount)->setEnabled(false);
++_userAssetCount;
// Add all the files with the .profile extension to the dropdown
std::vector<fs::directory_entry> profiles;
for (const fs::directory_entry& p : fs::directory_iterator(_userProfilePath)) {
if (p.path().extension() != ".profile") {
continue;
}
_profileBox->addItem(QString::fromStdString(p.path().stem().string()));
profiles.push_back(p);
++_userAssetCount;
}
std::sort(profiles.begin(), profiles.end());
for (const fs::directory_entry& profile : profiles) {
std::filesystem::path path = profile.path();
_profileBox->addItem(
QString::fromStdString(path.stem().string()),
QString::fromStdString(path.string())
);
// Add toooltip
std::optional<Profile> p = loadProfileFromFile(this, path.string());
int idx = _profileBox->count() - 1;
if (p.has_value() && (*p).meta.has_value()) {
const std::optional<std::string>& d = (*p).meta.value().description;
if (d.has_value()) {
// Tooltip has to be 'rich text' to linebreak properly
QString tooltip = QString::fromStdString(fmt::format("<p>{}</p>", *d));
_profileBox->setItemData(idx, tooltip, Qt::ToolTipRole);
}
}
}
_profileBox->addItem(QString::fromStdString("--- OpenSpace Profiles ---"));
model = qobject_cast<const QStandardItemModel*>(_profileBox->model());
@@ -349,18 +446,70 @@ void LauncherWindow::populateProfilesList(std::string preset) {
++_userAssetCount;
// Add all the files with the .profile extension to the dropdown
for (const fs::directory_entry& p : fs::directory_iterator(_profilePath)) {
if (p.path().extension() != ".profile") {
profiles.clear();
for (const fs::directory_entry& path : fs::directory_iterator(_profilePath)) {
if (path.path().extension() != ".profile") {
continue;
}
_profileBox->addItem(QString::fromStdString(p.path().stem().string()));
profiles.push_back(path);
}
std::sort(profiles.begin(), profiles.end());
// Add sorted items to list
for (const fs::directory_entry& profile : profiles) {
std::filesystem::path path = profile.path();
_profileBox->addItem(
QString::fromStdString(path.stem().string()),
QString::fromStdString(path.string())
);
// Add toooltip
std::optional<Profile> p = loadProfileFromFile(this, path.string());
int idx = _profileBox->count() - 1;
if (p.has_value() && (*p).meta.has_value()) {
const std::optional<std::string>& d = (*p).meta.value().description;
if (d.has_value()) {
// Tooltip has to be 'rich text' to linebreak properly
QString tooltip = QString::fromStdString(fmt::format("<p>{}</p>", *d));
_profileBox->setItemData(idx, tooltip, Qt::ToolTipRole);
}
}
}
// Try to find the requested profile and set it as the current one
const int idx = _profileBox->findText(QString::fromStdString(std::move(preset)));
if (idx != -1) {
_profileBox->setCurrentIndex(idx);
int idx = _profileBox->findText(QString::fromStdString(preset));
if (idx == -1) {
// We didn't find the preset, so the user probably specified a path in the
// configuration file that doesn't match any value in the list
_profileBox->addItem(QString::fromStdString("--- Configuration File ---"));
model = qobject_cast<const QStandardItemModel*>(_profileBox->model());
model->item(_profileBox->count() - 1)->setEnabled(false);
_profileBox->addItem(
QString::fromStdString(preset),
QString::fromStdString(preset)
);
idx = _profileBox->count() - 1;
}
_profileBox->setCurrentIndex(idx);
}
// Returns 'true' if the file was a configuration file, 'false' otherwise
bool handleConfigurationFile(QComboBox& box, const std::filesystem::directory_entry& p) {
const bool isXml = p.path().extension() == ".xml";
const bool isJson = p.path().extension() == ".json";
if (!isXml && !isJson) {
return false;
}
box.addItem(QString::fromStdString(p.path().filename().string()));
// For now, mark the XML configuration files to show that they are deprecated
if (isXml) {
box.setItemData(box.count() - 1, QBrush(Qt::darkYellow), Qt::ForegroundRole);
}
return true;
}
void LauncherWindow::populateWindowConfigsList(std::string preset) {
@@ -370,28 +519,45 @@ void LauncherWindow::populateWindowConfigsList(std::string preset) {
_userConfigCount = 0;
_windowConfigBox->addItem(QString::fromStdString("--- User Configurations ---"));
const QStandardItemModel* model = qobject_cast<const QStandardItemModel*>(_windowConfigBox->model());
const QStandardItemModel* model =
qobject_cast<const QStandardItemModel*>(_windowConfigBox->model());
model->item(_userConfigCount)->setEnabled(false);
++_userConfigCount;
// Add all the files with the .xml extension to the dropdown
bool hasXmlConfig = false;
// Sort files
std::vector<fs::directory_entry> files;
for (const fs::directory_entry& p : fs::directory_iterator(_userConfigPath)) {
if (p.path().extension() != ".xml") {
continue;
files.push_back(p);
}
std::sort(files.begin(), files.end());
// Add all the files with the .xml or .json extension to the dropdown
for (const fs::directory_entry& p : files) {
bool isConfigFile = handleConfigurationFile(*_windowConfigBox, p);
if (isConfigFile) {
++_userConfigCount;
}
_windowConfigBox->addItem(QString::fromStdString(p.path().stem().string()));
++_userConfigCount;
hasXmlConfig |= p.path().extension() == ".xml";
}
_windowConfigBox->addItem(QString::fromStdString("--- OpenSpace Configurations ---"));
model = qobject_cast<const QStandardItemModel*>(_windowConfigBox->model());
model->item(_userConfigCount)->setEnabled(false);
if (std::filesystem::exists(_configPath)) {
// Add all the files with the .xml extension to the dropdown
// Sort files
files.clear();
for (const fs::directory_entry& p : fs::directory_iterator(_configPath)) {
if (p.path().extension() != ".xml") {
continue;
}
_windowConfigBox->addItem(QString::fromStdString(p.path().stem().string()));
files.push_back(p);
}
std::sort(files.begin(), files.end());
// Add all the files with the .xml or .json extension to the dropdown
for (const fs::directory_entry& p : files) {
handleConfigurationFile(*_windowConfigBox, p);
hasXmlConfig |= p.path().extension() == ".xml";
}
}
else {
@@ -401,6 +567,19 @@ void LauncherWindow::populateWindowConfigsList(std::string preset) {
);
}
if (hasXmlConfig) {
// At least one XML configuration file is present, so we should show the tooltip
// informing the user that files will be deprecated
_windowConfigBox->setToolTip(
"Support for XML-based configuration files will be removed in the next "
"version of OpenSpace. Please convert the files to the new JSON format or "
"run the Node tool at "
"https://github.com/sgct/sgct/tree/master/support/config-converter"
);
}
// Always add the .cfg sgct default as first item
_windowConfigBox->insertItem(0, QString::fromStdString(_sgctConfigName));
// Try to find the requested configuration file and set it as the current one. As we
// have support for function-generated configuration files that will not be in the
// list we need to add a preset that doesn't exist a file for
@@ -410,22 +589,23 @@ void LauncherWindow::populateWindowConfigsList(std::string preset) {
}
else {
// Add the requested preset at the top
_windowConfigBox->insertItem(0, QString::fromStdString(preset));
_windowConfigBox->setCurrentIndex(0);
_windowConfigBox->insertItem(1, QString::fromStdString(preset));
// Increment the user config count because there is an additional option added
// before the user config options
_userConfigCount++;
_windowConfigBox->setCurrentIndex(1);
}
}
void LauncherWindow::openProfileEditor(const std::string& profile, const bool isUserProfile) {
void LauncherWindow::openProfileEditor(const std::string& profile, bool isUserProfile) {
std::optional<Profile> p;
std::string saveProfilePath = isUserProfile ? _userProfilePath : _profilePath;
if (profile.empty()) {
// If the requested profile is the empty string, then we want to create a new one
p = Profile();
}
else {
// Otherwise, we want to load that profile
std::string fullProfilePath = saveProfilePath + profile + ".profile";
p = loadProfileFromFile(this, fullProfilePath);
if (!p.has_value()) {
@@ -433,13 +613,21 @@ void LauncherWindow::openProfileEditor(const std::string& profile, const bool is
}
}
ProfileEdit editor(*p, profile, _assetPath, _userAssetPath, saveProfilePath, _readOnlyProfiles, this);
ProfileEdit editor(
*p,
profile,
_assetPath,
_userAssetPath,
saveProfilePath,
_readOnlyProfiles,
this
);
editor.exec();
if (editor.wasSaved()) {
if (editor.specifiedFilename() != profile) {
saveProfilePath = _userProfilePath;
}
const std::string path = saveProfilePath + editor.specifiedFilename() + ".profile";
std::string path = saveProfilePath + editor.specifiedFilename() + ".profile";
saveProfile(this, path, *p);
populateProfilesList(editor.specifiedFilename());
}
@@ -449,16 +637,35 @@ void LauncherWindow::openProfileEditor(const std::string& profile, const bool is
}
}
void LauncherWindow::openWindowEditor() {
SgctEdit editor(this, _userConfigPath);
int ret = editor.exec();
if (ret == QDialog::DialogCode::Accepted) {
sgct::config::Cluster cluster = editor.cluster();
std::filesystem::path savePath = editor.saveFilename();
saveWindowConfig(this, savePath, cluster);
// Truncate path to convert this back to path relative to _userConfigPath
std::string p = savePath.string().substr(_userConfigPath.size());
populateWindowConfigsList(p);
}
}
bool LauncherWindow::wasLaunchSelected() const {
return _shouldLaunch;
}
std::string LauncherWindow::selectedProfile() const {
return _profileBox->currentText().toStdString();
// The user data stores the full path to the profile
return _profileBox->currentData().toString().toStdString();
}
std::string LauncherWindow::selectedWindowConfig() const {
if (_windowConfigBox->currentIndex() > _userAssetCount) {
int idx = _windowConfigBox->currentIndex();
if (idx == 0) {
return _sgctConfigName;
}
else if (idx > _userConfigCount) {
return "${CONFIG}/" + _windowConfigBox->currentText().toStdString();
}
else {

View File

@@ -0,0 +1,842 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "profile/actiondialog.h"
#include "profile/line.h"
#include "profile/scriptlogdialog.h"
#include <openspace/util/keys.h>
#include <ghoul/fmt.h>
#include <ghoul/misc/assert.h>
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QMessageBox>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
using namespace openspace;
namespace {
void updateListItem(QListWidgetItem* item, const Profile::Action& action) {
ghoul_assert(item, "Item must exist at this point");
item->setText(
action.name.empty() ?
QString::fromStdString(action.identifier) :
QString::fromStdString(action.name)
);
}
void updateListItem(QListWidgetItem* item, const Profile::Keybinding& kb) {
ghoul_assert(item, "Item must exist at this point");
std::string name = fmt::format("{}\t{}", ghoul::to_string(kb.key), kb.action);
item->setText(QString::fromStdString(name));
}
} // namespace
ActionDialog::ActionDialog(QWidget* parent,
std::vector<openspace::Profile::Action>* actions,
std::vector<openspace::Profile::Keybinding>* keybindings)
: QDialog(parent)
, _actions(actions)
, _actionData(*_actions)
, _keybindings(keybindings)
, _keybindingsData(*_keybindings)
{
setWindowTitle("Actions and Keybindings");
createWidgets();
}
void ActionDialog::createWidgets() {
// Column 0 Column 1 Column 2 Col3
// *----------------------*---------------*----------|------*
// | Actions | Row 0
// | | Identifier | [oooooo] | Info | Row 1
// | | Name | [ooooooooooooo] | Row 2
// | | GUI Path | [ooooooooooooo] | Row 3
// | | Documentation | [ooooooooooooo] | Row 4
// | | Is Local | [] [choosescr] | Row 5
// | | Script | [ooooooooooooo] | Row 6
// *----------------------*---------------*-----------------*
// | [+] [-] | | <Save> <Cancel> | Row 7
// *----------------------*---------------*-----------------*
// |========================================================| Row 8
// | Keybindings | Row 9
// *----------------------*---------------*-----------------|
// | | Modifier | []S []C []A | Row 10
// | | Key | DDDDDDDDDDDD> | Row 11
// | | Add actions | DDDDDDDDDDDD> | Row 12
// | | Action | [ooooooooooooo] | Row 13
// *----------------------*---------------*-----------------*
// | [+] [-] | | <Save> <Cancel> | Row 14
// *----------------------*---------------*-----------------*
// |========================================================| Row 16
// *----------------------*---------------*-----------------*
// | | <Save> <Cancel> | Row 17
// *----------------------*---------------*-----------------*
QGridLayout* layout = new QGridLayout(this);
createActionWidgets(layout);
clearActionFields();
layout->addWidget(new Line, 8, 0, 1, 4);
createKeyboardWidgets(layout);
clearKeybindingFields();
layout->addWidget(new Line, 16, 0, 1, 4);
_mainButton = new QDialogButtonBox;
_mainButton->setStandardButtons(QDialogButtonBox::Close);
QObject::connect(
_mainButton, &QDialogButtonBox::rejected,
this, &ActionDialog::reject
);
layout->addWidget(_mainButton, 17, 2, Qt::AlignRight);
}
void ActionDialog::createActionWidgets(QGridLayout* layout) {
QLabel* title = new QLabel("Actions");
title->setObjectName("heading");
layout->addWidget(title, 0, 0, 1, 4);
_actionWidgets.list = new QListWidget;
_actionWidgets.list->setToolTip(
"The list of all actions currently defined in the profile. Select one to edit it "
"or use the + button below to create a new action"
);
_actionWidgets.list->setAlternatingRowColors(true);
_actionWidgets.list->setResizeMode(QListView::Adjust);
connect(
_actionWidgets.list, &QListWidget::itemSelectionChanged,
this, &ActionDialog::actionSelected
);
for (size_t i = 0; i < _actionData.size(); ++i) {
const Profile::Action& action = _actionData[i];
std::string name = action.name.empty() ? action.identifier : action.name;
_actionWidgets.list->addItem(new QListWidgetItem(QString::fromStdString(name)));
}
layout->addWidget(_actionWidgets.list, 1, 0, 6, 1);
layout->addWidget(new QLabel("Identifier"), 1, 1);
_actionWidgets.identifier = new QLineEdit;
_actionWidgets.identifier->setToolTip(
"The unique identifier for this action. The identifier name cannot be reused "
"between different actions and will lead to a failure to load the profile if it "
"happens. There are no restrictions on the name of the identifier, but a dot "
"separated hierarchical structure is suggested to prevent name clashes"
);
_actionWidgets.identifier->setEnabled(false);
connect(
_actionWidgets.identifier, &QLineEdit::textEdited,
[this]() {
// Check if the identifier is legal
std::string identifier = _actionWidgets.identifier->text().toStdString();
bool isLegal = identifier.find_first_of("\t\n. ") == std::string::npos;
if (isLegal) {
_actionWidgets.infoText->clear();
_actionWidgets.infoText->setHidden(true);
}
else {
_actionWidgets.infoText->setText(
"Identifier must not contain whitespace or ."
);
_actionWidgets.infoText->setHidden(false);
}
}
);
layout->addWidget(_actionWidgets.identifier, 1, 2);
_actionWidgets.infoText = new QLabel;
_actionWidgets.infoText->setHidden(true);
_actionWidgets.infoText->setObjectName("error-message");
layout->addWidget(_actionWidgets.infoText, 1, 3);
layout->addWidget(new QLabel("Name"), 2, 1);
_actionWidgets.name = new QLineEdit;
_actionWidgets.name->setToolTip(
"The user-facing name of this action. As it is displayed in user interfaces, the "
"name should be as concise and informative as possible"
);
_actionWidgets.name->setEnabled(false);
layout->addWidget(_actionWidgets.name, 2, 2, 1, 2);
layout->addWidget(new QLabel("GUI Path"), 3, 1);
_actionWidgets.guiPath = new QLineEdit;
_actionWidgets.guiPath->setToolTip(
"The path under which this action will be shown in user interfaces. The path "
"must use the '/' character as separators between folders and start with a '/' "
"character that denotes the root folder"
);
_actionWidgets.guiPath->setEnabled(false);
layout->addWidget(_actionWidgets.guiPath, 3, 2, 1, 2);
layout->addWidget(new QLabel("Documentation"), 4, 1);
_actionWidgets.documentation = new QLineEdit;
_actionWidgets.documentation->setToolTip(
"A longer user-facing documentation that describes the action in more detail. "
"The user can request the documentation on demand, so it might be longer and "
"more descriptive than the name itself and might also explain some optional "
"parameters that that action can consume"
);
_actionWidgets.documentation->setEnabled(false);
layout->addWidget(_actionWidgets.documentation, 4, 2, 1, 2);
layout->addWidget(new QLabel("Is Local"), 5, 1);
_actionWidgets.isLocal = new QCheckBox;
_actionWidgets.isLocal->setToolTip(
"If this value is checked, the action will only ever affect the OpenSpace "
"instance that is executing it. If running a 'regular' OpenSpace instance, this "
"setting will not make any difference, but it is necessary in a clustered "
"environment or when using a parallel connection, in which case it determines "
"whether a command should be executed only locally or send to all remote "
"instances as well"
);
_actionWidgets.isLocal->setEnabled(false);
layout->addWidget(_actionWidgets.isLocal, 5, 2, 1, 2);
_actionWidgets.chooseScripts = new QPushButton("Choose Scripts");
_actionWidgets.chooseScripts->setToolTip(
"Press this button to choose scripts for your action from the logs/scriptlog.txt"
);
connect(
_actionWidgets.chooseScripts, &QPushButton::clicked,
this, &ActionDialog::chooseScripts
);
_actionWidgets.chooseScripts->setEnabled(false);
layout->addWidget(_actionWidgets.chooseScripts, 5, 2, 1, 2, Qt::AlignRight);
layout->addWidget(new QLabel("Script"), 6, 1);
_actionWidgets.script = new QTextEdit;
_actionWidgets.script->setToolTip(
"This is the Lua script that gets executed when this action is triggered. "
"Actions can make use of optional arguments which are already defined as the "
"`args` variable when this script executes. If no arguments are passed, this "
"variable does not exist"
);
_actionWidgets.script->setEnabled(false);
layout->addWidget(_actionWidgets.script, 6, 2, 1, 2);
// + / - buttons
QWidget* container = new QWidget;
QBoxLayout* containerLayout = new QHBoxLayout(container);
_actionWidgets.addButton = new QPushButton("+");
_actionWidgets.addButton->setObjectName("add-button");
_actionWidgets.addButton->setToolTip("Adds a new action to the list of all actions");
QObject::connect(
_actionWidgets.addButton, &QPushButton::clicked,
this, &ActionDialog::actionAdd
);
containerLayout->addWidget(_actionWidgets.addButton);
_actionWidgets.removeButton = new QPushButton("-");
_actionWidgets.removeButton->setObjectName("remove-button");
_actionWidgets.removeButton->setToolTip("Removes the currently selected action");
_actionWidgets.removeButton->setEnabled(false);
QObject::connect(
_actionWidgets.removeButton, &QPushButton::clicked,
this, &ActionDialog::actionRemove
);
containerLayout->addWidget(_actionWidgets.removeButton);
layout->addWidget(container, 7, 0, Qt::AlignLeft);
// Save / Cancel buttons
_actionWidgets.saveButtons = new QDialogButtonBox;
_actionWidgets.saveButtons->setToolTip(
"Saves or discards all changes to the currently selected action"
);
_actionWidgets.saveButtons->setEnabled(false);
_actionWidgets.saveButtons->setStandardButtons(
QDialogButtonBox::Save | QDialogButtonBox::Cancel
);
QObject::connect(
_actionWidgets.saveButtons, &QDialogButtonBox::accepted,
this, &ActionDialog::actionSaved
);
QObject::connect(
_actionWidgets.saveButtons, &QDialogButtonBox::rejected,
this, &ActionDialog::actionRejected
);
layout->addWidget(_actionWidgets.saveButtons, 7, 2, 1, 2, Qt::AlignRight);
}
void ActionDialog::createKeyboardWidgets(QGridLayout* layout) {
QLabel* title = new QLabel("Keybindings");
title->setObjectName("heading");
layout->addWidget(title, 9, 0, 1, 4);
_keybindingWidgets.list = new QListWidget;
_keybindingWidgets.list->setToolTip(
"The list of all keybindings currently assigned in this profile"
);
_keybindingWidgets.list->setAlternatingRowColors(true);
_keybindingWidgets.list->setResizeMode(QListView::Adjust);
connect(
_keybindingWidgets.list, &QListWidget::itemSelectionChanged,
this, &ActionDialog::keybindingSelected
);
for (size_t i = 0; i < _keybindingsData.size(); ++i) {
const Profile::Keybinding& kv = _keybindingsData[i];
QListWidgetItem* item = new QListWidgetItem;
updateListItem(item, kv);
_keybindingWidgets.list->addItem(item);
}
layout->addWidget(_keybindingWidgets.list, 10, 0, 4, 1);
layout->addWidget(new QLabel("Modifier"), 10, 1);
{
QWidget* container = new QWidget;
QBoxLayout* containerLayout = new QHBoxLayout(container);
_keybindingWidgets.shiftModifier = new QCheckBox("Shift");
_keybindingWidgets.shiftModifier->setEnabled(false);
containerLayout->addWidget(_keybindingWidgets.shiftModifier);
_keybindingWidgets.ctrlModifier = new QCheckBox("Control");
_keybindingWidgets.ctrlModifier->setEnabled(false);
containerLayout->addWidget(_keybindingWidgets.ctrlModifier);
_keybindingWidgets.altModifier = new QCheckBox("Alt");
_keybindingWidgets.altModifier->setEnabled(false);
containerLayout->addWidget(_keybindingWidgets.altModifier);
layout->addWidget(container, 10, 2, 1, 2);
}
layout->addWidget(new QLabel("Key"), 11, 1);
_keybindingWidgets.key = new QComboBox;
QStringList keyList;
for (const KeyInfo& ki : KeyInfos) {
// We don't want to use the Shift, Alt, and Ctrl keys directly since we already
// have them as modifier keys
if (ki.key == Key::LeftShift || ki.key == Key::RightShift ||
ki.key == Key::LeftAlt || ki.key == Key::RightAlt ||
ki.key == Key::LeftControl || ki.key == Key::RightControl ||
ki.key == Key::LeftSuper || ki.key == Key::RightSuper ||
ki.key == Key::Menu || ki.key == Key::NumLock ||
ki.key == Key::World1 || ki.key == Key::World2)
{
continue;
}
keyList += QString::fromStdString(std::string(ki.name));
}
_keybindingWidgets.key->addItems(keyList);
_keybindingWidgets.key->setCurrentIndex(-1);
_keybindingWidgets.key->setEnabled(false);
connect(
_keybindingWidgets.key, QOverload<int>::of(&QComboBox::currentIndexChanged),
[this](int newIndex) {
_keybindingWidgets.saveButtons->button(QDialogButtonBox::Save)->setEnabled(
newIndex > 0
);
}
);
layout->addWidget(_keybindingWidgets.key, 11, 2, 1, 2);
layout->addWidget(new QLabel("Action chooser"), 12, 1);
_keybindingWidgets.action = new QComboBox;
_keybindingWidgets.action->setToolTip(
"You can select any of the actions defined above here to be associated with the "
"selected keybind. Selecting an action from this dropdown menu will "
"automatically enter it into the text field below and overwrite any value that "
"already is entered in there"
);
for (const Profile::Action& action : _actionData) {
_keybindingWidgets.action->addItem(QString::fromStdString(action.identifier));
}
connect(
_keybindingWidgets.action, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &ActionDialog::keybindingActionSelected
);
_keybindingWidgets.action->setEnabled(false);
layout->addWidget(_keybindingWidgets.action, 12, 2, 1, 2);
layout->addWidget(new QLabel("Action"), 13, 1);
_keybindingWidgets.actionText = new QLineEdit;
_keybindingWidgets.actionText->setToolTip(
"This is the action that will be triggered when the keybind is pressed. In the "
"majority of cases, you do not need to enter something here manually, but "
"instead select the action from the dropdown list above. However, if you know "
"that an action with a specific identifier will exist at runtime, for example if "
"it is defined in an asset included in this profile, you can enter the "
"identifier of that action manually here to associate a key with it. If the "
"identifer does not exist, an error will be logged when trying to bind the key "
"at startup"
);
_keybindingWidgets.actionText->setEnabled(false);
layout->addWidget(_keybindingWidgets.actionText, 13, 2, 1, 2);
// +/- buttons
QWidget* container = new QWidget;
QBoxLayout* containerLayout = new QHBoxLayout(container);
_keybindingWidgets.addButton = new QPushButton("+");
_keybindingWidgets.addButton->setObjectName("add-button");
_keybindingWidgets.addButton->setToolTip(
"Adds a new keybinding to the list of all keybindings"
);
QObject::connect(
_keybindingWidgets.addButton, &QPushButton::clicked,
this, &ActionDialog::keybindingAdd
);
containerLayout->addWidget(_keybindingWidgets.addButton);
_keybindingWidgets.removeButton = new QPushButton("-");
_keybindingWidgets.removeButton->setObjectName("remove-button");
_keybindingWidgets.removeButton->setToolTip(
"Removes the currently selected keybinding"
);
_keybindingWidgets.removeButton->setEnabled(false);
QObject::connect(
_keybindingWidgets.removeButton, &QPushButton::clicked,
this, &ActionDialog::keybindingRemove
);
containerLayout->addWidget(_keybindingWidgets.removeButton);
layout->addWidget(container, 14, 0, Qt::AlignLeft);
// Save/Cancel
_keybindingWidgets.saveButtons = new QDialogButtonBox;
_keybindingWidgets.saveButtons->setToolTip(
"Saves or discards all changes to the currently selected keybinding"
);
_keybindingWidgets.saveButtons->setEnabled(false);
_keybindingWidgets.saveButtons->setStandardButtons(
QDialogButtonBox::Save | QDialogButtonBox::Cancel
);
QObject::connect(
_keybindingWidgets.saveButtons, &QDialogButtonBox::accepted,
this, &ActionDialog::keybindingSaved
);
QObject::connect(
_keybindingWidgets.saveButtons, &QDialogButtonBox::rejected,
this, &ActionDialog::keybindingRejected
);
layout->addWidget(_keybindingWidgets.saveButtons, 14, 2, 1, 2, Qt::AlignRight);
}
Profile::Action* ActionDialog::selectedAction() {
QListWidgetItem* item = _actionWidgets.list->currentItem();
const int idx = _actionWidgets.list->row(item);
return idx != -1 ? &_actionData[idx] : nullptr;
}
void ActionDialog::actionAdd() {
_actionWidgets.list->addItem("");
_actionData.push_back(Profile::Action());
_actionWidgets.list->setCurrentRow(_actionWidgets.list->count() - 1);
}
void ActionDialog::actionRemove() {
const openspace::Profile::Action* action = selectedAction();
ghoul_assert(action, "An action must exist at this point");
ghoul_assert(
_actionWidgets.list->count() == static_cast<int>(_actionData.size()),
"Action list and data has desynced"
);
// We can't remove an action if it has a keyboard shortcut attached to it
for (size_t i = 0; i < _keybindingsData.size(); ++i) {
const Profile::Keybinding& kb = _keybindingsData[i];
if (kb.action != action->identifier) {
continue;
}
QMessageBox::StandardButton button = QMessageBox::information(
this,
"Remove action",
QString::fromStdString(fmt::format(
"Action '{}' is used in the keybind '{}' and cannot be removed unless "
"the keybind is removed as well. Do you want to remove the keybind as "
"well?",
action->identifier, ghoul::to_string(kb.key)
)),
QMessageBox::StandardButton::Yes,
QMessageBox::StandardButton::No
);
if (button == QMessageBox::StandardButton::Yes) {
_keybindingsData.erase(_keybindingsData.begin() + i);
delete _keybindingWidgets.list->takeItem(static_cast<int>(i));
i--;
//Save the updated keybindings to the profile
if (_keybindings) {
*_keybindings = _keybindingsData;
}
}
else {
// If the user chooses 'No' at least once, we have to bail
return;
}
}
for (size_t i = 0; i < _actionData.size(); ++i) {
if (_actionData[i].identifier == action->identifier) {
clearActionFields();
_actionData.erase(_actionData.begin() + i);
delete _actionWidgets.list->takeItem(static_cast<int>(i));
_keybindingWidgets.action->clear();
for (const Profile::Action& a : _actionData) {
_keybindingWidgets.action->addItem(QString::fromStdString(a.identifier));
}
clearKeybindingFields();
//Save the updated actions to the profile
if (_actions) {
*_actions = _actionData;
}
return;
}
}
ghoul_assert(false, "We shouldn't be able to get here");
}
void ActionDialog::actionSelected() {
const Profile::Action* action = selectedAction();
if (action) {
// Action selected
_actionWidgets.identifier->setText(QString::fromStdString(action->identifier));
_actionWidgets.identifier->setEnabled(true);
_actionWidgets.name->setText(QString::fromStdString(action->name));
_actionWidgets.name->setEnabled(true);
_actionWidgets.guiPath->setText(QString::fromStdString(action->guiPath));
_actionWidgets.guiPath->setEnabled(true);
_actionWidgets.documentation->setText(
QString::fromStdString(action->documentation)
);
_actionWidgets.documentation->setEnabled(true);
_actionWidgets.isLocal->setChecked(action->isLocal);
_actionWidgets.isLocal->setEnabled(true);
_actionWidgets.chooseScripts->setEnabled(true);
_actionWidgets.script->setText(QString::fromStdString(action->script));
_actionWidgets.script->setEnabled(true);
_actionWidgets.addButton->setEnabled(false);
_actionWidgets.removeButton->setEnabled(true);
_actionWidgets.saveButtons->setEnabled(true);
if (_mainButton) {
_mainButton->setEnabled(false);
}
_actionWidgets.list->setEnabled(false);
}
else {
// No action selected
_actionWidgets.addButton->setEnabled(true);
_actionWidgets.removeButton->setEnabled(false);
_actionWidgets.saveButtons->setEnabled(false);
//Keybinding panel must also be in valid state to re-enable main start button
if (_mainButton && !_keybindingWidgets.saveButtons->isEnabled()) {
_mainButton->setEnabled(true);
}
_actionWidgets.list->setEnabled(true);
}
}
void ActionDialog::actionSaved() {
std::string newIdentifier = _actionWidgets.identifier->text().toStdString();
if (newIdentifier.empty()) {
QMessageBox::critical(this, "Empty identifier", "Identifier must not be empty");
return;
}
Profile::Action* action = selectedAction();
std::string oldIdentifier = action->identifier;
if (oldIdentifier != newIdentifier) {
// The identifier is a bit special as we need to make sure that we didn't
// accidentally create a duplicate while renaming the currently selected action.
// Also if we didn't create a duplicate, meaning that we renamed an action to a
// new valid identifier, we need to make sure that we update all keybinds that
// referenced the old value are updated to use the new name instead
const auto it = std::find_if(
_actionData.begin(), _actionData.end(),
[id = newIdentifier](const Profile::Action& a) { return a.identifier == id; }
);
if (it != _actionData.end()) {
QMessageBox::critical(
this,
"Duplicate identifier",
"The chosen identifier was already used in another action. Identifiers "
"have to be unique. Please choose a different identfier"
);
return;
}
// If we got this far, we have a new identifier and it is a new one, so we need to
// update other keybinds now
ghoul_assert(
_keybindingWidgets.list->count() == static_cast<int>(_keybindingsData.size()),
"The list and data got out of sync"
);
for (int i = 0; i < _keybindingWidgets.list->count(); ++i) {
if (_keybindingsData[i].action == oldIdentifier) {
_keybindingsData[i].action = newIdentifier;
updateListItem(_keybindingWidgets.list->item(i), _keybindingsData[i]);
}
}
for (int i = 0; i < _keybindingWidgets.action->count(); ++i) {
if (_keybindingWidgets.action->itemText(i).toStdString() == oldIdentifier) {
_keybindingWidgets.action->setItemText(
i,
QString::fromStdString(newIdentifier)
);
}
}
action->identifier = newIdentifier;
}
action->name = _actionWidgets.name->text().toStdString();
std::string guiPath = _actionWidgets.guiPath->text().toStdString();
if (!guiPath.starts_with('/')) {
guiPath = "/" + guiPath;
}
action->guiPath = guiPath;
action->documentation = _actionWidgets.documentation->text().toStdString();
action->isLocal = _actionWidgets.isLocal->isChecked();
action->script = _actionWidgets.script->toPlainText().toStdString();
updateListItem(_actionWidgets.list->currentItem(), *action);
// Update the list of actions available in the action chooser
_keybindingWidgets.action->clear();
for (const Profile::Action& a : _actionData) {
_keybindingWidgets.action->addItem(QString::fromStdString(a.identifier));
}
//Save the updated actions to the profile
if (_actions) {
*_actions = _actionData;
}
clearKeybindingFields();
clearActionFields();
}
void ActionDialog::clearActionFields() {
_actionWidgets.list->setCurrentRow(-1);
_actionWidgets.identifier->clear();
_actionWidgets.identifier->setEnabled(false);
_actionWidgets.infoText->clear();
_actionWidgets.infoText->setHidden(true);
_actionWidgets.name->clear();
_actionWidgets.name->setEnabled(false);
_actionWidgets.guiPath->clear();
_actionWidgets.guiPath->setEnabled(false);
_actionWidgets.documentation->clear();
_actionWidgets.documentation->setEnabled(false);
_actionWidgets.isLocal->setChecked(false);
_actionWidgets.isLocal->setEnabled(false);
_actionWidgets.chooseScripts->setEnabled(false);
_actionWidgets.script->clear();
_actionWidgets.script->setEnabled(false);
_actionWidgets.saveButtons->setEnabled(false);
_actionWidgets.list->setEnabled(true);
}
void ActionDialog::actionRejected() {
if (_actionData.back().identifier.empty()) {
// This happens if someone creates a new action and never gave an identifier
delete _actionWidgets.list->takeItem(_actionWidgets.list->count() - 1);
_actionData.erase(_actionData.begin() + _actionData.size() - 1);
}
clearActionFields();
}
void ActionDialog::chooseScripts() {
ScriptlogDialog d(this);
connect(
&d, &ScriptlogDialog::scriptsSelected,
this, &ActionDialog::appendScriptsToTextfield
);
d.exec();
}
void ActionDialog::appendScriptsToTextfield(std::vector<std::string> scripts) {
for (std::string script : scripts) {
_actionWidgets.script->append(QString::fromStdString(std::move(script)));
}
}
Profile::Keybinding* ActionDialog::selectedKeybinding() {
QListWidgetItem* item = _keybindingWidgets.list->currentItem();
const int idx = _keybindingWidgets.list->row(item);
return idx != -1 ? &_keybindingsData[idx] : nullptr;
}
void ActionDialog::keybindingAdd() {
_keybindingWidgets.list->addItem("");
_keybindingsData.push_back(Profile::Keybinding());
_keybindingWidgets.list->setCurrentRow(_keybindingWidgets.list->count() - 1);
}
void ActionDialog::keybindingRemove() {
const Profile::Keybinding* keybinding = selectedKeybinding();
ghoul_assert(keybinding, "A keybinding must be selected at this point");
for (size_t i = 0; i < _keybindingsData.size(); ++i) {
if (_keybindingsData[i].key == keybinding->key &&
_keybindingsData[i].action == keybinding->action)
{
clearKeybindingFields();
_keybindingsData.erase(_keybindingsData.begin() + i);
delete _keybindingWidgets.list->takeItem(static_cast<int>(i));
//Save the updated keybindings to the profile
if (_keybindings) {
*_keybindings = _keybindingsData;
}
return;
}
}
}
void ActionDialog::keybindingSelected() {
const Profile::Keybinding* keybinding = selectedKeybinding();
if (keybinding) {
_keybindingWidgets.shiftModifier->setEnabled(true);
_keybindingWidgets.shiftModifier->setChecked(
hasKeyModifier(keybinding->key.modifier, KeyModifier::Shift)
);
_keybindingWidgets.ctrlModifier->setEnabled(true);
_keybindingWidgets.ctrlModifier->setChecked(
hasKeyModifier(keybinding->key.modifier, KeyModifier::Control)
);
_keybindingWidgets.altModifier->setEnabled(true);
_keybindingWidgets.altModifier->setChecked(
hasKeyModifier(keybinding->key.modifier, KeyModifier::Alt)
);
std::string key = ghoul::to_string(keybinding->key.key);
_keybindingWidgets.key->setCurrentText(QString::fromStdString(key));
_keybindingWidgets.key->setEnabled(true);
_keybindingWidgets.action->setCurrentText(
QString::fromStdString(keybinding->action)
);
_keybindingWidgets.action->setEnabled(true);
_keybindingWidgets.actionText->setText(
QString::fromStdString(keybinding->action)
);
_keybindingWidgets.actionText->setEnabled(true);
_keybindingWidgets.addButton->setEnabled(false);
_keybindingWidgets.removeButton->setEnabled(true);
_keybindingWidgets.saveButtons->setEnabled(true);
// Only enable the save buttons if a key is selected, otherwise we would get an
// exception as the None key cannot be bound
_keybindingWidgets.saveButtons->button(QDialogButtonBox::Save)->setEnabled(
_keybindingWidgets.key->currentIndex() > 0
);
if (_mainButton) {
_mainButton->setEnabled(false);
}
_keybindingWidgets.list->setEnabled(false);
}
else {
// No keybinding selected
_keybindingWidgets.addButton->setEnabled(true);
_keybindingWidgets.removeButton->setEnabled(false);
_keybindingWidgets.saveButtons->setEnabled(false);
//Action panel must also be in valid state to re-enable main start button
if (_mainButton && !_actionWidgets.saveButtons->isEnabled()) {
_mainButton->setEnabled(true);
}
_keybindingWidgets.list->setEnabled(true);
}
}
void ActionDialog::keybindingActionSelected(int) {
_keybindingWidgets.actionText->setText(_keybindingWidgets.action->currentText());
}
void ActionDialog::keybindingSaved() {
if (_keybindingWidgets.key->currentIndex() == -1) {
QMessageBox::critical(this, "Missing key", "Key must have an assignment");
return;
}
//A selection can be made from the combo box without typing text, but selecting from
//the combo will fill the text, so using the text box as criteria covers both cases.
if (_keybindingWidgets.actionText->text().isEmpty()) {
QMessageBox::critical(this, "Missing action", "Key action must not be empty");
return;
}
Profile::Keybinding* keybinding = selectedKeybinding();
ghoul_assert(keybinding, "There must be a selected keybinding at this point");
KeyModifier km = KeyModifier::None;
if (_keybindingWidgets.shiftModifier->isChecked()) {
km |= KeyModifier::Shift;
}
if (_keybindingWidgets.altModifier->isChecked()) {
km |= KeyModifier::Alt;
}
if (_keybindingWidgets.ctrlModifier->isChecked()) {
km |= KeyModifier::Control;
}
keybinding->key = stringToKey(_keybindingWidgets.key->currentText().toStdString());
keybinding->key.modifier = km;
keybinding->action = _keybindingWidgets.actionText->text().toStdString();
updateListItem(_keybindingWidgets.list->currentItem(), *keybinding);
//Save the updated keybindings to the profile
if (_keybindings) {
*_keybindings = _keybindingsData;
}
clearKeybindingFields();
}
void ActionDialog::clearKeybindingFields() {
_keybindingWidgets.list->setCurrentRow(-1);
_keybindingWidgets.shiftModifier->setChecked(false);
_keybindingWidgets.shiftModifier->setEnabled(false);
_keybindingWidgets.ctrlModifier->setChecked(false);
_keybindingWidgets.ctrlModifier->setEnabled(false);
_keybindingWidgets.altModifier->setChecked(false);
_keybindingWidgets.altModifier->setEnabled(false);
_keybindingWidgets.key->setCurrentIndex(-1);
_keybindingWidgets.key->setEnabled(false);
_keybindingWidgets.action->setCurrentIndex(-1);
_keybindingWidgets.action->setEnabled(false);
_keybindingWidgets.actionText->clear();
_keybindingWidgets.actionText->setEnabled(false);
_keybindingWidgets.list->setEnabled(true);
}
void ActionDialog::keybindingRejected() {
bool isKeyEmpty = (_keybindingsData.back().key.key == Key::Unknown);
bool isActionEmpty = _keybindingsData.back().action.empty();
if (isKeyEmpty || isActionEmpty) {
delete _keybindingWidgets.list->takeItem(_keybindingWidgets.list->count() - 1);
_keybindingsData.erase(_keybindingsData.begin() + _keybindingsData.size() - 1);
}
clearKeybindingFields();
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -33,18 +33,19 @@
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
#include <sstream>
AdditionalScriptsDialog::AdditionalScriptsDialog(openspace::Profile& profile,
QWidget* parent)
AdditionalScriptsDialog::AdditionalScriptsDialog(QWidget* parent,
std::vector<std::string>* scripts)
: QDialog(parent)
, _profile(profile)
, _scripts(scripts)
, _scriptsData(*_scripts)
{
setWindowTitle("Additional Scripts");
createWidgets();
std::vector<std::string> scripts = _profile.additionalScripts();
std::string scriptText = std::accumulate(
scripts.begin(), scripts.end(),
_scriptsData.begin(), _scriptsData.end(),
std::string(), [](std::string lhs, std::string rhs) { return lhs + rhs + '\n'; }
);
_textScripts->setText(QString::fromStdString(std::move(scriptText)));
@@ -95,16 +96,21 @@ void AdditionalScriptsDialog::parseScript() {
std::getline(iss, s);
additionalScripts.push_back(std::move(s));
}
_profile.setAdditionalScripts(additionalScripts);
*_scripts = std::move(additionalScripts);
accept();
}
void AdditionalScriptsDialog::chooseScripts() {
ScriptlogDialog d(this);
connect(&d, &ScriptlogDialog::scriptsSelected, this, &AdditionalScriptsDialog::appendScriptsToTextfield);
connect(
&d, &ScriptlogDialog::scriptsSelected,
this, &AdditionalScriptsDialog::appendScriptsToTextfield
);
d.exec();
}
void AdditionalScriptsDialog::appendScriptsToTextfield(std::string scripts) {
_textScripts->append(QString::fromStdString(std::move(scripts)));
void AdditionalScriptsDialog::appendScriptsToTextfield(std::vector<std::string> scripts) {
for (std::string script : scripts) {
_textScripts->append(QString::fromStdString(std::move(script)));
}
}

View File

@@ -0,0 +1,207 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "profile/assetedit.h"
#include "profile/horizonsdialog.h"
#include "profile/line.h"
#include <openspace/scene/asset.h>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <filesystem>
#include <string>
AssetEdit::AssetEdit(QWidget* parent)
: QDialog(parent)
{
setWindowTitle("Asset Editor");
createWidgets();
}
void AssetEdit::createWidgets() {
_layout = new QVBoxLayout(this);
{
QLabel* heading = new QLabel("The Asset Editor is coming in a future release");
heading->setObjectName("heading");
_layout->addWidget(heading);
}
{
QPushButton* generateButton = new QPushButton("Generate Horizons File");
connect(
generateButton, &QPushButton::released,
this, &AssetEdit::openHorizons
);
generateButton->setDefault(false);
// In order to generate a Horizons File the Space module is required
#ifndef OPENSPACE_MODULE_SPACE_ENABLED
generateButton->setEnabled(false);
generateButton->setToolTip(
"Cannot generate Horizons file without the space module enabled"
);
#else
generateButton->setCursor(Qt::PointingHandCursor);
#endif // OPENSPACE_MODULE_SPACE_ENABLED
_layout->addWidget(generateButton);
}
//{
// QBoxLayout* container = new QHBoxLayout(this);
// QLabel* assetLabel = new QLabel("Asset Name:");
// assetLabel->setObjectName("profile");
// container->addWidget(assetLabel);
// _nameEdit = new QLineEdit();
// container->addWidget(_nameEdit);
// _layout->addLayout(container);
//}
//_layout->addWidget(new Line);
//{
// QBoxLayout* container = new QHBoxLayout(this);
// _components = new QComboBox(this);
// _components->addItems(_supportedComponents);
// _components->setCurrentIndex(0);
// container->addWidget(_components);
// QPushButton* addButton = new QPushButton("Add", this);
// connect(addButton, &QPushButton::clicked, this, &AssetEdit::openComponent);
// addButton->setCursor(Qt::PointingHandCursor);
// container->addWidget(addButton);
// _layout->addLayout(container);
//}
_layout->addWidget(new Line);
{
QBoxLayout* footer = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footer->addWidget(_errorMsg);
QDialogButtonBox* buttons = new QDialogButtonBox;
buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &AssetEdit::approved);
connect(buttons, &QDialogButtonBox::rejected, this, &AssetEdit::reject);
footer->addWidget(buttons);
_layout->addLayout(footer);
}
}
//void AssetEdit::openComponent() {
// switch (_components->currentIndex()) {
// case 0:
// _errorMsg->setText("Choose a component to add to the asset");
// break;
// case 1: {
// QBoxLayout* horizonsLayout = new QVBoxLayout(this);
// {
// QLabel* label = new QLabel("Horizons Translation:", this);
// label->setObjectName("heading");
// horizonsLayout->addWidget(label);
// }
// {
// QBoxLayout* container = new QHBoxLayout(this);
// QLabel* fileLabel = new QLabel("File path:", this);
// container->addWidget(fileLabel);
//
// _horizonsFileEdit = new QLineEdit(this);
// container->addWidget(_horizonsFileEdit);
//
// QPushButton* fileButton = new QPushButton("Browse", this);
// connect(
// fileButton,
// &QPushButton::released,
// this,
// &AssetEdit::openHorizonsFile
// );
// fileButton->setCursor(Qt::PointingHandCursor);
// container->addWidget(fileButton);
//
// QPushButton* generateButton = new QPushButton("Generate", this);
// connect(
// generateButton,
// &QPushButton::released,
// this,
// &AssetEdit::openHorizons
// );
//
// // In order to generate a Horizons File the Space module is required
// #ifndef OPENSPACE_MODULE_SPACE_ENABLED
// generateButton->setEnabled(false);
// generateButton->setToolTip(
// "Connot generate Horizons file without the space module enabled"
// );
// #else
// generateButton->setCursor(Qt::PointingHandCursor);
// #endif // OPENSPACE_MODULE_SPACE_ENABLED
//
// container->addWidget(generateButton);
// horizonsLayout->addLayout(container);
// }
// horizonsLayout->addWidget(new Line);
// _layout->insertLayout(_layout->count() - 3, horizonsLayout);
// break;
// }
// default:
// _errorMsg->setText("Unkown component");
// break;
// }
//}
//
//void AssetEdit::openHorizonsFile() {
// std::string filePath = QFileDialog::getOpenFileName(
// this,
// tr("Open Horizons file"),
// "",
// tr("Horiozons file (*.dat)")
// ).toStdString();
// _horizonsFile = std::filesystem::absolute(filePath);
// _horizonsFileEdit->setText(QString(_horizonsFile.string().c_str()));
//}
void AssetEdit::openHorizons() {
_errorMsg->clear();
#ifdef OPENSPACE_MODULE_SPACE_ENABLED
HorizonsDialog* horizonsDialog = new HorizonsDialog(this);
horizonsDialog->exec();
//_horizonsFile = horizonsDialog->file();
//_horizonsFileEdit->setText(QString(_horizonsFile.string().c_str()));
#endif // OPENSPACE_MODULE_SPACE_ENABLED
}
void AssetEdit::approved() {
// std::string assetName = _nameEdit->text().toStdString();
// if (assetName.empty()) {
// _errorMsg->setText("Asset name must be specified");
// return;
// }
accept();
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -24,18 +24,21 @@
#include "profile/assetsdialog.h"
#include "profile/assetedit.h"
#include "profile/line.h"
#include <openspace/scene/profile.h>
#include <ghoul/fmt.h>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QTreeView>
namespace {
bool traverseToExpandSelectedItems(QTreeView& tree, AssetTreeModel& model,
int rows, QModelIndex parent)
bool traverseToExpandSelectedItems(QTreeView& tree, AssetTreeModel& model, int rows,
QModelIndex parent)
{
bool isExpanded = false;
@@ -63,7 +66,7 @@ namespace {
int startIndex = 0;
std::string token = "${USER_ASSETS}/";
if (path.find(token) == 0) {
startIndex = token.length();
startIndex = static_cast<int>(token.length());
}
const size_t slash = path.find_first_of('/', startIndex);
const bool endOfPath = (slash == std::string::npos);
@@ -103,8 +106,8 @@ namespace {
for (int r = 0; r < nRows; r++) {
QModelIndex idx = model.index(r, 0, parent);
std::string assetName = model.name(idx).toStdString();
if (path == assetName) {
// Need to check if it actually is an asset to prevent issue #2154
if (model.isAsset(idx) && path == assetName) {
foundFileMatch = true;
model.setChecked(idx, true);
break;
@@ -122,29 +125,40 @@ namespace {
}
} // namespace
AssetsDialog::AssetsDialog(openspace::Profile& profile, const std::string& assetBasePath,
const std::string& userAssetBasePath, QWidget* parent)
AssetsDialog::AssetsDialog(QWidget* parent, openspace::Profile* profile,
const std::string& assetBasePath,
const std::string& userAssetBasePath)
: QDialog(parent)
, _profile(profile)
{
setWindowTitle("Assets");
_assetTreeModel.importModelData(assetBasePath, userAssetBasePath);
createWidgets();
}
void AssetsDialog::createWidgets() {
QBoxLayout* layout = new QVBoxLayout(this);
{
QGridLayout* container = new QGridLayout;
container->setColumnStretch(1, 1);
QLabel* heading = new QLabel("Select assets from /data/assets");
heading->setObjectName("heading");
layout->addWidget(heading);
container->addWidget(heading, 0, 0);
QPushButton* newAssetButton = new QPushButton("New Asset");
connect(
newAssetButton, &QPushButton::released,
this, &AssetsDialog::openAssetEditor
);
newAssetButton->setCursor(Qt::PointingHandCursor);
newAssetButton->setDefault(false);
container->addWidget(newAssetButton, 0, 2);
layout->addLayout(container);
}
{
_assetTree = new QTreeView;
_assetTree->setToolTip(
"Expand arrow entries to browse assets in this OpenSpace installation. "
"Enable checkbox to include an asset. Those assets highlighted in red are "
"present in the profile but do not exist in this OpenSpace installation."
"present in the profile but do not exist in this OpenSpace installation"
);
_assetTree->setAlternatingRowColors(true);
_assetTree->setModel(&_assetTreeModel);
@@ -160,10 +174,10 @@ void AssetsDialog::createWidgets() {
connect(_assetTree, &QTreeView::clicked, this, &AssetsDialog::selected);
for (const std::string& a : _profile.assets()) {
QModelIndex parent = _assetTreeModel.index(-1, 0);
int nRows = _assetTreeModel.rowCount(parent);
traverseToFindFilesystemMatch(_assetTreeModel, parent, nRows, a);
for (const std::string& a : _profile->assets) {
QModelIndex p = _assetTreeModel.index(-1, 0);
int nRows = _assetTreeModel.rowCount(p);
traverseToFindFilesystemMatch(_assetTreeModel, p, nRows, a);
}
int nRows = _assetTreeModel.rowCount(_assetTreeModel.index(-1, 0));
@@ -173,18 +187,19 @@ void AssetsDialog::createWidgets() {
nRows,
_assetTreeModel.index(-1, 0)
);
layout->addWidget(_assetTree);
layout->addWidget(_assetTree, 4);
}
{
QWidget* box = new QWidget;
QBoxLayout* boxLayout = new QVBoxLayout(box);
QLabel* summaryHeading = new QLabel("Selection summary");
summaryHeading->setObjectName("heading");
layout->addWidget(summaryHeading);
}
{
boxLayout->addWidget(summaryHeading);
_summary = new QTextEdit;
_summary->setReadOnly(true);
_summary->setText(createTextSummary());
layout->addWidget(_summary);
boxLayout->addWidget(_summary);
layout->addWidget(box, 1);
}
layout->addWidget(new Line);
@@ -224,14 +239,19 @@ QString AssetsDialog::createTextSummary() {
return summary;
}
void AssetsDialog::openAssetEditor() {
AssetEdit editor(this);
editor.exec();
}
void AssetsDialog::parseSelections() {
_profile.clearAssets();
_profile->assets.clear();
std::vector<std::string> summaryPaths;
std::vector<AssetTreeItem*> summaryItems;
_assetTreeModel.getSelectedAssets(summaryPaths, summaryItems);
for (const std::string& sel : summaryPaths) {
_profile.addAsset(sel);
_profile->addAsset(sel);
}
accept();
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -24,8 +24,8 @@
#include "profile/assettreeitem.h"
AssetTreeItem::AssetTreeItem(const std::vector<QVariant>& data, AssetTreeItem* parentItem)
: _itemData(data)
AssetTreeItem::AssetTreeItem(std::vector<QVariant> data, AssetTreeItem* parentItem)
: _itemData(std::move(data))
, _parentItem(parentItem)
{}
@@ -35,12 +35,12 @@ AssetTreeItem::~AssetTreeItem() {
}
}
AssetTreeItem* AssetTreeItem::child(int row) {
if (row < 0 || row >= static_cast<int>(_childItems.size())) {
return nullptr;
AssetTreeItem* AssetTreeItem::child(int row) const {
if (row >= 0 && row < static_cast<int>(_childItems.size())) {
return _childItems.at(row);
}
else {
return _childItems.at(row);
return nullptr;
}
}
@@ -67,11 +67,11 @@ int AssetTreeItem::columnCount() const {
}
QVariant AssetTreeItem::data(int column) const {
if (column < 0 || column >= static_cast<int>(_itemData.size())) {
return QVariant();
if (column >= 0 && column < static_cast<int>(_itemData.size())) {
return _itemData.at(column);
}
else {
return _itemData.at(column);
return QVariant();
}
}
@@ -96,11 +96,11 @@ void AssetTreeItem::setChecked(bool set) {
}
bool AssetTreeItem::isAsset() const {
return (childCount() == 0);
return childCount() == 0;
}
bool AssetTreeItem::isCategory() const {
return (childCount() > 0);
return childCount() > 0;
}
void AssetTreeItem::setExistsInFilesystem(bool fileExists) {

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -29,8 +29,8 @@
#include <QColor>
namespace {
constexpr const char* Header1 = "Asset";
constexpr const char* Header2 = "Enabled";
constexpr std::string_view Header1 = "Asset";
constexpr std::string_view Header2 = "Enabled";
struct ImportElement {
std::string line;
@@ -139,8 +139,9 @@ AssetTreeModel::AssetTreeModel(QObject* parent)
: QAbstractItemModel(parent)
{
_rootItem = std::make_unique<AssetTreeItem>(
std::vector<QVariant>{
QString::fromStdString(Header1), QString::fromStdString(Header2)
std::vector<QVariant> {
QString::fromStdString(std::string(Header1)),
QString::fromStdString(std::string(Header2))
}
);
}
@@ -149,23 +150,23 @@ void AssetTreeModel::importModelData(const std::string& assetBasePath,
const std::string& userAssetBasePath) {
FileSystemAccess assets(
".asset",
// @TODO (abock, 2021-03-24) We need some better solution for this; what is the
// problem of just including all subfolders instead?
{ "scene", "global", "customization", "dashboard", "examples", "util" },
true,
true
);
std::string assetList = assets.useQtFileSystemModelToTraverseDir(assetBasePath);
assetList += assets.useQtFileSystemModelToTraverseDir(userAssetBasePath, true);
std::istringstream iss(assetList);
ImportElement rootElem = { "", 0, false };
ImportElement rootElem = {
.line = "",
.level = 0,
};
if (importGetNextLine(rootElem, iss)) {
importInsertItem(iss, _rootItem.get(), rootElem, 0);
}
}
AssetTreeItem* AssetTreeModel::getItem(const QModelIndex& index) const {
AssetTreeItem* AssetTreeModel::item(const QModelIndex& index) const {
if (index.isValid()) {
AssetTreeItem* item = static_cast<AssetTreeItem*>(index.internalPointer());
if (item) {
@@ -176,46 +177,46 @@ AssetTreeItem* AssetTreeModel::getItem(const QModelIndex& index) const {
}
bool AssetTreeModel::isChecked(QModelIndex& index) const {
AssetTreeItem* item = getItem(index);
const int isChecked = item->data(1).toInt();
AssetTreeItem* i = item(index);
const int isChecked = i->data(1).toInt();
return isChecked == Qt::Checked;
}
bool AssetTreeModel::isAsset(QModelIndex& index) const {
AssetTreeItem* item = getItem(index);
return item->isAsset();
AssetTreeItem* i = item(index);
return i->isAsset();
}
bool AssetTreeModel::inFilesystem(QModelIndex& index) const {
AssetTreeItem* item = getItem(index);
return item->doesExistInFilesystem();
AssetTreeItem* i = item(index);
return i->doesExistInFilesystem();
}
int AssetTreeModel::childCount(QModelIndex& index) const {
return getItem(index)->childCount();
return item(index)->childCount();
}
QString AssetTreeModel::name(QModelIndex& index) const {
return getItem(index)->name();
return item(index)->name();
}
void AssetTreeModel::setName(QModelIndex& index, QString name) {
getItem(index)->setData(0, name);
item(index)->setData(0, name);
}
void AssetTreeModel::setChecked(QModelIndex& index, bool checked) {
getItem(index)->setData(1, checked ? Qt::Checked : Qt::Unchecked);
item(index)->setData(1, checked ? Qt::Checked : Qt::Unchecked);
}
void AssetTreeModel::setExistenceInFilesystem(QModelIndex& index, bool fileExists) {
getItem(index)->setExistsInFilesystem(fileExists);
item(index)->setExistsInFilesystem(fileExists);
}
AssetTreeItem* AssetTreeModel::child(int row) const {
QModelIndex i = index(row, 0);
int nKids = childCount(i);
if (row < nKids) {
return getItem(i)->child(row);
const int nChildren = childCount(i);
if (row < nChildren) {
return item(i)->child(row);
}
return nullptr;
}
@@ -227,7 +228,7 @@ QModelIndex AssetTreeModel::index(int row, int column, const QModelIndex& parent
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
AssetTreeItem* parentItem = getItem(parent);
AssetTreeItem* parentItem = item(parent);
if (!parentItem) {
return QModelIndex();
}
@@ -253,7 +254,7 @@ QModelIndex AssetTreeModel::parent(const QModelIndex& index) const {
return QModelIndex();
}
AssetTreeItem* childItem = getItem(index);
AssetTreeItem* childItem = item(index);
AssetTreeItem* parentItem = childItem ? childItem->parent() : nullptr;
if (parentItem == _rootItem.get() || !parentItem) {
return QModelIndex();
@@ -263,11 +264,11 @@ QModelIndex AssetTreeModel::parent(const QModelIndex& index) const {
}
AssetTreeItem* AssetTreeModel::assetItem(const QModelIndex& index) {
return getItem(index);
return item(index);
}
int AssetTreeModel::rowCount(const QModelIndex& parent) const {
const AssetTreeItem* parentItem = getItem(parent);
const AssetTreeItem* parentItem = item(parent);
return parentItem ? parentItem->childCount() : 0;
}
@@ -292,12 +293,7 @@ QVariant AssetTreeModel::data(const QModelIndex& index, int role) const {
}
if (role == Qt::ForegroundRole) {
if (item->doesExistInFilesystem()) {
return QVariant(QColor(Qt::black));
}
else {
return QVariant(QColor(Qt::red));
}
return item->doesExistInFilesystem() ? QColor(Qt::black) : QColor(Qt::red);
}
else if (role == Qt::DisplayRole) {
return item->data(index.column());
@@ -348,7 +344,7 @@ QVariant AssetTreeModel::headerData(int section, Qt::Orientation orientation,
}
void AssetTreeModel::getSelectedAssets(std::vector<std::string>& outputPaths,
std::vector<AssetTreeItem*>& outputItems)
std::vector<AssetTreeItem*>& outputItems)
{
parseChildrenForSelected(_rootItem.get(), outputPaths, outputItems, "");
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -25,7 +25,6 @@
#include "profile/cameradialog.h"
#include "profile/line.h"
#include <openspace/scene/profile.h>
#include <QDialogButtonBox>
#include <QDoubleValidator>
#include <QFrame>
@@ -36,11 +35,11 @@
#include <QTabWidget>
namespace {
constexpr const int CameraTypeNav = 0;
constexpr const int CameraTypeGeo = 1;
constexpr int CameraTypeNav = 0;
constexpr int CameraTypeGeo = 1;
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
bool inNumericalRange(QLineEdit* le, float min, float max) {
QString s = le->text();
@@ -56,28 +55,29 @@ namespace {
}
} // namespace
CameraDialog::CameraDialog(openspace::Profile& profile, QWidget *parent)
CameraDialog::CameraDialog(QWidget* parent,
std::optional<openspace::Profile::CameraType>* camera)
: QDialog(parent)
, _profile(profile)
, _camera(camera)
{
setWindowTitle("Set Camera Position");
createWidgets();
if (_profile.camera().has_value()) {
openspace::Profile::CameraType type = *_profile.camera();
if (_camera->has_value()) {
const openspace::Profile::CameraType& type = **_camera;
std::visit(overloaded {
[this](const openspace::Profile::CameraNavState& nav) {
_tabWidget->setCurrentIndex(CameraTypeNav);
_navState.anchor->setText(QString::fromStdString(nav.anchor));
_navState.aim->setText(QString::fromStdString(*nav.aim));
_navState.refFrame->setText(QString::fromStdString(nav.referenceFrame));
_navState.positionX->setText(QString::number(nav.position.x));
_navState.positionY->setText(QString::number(nav.position.y));
_navState.positionZ->setText(QString::number(nav.position.z));
_navState.positionX->setText(QString::number(nav.position.x, 'g', 17));
_navState.positionY->setText(QString::number(nav.position.y, 'g', 17));
_navState.positionZ->setText(QString::number(nav.position.z, 'g', 17));
if (nav.up.has_value()) {
_navState.upX->setText(QString::number(nav.up.value().x));
_navState.upY->setText(QString::number(nav.up.value().y));
_navState.upZ->setText(QString::number(nav.up.value().z));
_navState.upX->setText(QString::number(nav.up.value().x, 'g', 17));
_navState.upY->setText(QString::number(nav.up.value().y, 'g', 17));
_navState.upZ->setText(QString::number(nav.up.value().z, 'g', 17));
}
else {
_navState.upX->clear();
@@ -85,13 +85,13 @@ CameraDialog::CameraDialog(openspace::Profile& profile, QWidget *parent)
_navState.upZ->clear();
}
if (nav.yaw.has_value()) {
_navState.yaw->setText(QString::number(*nav.yaw));
_navState.yaw->setText(QString::number(*nav.yaw, 'g', 17));
}
else {
_navState.yaw->clear();
}
if (nav.pitch.has_value()) {
_navState.pitch->setText(QString::number(*nav.pitch));
_navState.pitch->setText(QString::number(*nav.pitch, 'g', 17));
}
else {
_navState.pitch->clear();
@@ -101,10 +101,10 @@ CameraDialog::CameraDialog(openspace::Profile& profile, QWidget *parent)
[this](const openspace::Profile::CameraGoToGeo& geo) {
_tabWidget->setCurrentIndex(CameraTypeGeo);
_geoState.anchor->setText(QString::fromStdString(geo.anchor));
_geoState.latitude->setText(QString::number(geo.latitude));
_geoState.longitude->setText(QString::number(geo.longitude));
_geoState.latitude->setText(QString::number(geo.latitude, 'g', 17));
_geoState.longitude->setText(QString::number(geo.longitude, 'g', 17));
if (geo.altitude.has_value()) {
_geoState.altitude->setText(QString::number(*geo.altitude));
_geoState.altitude->setText(QString::number(*geo.altitude, 'g', 17));
}
else {
_geoState.altitude->clear();
@@ -384,11 +384,11 @@ void CameraDialog::approved() {
!_navState.upY->text().isEmpty() &&
!_navState.upZ->text().isEmpty())
{
glm::dvec3 u = {
glm::dvec3 u = glm::dvec3(
_navState.upX->text().toDouble(),
_navState.upY->text().toDouble(),
_navState.upZ->text().toDouble()
};
);
nav.up = u;
}
else {
@@ -406,7 +406,7 @@ void CameraDialog::approved() {
else {
nav.pitch = std::nullopt;
}
_profile.setCamera(nav);
*_camera = std::move(nav);
}
else if (_tabWidget->currentIndex() == CameraTypeGeo) {
openspace::Profile::CameraGoToGeo geo;
@@ -416,7 +416,7 @@ void CameraDialog::approved() {
if (!_geoState.altitude->text().isEmpty()) {
geo.altitude = _geoState.altitude->text().toDouble();
}
_profile.setCamera(geo);
*_camera = std::move(geo);
}
accept();

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -26,6 +26,7 @@
#include "profile/line.h"
#include <openspace/scene/profile.h>
#include <ghoul/fmt.h>
#include <QDialogButtonBox>
#include <QDoubleValidator>
#include <QEvent>
@@ -40,14 +41,14 @@
#include <iostream>
namespace {
constexpr const int MaxNumberOfKeys = 30;
constexpr int MaxNumberOfKeys = 30;
struct TimeInterval {
uint64_t secondsPerInterval;
std::string intervalName;
};
std::array<TimeInterval, 7> TimeIntervals = {
const std::array<TimeInterval, 7> TimeIntervals = {
TimeInterval{ 31536000, "year" },
TimeInterval{ 18144000, "month" },
TimeInterval{ 604800, "week" },
@@ -79,16 +80,15 @@ namespace {
}
} // namespace
DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent)
DeltaTimesDialog::DeltaTimesDialog(QWidget* parent, std::vector<double>* deltaTimes)
: QDialog(parent)
, _profile(profile)
, _deltaTimes(deltaTimes)
, _deltaTimesData(*_deltaTimes)
{
setWindowTitle("Simulation Time Increments");
createWidgets();
_data = _profile.deltaTimes();
for (size_t d = 0; d < _data.size(); ++d) {
for (size_t d = 0; d < _deltaTimesData.size(); ++d) {
std::string summary = createSummaryForDeltaTime(d, true);
_listWidget->addItem(new QListWidgetItem(QString::fromStdString(summary)));
}
@@ -98,17 +98,16 @@ DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent)
void DeltaTimesDialog::createWidgets() {
QBoxLayout* layout = new QVBoxLayout(this);
{
_listWidget = new QListWidget;
connect(
_listWidget, &QListWidget::itemSelectionChanged,
this, &DeltaTimesDialog::listItemSelected
);
_listWidget->setAutoScroll(true);
_listWidget->setLayoutMode(QListView::SinglePass);
layout->addWidget(_listWidget);
}
_listWidget = new QListWidget;
connect(
_listWidget, &QListWidget::itemSelectionChanged,
this, &DeltaTimesDialog::listItemSelected
);
_listWidget->setAutoScroll(true);
_listWidget->setLayoutMode(QListView::SinglePass);
layout->addWidget(_listWidget);
{
QBoxLayout* buttonLayout = new QHBoxLayout;
_addButton = new QPushButton("Add Entry");
@@ -128,10 +127,10 @@ void DeltaTimesDialog::createWidgets() {
buttonLayout->addStretch();
layout->addLayout(buttonLayout);
}
{
_adjustLabel = new QLabel("Set Simulation Time Increment for key");
layout->addWidget(_adjustLabel);
}
_adjustLabel = new QLabel("Set Simulation Time Increment for key");
layout->addWidget(_adjustLabel);
{
QBoxLayout* box = new QHBoxLayout;
_seconds = new QLineEdit;
@@ -184,7 +183,7 @@ void DeltaTimesDialog::createWidgets() {
}
std::string DeltaTimesDialog::createSummaryForDeltaTime(size_t idx, bool forListView) {
int k = (idx%10 == 9) ? 0 : idx%10 + 1;
int k = (idx % 10 == 9) ? 0 : idx % 10 + 1;
k = (idx == 0) ? 1 : k;
std::string key = std::to_string(k);
@@ -203,7 +202,9 @@ std::string DeltaTimesDialog::createSummaryForDeltaTime(size_t idx, bool forList
}
if (forListView) {
s += '\t' + std::to_string(_data.at(idx)) + '\t' + timeDescription(_data.at(idx));
s += fmt::format(
"\t{}\t{}", _deltaTimesData.at(idx), timeDescription(_deltaTimesData.at(idx))
);
}
return s;
}
@@ -212,16 +213,16 @@ void DeltaTimesDialog::listItemSelected() {
QListWidgetItem *item = _listWidget->currentItem();
int index = _listWidget->row(item);
if (index < (static_cast<int>(_data.size()) - 1)) {
if (index < (static_cast<int>(_deltaTimesData.size()) - 1)) {
_listWidget->setCurrentRow(index);
}
if (!_data.empty()) {
if (_data.at(index) == 0) {
if (!_deltaTimesData.empty()) {
if (_deltaTimesData.at(index) == 0) {
_seconds->clear();
}
else {
_seconds->setText(QString::number(_data.at(index)));
_seconds->setText(QString::number(_deltaTimesData.at(index)));
}
}
_editModeNewItem = true;
@@ -230,8 +231,8 @@ void DeltaTimesDialog::listItemSelected() {
void DeltaTimesDialog::setLabelForKey(int index, bool editMode, std::string color) {
std::string labelS = "Set Simulation Time Increment for key";
if (index >= static_cast<int>(_data.size())) {
index = static_cast<int>(_data.size()) - 1;
if (index >= static_cast<int>(_deltaTimesData.size())) {
index = static_cast<int>(_deltaTimesData.size()) - 1;
}
if (editMode) {
labelS += " '" + createSummaryForDeltaTime(index, false) + "':";
@@ -248,9 +249,7 @@ void DeltaTimesDialog::valueChanged(const QString& text) {
else {
int value = text.toDouble();
if (value != 0) {
_value->setText(QString::fromStdString(
timeDescription(text.toDouble()))
);
_value->setText(QString::fromStdString(timeDescription(text.toDouble())));
_errorMsg->setText("");
}
}
@@ -261,7 +260,7 @@ bool DeltaTimesDialog::isLineEmpty(int index) {
if (!_listWidget->item(index)->text().isEmpty()) {
isEmpty = false;
}
if (!_data.empty() && (_data.at(0) != 0)) {
if (!_deltaTimesData.empty() && (_deltaTimesData.at(0) != 0)) {
isEmpty = false;
}
return isEmpty;
@@ -275,11 +274,11 @@ void DeltaTimesDialog::addDeltaTimeValue() {
// Special case where list is "empty" but really has one line that is blank.
// This is done because QListWidget does not seem to like having its sole
// remaining item being removed.
_data.at(0) = 0;
_deltaTimesData.at(0) = 0;
_listWidget->item(0)->setText(messageAddValue);
}
else if (_data.size() < MaxNumberOfKeys) {
_data.push_back(0);
else if (_deltaTimesData.size() < MaxNumberOfKeys) {
_deltaTimesData.push_back(0);
_listWidget->addItem(new QListWidgetItem(messageAddValue));
}
else {
@@ -292,22 +291,20 @@ void DeltaTimesDialog::addDeltaTimeValue() {
void DeltaTimesDialog::saveDeltaTimeValue() {
QListWidgetItem* item = _listWidget->currentItem();
if (item != nullptr) {
if (item && !_deltaTimesData.empty()) {
int index = _listWidget->row(item);
if (_data.size() > 0) {
_data.at(index) = _seconds->text().toDouble();
std::string summary = createSummaryForDeltaTime(index, true);
_listWidget->item(index)->setText(QString::fromStdString(summary));
transitionEditMode(index, false);
_editModeNewItem = false;
}
_deltaTimesData.at(index) = _seconds->text().toDouble();
std::string summary = createSummaryForDeltaTime(index, true);
_listWidget->item(index)->setText(QString::fromStdString(summary));
transitionEditMode(index, false);
_editModeNewItem = false;
}
}
void DeltaTimesDialog::discardDeltaTimeValue() {
listItemSelected();
transitionEditMode(_listWidget->count() - 1, false);
if (_editModeNewItem && !_data.empty() && _data.back() == 0) {
if (_editModeNewItem && !_deltaTimesData.empty() && _deltaTimesData.back() == 0) {
removeDeltaTimeValue();
}
_editModeNewItem = false;
@@ -316,13 +313,13 @@ void DeltaTimesDialog::discardDeltaTimeValue() {
void DeltaTimesDialog::removeDeltaTimeValue() {
if (_listWidget->count() > 0) {
if (_listWidget->count() == 1) {
_data.at(0) = 0;
_deltaTimesData.at(0) = 0;
_listWidget->item(0)->setText("");
}
else {
delete _listWidget->takeItem(_listWidget->count() - 1);
if (!_data.empty()) {
_data.pop_back();
if (!_deltaTimesData.empty()) {
_deltaTimesData.pop_back();
}
}
}
@@ -355,20 +352,20 @@ void DeltaTimesDialog::transitionEditMode(int index, bool state) {
}
void DeltaTimesDialog::parseSelections() {
if ((_data.size() == 1) && (_data.at(0) == 0)) {
_data.clear();
if ((_deltaTimesData.size() == 1) && (_deltaTimesData.at(0) == 0)) {
_deltaTimesData.clear();
}
int finalNonzeroIndex = static_cast<int>(_data.size()) - 1;
int finalNonzeroIndex = static_cast<int>(_deltaTimesData.size()) - 1;
for (; finalNonzeroIndex >= 0; --finalNonzeroIndex) {
if (_data.at(finalNonzeroIndex) != 0) {
if (_deltaTimesData.at(finalNonzeroIndex) != 0) {
break;
}
}
std::vector<double> tempDt;
for (int i = 0; i < (finalNonzeroIndex + 1); ++i) {
tempDt.push_back(_data[i]);
tempDt.push_back(_deltaTimesData[i]);
}
_profile.setDeltaTimes(tempDt);
*_deltaTimes = std::move(_deltaTimesData);
accept();
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,545 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "profile/keybindingsdialog.h"
#include "profile/line.h"
#include "profile/scriptlogdialog.h"
#include <openspace/scene/profile.h>
#include <openspace/util/keys.h>
#include <qevent.h>
#include <algorithm>
#include <QKeyEvent>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QComboBox>
#include <QLineEdit>
#include <QTextEdit>
#include <QDialogButtonBox>
using namespace openspace;
namespace {
const Profile::Keybinding BlankKey= {
{ Key::Unknown, KeyModifier::NoModifier },
"",
"",
"",
true,
""
};
void replaceChars(std::string& src, const std::string& from, const std::string& to) {
std::string newString;
std::string::size_type found, last = 0;
while ((found = src.find(from, last)) != std::string::npos) {
newString.append(src, last, (found - last));
newString += to;
last = found + from.length();
}
newString += src.substr(last);
src.swap(newString);
}
std::string truncateString(std::string& s) {
const size_t maxLength = 50;
replaceChars(s, "\n", ";");
if (s.length() > maxLength) {
s.resize(maxLength);
s += "...";
}
return s;
}
std::string createOneLineSummary(Profile::Keybinding k) {
std::string summary;
int keymod = static_cast<int>(k.key.modifier);
if (keymod != static_cast<int>(KeyModifier::NoModifier)) {
summary += KeyModifierNames.at(keymod) + " ";
}
int keyname = static_cast<int>(k.key.key);
summary += KeyNames.at(keyname) + " ";
summary += truncateString(k.name) + " (";
summary += truncateString(k.documentation) + ") @ ";
summary += truncateString(k.guiPath) + " ";
summary += (k.isLocal) ? "local" : "remote";
summary += " `" + truncateString(k.script) + "`";
return summary;
}
} // namespace
KeybindingsDialog::KeybindingsDialog(Profile& profile, QWidget *parent)
: QDialog(parent)
, _profile(profile)
, _data(_profile.keybindings())
{
setWindowTitle("Assign Keybindings");
createWidgets();
transitionFromEditMode();
}
void KeybindingsDialog::appendScriptsToKeybind(std::string scripts) {
_scriptEdit->append(QString::fromStdString(std::move(scripts)));
}
void KeybindingsDialog::createWidgets() {
QBoxLayout* layout = new QVBoxLayout(this);
{
_list = new QListWidget;
connect(
_list, &QListWidget::itemSelectionChanged,
this, &KeybindingsDialog::listItemSelected
);
_list->setAlternatingRowColors(true);
_list->setMovement(QListView::Free);
_list->setResizeMode(QListView::Adjust);
for (size_t i = 0; i < _data.size(); ++i) {
std::string summary = createOneLineSummary(_data[i]);
_list->addItem(new QListWidgetItem(QString::fromStdString(summary)));
}
layout->addWidget(_list);
}
{
QBoxLayout* box = new QHBoxLayout;
_addButton = new QPushButton("Add new");
connect(
_addButton, &QPushButton::clicked,
this, &KeybindingsDialog::listItemAdded
);
box->addWidget(_addButton);
_removeButton = new QPushButton("Remove");
connect(
_removeButton, &QPushButton::clicked,
this, &KeybindingsDialog::listItemRemove
);
box->addWidget(_removeButton);
box->addStretch();
layout->addLayout(box);
}
layout->addWidget(new Line);
{
QGridLayout* box = new QGridLayout;
_keyModLabel = new QLabel("Key Modifier");
box->addWidget(_keyModLabel, 0, 0);
_keyModCombo = new QComboBox;
_keyModCombo->setToolTip(
"Modifier keys to hold while key is pressed (blank means none)"
);
QStringList comboModKeysStringList;
int modIdx = 0;
for (const std::pair<const int, std::string>& m : KeyModifierNames) {
comboModKeysStringList += QString::fromStdString(m.second);
_mapModKeyComboBoxIndexToKeyValue.push_back(modIdx++);
}
_keyModCombo->addItems(comboModKeysStringList);
box->addWidget(_keyModCombo, 0, 1);
_keyLabel = new QLabel("Key");
box->addWidget(_keyLabel, 1, 0);
_keyCombo = new QComboBox;
_keyCombo->setToolTip("Key to press for this keybinding");
QStringList comboKeysStringList;
for (int i = 0; i < static_cast<int>(Key::Last); ++i) {
if (KeyNames.find(i) != KeyNames.end()) {
comboKeysStringList += QString::fromStdString(KeyNames.at(i));
// Create map to relate key combo box to integer value defined in Key
_mapKeyComboBoxIndexToKeyValue.push_back(i);
}
}
_keyCombo->addItems(comboKeysStringList);
connect(
_keyCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &KeybindingsDialog::keySelected
);
box->addWidget(_keyCombo, 1, 1);
_nameLabel = new QLabel("Name:");
box->addWidget(_nameLabel, 2, 0);
_nameEdit = new QLineEdit;
_nameEdit->setToolTip("Name assigned to this keybinding");
box->addWidget(_nameEdit, 2, 1);
_guiPathLabel = new QLabel("GUI Path:");
box->addWidget(_guiPathLabel, 3, 0);
_guiPathEdit = new QLineEdit;
_guiPathEdit->setToolTip(
"[OPTIONAL] Path for where this keybinding appears in GUI menu"
);
box->addWidget(_guiPathEdit, 3, 1);
_documentationLabel = new QLabel("Documentation:");
box->addWidget(_documentationLabel, 4, 0);
_documentationEdit = new QLineEdit;
_documentationEdit->setToolTip(
"[OPTIONAL] Documentation entry for keybinding"
);
box->addWidget(_documentationEdit, 4, 1);
_localCheck = new QCheckBox("Local");
_localCheck->setToolTip(
"Determines whether the command, when executed, should be shared with "
"connected instances or only executed locally"
);
box->addWidget(_localCheck, 5, 0, 1, 2);
_scriptLabel = new QLabel("Script");
box->addWidget(_scriptLabel, 6, 0, 1, 2);
_chooseScriptsButton = new QPushButton("Choose Scripts");
connect(
_chooseScriptsButton, &QPushButton::clicked,
this, &KeybindingsDialog::chooseScripts
);
box->addWidget(_chooseScriptsButton, 6, 1, 1, 1);
_scriptEdit = new QTextEdit;
_scriptEdit->setAcceptRichText(false);
_scriptEdit->setToolTip("Command(s) to execute at keypress event");
_scriptEdit->setTabChangesFocus(true);
box->addWidget(_scriptEdit, 7, 0, 1, 2);
box->setRowStretch(7, 1);
QBoxLayout* buttonBox = new QHBoxLayout;
_saveButton = new QPushButton("Save");
connect(
_saveButton, &QPushButton::clicked,
this, &KeybindingsDialog::listItemSave
);
buttonBox->addWidget(_saveButton);
_cancelButton = new QPushButton("Cancel");
connect(
_cancelButton, &QPushButton::clicked,
this, &KeybindingsDialog::listItemCancelSave
);
buttonBox->addWidget(_cancelButton);
buttonBox->addStretch();
box->addLayout(buttonBox, 8, 1, 1, 2);
layout->addLayout(box);
}
layout->addWidget(new Line);
{
QBoxLayout* footerLayout = new QHBoxLayout;
_errorMsg = new QLabel;
_errorMsg->setObjectName("error-message");
_errorMsg->setWordWrap(true);
footerLayout->addWidget(_errorMsg);
_buttonBox = new QDialogButtonBox;
_buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
QObject::connect(
_buttonBox, &QDialogButtonBox::accepted,
this, &KeybindingsDialog::parseSelections
);
QObject::connect(
_buttonBox, &QDialogButtonBox::rejected,
this, &KeybindingsDialog::reject
);
footerLayout->addWidget(_buttonBox);
layout->addLayout(footerLayout);
}
}
void KeybindingsDialog::listItemSelected() {
QListWidgetItem *item = _list->currentItem();
int index = _list->row(item);
if (_data.size() > 0) {
Profile::Keybinding& k = _data[index];
const int modifierKey = indexInKeyMapping(
_mapModKeyComboBoxIndexToKeyValue,
static_cast<int>(k.key.modifier)
);
_keyModCombo->setCurrentIndex(modifierKey);
if (k.key.key == Key::Unknown) {
_keyCombo->setCurrentIndex(0);
}
else {
const int key = indexInKeyMapping(
_mapKeyComboBoxIndexToKeyValue,
static_cast<int>(k.key.key)
);
_keyCombo->setCurrentIndex(key);
}
// Do key here
_nameEdit->setText(QString::fromStdString(k.name));
_guiPathEdit->setText(QString::fromStdString(k.guiPath));
_documentationEdit->setText(QString::fromStdString(k.documentation));
_localCheck->setChecked(k.isLocal);
_scriptEdit->setText(QString::fromStdString(k.script));
}
transitionToEditMode();
}
void KeybindingsDialog::keySelected(int index) {
const QString numKeyWarning = "Warning: Using a number key may conflict with the "
"keybindings for simulation time increments.";
QString errorContents = _errorMsg->text();
bool alreadyContainsWarning = (errorContents.length() >= numKeyWarning.length() &&
errorContents.left(numKeyWarning.length()) == numKeyWarning);
if (_mapKeyComboBoxIndexToKeyValue[index] >= static_cast<int>(Key::Num0)
&& _mapKeyComboBoxIndexToKeyValue[index] <= static_cast<int>(Key::Num9))
{
if (!alreadyContainsWarning) {
errorContents = numKeyWarning + errorContents;
_errorMsg->setText(errorContents);
}
}
else if (alreadyContainsWarning) {
_errorMsg->setText(errorContents.mid(numKeyWarning.length()));
}
}
int KeybindingsDialog::indexInKeyMapping(std::vector<int>& mapVector, int keyInt) {
const auto it = std::find(mapVector.cbegin(), mapVector.cend(), keyInt);
return static_cast<int>(std::distance(mapVector.cbegin(), it));
}
bool KeybindingsDialog::isLineEmpty(int index) {
bool isEmpty = true;
if (!_list->item(index)->text().isEmpty()) {
isEmpty = false;
}
if (!_data.empty() && !_data.at(0).name.empty()) {
isEmpty = false;
}
return isEmpty;
}
void KeybindingsDialog::listItemAdded() {
_data.push_back(BlankKey);
_list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')"));
// Scroll down to that blank line highlighted
_list->setCurrentRow(_list->count() - 1);
// Blank-out the 2 text fields, set combo box to index 0
_keyModCombo->setCurrentIndex(static_cast<int>(_data.back().key.modifier));
if (_data.back().key.key == Key::Unknown) {
_keyCombo->setCurrentIndex(0);
}
else {
_keyCombo->setCurrentIndex(static_cast<int>(_data.back().key.key));
}
_keyModCombo->setFocus(Qt::OtherFocusReason);
_nameEdit->setText(QString::fromStdString(_data.back().name));
_guiPathEdit->setText("/");
_documentationEdit->setText(QString::fromStdString(_data.back().documentation));
_localCheck->setChecked(false);
_scriptEdit->setText(QString::fromStdString(_data.back().script));
_editModeNewItem = true;
}
void KeybindingsDialog::listItemSave() {
if (!areRequiredFormsFilled()) {
return;
}
QListWidgetItem* item = _list->currentItem();
int index = _list->row(item);
if (!_data.empty()) {
int keyModIdx = _mapModKeyComboBoxIndexToKeyValue.at(
_keyModCombo->currentIndex());
_data[index].key.modifier = static_cast<KeyModifier>(keyModIdx);
int keyIdx = _mapKeyComboBoxIndexToKeyValue.at(_keyCombo->currentIndex());
_data[index].key.key = static_cast<Key>(keyIdx);
_data[index].name = _nameEdit->text().toStdString();
_data[index].guiPath = _guiPathEdit->text().toStdString();
_data[index].documentation = _documentationEdit->text().toStdString();
_data[index].script = _scriptEdit->toPlainText().toStdString();
_data[index].isLocal = (_localCheck->isChecked());
std::string summary = createOneLineSummary(_data[index]);
_list->item(index)->setText(QString::fromStdString(summary));
}
transitionFromEditMode();
}
bool KeybindingsDialog::areRequiredFormsFilled() {
bool requiredFormsFilled = true;
std::string errors;
if (_keyCombo->currentIndex() < 0) {
errors += "Missing key";
requiredFormsFilled = false;
}
if (_nameEdit->text().length() == 0) {
if (!errors.empty()) {
errors += ", ";
}
errors += "Missing keybinding name";
requiredFormsFilled = false;
}
if (_scriptEdit->toPlainText().isEmpty()) {
if (!errors.empty()) {
errors += ", ";
}
errors += "Missing script";
requiredFormsFilled = false;
}
_errorMsg->setText(QString::fromStdString(errors));
return requiredFormsFilled;
}
void KeybindingsDialog::listItemCancelSave() {
listItemSelected();
transitionFromEditMode();
if (_editModeNewItem && !_data.empty() &&
(_data.back().name.length() == 0 || _data.back().script.length() == 0 ||
_data.back().key.key == Key::Unknown))
{
listItemRemove();
}
_editModeNewItem = false;
}
void KeybindingsDialog::listItemRemove() {
if (_list->count() > 0) {
if (_list->count() == 1) {
// Special case where last remaining item is being removed (QListWidget does
// not like the final item being removed so instead clear it & leave it)
_data.at(0) = BlankKey;
_list->item(0)->setText("");
}
else {
int index = _list->currentRow();
if (index >= 0 && index < _list->count()) {
_list->takeItem(index);
if (!_data.empty()) {
_data.erase(_data.begin() + index);
}
}
}
}
_list->clearSelection();
transitionFromEditMode();
}
void KeybindingsDialog::transitionToEditMode() {
_list->setDisabled(true);
_addButton->setDisabled(true);
_removeButton->setDisabled(true);
_saveButton->setDisabled(true);
_cancelButton->setDisabled(true);
_buttonBox->setDisabled(true);
_keyLabel->setText("<font color='black'>Key</font>");
_keyModLabel->setText("<font color='black'>Key Modifier</font>");
_nameLabel->setText("<font color='black'>Name</font>");
_scriptLabel->setText("<font color='black'>Script</font>");
_guiPathLabel->setText("<font color='black'>GUI Path</font>");
_documentationLabel->setText("<font color='black'>Documentation</font>");
editBoxDisabled(false);
_errorMsg->setText("");
}
void KeybindingsDialog::transitionFromEditMode() {
_list->setDisabled(false);
_addButton->setDisabled(false);
_removeButton->setDisabled(false);
_saveButton->setDisabled(false);
_cancelButton->setDisabled(false);
_buttonBox->setDisabled(false);
_keyLabel->setText("<font color='light gray'>Key</font>");
_keyModLabel->setText("<font color='light gray'>Key Modifier</font>");
_nameLabel->setText("<font color='light gray'>Name</font>");
_scriptLabel->setText("<font color='light gray'>Script</font>");
_guiPathLabel->setText("<font color='light gray'>GUI Path</font>");
_documentationLabel->setText("<font color='light gray'>Documentation</font>");
editBoxDisabled(true);
_errorMsg->setText("");
}
void KeybindingsDialog::editBoxDisabled(bool disabled) {
_keyLabel->setDisabled(disabled);
_keyCombo->setDisabled(disabled);
_keyModLabel->setDisabled(disabled);
_keyModCombo->setDisabled(disabled);
_nameLabel->setDisabled(disabled);
_nameEdit->setDisabled(disabled);
_guiPathLabel->setDisabled(disabled);
_guiPathEdit->setDisabled(disabled);
_documentationLabel->setDisabled(disabled);
_documentationEdit->setDisabled(disabled);
_localCheck->setDisabled(disabled);
_scriptLabel->setDisabled(disabled);
_scriptEdit->setDisabled(disabled);
_cancelButton->setDisabled(disabled);
_saveButton->setDisabled(disabled);
_chooseScriptsButton->setDisabled(disabled);
}
void KeybindingsDialog::parseSelections() {
// Handle case with only one remaining but empty line
if ((_data.size() == 1) && (_data.at(0).name.empty())) {
_data.clear();
}
_profile.setKeybindings(_data);
accept();
}
void KeybindingsDialog::chooseScripts() {
_errorMsg->clear();
ScriptlogDialog d(this);
connect(&d, &ScriptlogDialog::scriptsSelected, this, &KeybindingsDialog::appendScriptsToKeybind);
d.exec();
}
void KeybindingsDialog::keyPressEvent(QKeyEvent* evt) {
if (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) {
return;
}
else if (evt->key() == Qt::Key_Escape) {
if (_editModeNewItem) {
listItemCancelSave();
return;
}
}
QDialog::keyPressEvent(evt);
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -35,10 +35,9 @@
#include <QPushButton>
#include <QVBoxLayout>
MarkNodesDialog::MarkNodesDialog(openspace::Profile& profile, QWidget* parent)
MarkNodesDialog::MarkNodesDialog(QWidget* parent, std::vector<std::string>* markedNodes)
: QDialog(parent)
, _profile(profile)
, _data(_profile.markNodes())
, _markedNodes(markedNodes)
{
setWindowTitle("Mark Interesting Nodes");
createWidgets();
@@ -49,17 +48,19 @@ void MarkNodesDialog::createWidgets() {
_list = new QListWidget;
connect(
_list, &QListWidget::itemSelectionChanged,
this, &MarkNodesDialog::listItemSelected
[this]() {
_removeButton->setEnabled(true);
}
);
_list->setAlternatingRowColors(true);
_list->setMovement(QListView::Free);
_list->setDragDropMode(QListView::InternalMove);
_list->setResizeMode(QListView::Adjust);
for (size_t i = 0; i < _data.size(); ++i) {
_markedNodesListItems.push_back(
new QListWidgetItem(QString::fromStdString(_data[i]))
);
_list->addItem(_markedNodesListItems[i]);
for (size_t i = 0; i < _markedNodes->size(); ++i) {
QListWidgetItem* item =
new QListWidgetItem(QString::fromStdString(_markedNodes->at(i)));
_list->addItem(item);
}
layout->addWidget(_list);
@@ -77,10 +78,7 @@ void MarkNodesDialog::createWidgets() {
box->addWidget(_newNode);
QPushButton* addButton = new QPushButton("Add new");
connect(
addButton, &QPushButton::clicked,
this, &MarkNodesDialog::listItemAdded
);
connect(addButton, &QPushButton::clicked, this, &MarkNodesDialog::listItemAdded);
box->addWidget(addButton);
layout->addLayout(box);
}
@@ -88,40 +86,26 @@ void MarkNodesDialog::createWidgets() {
{
QDialogButtonBox* buttons = new QDialogButtonBox;
buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
QObject::connect(
connect(
buttons, &QDialogButtonBox::accepted,
this, &MarkNodesDialog::parseSelections
);
QObject::connect(
buttons, &QDialogButtonBox::rejected,
this, &MarkNodesDialog::reject
);
connect(buttons, &QDialogButtonBox::rejected, this, &MarkNodesDialog::reject);
layout->addWidget(buttons);
}
}
void MarkNodesDialog::listItemSelected() {
_removeButton->setEnabled(true);
}
void MarkNodesDialog::listItemAdded() {
if (_newNode->text().isEmpty()) {
return;
}
std::string itemToAdd = _newNode->text().toStdString();
const auto it = std::find(_data.cbegin(), _data.cend(), itemToAdd);
if (it != _data.end()) {
_list->setCurrentRow(static_cast<int>(std::distance(_data.cbegin(), it)));
}
else {
_data.push_back(itemToAdd);
_markedNodesListItems.push_back(new QListWidgetItem(_newNode->text()));
_list->addItem(_markedNodesListItems.back());
QListWidgetItem* item = new QListWidgetItem(_newNode->text());
_list->addItem(item);
// Scroll down to that blank line highlighted
_list->setCurrentItem(_markedNodesListItems.back());
}
// Scroll down to that blank line highlighted
_list->setCurrentItem(item);
// Blank-out entry again
_newNode->clear();
@@ -130,28 +114,25 @@ void MarkNodesDialog::listItemAdded() {
void MarkNodesDialog::listItemRemove() {
QListWidgetItem* item = _list->currentItem();
int index = _list->row(item);
if (index < 0 || index >= static_cast<int>(_markedNodesListItems.size())) {
return;
}
_list->takeItem(index);
_data.erase(_data.begin() + index);
_markedNodesListItems.erase(_markedNodesListItems.begin() + index);
}
void MarkNodesDialog::parseSelections() {
_profile.setMarkNodes(_data);
std::vector<std::string> nodes;
for (int i = 0; i < _list->count(); i++) {
QString node = _list->item(i)->text();
nodes.push_back(node.toStdString());
}
*_markedNodes = std::move(nodes);
accept();
}
void MarkNodesDialog::keyPressEvent(QKeyEvent* evt) {
if (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) {
if (_newNode->text().length() > 0 && _newNode->hasFocus()) {
listItemAdded();
return;
}
}
if ((evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) &&
(!_newNode->text().isEmpty() && _newNode->hasFocus()))
{
listItemAdded();
return;
}
QDialog::keyPressEvent(evt);
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -34,32 +34,32 @@
#include <QVBoxLayout>
#include <algorithm>
MetaDialog::MetaDialog(openspace::Profile& profile, QWidget *parent)
MetaDialog::MetaDialog(QWidget* parent, std::optional<openspace::Profile::Meta>* meta)
: QDialog(parent)
, _profile(profile)
, _meta(meta)
{
setWindowTitle("Meta");
createWidgets();
if (_profile.meta().has_value()) {
openspace::Profile::Meta meta = *_profile.meta();
if (meta.name.has_value()) {
_nameEdit->setText(QString::fromStdString(*meta.name));
if (_meta->has_value()) {
const openspace::Profile::Meta& m = **_meta;
if (m.name.has_value()) {
_nameEdit->setText(QString::fromStdString(*m.name));
}
if (meta.version.has_value()) {
_versionEdit->setText(QString::fromStdString(*meta.version));
if (m.version.has_value()) {
_versionEdit->setText(QString::fromStdString(*m.version));
}
if (meta.description.has_value()) {
_descriptionEdit->setText(QString::fromStdString(*meta.description));
if (m.description.has_value()) {
_descriptionEdit->setText(QString::fromStdString(*m.description));
}
if (meta.author.has_value()) {
_authorEdit->setText(QString::fromStdString(*meta.author));
if (m.author.has_value()) {
_authorEdit->setText(QString::fromStdString(*m.author));
}
if (meta.url.has_value()) {
_urlEdit->setText(QString::fromStdString(*meta.url));
if (m.url.has_value()) {
_urlEdit->setText(QString::fromStdString(*m.url));
}
if (meta.license.has_value()) {
_licenseEdit->setText(QString::fromStdString(*meta.license));
if (m.license.has_value()) {
_licenseEdit->setText(QString::fromStdString(*m.license));
}
}
}
@@ -127,10 +127,10 @@ void MetaDialog::save() {
if (!_licenseEdit->text().isEmpty()) {
m.license = _licenseEdit->text().toStdString();
}
_profile.setMeta(m);
*_meta = std::move(m);
}
else {
_profile.clearMeta();
*_meta = std::nullopt;
}
accept();
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -37,13 +37,18 @@
using namespace openspace;
namespace {
const Profile::Module Blank = { "", "", "" };
const Profile::Module Blank = {
.name = "",
.loadedInstruction = std::nullopt,
.notLoadedInstruction = std::nullopt
};
} // namespace
ModulesDialog::ModulesDialog(Profile& profile, QWidget *parent)
ModulesDialog::ModulesDialog(QWidget* parent,
std::vector<openspace::Profile::Module>* modules)
: QDialog(parent)
, _profile(profile)
, _data(_profile.modules())
, _modules(modules)
, _moduleData(*_modules)
{
setWindowTitle("Modules");
createWidgets();
@@ -53,21 +58,21 @@ ModulesDialog::ModulesDialog(Profile& profile, QWidget *parent)
void ModulesDialog::createWidgets() {
QBoxLayout* layout = new QVBoxLayout(this);
{
_list = new QListWidget;
connect(
_list, &QListWidget::itemSelectionChanged,
this, &ModulesDialog::listItemSelected
);
_list->setAlternatingRowColors(true);
_list->setMovement(QListView::Free);
_list->setResizeMode(QListView::Adjust);
for (const Profile::Module& m : _data) {
_list->addItem(new QListWidgetItem(createOneLineSummary(m)));
}
layout->addWidget(_list);
_list = new QListWidget;
connect(
_list, &QListWidget::itemSelectionChanged,
this, &ModulesDialog::listItemSelected
);
_list->setAlternatingRowColors(true);
_list->setMovement(QListView::Free);
_list->setResizeMode(QListView::Adjust);
for (const Profile::Module& m : _moduleData) {
_list->addItem(new QListWidgetItem(createOneLineSummary(m)));
}
layout->addWidget(_list);
{
QBoxLayout* box = new QHBoxLayout;
_buttonAdd = new QPushButton("Add new");
@@ -88,12 +93,14 @@ void ModulesDialog::createWidgets() {
{
_moduleLabel = new QLabel("Module");
layout->addWidget(_moduleLabel);
_moduleEdit = new QLineEdit;
_moduleEdit->setToolTip("Name of OpenSpace module related to this profile");
layout->addWidget(_moduleEdit);
_loadedLabel = new QLabel("Command if Module is Loaded");
layout->addWidget(_loadedLabel);
_loadedEdit = new QLineEdit;
_loadedEdit->setToolTip(
"Lua command(s) to execute if OpenSpace has been compiled with the module"
@@ -102,6 +109,7 @@ void ModulesDialog::createWidgets() {
_notLoadedLabel = new QLabel("Command if Module is NOT Loaded");
layout->addWidget(_notLoadedLabel);
_notLoadedEdit = new QLineEdit;
_notLoadedEdit->setToolTip(
"Lua command(s) to execute if the module is not present in the OpenSpace "
@@ -154,8 +162,8 @@ void ModulesDialog::createWidgets() {
QString ModulesDialog::createOneLineSummary(Profile::Module m) {
QString summary = QString::fromStdString(m.name);
bool hasCommandForLoaded = (m.loadedInstruction->length() > 0);
bool hasCommandForNotLoaded = (m.notLoadedInstruction->length() > 0);
bool hasCommandForLoaded = !m.loadedInstruction->empty();
bool hasCommandForNotLoaded = !m.notLoadedInstruction->empty();
if (hasCommandForLoaded && hasCommandForNotLoaded) {
summary += " (commands set for both loaded & not-loaded conditions)";
@@ -176,8 +184,8 @@ void ModulesDialog::listItemSelected() {
QListWidgetItem* item = _list->currentItem();
int index = _list->row(item);
if (!_data.empty()) {
const Profile::Module& m = _data[index];
if (!_moduleData.empty()) {
const Profile::Module& m = _moduleData[index];
_moduleEdit->setText(QString::fromStdString(m.name));
if (m.loadedInstruction.has_value()) {
_loadedEdit->setText(QString::fromStdString(*m.loadedInstruction));
@@ -185,6 +193,7 @@ void ModulesDialog::listItemSelected() {
else {
_loadedEdit->clear();
}
if (m.notLoadedInstruction.has_value()) {
_notLoadedEdit->setText(QString::fromStdString(*m.notLoadedInstruction));
}
@@ -196,14 +205,9 @@ void ModulesDialog::listItemSelected() {
}
bool ModulesDialog::isLineEmpty(int index) const {
bool isEmpty = true;
if (!_list->item(index)->text().isEmpty()) {
isEmpty = false;
}
if (!_data.empty() && !_data.at(0).name.empty()) {
isEmpty = false;
}
return isEmpty;
return
_list->item(index)->text().isEmpty() &&
_moduleData.empty() && !_moduleData.at(0).name.empty();
}
void ModulesDialog::listItemAdded() {
@@ -213,13 +217,13 @@ void ModulesDialog::listItemAdded() {
// Special case where list is "empty" but really has one line that is blank.
// This is done because QListWidget does not seem to like having its sole
// remaining item being removed.
_data.at(0) = Blank;
_moduleData.at(0) = Blank;
_list->item(0)->setText(" (Enter details below & click 'Save')");
_list->setCurrentRow(0);
transitionToEditMode();
}
else {
_data.push_back(Blank);
_moduleData.push_back(Blank);
_list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')"));
//Scroll down to that blank line highlighted
_list->setCurrentRow(_list->count() - 1);
@@ -227,16 +231,18 @@ void ModulesDialog::listItemAdded() {
}
// Blank-out the 2 text fields, set combo box to index 0
_moduleEdit->setText(QString::fromStdString(_data.back().name));
if (_data.back().loadedInstruction.has_value()) {
_loadedEdit->setText(QString::fromStdString(*_data.back().loadedInstruction));
_moduleEdit->setText(QString::fromStdString(_moduleData.back().name));
if (_moduleData.back().loadedInstruction.has_value()) {
_loadedEdit->setText(QString::fromStdString(
*_moduleData.back().loadedInstruction
));
}
else {
_loadedEdit->clear();
}
if (_data.back().notLoadedInstruction.has_value()) {
if (_moduleData.back().notLoadedInstruction.has_value()) {
_notLoadedEdit->setText(
QString::fromStdString(*_data.back().notLoadedInstruction)
QString::fromStdString(*_moduleData.back().notLoadedInstruction)
);
}
else {
@@ -255,11 +261,11 @@ void ModulesDialog::listItemSave() {
QListWidgetItem* item = _list->currentItem();
int index = _list->row(item);
if ( _data.size() > 0) {
_data[index].name = _moduleEdit->text().toStdString();
_data[index].loadedInstruction = _loadedEdit->text().toStdString();
_data[index].notLoadedInstruction = _notLoadedEdit->text().toStdString();
_list->item(index)->setText(createOneLineSummary(_data[index]));
if (!_moduleData.empty()) {
_moduleData[index].name = _moduleEdit->text().toStdString();
_moduleData[index].loadedInstruction = _loadedEdit->text().toStdString();
_moduleData[index].notLoadedInstruction = _notLoadedEdit->text().toStdString();
_list->item(index)->setText(createOneLineSummary(_moduleData[index]));
}
transitionFromEditMode();
_editModeNewItem = false;
@@ -267,28 +273,28 @@ void ModulesDialog::listItemSave() {
void ModulesDialog::listItemCancelSave() {
transitionFromEditMode();
if (_editModeNewItem && !_data.empty() && _data.back().name.empty()) {
if (_editModeNewItem && !_moduleData.empty() && _moduleData.back().name.empty()) {
listItemRemove();
}
_editModeNewItem = false;
}
void ModulesDialog::listItemRemove() {
if (_list->count() > 0) {
if (_list->currentRow() >= 0 && _list->currentRow() < _list->count()) {
if (_list->count() == 1) {
// Special case where last remaining item is being removed (QListWidget
// doesn't like the final item being removed so instead clear it)
_data.at(0) = Blank;
_list->item(0)->setText("");
}
else {
int index = _list->currentRow();
if (index >= 0 && index < _list->count()) {
delete _list->takeItem(index);
if (!_data.empty()) {
_data.erase(_data.begin() + index);
}
if (_list->count() > 0 &&
_list->currentRow() >= 0 && _list->currentRow() < _list->count())
{
if (_list->count() == 1) {
// Special case where last remaining item is being removed (QListWidget
// doesn't like the final item being removed so instead clear it)
_moduleData.at(0) = Blank;
_list->item(0)->setText("");
}
else {
int index = _list->currentRow();
if (index >= 0 && index < _list->count()) {
delete _list->takeItem(index);
if (!_moduleData.empty()) {
_moduleData.erase(_moduleData.begin() + index);
}
}
}
@@ -297,9 +303,6 @@ void ModulesDialog::listItemRemove() {
}
void ModulesDialog::transitionToEditMode() {
_list->setDisabled(true);
_buttonAdd->setDisabled(true);
_buttonRemove->setDisabled(true);
_buttonSave->setDisabled(true);
_buttonCancel->setDisabled(true);
_buttonBox->setDisabled(true);
@@ -339,10 +342,10 @@ void ModulesDialog::editBoxDisabled(bool disabled) {
void ModulesDialog::parseSelections() {
// Handle case with only one remaining but empty line
if ((_data.size() == 1) && (_data.at(0).name.empty())) {
_data.clear();
if ((_moduleData.size() == 1) && (_moduleData.at(0).name.empty())) {
_moduleData.clear();
}
_profile.setModules(_data);
*_modules = std::move(_moduleData);
accept();
}
@@ -361,4 +364,3 @@ void ModulesDialog::keyPressEvent(QKeyEvent* evt) {
}
QDialog::keyPressEvent(evt);
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -24,11 +24,11 @@
#include "profile/profileedit.h"
#include "profile/actiondialog.h"
#include "profile/additionalscriptsdialog.h"
#include "profile/assetsdialog.h"
#include "profile/cameradialog.h"
#include "profile/deltatimesdialog.h"
#include "profile/keybindingsdialog.h"
#include "profile/line.h"
#include "profile/marknodesdialog.h"
#include "profile/metadialog.h"
@@ -36,6 +36,7 @@
#include "profile/propertiesdialog.h"
#include "profile/timedialog.h"
#include <openspace/scene/profile.h>
#include <ghoul/fmt.h>
#include <QDialogButtonBox>
#include <QKeyEvent>
#include <QLabel>
@@ -69,17 +70,18 @@ namespace {
return results;
}
std::string summarizeKeybindings(const std::vector<Profile::Keybinding>& keybindings)
std::string summarizeKeybindings(const std::vector<Profile::Keybinding>& keybindings,
const std::vector<Profile::Action>& actions)
{
std::string results;
for (Profile::Keybinding k : keybindings) {
results += k.name + " (";
int keymod = static_cast<int>(k.key.modifier);
if (keymod != static_cast<int>(openspace::KeyModifier::NoModifier)) {
results += openspace::KeyModifierNames.at(keymod) + "+";
}
results += openspace::KeyNames.at(static_cast<int>(k.key.key));
results += ")\n";
const auto it = std::find_if(
actions.cbegin(), actions.cend(),
[id = k.action](const Profile::Action& a) { return a.identifier == id; }
);
std::string name = it != actions.end() ? it->name : "Unknown action";
results += fmt::format("{} ({})\n", name, ghoul::to_string(k.key));
}
return results;
}
@@ -87,7 +89,7 @@ namespace {
std::string summarizeProperties(const std::vector<Profile::Property>& properties) {
std::string results;
for (openspace::Profile::Property p : properties) {
results += p.name + " = " + p.value + '\n';
results += fmt::format("{} = {}\n", p.name, p.value);
}
return results;
}
@@ -182,9 +184,8 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
QGridLayout* container = new QGridLayout;
container->setColumnStretch(1, 1);
_keybindingsLabel = new QLabel("Keybindings");
_keybindingsLabel = new QLabel("Actions & Keybindings");
_keybindingsLabel->setObjectName("heading");
_keybindingsLabel->setWordWrap(true);
container->addWidget(_keybindingsLabel, 0, 0);
QPushButton* keybindingsProperties = new QPushButton("Edit");
@@ -333,26 +334,28 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
}
void ProfileEdit::initSummaryTextForEachCategory() {
_modulesLabel->setText(labelText(_profile.modules().size(), "Modules"));
_modulesLabel->setText(labelText(_profile.modules.size(), "Modules"));
_assetsLabel->setText(labelText(_profile.assets().size(), "Assets"));
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets())));
_assetsLabel->setText(labelText(_profile.assets.size(), "Assets"));
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets)));
_propertiesLabel->setText(labelText(_profile.properties().size(), "Properties"));
_propertiesLabel->setText(labelText(_profile.properties.size(), "Properties"));
_propertiesEdit->setText(
QString::fromStdString(summarizeProperties(_profile.properties()))
QString::fromStdString(summarizeProperties(_profile.properties))
);
_keybindingsLabel->setText(labelText(_profile.keybindings().size(), "Keybindings"));
_keybindingsEdit->setText(
QString::fromStdString(summarizeKeybindings(_profile.keybindings()))
_keybindingsLabel->setText(
labelText(_profile.keybindings.size(), "Actions & Keybindings")
);
_keybindingsEdit->setText(QString::fromStdString(
summarizeKeybindings(_profile.keybindings, _profile.actions)
));
_deltaTimesLabel->setText(
labelText(_profile.deltaTimes().size(), "Simulation Time Increments")
labelText(_profile.deltaTimes.size(), "Simulation Time Increments")
);
_interestingNodesLabel->setText(
labelText(_profile.markNodes().size(), "Mark Interesting Nodes")
labelText(_profile.markNodes.size(), "Mark Interesting Nodes")
);
}
@@ -363,7 +366,7 @@ void ProfileEdit::duplicateProfile() {
return;
}
constexpr const char Separator = '_';
constexpr char Separator = '_';
int version = 0;
if (size_t it = profile.rfind(Separator); it != std::string::npos) {
// If the value exists, we have a profile that potentially already has a version
@@ -401,68 +404,68 @@ void ProfileEdit::duplicateProfile() {
void ProfileEdit::openMeta() {
_errorMsg->clear();
MetaDialog(_profile, this).exec();
MetaDialog(this, &_profile.meta).exec();
}
void ProfileEdit::openModules() {
_errorMsg->clear();
ModulesDialog(_profile, this).exec();
_modulesLabel->setText(labelText(_profile.modules().size(), "Modules"));
ModulesDialog(this, &_profile.modules).exec();
_modulesLabel->setText(labelText(_profile.modules.size(), "Modules"));
}
void ProfileEdit::openProperties() {
_errorMsg->clear();
PropertiesDialog(_profile, this).exec();
_propertiesLabel->setText(labelText(_profile.properties().size(), "Properties"));
PropertiesDialog(this, &_profile.properties).exec();
_propertiesLabel->setText(labelText(_profile.properties.size(), "Properties"));
_propertiesEdit->setText(
QString::fromStdString(summarizeProperties(_profile.properties()))
QString::fromStdString(summarizeProperties(_profile.properties))
);
}
void ProfileEdit::openKeybindings() {
_errorMsg->clear();
KeybindingsDialog(_profile, this).exec();
_keybindingsLabel->setText(labelText(_profile.keybindings().size(), "Keybindings"));
_keybindingsEdit->setText(
QString::fromStdString(summarizeKeybindings(_profile.keybindings()))
);
ActionDialog(this, &_profile.actions, &_profile.keybindings).exec();
_keybindingsLabel->setText(labelText(_profile.keybindings.size(), "Keybindings"));
_keybindingsEdit->setText(QString::fromStdString(
summarizeKeybindings(_profile.keybindings, _profile.actions)
));
}
void ProfileEdit::openAssets() {
_errorMsg->clear();
AssetsDialog(_profile, _assetBasePath, _userAssetBasePath, this).exec();
_assetsLabel->setText(labelText(_profile.assets().size(), "Assets"));
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets())));
AssetsDialog(this, &_profile, _assetBasePath, _userAssetBasePath).exec();
_assetsLabel->setText(labelText(_profile.assets.size(), "Assets"));
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets)));
}
void ProfileEdit::openTime() {
_errorMsg->clear();
TimeDialog(_profile, this).exec();
TimeDialog(this, &_profile.time).exec();
}
void ProfileEdit::openDeltaTimes() {
_errorMsg->clear();
DeltaTimesDialog(_profile, this).exec();
DeltaTimesDialog(this, &_profile.deltaTimes).exec();
_deltaTimesLabel->setText(
labelText(_profile.deltaTimes().size(), "Simulation Time Increments")
labelText(_profile.deltaTimes.size(), "Simulation Time Increments")
);
}
void ProfileEdit::openAddedScripts() {
_errorMsg->clear();
AdditionalScriptsDialog(_profile, this).exec();
AdditionalScriptsDialog(this, &_profile.additionalScripts).exec();
}
void ProfileEdit::openCamera() {
_errorMsg->clear();
CameraDialog(_profile, this).exec();
CameraDialog(this, &_profile.camera).exec();
}
void ProfileEdit::openMarkNodes() {
_errorMsg->clear();
MarkNodesDialog(_profile, this).exec();
MarkNodesDialog(this, &_profile.markNodes).exec();
_interestingNodesLabel->setText(
labelText(_profile.markNodes().size(), "Mark Interesting Nodes")
labelText(_profile.markNodes.size(), "Mark Interesting Nodes")
);
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -25,14 +25,18 @@
#include "profile/propertiesdialog.h"
#include "profile/line.h"
#include "profile/scriptlogdialog.h"
#include <ghoul/filesystem/filesystem.h>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QEvent>
#include <QFile>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QTextStream>
#include <QVBoxLayout>
#include <iostream>
@@ -46,10 +50,11 @@ namespace {
};
} // namespace
PropertiesDialog::PropertiesDialog(Profile& profile, QWidget *parent)
PropertiesDialog::PropertiesDialog(QWidget* parent,
std::vector<openspace::Profile::Property>* properties)
: QDialog(parent)
, _profile(profile)
, _data(_profile.properties())
, _properties(properties)
, _propertyData(*_properties)
{
setWindowTitle("Set Property Values");
createWidgets();
@@ -65,8 +70,8 @@ void PropertiesDialog::createWidgets() {
_list, &QListWidget::itemSelectionChanged,
this, &PropertiesDialog::listItemSelected
);
for (size_t i = 0; i < _data.size(); ++i) {
_list->addItem(new QListWidgetItem(createOneLineSummary(_data[i])));
for (size_t i = 0; i < _propertyData.size(); ++i) {
_list->addItem(new QListWidgetItem(createOneLineSummary(_propertyData[i])));
}
layout->addWidget(_list);
}
@@ -88,6 +93,13 @@ void PropertiesDialog::createWidgets() {
box->addStretch();
_fillFromScriptLog = new QPushButton("Fill from ScriptLog");
connect(
_fillFromScriptLog, &QPushButton::clicked,
this, &PropertiesDialog::selectLineFromScriptLog
);
box->addWidget(_fillFromScriptLog);
layout->addLayout(box);
}
layout->addWidget(new Line);
@@ -172,8 +184,8 @@ void PropertiesDialog::listItemSelected() {
QListWidgetItem* item = _list->currentItem();
int index = _list->row(item);
if (_data.size() > 0) {
Profile::Property& p = _data[index];
if (!_propertyData.empty()) {
Profile::Property& p = _propertyData[index];
if (p.setType == Profile::Property::SetType::SetPropertyValueSingle) {
_commandCombo->setCurrentIndex(0);
}
@@ -191,7 +203,7 @@ bool PropertiesDialog::isLineEmpty(int index) {
if (!_list->item(index)->text().isEmpty()) {
isEmpty = false;
}
if (!_data.empty() && !_data.at(0).name.empty()) {
if (!_propertyData.empty() && !_propertyData.at(0).name.empty()) {
isEmpty = false;
}
return isEmpty;
@@ -200,26 +212,26 @@ bool PropertiesDialog::isLineEmpty(int index) {
void PropertiesDialog::listItemAdded() {
int currentListSize = _list->count();
if ((currentListSize == 1) && (isLineEmpty(0))) {
// Special case where list is "empty" but really has one line that is blank.
// This is done because QListWidget does not seem to like having its sole
// remaining item being removed.
_data.at(0) = Blank;
_list->item(0)->setText(" (Enter details below & click 'Save')");
_list->setCurrentRow(0);
transitionToEditMode();
}
else {
_data.push_back(Blank);
_list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')"));
//Scroll down to that blank line highlighted
_list->setCurrentRow(_list->count() - 1);
}
if ((currentListSize == 1) && (isLineEmpty(0))) {
// Special case where list is "empty" but really has one line that is blank.
// This is done because QListWidget does not seem to like having its sole
// remaining item being removed.
_propertyData.at(0) = Blank;
_list->item(0)->setText(" (Enter details below & click 'Save')");
_list->setCurrentRow(0);
transitionToEditMode();
}
else {
_propertyData.push_back(Blank);
_list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')"));
//Scroll down to that blank line highlighted
_list->setCurrentRow(_list->count() - 1);
}
// Blank-out the 2 text fields, set combo box to index 0
_commandCombo->setCurrentIndex(0);
_propertyEdit->setText(QString::fromStdString(_data.back().name));
_valueEdit->setText(QString::fromStdString(_data.back().value));
_propertyEdit->setText(QString::fromStdString(_propertyData.back().name));
_valueEdit->setText(QString::fromStdString(_propertyData.back().value));
_commandCombo->setFocus(Qt::OtherFocusReason);
_editModeNewItem = true;
}
@@ -232,16 +244,17 @@ void PropertiesDialog::listItemSave() {
QListWidgetItem* item = _list->currentItem();
int index = _list->row(item);
if ( _data.size() > 0) {
if (!_propertyData.empty()) {
if (_commandCombo->currentIndex() == 0) {
_data[index].setType = Profile::Property::SetType::SetPropertyValueSingle;
_propertyData[index].setType =
Profile::Property::SetType::SetPropertyValueSingle;
}
else {
_data[index].setType = Profile::Property::SetType::SetPropertyValue;
_propertyData[index].setType = Profile::Property::SetType::SetPropertyValue;
}
_data[index].name = _propertyEdit->text().toStdString();
_data[index].value = _valueEdit->text().toStdString();
_list->item(index)->setText(createOneLineSummary(_data[index]));
_propertyData[index].name = _propertyEdit->text().toStdString();
_propertyData[index].value = _valueEdit->text().toStdString();
_list->item(index)->setText(createOneLineSummary(_propertyData[index]));
}
transitionFromEditMode();
_editModeNewItem = false;
@@ -250,12 +263,12 @@ void PropertiesDialog::listItemSave() {
bool PropertiesDialog::areRequiredFormsFilled() {
bool requiredFormsFilled = true;
QString errors;
if (_propertyEdit->text().length() == 0) {
if (_propertyEdit->text().isEmpty()) {
errors += "Missing property name";
requiredFormsFilled = false;
}
if (_valueEdit->text().length() == 0) {
if (errors.length() > 0) {
if (_valueEdit->text().isEmpty()) {
if (!errors.isEmpty()) {
errors += ", ";
}
errors += "Missing value";
@@ -268,32 +281,30 @@ bool PropertiesDialog::areRequiredFormsFilled() {
void PropertiesDialog::listItemCancelSave() {
listItemSelected();
transitionFromEditMode();
if (_editModeNewItem) {
if (_data.size() > 0) {
if (_data.back().name.length() == 0 || _data.back().value.length() == 0) {
listItemRemove();
}
}
if (_editModeNewItem && !_propertyData.empty() &&
(_propertyData.back().name.empty() || _propertyData.back().value.empty()))
{
listItemRemove();
}
_editModeNewItem = false;
}
void PropertiesDialog::listItemRemove() {
if (_list->count() > 0) {
if (_list->currentRow() >= 0 && _list->currentRow() < _list->count()) {
if (_list->count() == 1) {
//Special case where last remaining item is being removed (QListWidget
// doesn't like the final item being removed so instead clear it)
_data.at(0) = Blank;
_list->item(0)->setText("");
}
else {
int index = _list->currentRow();
if (index >= 0 && index < _list->count()) {
delete _list->takeItem(index);
if (_data.size() > 0) {
_data.erase(_data.begin() + index);
}
if (_list->count() > 0 &&
(_list->currentRow() >= 0 && _list->currentRow() < _list->count()))
{
if (_list->count() == 1) {
// Special case where last remaining item is being removed (QListWidget
// doesn't like the final item being removed so instead clear it)
_propertyData.at(0) = Blank;
_list->item(0)->setText("");
}
else {
int index = _list->currentRow();
if (index >= 0 && index < _list->count()) {
delete _list->takeItem(index);
if (_propertyData.size() > 0) {
_propertyData.erase(_propertyData.begin() + index);
}
}
}
@@ -302,9 +313,6 @@ void PropertiesDialog::listItemRemove() {
}
void PropertiesDialog::transitionToEditMode() {
_list->setDisabled(true);
_addButton->setDisabled(true);
_removeButton->setDisabled(true);
_saveButton->setDisabled(true);
_cancelButton->setDisabled(true);
_buttonBox->setDisabled(true);
@@ -344,10 +352,10 @@ void PropertiesDialog::editBoxDisabled(bool disabled) {
void PropertiesDialog::parseSelections() {
// Handle case with only one remaining but empty line
if ((_data.size() == 1) && (_data.at(0).name.compare("") == 0)) {
_data.clear();
if ((_propertyData.size() == 1) && _propertyData.at(0).name.empty()) {
_propertyData.clear();
}
_profile.setProperties(_data);
*_properties = std::move(_propertyData);
accept();
}
@@ -358,12 +366,60 @@ void PropertiesDialog::keyPressEvent(QKeyEvent* evt) {
}
return;
}
else if (evt->key() == Qt::Key_Escape) {
if (_editModeNewItem) {
listItemCancelSave();
return;
}
else if (evt->key() == Qt::Key_Escape && _editModeNewItem) {
listItemCancelSave();
return;
}
QDialog::keyPressEvent(evt);
}
void PropertiesDialog::selectLineFromScriptLog() {
ScriptlogDialog d(this, "openspace.setPropertyValue");
connect(
&d, &ScriptlogDialog::scriptsSelected,
[this](std::vector<std::string> scripts) {
for (const std::string& script : scripts) {
listItemAdded();
QString text = QString::fromStdString(script);
if (!text.startsWith("openspace.setPropertyValue")) {
return;
}
// We have a string that is of the form:
// openspace.setPropertyValue('prop', value);
if (text.startsWith("openspace.setPropertyValueSingle")) {
_commandCombo->setCurrentIndex(0);
std::string_view prefix = "openspace.setPropertyValueSingle";
text = text.mid(static_cast<int>(prefix.size()) + 1); // +1 for (
}
else {
// command == "openspace.setPropertyValue"
_commandCombo->setCurrentIndex(1);
std::string_view prefix = "openspace.setPropertyValue";
text = text.mid(static_cast<int>(prefix.size()) + 1); // +1 for (
}
// Remove everything past the closing brace
text = text.left(text.indexOf(")"));
QStringList textList = text.split(",");
if (textList.size() < 2) {
return;
}
// Remove the string markers around the property
QString property = textList[0].mid(1, textList[0].size() - 2);
textList.removeFirst();
QString value = textList.join(",");
_propertyEdit->setText(property.trimmed());
_valueEdit->setText(value.trimmed());
listItemSave();
}
}
);
d.exec();
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -23,49 +23,90 @@
****************************************************************************************/
#include "profile/scriptlogdialog.h"
#include "profile/line.h"
#include <openspace/engine/configuration.h>
#include <openspace/engine/globals.h>
#include <openspace/scene/profile.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/fmt.h>
#include <QGridLayout>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QFile>
#include <QPushButton>
#include <QTextStream>
#include <QVBoxLayout>
ScriptlogDialog::ScriptlogDialog(QWidget* parent)
ScriptlogDialog::ScriptlogDialog(QWidget* parent, std::string filter)
: QDialog(parent)
, _scriptLogFile(openspace::global::configuration->scriptLog)
, _fixedFilter(std::move(filter))
{
setWindowTitle("Scriptlog");
createWidgets();
QFile file(QString::fromStdString(absPath("${LOGS}/scriptLog.txt").string()));
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
// removing return from a few statments
// these are usually generated by gui panels
line.remove(QRegularExpression("^return "));
if (!line.isEmpty()) {
_scriptlogList->addItem(line);
}
}
}
loadScriptFile();
}
void ScriptlogDialog::createWidgets() {
QBoxLayout* layout = new QVBoxLayout(this);
// Column 0 Column 1
// *-------------------------*------------*
// | Title |
// *--------------------------------------*
// | Filter scripts * Reload |
// *--------------------------------------*
// | Script list |
// *--------------------------------------*
// * Save Cancel *
// *-------------------------*------------*
QGridLayout* layout = new QGridLayout(this);
{
QLabel* heading = new QLabel("Choose commands from log/scriptLog.txt");
QLabel* heading = new QLabel(QString::fromStdString(fmt::format(
"Choose commands from \"{}\"", _scriptLogFile
)));
heading->setObjectName("heading");
layout->addWidget(heading);
layout->addWidget(heading, 0, 0, 1, 2);
QPushButton* open = new QPushButton;
open->setIcon(open->style()->standardIcon(QStyle::SP_FileIcon));
connect(
open, &QPushButton::clicked,
[this, heading]() {
QString file = QFileDialog::getOpenFileName(
this,
"Select log file",
"",
"*.txt"
);
_scriptLogFile = file.toStdString();
heading->setText(QString::fromStdString(fmt::format(
"Choose commands from \"{}\"", _scriptLogFile
)));
loadScriptFile();
}
);
layout->addWidget(open, 0, 1, Qt::AlignRight);
}
_filter = new QLineEdit;
_filter->setPlaceholderText("Filter the list of scripts");
connect(_filter, &QLineEdit::textEdited, this, &ScriptlogDialog::updateScriptList);
layout->addWidget(_filter, 1, 0);
_reloadFile = new QPushButton("Reload");
_reloadFile->setToolTip("Reload the script log file");
connect(_reloadFile, &QPushButton::clicked, this, &ScriptlogDialog::loadScriptFile);
layout->addWidget(_reloadFile, 1, 1);
_scriptlogList = new QListWidget;
_scriptlogList->setSelectionMode(QAbstractItemView::SelectionMode::MultiSelection);
layout->addWidget(_scriptlogList);
layout->addWidget(_scriptlogList, 2, 0, 1, 2);
layout->addWidget(new Line);
{
QDialogButtonBox* buttons = new QDialogButtonBox;
@@ -78,20 +119,57 @@ void ScriptlogDialog::createWidgets() {
buttons, &QDialogButtonBox::rejected,
this, &ScriptlogDialog::reject
);
layout->addWidget(buttons);
layout->addWidget(buttons, 3, 0, 1, 2);
}
}
void ScriptlogDialog::loadScriptFile() {
_scripts.clear();
std::string log = absPath(_scriptLogFile).string();
QFile file(QString::fromStdString(log));
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
// removing return from statements which are usually generated by gui panels
line.remove(QRegularExpression("^return "));
if (!line.isEmpty()) {
_scripts.push_back(line.toStdString());
}
}
}
updateScriptList();
}
void ScriptlogDialog::updateScriptList() {
std::string filter = _filter->text().toStdString();
QListWidgetItem* curr = _scriptlogList->currentItem();
std::string selection;
if (curr) {
selection = curr->text().toStdString();
}
int index = -1;
_scriptlogList->clear();
for (const std::string& script : _scripts) {
bool foundDynamic = script.find(filter) != std::string::npos;
bool foundStatic =
_fixedFilter.empty() ? true : script.find(_fixedFilter) != std::string::npos;
if (foundDynamic && foundStatic) {
if (script == selection && index == -1) {
index = _scriptlogList->count();
}
_scriptlogList->addItem(QString::fromStdString(script));
}
}
}
void ScriptlogDialog::saveChosenScripts() {
std::string chosenScripts;
std::vector<std::string> chosenScripts;
QList<QListWidgetItem*> itemList = _scriptlogList->selectedItems();
for (int i = 0; i < itemList.size(); ++i) {
chosenScripts += itemList.at(i)->text().toStdString();
if (i < itemList.size()) {
chosenScripts += "\n";
}
for (QListWidgetItem* item : _scriptlogList->selectedItems()) {
chosenScripts.push_back(item->text().toStdString());
}
emit scriptsSelected(chosenScripts);
accept();
}

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -25,6 +25,7 @@
#include "profile/timedialog.h"
#include "profile/line.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDateTimeEdit>
#include <QDialogButtonBox>
@@ -37,33 +38,36 @@
using namespace openspace;
TimeDialog::TimeDialog(openspace::Profile& profile, QWidget* parent)
TimeDialog::TimeDialog(QWidget* parent, std::optional<openspace::Profile::Time>* time)
: QDialog(parent)
, _profile(profile)
, _time(time)
{
setWindowTitle("Time");
createWidgets();
QStringList types = { "Absolute", "Relative" };
_typeCombo->addItems(types);
if (_profile.time().has_value()) {
_data = *_profile.time();
if (_data.type == Profile::Time::Type::Relative) {
if (_data.value == "") {
_data.value = "now";
if (_time->has_value()) {
_timeData = **_time;
if (_timeData.type == Profile::Time::Type::Relative) {
if (_timeData.value.empty()) {
_timeData.value = "0d";
}
_relativeEdit->setSelection(0, _relativeEdit->text().length());
int len = static_cast<int>(_relativeEdit->text().length());
_relativeEdit->setSelection(0, len);
}
else {
_absoluteEdit->setSelectedSection(QDateTimeEdit::YearSection);
}
}
else {
_data.type = Profile::Time::Type::Relative;
_data.value = "now";
_timeData.type = Profile::Time::Type::Relative;
_timeData.value = "0d";
}
_initializedAsAbsolute = (_data.type == Profile::Time::Type::Absolute);
enableAccordingToType(static_cast<int>(_data.type));
_startPaused->setChecked(_timeData.startPaused);
_initializedAsAbsolute = (_timeData.type == Profile::Time::Type::Absolute);
enableAccordingToType(static_cast<int>(_timeData.type));
}
void TimeDialog::createWidgets() {
@@ -81,6 +85,7 @@ void TimeDialog::createWidgets() {
{
_absoluteLabel = new QLabel("Absolute UTC:");
layout->addWidget(_absoluteLabel);
_absoluteEdit = new QDateTimeEdit;
_absoluteEdit->setDisplayFormat("yyyy-MM-dd T hh:mm:ss");
_absoluteEdit->setDateTime(QDateTime::currentDateTime());
@@ -89,12 +94,21 @@ void TimeDialog::createWidgets() {
{
_relativeLabel = new QLabel("Relative Time:");
layout->addWidget(_relativeLabel);
_relativeEdit = new QLineEdit;
_relativeEdit->setToolTip(
"String for relative time to actual (e.g. \"-1d\" for back 1 day)"
);
layout->addWidget(_relativeEdit);
}
{
_startPaused = new QCheckBox("Start with time paused");
_startPaused->setChecked(false);
_startPaused->setToolTip(
"If this is checked, the profile will start with the delta time paused"
);
layout->addWidget(_startPaused);
}
layout->addWidget(new Line);
{
QDialogButtonBox* buttons = new QDialogButtonBox;
@@ -114,18 +128,18 @@ void TimeDialog::enableAccordingToType(int idx) {
if (comboIdx == Profile::Time::Type::Relative) {
_relativeEdit->setText("<font color='black'>Relative Time:</font>");
if (_initializedAsAbsolute) {
_relativeEdit->setText("now");
_relativeEdit->setText("0d");
}
else {
_relativeEdit->setText(QString::fromStdString(_data.value));
_relativeEdit->setText(QString::fromStdString(_timeData.value));
}
_relativeEdit->setFocus(Qt::OtherFocusReason);
}
else {
_relativeEdit->setText("<font color='gray'>Relative Time:</font>");
size_t tIdx = _data.value.find_first_of('T', 0);
QString importDate = QString::fromStdString(_data.value.substr(0, tIdx));
QString importTime = QString::fromStdString(_data.value.substr(tIdx + 1));
size_t tIdx = _timeData.value.find_first_of('T', 0);
QString importDate = QString::fromStdString(_timeData.value.substr(0, tIdx));
QString importTime = QString::fromStdString(_timeData.value.substr(tIdx + 1));
_absoluteEdit->setDate(QDate::fromString(importDate, Qt::DateFormat::ISODate));
_absoluteEdit->setTime(QTime::fromString(importTime));
_relativeEdit->clear();
@@ -141,16 +155,17 @@ void TimeDialog::enableFormatForAbsolute(bool enableAbs) {
}
void TimeDialog::approved() {
constexpr const int Relative = static_cast<int>(Profile::Time::Type::Relative);
constexpr int Relative = static_cast<int>(Profile::Time::Type::Relative);
if (_typeCombo->currentIndex() == Relative) {
if (_relativeEdit->text().isEmpty()) {
_profile.clearTime();
*_time = std::nullopt;
}
else {
Profile::Time t;
t.type = Profile::Time::Type::Relative;
t.value = _relativeEdit->text().toStdString();
_profile.setTime(t);
t.startPaused = _startPaused->isChecked();
*_time = t;
}
}
else {
@@ -161,7 +176,8 @@ void TimeDialog::approved() {
_absoluteEdit->date().toString("yyyy-MM-dd").toStdString(),
_absoluteEdit->time().toString().toStdString()
);
_profile.setTime(t);
t.startPaused = _startPaused->isChecked();
*_time = t;
}
accept();
}

View File

@@ -0,0 +1,160 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "sgctedit/displaywindowunion.h"
#include "sgctedit/windowcontrol.h"
#include <ghoul/fmt.h>
#include <QColor>
#include <QFrame>
#include <QPushButton>
#include <QVBoxLayout>
#include <array>
#include <string>
DisplayWindowUnion::DisplayWindowUnion(const std::vector<QRect>& monitorSizeList,
int nMaxWindows,
const std::array<QColor, 4>& windowColors,
QWidget* parent)
: QWidget(parent)
{
createWidgets(nMaxWindows, monitorSizeList, windowColors);
showWindows();
}
void DisplayWindowUnion::createWidgets(int nMaxWindows,
std::vector<QRect> monitorResolutions,
std::array<QColor, 4> windowColors)
{
// Add all window controls (some will be hidden from GUI initially)
for (int i = 0; i < nMaxWindows; ++i) {
const int monitorNumForThisWindow =
(monitorResolutions.size() > 1 && i >= 2) ? 1 : 0;
WindowControl* ctrl = new WindowControl(
monitorNumForThisWindow,
i,
monitorResolutions,
windowColors[i],
this
);
_windowControl.push_back(ctrl);
connect(
ctrl, &WindowControl::windowChanged,
this, &DisplayWindowUnion::windowChanged
);
}
QBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSizeConstraint(QLayout::SizeConstraint::SetMinimumSize);
{
QBoxLayout* layoutMonButton = new QHBoxLayout;
_removeWindowButton = new QPushButton("Remove Window");
_removeWindowButton->setFocusPolicy(Qt::NoFocus);
_removeWindowButton->setToolTip(
"Remove window from the configuration (at least one window is required)"
);
connect(
_removeWindowButton, &QPushButton::clicked,
this, &DisplayWindowUnion::removeWindow
);
layoutMonButton->addWidget(_removeWindowButton);
layoutMonButton->addStretch(1);
_addWindowButton = new QPushButton("Add Window");
_addWindowButton->setToolTip(QString::fromStdString(fmt::format(
"Add a window to the configuration (up to {} windows allowed)", nMaxWindows
)));
_addWindowButton->setFocusPolicy(Qt::NoFocus);
connect(
_addWindowButton, &QPushButton::clicked,
this, &DisplayWindowUnion::addWindow
);
layoutMonButton->addWidget(_addWindowButton);
layout->addLayout(layoutMonButton);
}
QFrame* line = new QFrame;
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
layout->addWidget(line);
QBoxLayout* layoutWindows = new QHBoxLayout;
layoutWindows->setContentsMargins(0, 0, 0, 0);
layoutWindows->setSpacing(0);
for (int i = 0; i < nMaxWindows; ++i) {
layoutWindows->addWidget(_windowControl[i]);
if (i < (nMaxWindows - 1)) {
QFrame* frameForNextWindow = new QFrame;
frameForNextWindow->setFrameShape(QFrame::VLine);
_frameBorderLines.push_back(frameForNextWindow);
layoutWindows->addWidget(frameForNextWindow);
}
}
layout->addLayout(layoutWindows);
layout->addStretch();
}
std::vector<WindowControl*> DisplayWindowUnion::windowControls() const {
std::vector<WindowControl*> res;
res.reserve(_nWindowsDisplayed);
for (unsigned int i = 0; i < _nWindowsDisplayed; ++i) {
res.push_back(_windowControl[i]);
}
return res;
}
void DisplayWindowUnion::addWindow() {
if (_nWindowsDisplayed < _windowControl.size()) {
_windowControl[_nWindowsDisplayed]->resetToDefaults();
_nWindowsDisplayed++;
showWindows();
}
}
void DisplayWindowUnion::removeWindow() {
if (_nWindowsDisplayed > 1) {
_nWindowsDisplayed--;
showWindows();
}
}
void DisplayWindowUnion::showWindows() {
for (size_t i = 0; i < _windowControl.size(); ++i) {
_windowControl[i]->setVisible(i < _nWindowsDisplayed);
}
for (size_t i = 0; i < _frameBorderLines.size(); ++i) {
_frameBorderLines[i]->setVisible(i < (_nWindowsDisplayed - 1));
}
_removeWindowButton->setEnabled(_nWindowsDisplayed > 1);
_addWindowButton->setEnabled(_nWindowsDisplayed != _windowControl.size());
for (WindowControl* w : _windowControl) {
w->showWindowLabel(_nWindowsDisplayed > 1);
}
emit nWindowsChanged(_nWindowsDisplayed);
}

View File

@@ -0,0 +1,206 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "sgctedit/monitorbox.h"
#include <QPainter>
namespace {
constexpr float MarginFractionOfWidgetSize = 0.05f;
constexpr int WindowOpacity = 170;
QRectF computeUnion(const std::vector<QRect>& monitorResolutions) {
QRectF res = QRectF(0.f, 0.f, 0.f, 0.f);
for (const QRect& m : monitorResolutions) {
res |= m;
}
return res;
}
} // namespace
MonitorBox::MonitorBox(QRect widgetDims, const std::vector<QRect>& monitorResolutions,
unsigned int nWindows, const std::array<QColor, 4>& windowColors,
QWidget* parent)
: QWidget(parent)
, _nWindows(nWindows)
, _colorsForWindows(windowColors)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QRectF monitorArrangement = computeUnion(monitorResolutions);
const float aspectRatio = monitorArrangement.width() / monitorArrangement.height();
if (aspectRatio > 1.0) {
float borderMargin = 2.f * MarginFractionOfWidgetSize * widgetDims.width();
widgetDims.setHeight(widgetDims.width() / aspectRatio + borderMargin);
}
else {
float borderMargin = 2.f * MarginFractionOfWidgetSize * widgetDims.height();
widgetDims.setWidth(widgetDims.height() * aspectRatio + borderMargin);
}
setFixedSize(widgetDims.width(), widgetDims.height());
//
// Map monitor resolution to widget coordinates
std::vector<QSizeF> offsets =
aspectRatio >= 1.f ?
computeScaledResolutionLandscape(monitorArrangement, monitorResolutions) :
computeScaledResolutionPortrait(monitorArrangement, monitorResolutions);
for (size_t i = 0; i < monitorResolutions.size(); ++i) {
_monitorDimensionsScaled.emplace_back(
offsets[i].width(),
offsets[i].height(),
monitorResolutions[i].width() * _monitorScaleFactor,
monitorResolutions[i].height() * _monitorScaleFactor
);
}
}
void MonitorBox::paintEvent(QPaintEvent*) {
QPainter painter(this);
//
// Draw widget border
constexpr int Radius = 10;
painter.setPen(QPen(Qt::gray, 4));
painter.drawRoundedRect(0, 0, width() - 1, height() - 1, Radius, Radius);
//
// Draw window out-of-bounds region(s) first
for (int i = 0; i < _nWindows; ++i) {
painter.setBrush(Qt::BDiagPattern);
painter.setPen(QPen(_colorsForWindows[i], 0));
painter.drawRect(_windowRendering[i]);
}
// Draw & fill monitors over the out-of-bounds regions
painter.setPen(QPen(Qt::black, 2));
painter.setBrush(Qt::NoBrush);
for (size_t i = 0; i < _monitorDimensionsScaled.size(); ++i) {
const QColor Grey = QColor(0xDD, 0xDD, 0xDD);
painter.drawRect(_monitorDimensionsScaled[i]);
painter.fillRect(_monitorDimensionsScaled[i], QBrush(Grey, Qt::SolidPattern));
if (_monitorDimensionsScaled.size() > 1 && i == 0) {
// We only want to render the "Primary" if there are multiple windows
QPointF textPos = QPointF(
_monitorDimensionsScaled[i].left() + 4.0,
_monitorDimensionsScaled[i].top() + 24.0
);
QFont f("Arial");
f.setPixelSize(24);
painter.setFont(f);
painter.drawText(textPos, "Primary");
}
}
// Draw window number(s) first for darker contrast, then window(s) over both
// out-of-bounds and monitors
for (int i = 0; i < _nWindows; ++i) {
QPointF p = QPointF(
_windowRendering[i].left() + 5.0,
_windowRendering[i].bottom() - 5.0
);
p.setX(std::clamp(p.x(), 0.0, static_cast<double>(size().width()) - 10.0));
p.setY(std::clamp(p.y(), 20.0, static_cast<double>(size().height())));
painter.drawText(p, QString::number(i + 1));
}
//
// Paint window
for (int i = 0; i < _nWindows; ++i) {
painter.setPen(QPen(_colorsForWindows[i], 1));
painter.drawRect(_windowRendering[i]);
QColor fillColor = _colorsForWindows[i];
fillColor.setAlpha(WindowOpacity);
painter.fillRect(_windowRendering[i], QBrush(fillColor, Qt::SolidPattern));
}
}
void MonitorBox::windowDimensionsChanged(unsigned int mIdx, unsigned int wIdx,
const QRectF& newDimensions)
{
_windowRendering[wIdx] = QRectF(
_monitorDimensionsScaled[mIdx].x() + newDimensions.left() * _monitorScaleFactor,
_monitorDimensionsScaled[mIdx].y() + newDimensions.top() * _monitorScaleFactor,
newDimensions.width() * _monitorScaleFactor,
newDimensions.height() * _monitorScaleFactor
);
update();
}
std::vector<QSizeF> MonitorBox::computeScaledResolutionLandscape(QRectF arrangement,
const std::vector<QRect>& resolutions)
{
std::vector<QSizeF> offsets;
float marginWidget = size().width() * MarginFractionOfWidgetSize;
float virtualWidth = size().width() * (1.f - MarginFractionOfWidgetSize * 2.f);
_monitorScaleFactor = virtualWidth / arrangement.width();
const float aspectRatio = arrangement.width() / arrangement.height();
const float newHeight = virtualWidth / aspectRatio;
for (const QRect& res : resolutions) {
float x = marginWidget + (res.x() - arrangement.x()) * _monitorScaleFactor;
float y = marginWidget + (size().height() - newHeight - marginWidget) / 4.f +
(res.y() - arrangement.y()) * _monitorScaleFactor;
offsets.emplace_back(x, y);
}
return offsets;
}
std::vector<QSizeF> MonitorBox::computeScaledResolutionPortrait(QRectF arrangement,
const std::vector<QRect>& resolutions)
{
std::vector<QSizeF> offsets;
float marginWidget = size().height() * MarginFractionOfWidgetSize;
float virtualHeight = size().height() * (1.f - MarginFractionOfWidgetSize * 2.f);
_monitorScaleFactor = virtualHeight / arrangement.height();
const float aspectRatio = arrangement.width() / arrangement.height();
const float newWidth = virtualHeight * aspectRatio;
for (const QRect& res : resolutions) {
float x = marginWidget + (size().width() - newWidth - marginWidget) / 4.f +
(res.x() - arrangement.x()) * _monitorScaleFactor;
float y = marginWidget + (res.y() - arrangement.y()) * _monitorScaleFactor;
offsets.emplace_back(x, y);
}
return offsets;
}
void MonitorBox::nWindowsDisplayedChanged(int nWindows) {
if (_nWindows != nWindows) {
_nWindows = nWindows;
update();
}
}

View File

@@ -0,0 +1,109 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "sgctedit/orientationdialog.h"
#include <QDialogButtonBox>
#include <QDoubleValidator>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <ghoul/glm.h>
OrientationDialog::OrientationDialog(sgct::quat& orientation, QWidget* parent)
: QDialog(parent)
, _orientationValue(orientation)
{
setWindowTitle("Global Orientation");
QGridLayout* layoutWindow = new QGridLayout(this);
{
QString pitchTip = "Pitch or elevation: negative numbers tilt the camera "
"downwards; positive numbers tilt upwards.\nThe allowed range is [-90, 90]. "
"Internally, this corresponds to the x value in the quaternion";
QLabel* labelPitch = new QLabel("Pitch");
labelPitch->setToolTip(pitchTip);
layoutWindow->addWidget(labelPitch, 0, 0);
_linePitch = new QLineEdit;
_linePitch->setText(QString::number(glm::degrees(_orientationValue.x)));
_linePitch->setToolTip(pitchTip);
QDoubleValidator* validatorPitch = new QDoubleValidator(-90.0, 90.0, 15);
validatorPitch->setNotation(QDoubleValidator::StandardNotation);
_linePitch->setValidator(validatorPitch);
layoutWindow->addWidget(_linePitch, 0, 1);
}
{
QString rollTip = "Roll or bank: negative numbers rotate the camera counter-"
"clockwise; positive numbers clockwise.\nThe allowed range is [-180, 180]. "
"Internally, this corresponds to the z value in the quaternion";
QLabel* labelRoll = new QLabel("Roll");
labelRoll->setToolTip(rollTip);
layoutWindow->addWidget(labelRoll, 1, 0);
_lineRoll = new QLineEdit;
_lineRoll->setText(QString::number(glm::degrees(_orientationValue.z)));
_lineRoll->setToolTip(rollTip);
QDoubleValidator* validatorRoll = new QDoubleValidator(-360.0, 360.0, 15);
validatorRoll->setNotation(QDoubleValidator::StandardNotation);
_lineRoll->setValidator(validatorRoll);
layoutWindow->addWidget(_lineRoll, 1, 1);
}
{
QString yawTip = "Yaw, heading, or azimuth: negative numbers pan the camera "
"to the left; positive numbers pan to the\nright. The allowed range is "
"[-360, 360]. Internally, this corresponds to the y value in the quaternion";
QLabel* labelYaw = new QLabel;
labelYaw ->setText("Yaw");
labelYaw->setToolTip(yawTip);
layoutWindow->addWidget(labelYaw, 2, 0);
_lineYaw = new QLineEdit;
_lineYaw->setText(QString::number(glm::degrees(_orientationValue.y)));
_lineYaw->setToolTip(yawTip);
QDoubleValidator* validatorYaw = new QDoubleValidator(-180.0, 180.0, 15, this);
validatorYaw->setNotation(QDoubleValidator::StandardNotation);
_lineYaw->setValidator(validatorYaw);
layoutWindow->addWidget(_lineYaw, 2, 1);
}
{
QDialogButtonBox* buttons = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel
);
connect(buttons, &QDialogButtonBox::accepted, this, &OrientationDialog::ok);
connect(buttons, &QDialogButtonBox::rejected, this, &OrientationDialog::reject);
layoutWindow->addWidget(buttons, 3, 0, 1, 2);
}
}
void OrientationDialog::ok() {
_orientationValue.x = glm::radians(_linePitch->text().toFloat());
_orientationValue.y = glm::radians(_lineYaw->text().toFloat());
_orientationValue.z = glm::radians(_lineRoll->text().toFloat());
_orientationValue.w = 1.0;
accept();
}

View File

@@ -0,0 +1,82 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "sgctedit/settingswidget.h"
#include "sgctedit/orientationdialog.h"
#include <QCheckBox>
#include <QPushButton>
#include <QVBoxLayout>
SettingsWidget::SettingsWidget(sgct::quat orientation, QWidget* parent)
: QWidget(parent)
, _orientationValue(std::move(orientation))
{
QBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
_showUiOnFirstWindow = new QCheckBox("Show only user interface on the first window");
_showUiOnFirstWindow->setChecked(false);
_showUiOnFirstWindow->setToolTip(
"If enabled the first window is marked as a GUI window resulting in the user "
"interface only being shown on that window and the rendering is suppressed on "
"this first window. The remaining windows will render normally but they will not "
"show the user interface"
);
layout->addWidget(_showUiOnFirstWindow);
_checkBoxVsync = new QCheckBox("Enable VSync");
_checkBoxVsync->setToolTip(
"If enabled the framerate will be locked to the refresh rate of the monitor"
);
layout->addWidget(_checkBoxVsync);
QPushButton* orientationButton = new QPushButton("Global Orientation");
orientationButton->setToolTip(
"Opens a separate dialog for setting the pitch, yaw, and roll of the camera\n"
"(the orientation applies to all viewports)"
);
orientationButton->setFocusPolicy(Qt::NoFocus);
layout->addWidget(orientationButton);
connect(
orientationButton, &QPushButton::released,
[this]() {
OrientationDialog _orientationDialog(_orientationValue, this);
_orientationDialog.exec();
}
);
}
sgct::quat SettingsWidget::orientation() const {
return _orientationValue;
}
bool SettingsWidget::vsync() const {
return _checkBoxVsync->isChecked();
}
bool SettingsWidget::showUiOnFirstWindow() const {
return _showUiOnFirstWindow->isChecked();
}

View File

@@ -0,0 +1,299 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "sgctedit/sgctedit.h"
#include <sgctedit/displaywindowunion.h>
#include <sgctedit/monitorbox.h>
#include <sgctedit/settingswidget.h>
#include <sgctedit/windowcontrol.h>
#include <ghoul/filesystem/filesystem.h>
#include <QApplication>
#include <QFileDialog>
#include <QFrame>
#include <QMessageBox>
#include <QPushButton>
#include <QScreen>
#include <QVBoxLayout>
#include <filesystem>
namespace {
constexpr QRect MonitorWidgetSize = QRect(0, 0, 500, 500);
constexpr int MaxNumberWindows = 4;
// Returns true if the windows are not ordered correctly. 'Correct' in this means that
// there is a smaller window defined before a bigger one
// This check is only necessary until
// https://github.com/OpenSpace/OpenSpace/issues/507
// is fixed
bool hasWindowIssues(const sgct::config::Cluster& cluster) {
sgct::ivec2 size = sgct::ivec2(
std::numeric_limits<int>::max(),
std::numeric_limits<int>::max()
);
for (const sgct::config::Window& window : cluster.nodes.front().windows) {
if (window.size.x <= size.x && window.size.y <= size.y) {
size = window.size;
}
else {
// The window size is bigger than a previous one, so we gotta bail
return true;
}
}
// We got to the end without running into any problems, so we are golden
return false;
}
} // namespace
SgctEdit::SgctEdit(QWidget* parent, std::string userConfigPath)
: QDialog(parent)
, _userConfigPath(std::move(userConfigPath))
{
QList<QScreen*> screens = qApp->screens();
setWindowTitle("Window Configuration Editor");
int nScreensManaged = std::min(static_cast<int>(screens.length()), 4);
std::vector<QRect> monitorSizes;
for (int s = 0; s < nScreensManaged; ++s) {
QSize size = screens[s]->size();
QRect geometry = screens[s]->availableGeometry();
int actualWidth = std::max(size.width(), geometry.width());
int actualHeight = std::max(size.height(), geometry.height());
monitorSizes.emplace_back(
geometry.x(),
geometry.y(),
static_cast<int>(actualWidth * screens[s]->devicePixelRatio()),
static_cast<int>(actualHeight * screens[s]->devicePixelRatio())
);
}
createWidgets(monitorSizes);
}
void SgctEdit::createWidgets(const std::vector<QRect>& monitorSizes) {
QBoxLayout* layout = new QVBoxLayout(this);
layout->setSizeConstraint(QLayout::SetFixedSize);
sgct::quat orientation = sgct::quat(0.f, 0.f, 0.f, 0.f);
if (_cluster.scene.has_value() && _cluster.scene->orientation.has_value()) {
orientation = *_cluster.scene->orientation;
}
{
MonitorBox* monitorBox = new MonitorBox(
MonitorWidgetSize,
monitorSizes,
MaxNumberWindows,
_colorsForWindows,
this
);
layout->addWidget(monitorBox, 0, Qt::AlignCenter);
QFrame* displayFrame = new QFrame;
displayFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
QBoxLayout* displayLayout = new QVBoxLayout(displayFrame);
_displayWidget = new DisplayWindowUnion(
monitorSizes,
MaxNumberWindows,
_colorsForWindows,
this
);
connect(
_displayWidget, &DisplayWindowUnion::windowChanged,
monitorBox, &MonitorBox::windowDimensionsChanged
);
connect(
_displayWidget, &DisplayWindowUnion::nWindowsChanged,
monitorBox, &MonitorBox::nWindowsDisplayedChanged
);
_displayWidget->addWindow();
displayLayout->addWidget(_displayWidget);
layout->addWidget(displayFrame);
}
_settingsWidget = new SettingsWidget(orientation, this);
layout->addWidget(_settingsWidget);
{
QHBoxLayout* layoutButtonBox = new QHBoxLayout;
layoutButtonBox->addStretch(1);
QFrame* bottomBorder = new QFrame;
bottomBorder->setFrameShape(QFrame::HLine);
layout->addWidget(bottomBorder);
_cancelButton = new QPushButton("Cancel");
_cancelButton->setToolTip("Cancel changes");
_cancelButton->setFocusPolicy(Qt::NoFocus);
connect(_cancelButton, &QPushButton::released, this, &SgctEdit::reject);
layoutButtonBox->addWidget(_cancelButton);
_saveButton = new QPushButton("Save As");
_saveButton->setToolTip("Save configuration changes");
_saveButton->setFocusPolicy(Qt::NoFocus);
connect(_saveButton, &QPushButton::released, this, &SgctEdit::save);
layoutButtonBox->addWidget(_saveButton);
_applyButton = new QPushButton("Apply Without Saving");
_applyButton->setToolTip("Apply configuration changes without saving to file");
_applyButton->setFocusPolicy(Qt::NoFocus);
connect(_applyButton, &QPushButton::released, this, &SgctEdit::apply);
layoutButtonBox->addWidget(_applyButton);
layout->addLayout(layoutButtonBox);
}
}
std::filesystem::path SgctEdit::saveFilename() const {
return _saveTarget;
}
void SgctEdit::save() {
sgct::config::Cluster cluster = generateConfiguration();
if (hasWindowIssues(cluster)) {
int ret = QMessageBox::warning(
this,
"Window Sizes Incompatible",
"Window sizes for multiple windows have to be strictly ordered, meaning that "
"the size of window 1 has to be bigger in each dimension than window 2, "
"window 2 has to be bigger than window 3 (if it exists), and window 3 has to "
"be bigger than window 4.\nOtherwise, rendering errors might occur.\n\nAre "
"you sure you want to continue?",
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No)
);
if (ret == QMessageBox::No) {
return;
}
}
QString fileName = QFileDialog::getSaveFileName(
this,
"Save Window Configuration File",
QString::fromStdString(_userConfigPath),
"Window Configuration (*.json)",
nullptr
#ifdef __linux__
// Linux in Qt5 and Qt6 crashes when trying to access the native dialog here
, QFileDialog::DontUseNativeDialog
#endif
);
if (!fileName.isEmpty()) {
_saveTarget = fileName.toStdString();
_cluster = std::move(cluster);
accept();
}
}
void SgctEdit::apply() {
sgct::config::Cluster cluster = generateConfiguration();
if (hasWindowIssues(cluster)) {
int ret = QMessageBox::warning(
this,
"Window Sizes Incompatible",
"Window sizes for multiple windows have to be strictly ordered, meaning that "
"the size of window 1 has to be bigger in each dimension than window 2, "
"window 2 has to be bigger than window 3 (if it exists), and window 3 has to "
"be bigger than window 4.\nOtherwise, rendering errors might occur.\n\nAre "
"you sure you want to continue?",
QMessageBox::Yes | QMessageBox::No
);
if (ret == QMessageBox::No) {
return;
}
}
std::string userCfgTempDir = _userConfigPath;
if (userCfgTempDir.back() != '/') {
userCfgTempDir += '/';
}
userCfgTempDir += "temp";
if (!std::filesystem::is_directory(absPath(userCfgTempDir))) {
std::filesystem::create_directories(absPath(userCfgTempDir));
}
_saveTarget = userCfgTempDir + "/apply-without-saving.json";
_cluster = std::move(cluster);
accept();
}
sgct::config::Cluster SgctEdit::generateConfiguration() const {
sgct::config::Cluster cluster;
sgct::config::Scene scene;
scene.orientation = _settingsWidget->orientation();
cluster.scene = std::move(scene);
cluster.masterAddress = "localhost";
if (_settingsWidget->vsync()) {
sgct::config::Settings::Display display;
display.swapInterval = 1;
sgct::config::Settings settings;
settings.display = display;
cluster.settings = settings;
}
sgct::config::Node node;
node.address = "localhost";
node.port = 20401;
// Save Windows
unsigned int windowIndex = 0;
for (WindowControl* wCtrl : _displayWidget->windowControls()) {
sgct::config::Window window = wCtrl->generateWindowInformation();
window.id = windowIndex++;
node.windows.push_back(std::move(window));
}
if (_settingsWidget->showUiOnFirstWindow()) {
sgct::config::Window& window = node.windows.front();
window.viewports.back().isTracked = false;
window.tags.push_back("GUI");
window.draw2D = true;
window.draw3D = false;
// Disable 2D rendering on all non-GUI windows
for (size_t w = 1; w < node.windows.size(); ++w) {
node.windows[w].draw2D = false;
}
}
cluster.nodes.push_back(node);
sgct::config::User user;
user.eyeSeparation = 0.065f;
user.position = sgct::vec3(0.f, 0.f, 4.f);
cluster.users = { user };
return cluster;
}
sgct::config::Cluster SgctEdit::cluster() const {
return _cluster;
}

View File

@@ -0,0 +1,829 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "sgctedit/windowcontrol.h"
#include <ghoul/misc/assert.h>
#include <ghoul/fmt.h>
#include "sgctedit/displaywindowunion.h"
#include "sgctedit/monitorbox.h"
#include <QCheckBox>
#include <QComboBox>
#include <QGridLayout>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QSpinBox>
namespace {
std::array<std::string, 4> MonitorNames = {
"Primary", "Secondary", "Tertiary", "Quaternary"
};
const QList<QString> QualityTypes = {
"Low (256)", "Medium (512)", "High (1K)", "1.5K (1536)", "2K (2048)", "4K (4096)",
"8K (8192)", "16K (16384)", "32K (32768)", "64K (65536)"
};
constexpr int QualityValues[10] = {
256, 512, 1024, 1536, 2048, 4096, 8192, 16384, 32768, 65536
};
constexpr std::array<QRectF, 4> DefaultWindowSizes = {
QRectF(50.f, 50.f, 1280.f, 720.f),
QRectF(150.f, 150.f, 1280.f, 720.f),
QRectF(50.f, 50.f, 1280.f, 720.f),
QRectF(150.f, 150.f, 1280.f, 720.f)
};
constexpr int LineEditWidthFixedWindowSize = 50;
constexpr float DefaultFovH = 80.f;
constexpr float DefaultFovV = 50.534f;
constexpr float DefaultHeightOffset = 0.f;
constexpr int MaxWindowSizePixels = 10000;
constexpr double FovEpsilon = 0.00001;
QList<QString> monitorNames(const std::vector<QRect>& resolutions) {
QList<QString> monitorNames;
for (size_t i = 0; i < resolutions.size(); i++) {
std::string fullName = fmt::format(
"{} ({}x{})",
MonitorNames[i], resolutions[i].width(), resolutions[i].height()
);
monitorNames.push_back(QString::fromStdString(fullName));
}
return monitorNames;
}
} // namespace
WindowControl::WindowControl(int monitorIndex, int windowIndex,
const std::vector<QRect>& monitorDims,
const QColor& winColor, QWidget* parent)
: QWidget(parent)
, _monitorIndexDefault(monitorIndex)
, _windowIndex(windowIndex)
, _monitorResolutions(monitorDims)
, _lockIcon(":/images/outline_locked.png")
, _unlockIcon(":/images/outline_unlocked.png")
{
createWidgets(winColor);
resetToDefaults();
}
void WindowControl::createWidgets(const QColor& windowColor) {
// Col 0 Col 1 Col 2 Col 3 Col 4 Col 5 Col 6 Col 7
// *----------*----------*-------*----------*-------*--------*-------*-------*
// | Window {n} | R0
// | Name * [oooooooooooooooooooooooooooooooooooooooooooooooooooooooooo] | R1
// | Monitor * DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD> | R2
// | Size * [xxxxxx] * x * [yyyyyy] * px * <lock> * < Set to | R3
// | Offset * [xxxxxx] * , * [yyyyyy] * px * * Fullscreen> | R4
// | [] Window Decoration | R5
// | [] UI only in this window | R6
// | ~~~~~~~~~~~~~~~~~~~~~~~~~Projection components~~~~~~~~~~~~~~~~~~~~~~~~~ | R7
// *----------*----------*-------*----------*-------*--------*-------*-------*
QGridLayout* layout = new QGridLayout(this);
QMargins margins = layout->contentsMargins();
layout->setContentsMargins(margins.left(), 0, margins.right(), 0);
layout->setColumnStretch(6, 1);
layout->setRowStretch(8, 1);
_windowNumber = new QLabel("Window " + QString::number(_windowIndex + 1));
_windowNumber->setStyleSheet(QString::fromStdString(fmt::format(
"QLabel {{ color : #{:02x}{:02x}{:02x}; }}",
windowColor.red(), windowColor.green(), windowColor.blue()
)));
layout->addWidget(_windowNumber, 0, 0, 1, 8, Qt::AlignCenter);
{
QString tip = "The name for the window (displayed in title bar)";
QLabel* labelName = new QLabel("Name");
labelName->setToolTip(tip);
layout->addWidget(labelName, 1, 0);
_windowName = new QLineEdit;
_windowName->setToolTip(tip);
layout->addWidget(_windowName, 1, 1, 1, 7);
}
QString tip = "The monitor where this window is located";
_monitor = new QComboBox;
_monitor->addItems(monitorNames(_monitorResolutions));
_monitor->setCurrentIndex(_monitorIndexDefault);
_monitor->setToolTip(tip);
connect(
_monitor, qOverload<int>(&QComboBox::currentIndexChanged),
[this]() {
emit windowChanged(
_monitor->currentIndex(),
_windowIndex,
_windowDimensions
);
}
);
if (_monitorResolutions.size() > 1) {
QLabel* labelLocation = new QLabel("Monitor");
labelLocation->setToolTip(tip);
layout->addWidget(labelLocation, 2, 0);
layout->addWidget(_monitor, 2, 1, 1, 7);
}
{
QLabel* size = new QLabel("Size");
size->setToolTip("The window's width & height in pixels");
size->setFixedWidth(55);
layout->addWidget(size, 3, 0);
_sizeX = new QSpinBox;
_sizeX->setMinimum(0);
_sizeX->setMaximum(MaxWindowSizePixels);
_sizeX->setFixedWidth(LineEditWidthFixedWindowSize);
_sizeX->setToolTip("The window's width (pixels)");
layout->addWidget(_sizeX, 3, 1);
connect(
_sizeX, QOverload<int>::of(&QSpinBox::valueChanged),
this, &WindowControl::onSizeXChanged
);
QLabel* delim = new QLabel("x");
delim->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
layout->addWidget(delim, 3, 2);
_sizeY = new QSpinBox;
_sizeY->setMinimum(0);
_sizeY->setMaximum(MaxWindowSizePixels);
_sizeY->setFixedWidth(LineEditWidthFixedWindowSize);
_sizeY->setToolTip("The window's height (pixels)");
layout->addWidget(_sizeY, 3, 3, Qt::AlignLeft);
connect(
_sizeY, QOverload<int>::of(&QSpinBox::valueChanged),
this, &WindowControl::onSizeYChanged
);
QLabel* unit = new QLabel("px");
unit->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
layout->addWidget(unit, 3, 4, Qt::AlignLeft);
QPushButton* lockAspectRatio = new QPushButton;
lockAspectRatio->setIcon(_unlockIcon);
lockAspectRatio->setFocusPolicy(Qt::NoFocus);
lockAspectRatio->setToolTip("Locks/Unlocks the aspect ratio of the window size");
layout->addWidget(lockAspectRatio, 3, 5, Qt::AlignLeft);
connect(
lockAspectRatio, &QPushButton::released,
this, &WindowControl::onAspectRatioLockClicked
);
connect(
lockAspectRatio, &QPushButton::released,
[this, lockAspectRatio]() {
lockAspectRatio->setIcon(_aspectRatioLocked ? _lockIcon : _unlockIcon);
}
);
}
{
QLabel* offset = new QLabel("Offset");
offset->setToolTip(
"The x,y location of the window's upper left corner from monitor's "
"upper-left corner origin (pixels)"
);
offset->setFixedWidth(55);
layout->addWidget(offset, 4, 0);
_offsetX = new QSpinBox;
_offsetX->setMinimum(0);
_offsetX->setMaximum(MaxWindowSizePixels);
_offsetX->setToolTip(
"The x location of the window's upper left corner from monitor's left side "
"(pixels)"
);
_offsetX->setFixedWidth(LineEditWidthFixedWindowSize);
layout->addWidget(_offsetX, 4, 1);
connect(
_offsetX, QOverload<int>::of(&QSpinBox::valueChanged),
this, &WindowControl::onOffsetXChanged
);
QLabel* comma = new QLabel(",");
comma->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
layout->addWidget(comma, 4, 2);
_offsetY = new QSpinBox;
_offsetY->setMinimum(0);
_offsetY->setMaximum(MaxWindowSizePixels);
_offsetY->setToolTip(
"The y location of the window's upper left corner from monitor's top edge "
"(pixels)"
);
_offsetY->setFixedWidth(LineEditWidthFixedWindowSize);
layout->addWidget(_offsetY, 4, 3, Qt::AlignLeft);
connect(
_offsetY, QOverload<int>::of(&QSpinBox::valueChanged),
this, &WindowControl::onOffsetYChanged
);
QLabel* unit = new QLabel("px");
unit->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));
layout->addWidget(unit, 4, 4, Qt::AlignLeft);
}
{
QBoxLayout* holderLayout = new QHBoxLayout;
QPushButton* setFullscreen = new QPushButton("Set Window\nto Fullscreen");
setFullscreen->setToolTip(
"If enabled, the window will be created in an exclusive fullscreen mode. The "
"size of this\nwindow will be set to the screen resolution, and the window "
"decoration automatically disabled"
);
setFullscreen->setFocusPolicy(Qt::NoFocus);
setFullscreen->setSizePolicy(
QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding
);
holderLayout->addStretch();
holderLayout->addWidget(setFullscreen);
holderLayout->addStretch();
layout->addLayout(holderLayout, 3, 6, 2, 2);
connect(
setFullscreen, &QPushButton::released,
this, &WindowControl::onFullscreenClicked
);
_windowDecoration = new QCheckBox("Window Decoration");
_windowDecoration->setChecked(true);
_windowDecoration->setToolTip(
"If disabled, the window will not have a border frame or title bar, and no\n "
"controls for minimizing/maximizing, resizing, or closing the window"
);
layout->addWidget(_windowDecoration, 5, 0, 1, 8);
}
{
QFrame* projectionGroup = new QFrame;
projectionGroup->setVisible(true);
projectionGroup->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
//
// Projection combobox
QBoxLayout* projectionLayout = new QVBoxLayout(projectionGroup);
projectionLayout->setContentsMargins(0, 0, 0, 0);
projectionLayout->setSpacing(0);
_projectionType = new QComboBox;
_projectionType->addItems({
"Planar Projection",
"Fisheye",
"Spherical Mirror Projection",
"Cylindrical Projection",
"Equirectangular Projection"
});
_projectionType->setToolTip("Select from the supported window projection types");
_projectionType->setCurrentIndex(0);
projectionLayout->addWidget(_projectionType);
connect(
_projectionType, qOverload<int>(&QComboBox::currentIndexChanged),
this, &WindowControl::onProjectionChanged
);
_planar.widget = createPlanarWidget();
projectionLayout->addWidget(_planar.widget);
_fisheye.widget = createFisheyeWidget();
projectionLayout->addWidget(_fisheye.widget);
_sphericalMirror.widget = createSphericalMirrorWidget();
projectionLayout->addWidget(_sphericalMirror.widget);
_cylindrical.widget = createCylindricalWidget();
projectionLayout->addWidget(_cylindrical.widget);
_equirectangular.widget = createEquirectangularWidget();
projectionLayout->addWidget(_equirectangular.widget);
// We need to trigger this once to ensure that all of the defaults are correct
onProjectionChanged(0);
layout->addWidget(projectionGroup, 7, 0, 1, 8);
}
}
QWidget* WindowControl::createPlanarWidget() {
// Column 0 Column 1 Column 2
// *----------*----------*----------*
// | { Informational text } | Row 0
// | HFOV * [oooooo] * Lock | Row 1
// | VFOV * [oooooo] * Button | Row 2
// *----------*----------*----------*
QWidget* widget = new QWidget;
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
QLabel* info = new QLabel(
"This projection type is the 'regular' projection with a horizontal and a "
"vertical field of view, given in degrees. The wider the field of view, the "
"more content is shown at the same time, but everything becomes smaller. Very "
"large values will introduce distorions on the corners"
);
info->setObjectName("info");
info->setWordWrap(true);
layout->addWidget(info, 0, 0, 1, 3);
QLabel* fovH = new QLabel("Horizontal FOV");
QString hfovTip = "The total horizontal field of view of the viewport (degrees)";
fovH->setToolTip(hfovTip);
layout->addWidget(fovH, 1, 0);
_planar.fovH = new QDoubleSpinBox;
_planar.fovH->setMinimum(FovEpsilon);
_planar.fovH->setMaximum(180.0 - FovEpsilon);
_planar.fovH->setValue(DefaultFovH);
_planar.fovH->setEnabled(false);
_planar.fovH->setToolTip(hfovTip);
_planar.fovH->setSizePolicy(
QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding
);
layout->addWidget(_planar.fovH, 1, 1);
QLabel* fovV = new QLabel("Vertical FOV");
QString vfovTip = "The total vertical field of view of the viewport (degrees). "
"Internally,\nthe values for 'up' & 'down' will each be half this value";
fovV->setToolTip(vfovTip);
layout->addWidget(fovV, 2, 0);
_planar.fovV = new QDoubleSpinBox;
_planar.fovV->setMinimum(FovEpsilon);
_planar.fovV->setMaximum(180.0 - FovEpsilon);
_planar.fovV->setValue(DefaultFovV);
_planar.fovV->setEnabled(false);
_planar.fovV->setToolTip(vfovTip);
_planar.fovV->setSizePolicy(
QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding
);
layout->addWidget(_planar.fovV, 2, 1);
QPushButton* lockFov = new QPushButton;
lockFov->setIcon(_lockIcon);
lockFov->setToolTip(
"Locks and scales the Horizontal & Vertical field-of-view to the ideal settings "
"based on the provided aspect ratio"
);
lockFov->setFocusPolicy(Qt::NoFocus);
layout->addWidget(lockFov, 1, 2, 2, 1);
connect(
lockFov, &QPushButton::released,
[this, lockFov]() {
lockFov->setIcon(_fovLocked ? _lockIcon : _unlockIcon);
}
);
connect(lockFov, &QPushButton::released, this, &WindowControl::onFovLockClicked);
return widget;
}
QWidget* WindowControl::createFisheyeWidget() {
// Column 0 Column 1
// *------------*-----------*
// | { Informational text } | Row 0
// | Quality * [DDDDD>] | Row 1
// | [] Spout Output | Row 2
// *------------*-----------*
QWidget* widget = new QWidget;
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
QLabel* info = new QLabel(
"This projection provides a rendering in a format that is suitable for "
"planetariums and other immersive environments. A field-of-view of 180 degrees "
"is presented as a circular image in the center of the screen. For this "
"projection a square window is suggested, but not necessary"
);
info->setObjectName("info");
info->setWordWrap(true);
layout->addWidget(info, 0, 0, 1, 2);
QLabel* qualityFisheye = new QLabel("Quality");
QString qualityTip = "Determines the pixel resolution of the projection rendering. "
"The higher resolution,\nthe better the rendering quality, but at the expense of "
"increased rendering times";
qualityFisheye->setToolTip(qualityTip);
layout->addWidget(qualityFisheye, 1, 0);
_fisheye.quality = new QComboBox;
_fisheye.quality->addItems(QualityTypes);
_fisheye.quality->setToolTip(qualityTip);
_fisheye.quality->setCurrentIndex(2);
layout->addWidget(_fisheye.quality, 1, 1);
_fisheye.spoutOutput = new QCheckBox("Spout Output");
_fisheye.spoutOutput->setToolTip(
"This projection method provides the ability to share the reprojected image "
"using the Spout library.\nThis library only supports the Windows operating "
"system. Spout makes it possible to make the rendered\nimages available to other "
"real-time applications on the same machine for further processing"
);
layout->addWidget(_fisheye.spoutOutput, 2, 0, 1, 2);
return widget;
}
QWidget* WindowControl::createSphericalMirrorWidget() {
// Column 0 Column 1
// *------------*-----------*
// | { Informational text } | Row 0
// | Quality * [DDDDD>] | Row 1
// *------------*-----------*
QWidget* widget = new QWidget;
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
QLabel* info = new QLabel(
"This projection is rendering a image suite for use with a spherical mirror "
"projection as described by Paul Bourke (http://paulbourke.net/dome/mirrordome/) "
"and which is a low-cost yet effective way to provide content for a sphericalal "
"display surface using a regular projector"
);
info->setObjectName("info");
info->setWordWrap(true);
layout->addWidget(info, 0, 0, 1, 2);
QLabel* qualitySphericalMirror = new QLabel("Quality");
QString qualityTip = "Determines the pixel resolution of the projection rendering. "
"The higher resolution,\nthe better the rendering quality, but at the expense of "
"increased rendering times";
qualitySphericalMirror->setToolTip(qualityTip);
layout->addWidget(qualitySphericalMirror, 1, 0);
_sphericalMirror.quality = new QComboBox;
_sphericalMirror.quality->addItems(QualityTypes);
_sphericalMirror.quality->setToolTip(qualityTip);
_sphericalMirror.quality->setCurrentIndex(2);
layout->addWidget(_sphericalMirror.quality, 1, 1);
return widget;
}
QWidget* WindowControl::createCylindricalWidget() {
// Column 0 Column 1
// *------------*-----------*
// | { Informational text } | Row 0
// | Quality * [DDDDD>] | Row 1
// | HOffset * [oooooo] | Row 2
// *------------*-----------*
QWidget* widget = new QWidget;
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
QLabel* info = new QLabel(
"This projection type provides a cylindrical rendering that covers 360 degrees "
"around the camera, which can be useful in immersive environments that are not "
"spherical, but where, for example, all walls of a room are covered with "
"projectors"
);
info->setObjectName("info");
info->setWordWrap(true);
layout->addWidget(info, 0, 0, 1, 2);
QLabel* qualityCylindrical = new QLabel("Quality");
QString qualityTip = "Determines the pixel resolution of the projection rendering. "
"The higher resolution,\nthe better the rendering quality, but at the expense of "
"increased rendering times";
qualityCylindrical->setToolTip(qualityTip);
layout->addWidget(qualityCylindrical, 1, 0);
_cylindrical.quality = new QComboBox;
_cylindrical.quality->addItems(QualityTypes);
_cylindrical.quality->setToolTip(qualityTip);
_cylindrical.quality->setCurrentIndex(2);
layout->addWidget(_cylindrical.quality, 1, 1);
QLabel* heightOffset = new QLabel("Height Offset");
QString heightTip = "Offsets the height from which the cylindrical projection is "
"generated.\nThis is, in general, only necessary if the user position is offset "
"and\ncountering that offset is desired in order to continue producing\na "
"'standard' cylindrical projection";
heightOffset->setToolTip(heightTip);
layout->addWidget(heightOffset, 2, 0);
_cylindrical.heightOffset = new QDoubleSpinBox;
_cylindrical.heightOffset->setMinimum(-1000000.0);
_cylindrical.heightOffset->setMaximum(1000000.0);
_cylindrical.heightOffset->setValue(DefaultHeightOffset);
_cylindrical.heightOffset->setToolTip(heightTip);
layout->addWidget(_cylindrical.heightOffset, 2, 1);
return widget;
}
QWidget* WindowControl::createEquirectangularWidget() {
// Column 0 Column 1
// *------------*-----------*
// | { Informational text } | Row 0
// | Quality * [DDDDD>] | Row 1
// | [] Spout Output | Row 2
// *------------*-----------*
QWidget* widget = new QWidget;
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
QLabel* info = new QLabel(
"This projection provides the rendering as an image in equirectangular "
"projection, which is a common display type for 360 surround video. When "
"uploading a video in equirectangular projection to YouTube, for example, it "
"will use it as a 360 video"
);
info->setObjectName("info");
info->setWordWrap(true);
layout->addWidget(info, 0, 0, 1, 2);
QLabel* qualityEquirectangular = new QLabel("Quality");
QString qualityTip = "Determines the pixel resolution of the projection rendering. "
"The higher resolution,\nthe better the rendering quality, but at the expense of "
"increased rendering times";
qualityEquirectangular->setToolTip(qualityTip);
layout->addWidget(qualityEquirectangular, 1, 0);
_equirectangular.quality = new QComboBox;
_equirectangular.quality->addItems(QualityTypes);
_equirectangular.quality->setToolTip(qualityTip);
_equirectangular.quality->setCurrentIndex(2);
layout->addWidget(_equirectangular.quality, 1, 1);
_equirectangular.spoutOutput = new QCheckBox("Spout Output");
_equirectangular.spoutOutput->setToolTip(
"This projection method provides the ability to share the reprojected image "
"using the Spout library.\nThis library only supports the Windows operating "
"system. Spout makes it possible to make the rendered\nimages available to other "
"real-time applications on the same machine for further processing"
);
layout->addWidget(_equirectangular.spoutOutput, 2, 0, 1, 2);
return widget;
}
void WindowControl::resetToDefaults() {
//
// Determine ideal window sizes
constexpr float IdealScaleVerticalLines = 2.f / 3.f;
constexpr int PrimaryMonitorIdx = 0;
_windowDimensions = DefaultWindowSizes[_windowIndex];
_offsetX->setValue(_windowDimensions.x());
_offsetY->setValue(_windowDimensions.y());
float newHeight =
_monitorResolutions[PrimaryMonitorIdx].height() * IdealScaleVerticalLines;
float newWidth = newHeight * IdealAspectRatio;
_windowDimensions.setHeight(newHeight);
_windowDimensions.setWidth(newWidth);
_sizeX->setValue(static_cast<int>(newWidth));
_sizeY->setValue(static_cast<int>(newHeight));
//
// Reset widgets
_windowName->clear();
if (_monitorResolutions.size() > 1) {
_monitor->setCurrentIndex(_monitorIndexDefault);
}
_windowDecoration->setChecked(true);
_fisheye.spoutOutput->setChecked(false);
_equirectangular.spoutOutput->setChecked(false);
_projectionType->setCurrentIndex(static_cast<int>(ProjectionIndices::Planar));
_planar.fovV->setValue(DefaultFovH);
_planar.fovV->setValue(DefaultFovV);
_cylindrical.heightOffset->setValue(DefaultHeightOffset);
_fisheye.quality->setCurrentIndex(2);
_sphericalMirror.quality->setCurrentIndex(2);
_cylindrical.quality->setCurrentIndex(2);
_equirectangular.quality->setCurrentIndex(2);
emit windowChanged(_monitorIndexDefault, _windowIndex, _windowDimensions);
}
void WindowControl::showWindowLabel(bool show) {
_windowNumber->setVisible(show);
}
sgct::config::Projections WindowControl::generateProjectionInformation() const {
ProjectionIndices type =
static_cast<WindowControl::ProjectionIndices>(_projectionType->currentIndex());
const bool isSpoutFisheye =
type == ProjectionIndices::Fisheye && _fisheye.spoutOutput->isChecked();
const bool isSpoutEquirectangular =
type == ProjectionIndices::Equirectangular &&
_equirectangular.spoutOutput->isChecked();
using namespace sgct::config;
switch (type) {
case ProjectionIndices::Fisheye:
if (isSpoutFisheye) {
SpoutOutputProjection projection;
projection.mapping = SpoutOutputProjection::Mapping::Fisheye;
projection.quality = QualityValues[_fisheye.quality->currentIndex()];
projection.mappingSpoutName = "OpenSpace";
return projection;
}
else {
FisheyeProjection projection;
projection.quality = QualityValues[_fisheye.quality->currentIndex()];
projection.fov = 180.f;
projection.tilt = 0.f;
return projection;
}
case ProjectionIndices::SphericalMirror:
{
SphericalMirrorProjection projection;
projection.quality =
QualityValues[_sphericalMirror.quality->currentIndex()];
return projection;
}
case ProjectionIndices::Cylindrical:
{
CylindricalProjection projection;
projection.quality = QualityValues[_cylindrical.quality->currentIndex()];
projection.heightOffset = _cylindrical.heightOffset->text().toFloat();
return projection;
}
case ProjectionIndices::Equirectangular:
if (isSpoutEquirectangular) {
SpoutOutputProjection projection;
projection.mapping = SpoutOutputProjection::Mapping::Equirectangular;
projection.quality =
QualityValues[_equirectangular.quality->currentIndex()];
projection.mappingSpoutName = "OpenSpace";
return projection;
}
else {
EquirectangularProjection projection;
projection.quality =
QualityValues[_equirectangular.quality->currentIndex()];
return projection;
}
case ProjectionIndices::Planar:
{
double fovH = _planar.fovH->text().toFloat();
fovH = std::clamp(fovH, FovEpsilon, 180.0 - FovEpsilon);
double fovV = _planar.fovV->text().toFloat();
fovV = std::clamp(fovV, FovEpsilon, 180.0 - FovEpsilon);
// The negative values for left & down are due to SGCT's convention
PlanarProjection projection;
projection.fov.right = fovH / 2.0;
projection.fov.left = -projection.fov.right;
projection.fov.up = fovV / 2.0;
projection.fov.down = -projection.fov.up;
return projection;
}
default:
throw ghoul::MissingCaseException();
}
}
sgct::config::Window WindowControl::generateWindowInformation() const {
sgct::config::Window window;
window.size = sgct::ivec2(_sizeX->text().toInt(), _sizeY->text().toInt());
QRect resolution = _monitorResolutions[_monitor->currentIndex()];
window.pos = sgct::ivec2(
resolution.x() + _offsetX->text().toInt(),
resolution.y() + _offsetY->text().toInt()
);
sgct::config::Viewport vp;
vp.isTracked = true;
vp.position = sgct::vec2(0.f, 0.f);
vp.size = sgct::vec2(1.f, 1.f);
vp.projection = generateProjectionInformation();
window.viewports.push_back(vp);
window.isDecorated = _windowDecoration->isChecked();
if (window.isFullScreen) {
window.monitor = _monitor->currentIndex();
}
if (!_windowName->text().isEmpty()) {
window.name = _windowName->text().toStdString();
}
return window;
}
void WindowControl::onSizeXChanged(int newValue) {
_windowDimensions.setWidth(newValue);
if (_aspectRatioLocked) {
int updatedHeight = _windowDimensions.width() / _aspectRatioSize;
_sizeY->blockSignals(true);
_sizeY->setValue(updatedHeight);
_sizeY->blockSignals(false);
_windowDimensions.setHeight(updatedHeight);
}
emit windowChanged(_monitor->currentIndex(), _windowIndex, _windowDimensions);
if (_fovLocked) {
updatePlanarLockedFov();
}
}
void WindowControl::onSizeYChanged(int newValue) {
_windowDimensions.setHeight(newValue);
if (_aspectRatioLocked) {
int updatedWidth = _windowDimensions.height() * _aspectRatioSize;
_sizeX->blockSignals(true);
_sizeX->setValue(updatedWidth);
_sizeX->blockSignals(false);
_windowDimensions.setWidth(updatedWidth);
}
emit windowChanged(_monitor->currentIndex(), _windowIndex, _windowDimensions);
if (_fovLocked) {
updatePlanarLockedFov();
}
}
void WindowControl::onOffsetXChanged(int newValue) {
float prevWidth = _windowDimensions.width();
_windowDimensions.setX(newValue);
_windowDimensions.setWidth(prevWidth);
emit windowChanged(_monitor->currentIndex(), _windowIndex, _windowDimensions);
}
void WindowControl::onOffsetYChanged(int newValue) {
float prevHeight = _windowDimensions.height();
_windowDimensions.setY(newValue);
_windowDimensions.setHeight(prevHeight);
emit windowChanged(_monitor->currentIndex(), _windowIndex, _windowDimensions);
}
void WindowControl::onFullscreenClicked() {
QRect resolution = _monitorResolutions[_monitor->currentIndex()];
_offsetX->setValue(0);
_offsetY->setValue(0);
_sizeX->setValue(resolution.width());
_sizeY->setValue(resolution.height());
_windowDecoration->setChecked(false);
}
void WindowControl::onProjectionChanged(int newSelection) {
ProjectionIndices selected = static_cast<ProjectionIndices>(newSelection);
_planar.widget->setVisible(selected == ProjectionIndices::Planar);
_fisheye.widget->setVisible(selected == ProjectionIndices::Fisheye);
_sphericalMirror.widget->setVisible(selected == ProjectionIndices::SphericalMirror);
_cylindrical.widget->setVisible(selected == ProjectionIndices::Cylindrical);
_equirectangular.widget->setVisible(selected == ProjectionIndices::Equirectangular);
}
void WindowControl::onAspectRatioLockClicked() {
_aspectRatioLocked = !_aspectRatioLocked;
if (_aspectRatioLocked) {
_aspectRatioSize = _windowDimensions.width() / _windowDimensions.height();
}
}
void WindowControl::onFovLockClicked() {
_fovLocked = !_fovLocked;
if (_fovLocked) {
_planar.fovH->setEnabled(false);
_planar.fovV->setEnabled(false);
updatePlanarLockedFov();
}
else {
_planar.fovH->setEnabled(true);
_planar.fovV->setEnabled(true);
}
}
void WindowControl::updatePlanarLockedFov() {
const float aspectRatio = _windowDimensions.width() / _windowDimensions.height();
const float ratio = aspectRatio / IdealAspectRatio;
if (ratio >= 1.f) {
_planar.fovH->setValue(std::min(DefaultFovH * ratio, 180.f));
_planar.fovV->setValue(DefaultFovV);
}
else {
_planar.fovH->setValue(DefaultFovH);
_planar.fovV->setValue(std::min(DefaultFovV / ratio, 180.f));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
# #
# OpenSpace #
# #
# Copyright (c) 2014-2021 #
# Copyright (c) 2014-2022 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #
@@ -22,7 +22,7 @@
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################################
include(${OPENSPACE_CMAKE_EXT_DIR}/application_definition.cmake)
include(${PROJECT_SOURCE_DIR}/support/cmake/application_definition.cmake)
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/openspace.icns
@@ -47,7 +47,7 @@ if (OPENSPACE_MODULE_WEBBROWSER AND CEF_ROOT)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "CMAKE_BUILD_TYPE")
if (WIN32)
set(RESOURCE_FILE ${OPENSPACE_APPS_DIR}/OpenSpace/openspace.rc)
set(RESOURCE_FILE openspace.rc)
endif ()
# Add the CEF binary distribution's cmake/ directory to the module path and

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -68,7 +68,7 @@ int main(int, char**) {
progressBar.print(static_cast<int>(progress * 100.f));
});
}
std::cout << "Done synchronizing." << std::endl;
std::cout << "Done synchronizing" << std::endl;
return 0;
};

View File

@@ -2,7 +2,7 @@
# #
# OpenSpace #
# #
# Copyright (c) 2014-2021 #
# Copyright (c) 2014-2022 #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy of this #
# software and associated documentation files (the "Software"), to deal in the Software #
@@ -22,7 +22,7 @@
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
##########################################################################################
include(${OPENSPACE_CMAKE_EXT_DIR}/application_definition.cmake)
include(${PROJECT_SOURCE_DIR}/support/cmake/application_definition.cmake)
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/openspace.icns
@@ -47,7 +47,7 @@ if (OPENSPACE_MODULE_WEBBROWSER AND CEF_ROOT)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "CMAKE_BUILD_TYPE")
if (WIN32)
set(RESOURCE_FILE ${OPENSPACE_APPS_DIR}/OpenSpace/openspace.rc)
set(RESOURCE_FILE openspace.rc)
endif ()
# Add the CEF binary distribution's cmake/ directory to the module path and

View File

@@ -2,7 +2,7 @@
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
@@ -29,8 +29,6 @@
#include <ghoul/ghoul.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/io/texture/texturereaderdevil.h>
#include <ghoul/io/texture/texturereaderfreeimage.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/logging/consolelog.h>
@@ -55,25 +53,15 @@
#include <openspace/scene/rotation.h>
#include <openspace/scene/scale.h>
#include <openspace/engine/moduleengine.h>
#ifdef WIN32
#include <Windows.h>
#endif // WIN32
namespace {
const std::string ConfigurationFile = "openspace.cfg";
const std::string _loggerCat = "TaskRunner Main";
}
void initTextureReaders() {
#ifdef GHOUL_USE_DEVIL
ghoul::io::TextureReader::ref().addReader(
std::make_unique<ghoul::io::TextureReaderDevIL>()
);
#endif // GHOUL_USE_DEVIL
#ifdef GHOUL_USE_FREEIMAGE
ghoul::io::TextureReader::ref().addReader(
std::make_unique<ghoul::io::TextureReaderFreeImage>()
);
#endif // GHOUL_USE_FREEIMAGE
}
void performTasks(const std::string& path) {
using namespace openspace;
@@ -99,7 +87,7 @@ void performTasks(const std::string& path) {
};
task.perform(onProgress);
}
std::cout << "Done performing tasks." << std::endl;
std::cout << "Done performing tasks" << std::endl;
}
int main(int argc, char** argv) {
@@ -124,11 +112,24 @@ int main(int argc, char** argv) {
// Register the base path as the directory where the configuration file lives
std::filesystem::path base = configFile.parent_path();
constexpr const char* BasePathToken = "${BASE}";
FileSys.registerPathToken(BasePathToken, base);
constexpr std::string_view BasePathToken = "${BASE}";
FileSys.registerPathToken(BasePathToken.data(), base);
// Using same configuration for size as in apps/OpenSpace/main.cpp
glm::ivec2 size = glm::ivec2(1920, 1080);
#ifdef WIN32
DEVMODEW dm = { 0 };
dm.dmSize = sizeof(DEVMODEW);
BOOL success = EnumDisplaySettingsW(nullptr, ENUM_CURRENT_SETTINGS, &dm);
if (success) {
size.x = dm.dmPelsWidth;
size.y = dm.dmPelsHeight;
}
#endif // WIN32
*global::configuration = configuration::loadConfigurationFromFile(
configFile.string(),
size,
""
);
openspace::global::openSpaceEngine->registerPathTokens();

View File

@@ -1 +0,0 @@
set(DEFAULT_APPLICATION ON)

View File

@@ -1,132 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <openspace/network/parallelserver.h>
#include <ghoul/fmt.h>
#include <ghoul/cmdparser/commandlineparser.h>
#include <ghoul/cmdparser/singlecommand.h>
#include <ghoul/logging/logmanager.h>
#include <iomanip>
namespace {
constexpr const char*_loggerCat = "Wormhole";
} // namespace
int main(int argc, char** argv) {
using namespace openspace;
using namespace ghoul::cmdparser;
std::vector<std::string> arguments(argv, argv + argc);
CommandlineParser commandlineParser(
"Wormhole",
CommandlineParser::AllowUnknownCommands::Yes
);
struct {
std::string port;
std::string password;
std::string changeHostPassword;
} settings;
commandlineParser.addCommand(
std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
settings.port,
"--port",
"-p",
"Sets the port to listen on"
)
);
commandlineParser.addCommand(
std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
settings.password,
"--password",
"-l",
"Sets the password to use"
)
);
commandlineParser.addCommand(
std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
settings.changeHostPassword,
"--hostpassword",
"-h",
"Sets the host password to use"
)
);
commandlineParser.setCommandLine(arguments);
commandlineParser.execute();
if (settings.password.empty()) {
std::stringstream defaultPassword;
defaultPassword << std::hex << std::setfill('0') << std::setw(6) <<
(std::hash<size_t>{}(
std::chrono::system_clock::now().time_since_epoch().count()
) % 0xffffff);
settings.password = defaultPassword.str();
}
if (settings.changeHostPassword.empty()) {
std::stringstream defaultChangeHostPassword;
defaultChangeHostPassword << std::hex << std::setfill('0') << std::setw(6) <<
(std::hash<size_t>{}(
std::chrono::system_clock::now().time_since_epoch().count() + 1
) % 0xffffff);
settings.changeHostPassword = defaultChangeHostPassword.str();
}
ghoul::logging::LogManager::initialize(
ghoul::logging::LogLevel::Debug,
ghoul::logging::LogManager::ImmediateFlush::Yes
);
LINFO(fmt::format("Connection password: {}", settings.password));
LINFO(fmt::format("Host password: {}", settings.changeHostPassword));
int port = 25001;
if (!settings.port.empty()) {
try {
port = std::stoi(settings.port);
}
catch (const std::invalid_argument&) {
LERROR(fmt::format("Invalid port: {}", settings.port));
}
}
ParallelServer server;
server.start(port, settings.password, settings.changeHostPassword);
server.setDefaultHostAddress("127.0.0.1");
LINFO(fmt::format("Server listening to port {}", port));
while (std::cin.get() != 'q') {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
server.stop();
LINFO("Server stopped");
return 0;
};

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1 +0,0 @@
IDI_ICON1 ICON DISCARDABLE "openspace.ico"

View File

@@ -0,0 +1,58 @@
{
"version": 1,
"masteraddress": "localhost",
"externalcontrolport": 20500,
"settings": {
"display": {
"swapinterval": 0
}
},
"nodes": [
{
"address": "localhost",
"port": 20401,
"windows": [
{
"name": "OpenSpace",
"fullscreen": false,
"draw2d": false,
"stereo": "none",
"pos": { "x": 50, "y": 50 },
"size": { "x": 1280, "y": 720 },
"viewports": [
{
"tracked": true,
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "EquirectangularProjection",
"quality": "1k"
}
}
]
},
{
"name": "GUI",
"tags": [ "GUI" ],
"fullscreen": false,
"draw3d": false,
"stereo": "none",
"pos": { "x": 50, "y": 50 },
"size": { "x": 1280, "y": 720 },
"viewports": [
{
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 }
}
]
}
]
}
],
"users": [
{
"eyeseparation": 0.065,
"pos": { "x": 0.0, "y": 0.0, "z": 0.0 }
}
]
}

View File

@@ -0,0 +1,47 @@
{
"version": 1,
"masteraddress": "localhost",
"externalcontrolport": 20500,
"settings": {
"display": {
"swapinterval": 0
}
},
"nodes": [
{
"address": "localhost",
"port": 20401,
"windows": [
{
"fullscreen": true,
"name": "OpenSpace",
"msaa": 4,
"stereo": "none",
"pos": { "x": 0, "y": 0 },
"size": { "x": 1920, "y": 1080 },
"viewports": [
{
"tracked": true,
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "PlanarProjection",
"fov": {
"hfov": 80.0,
"vfov": 50.534015846724
},
"orientation": { "yaw": 0.0, "pitch": 0.0, "roll": 0.0 }
}
}
]
}
]
}
],
"users": [
{
"eyeseparation": 0.065,
"pos": { "x": 0.0, "y": 0.0, "z": 0.0 }
}
]
}

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" ?>
<Cluster masterAddress="localhost" externalControlPort="20500">
<Settings>
<Display swapInterval="0" />
</Settings>
<Node address="localhost" port="20401">
<Window fullScreen="true" numberOfSamples="4" name="OpenSpace">
<Stereo type="none" />
<Size x="1920" y="1080" />
<Pos x="0" y="0" />
<Viewport tracked="true">
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<PlanarProjection>
<FOV down="25.267007923362" left="40.0" right="40.0" up="25.267007923362" />
<Orientation heading="0.0" pitch="0.0" roll="0.0" />
</PlanarProjection>
</Viewport>
</Window>
</Node>
<User eyeSeparation="0.065">
<Pos x="0.0" y="0.0" z="0.0" />
</User>
</Cluster>

63
config/gui_projector.json Normal file
View File

@@ -0,0 +1,63 @@
{
"version": 1,
"masteraddress": "localhost",
"externalcontrolport": 20500,
"settings": {
"display": {
"swapinterval": 0
}
},
"nodes": [
{
"address": "localhost",
"port": 20401,
"windows": [
{
"fullscreen": false,
"border": false,
"name": "GUI",
"tags": [ "GUI" ],
"draw3d": false,
"stereo": "none",
"pos": { "x": 0, "y": 0 },
"size": { "x": 1920, "y": 1080 },
"viewports": [
{
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 }
}
]
},
{
"fullscreen": true,
"name": "OpenSpace",
"draw2d": false,
"stereo": "none",
"pos": { "x": 1920, "y": 1080 },
"size": { "x": 1920, "y": 1080 },
"viewports": [
{
"tracked": true,
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "PlanarProjection",
"fov": {
"hfov": 80.0,
"vfov": 50.534015846724
},
"orientation": { "yaw": 0.0, "pitch": 0.0, "roll": 0.0 }
}
}
]
}
]
}
],
"users": [
{
"eyeseparation": 0.065,
"pos": { "x": 0.0, "y": 0.0, "z": 0.0 }
}
]
}

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" ?>
<Cluster masterAddress="localhost" externalControlPort="20500">
<Settings>
<Display swapInterval="0" />
</Settings>
<Node address="localhost" port="20401">
<Window fullscreen="true" monitor="1" name="OpenSpace" draw2d="false">
<Stereo type="none" />
<Size x="1920" y="1080" />
<Pos x="0" y="0" />
<Viewport tracked="true">
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<PlanarProjection>
<FOV down="25.267007923362" left="40.0" right="40.0" up="25.267007923362" />
<Orientation heading="0.0" pitch="0.0" roll="0.0" />
</PlanarProjection>
</Viewport>
</Window>
<Window fullscreen="false" border="false" name="GUI" tags="GUI" draw3D="false">
<Stereo type="none" />
<Size x="1920" y="1080" />
<Pos x="0" y="0" />
<Viewport>
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
</Viewport>
</Window>
</Node>
<User eyeSeparation="0.065">
<Pos x="0.0" y="0.0" z="0.0" />
</User>
</Cluster>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" ?>
<Cluster masterAddress="localhost">
<Node address="localhost" port="20401">
<Window tags="OpenVR" fullScreen="false" name="OpenSpace">
<Stereo type="side_by_side" />
<!-- Res is equal to the Recommend target size -->
<Size x="1332" y="840" />
<Res x="3024" y="1680" />
<Viewport>
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<Projectionplane>
<!-- Lower left -->
<Pos x="-1.7156" y="-0.965" z="0.0" />
<!-- Upper left -->
<Pos x="-1.7156" y="0.965" z="0.0" />
<!-- Upper right -->
<Pos x="1.7156" y="0.965" z="0.0" />
</Projectionplane>
</Viewport>
</Window>
</Node>
</Cluster>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" ?>
<Cluster masterAddress="localhost">
<Node address="localhost" port="20401">
<Window tags="OpenVR" fullScreen="false" name="OpenSpace">
<Stereo type="side_by_side" />
<!-- Res is equal to the Recommend target size -->
<Size x="1332" y="793" />
<Res x="2664" y="1586" />
<Viewport>
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<Projectionplane>
<!-- Lower left -->
<Pos x="-1.7156" y="-0.965" z="0.0" />
<!-- Upper left -->
<Pos x="-1.7156" y="0.965" z="0.0" />
<!-- Upper right -->
<Pos x="1.7156" y="0.965" z="0.0" />
</Projectionplane>
</Viewport>
</Window>
</Node>
</Cluster>

47
config/single.json Normal file
View File

@@ -0,0 +1,47 @@
{
"version": 1,
"masteraddress": "localhost",
"externalcontrolport": 20500,
"settings": {
"display": {
"swapinterval": 0
}
},
"nodes": [
{
"address": "localhost",
"port": 20401,
"windows": [
{
"name": "OpenSpace",
"fullscreen": false,
"stereo": "none",
"pos": { "x": 50, "y": 50 },
"size": { "x": 1280, "y": 720 },
"res": { "x": 2560, "y": 1440 },
"viewports": [
{
"tracked": true,
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "PlanarProjection",
"fov": {
"hfov": 80.0,
"vfov": 50.534015846724
},
"orientation": { "yaw": 0.0, "pitch": 0.0, "roll": 0.0 }
}
}
]
}
]
}
],
"users": [
{
"eyeseparation": 0.065,
"pos": { "x": 0.0, "y": 0.0, "z": 0.0 }
}
]
}

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" ?>
<Cluster masterAddress="localhost" externalControlPort="20500">
<Settings>
<Display swapInterval="0" />
</Settings>
<Node address="localhost" port="20401">
<Window fullScreen="false" name="OpenSpace">
<Stereo type="none" />
<Size x="1280" y="720" />
<Pos x="50" y="50" />
<Viewport tracked="true">
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<PlanarProjection>
<FOV down="25.267007923362" left="40.0" right="40.0" up="25.267007923362" />
<Orientation heading="0.0" pitch="0.0" roll="0.0" />
</PlanarProjection>
</Viewport>
</Window>
</Node>
<User eyeSeparation="0.065">
<Pos x="0.0" y="0.0" z="0.0" />
</User>
</Cluster>

View File

@@ -0,0 +1,37 @@
{
"version": 1,
"masteraddress": "localhost",
"nodes": [
{
"address": "localhost",
"port": 20401,
"windows": [
{
"name": "OpenSpace",
"fullscreen": false,
"stereo": "none",
"size": { "x": 1024, "y": 1024 },
"viewports": [
{
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "FisheyeProjection",
"fov": 180.0,
"quality": "1k",
"tilt": 27.0,
"background": { "r": 0.1, "g": 0.1, "b": 0.1, "a": 1.0 }
}
}
]
}
]
}
],
"users": [
{
"eyeseparation": 0.06,
"pos": { "x": 0.0, "y": 0.0, "z": 0.0 }
}
]
}

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" ?>
<Cluster masterAddress="localhost">
<!-- <Scene>
<Orientation yaw="0.0" pitch="-27.0" roll="0.0" />
<Offset x="0.0" y="0.0" z="0.0" />
<Scale value="1.0" />
</Scene> -->
<Node address="localhost" port="20401">
<Window fullScreen="false" name="OpenSpace">
<Stereo type="none" />
<!-- 16:9 aspect ratio -->
<Size x="1024" y="1024" />
<!-- Frame buffer resolution
<Res x="4096" y="4096" /> -->
<!--
quality options (cubemap size):
- low (256)
- medium (512)
- high/1k (1024)
- 2k (2048)
- 4k (4096)
- 8k (8192)
tilt specifies the dome tilt angle in degrees from the horizontal
-->
<Viewport name="fisheye">
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<FisheyeProjection fov="180" quality="1k" tilt="27.0">
<Background r="0.1" g="0.1" b="0.1" a="1.0" />
</FisheyeProjection>
</Viewport>
</Window>
</Node>
<User eyeSeparation="0.06">
<Pos x="0.0" y="0.0" z="0.0" />
</User>
</Cluster>

View File

@@ -0,0 +1,61 @@
{
"version": 1,
"masteraddress": "localhost",
"nodes": [
{
"address": "localhost",
"port": 20401,
"windows": [
{
"name": "GUI",
"tags": [ "GUI" ],
"fullscreen": false,
"draw3d": false,
"stereo": "none",
"pos": { "x": 50, "y": 50 },
"size": { "x": 1024, "y": 1024 },
"viewports": [
{
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "PlanarProjection",
"fov": {
"hfov": 80.0,
"vfov": 50.534015846724
},
"orientation": { "yaw": 0.0, "pitch": 0.0, "roll": 0.0 }
}
}
]
},
{
"name": "OpenSpace",
"fullscreen": false,
"draw2d": false,
"stereo": "none",
"size": { "x": 1024, "y": 1024 },
"viewports": [
{
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "FisheyeProjection",
"fov": 180.0,
"quality": "1k",
"tilt": 27.0,
"background": { "r": 0.1, "g": 0.1, "b": 0.1, "a": 1.0 }
}
}
]
}
]
}
],
"users": [
{
"eyeseparation": 0.06,
"pos": { "x": 0.0, "y": 0.0, "z": 0.0 }
}
]
}

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" ?>
<Cluster masterAddress="localhost">
<!-- <Scene>
<Orientation yaw="0.0" pitch="-27.0" roll="0.0" />
<Offset x="0.0" y="0.0" z="0.0" />
<Scale value="1.0" />
</Scene> -->
<Node address="localhost" port="20401">
<Window fullScreen="false" name="OpenSpace" draw2d="false">
<Stereo type="none" />
<!-- 16:9 aspect ratio -->
<Size x="1024" y="1024" />
<!-- Frame buffer resolution
<Res x="4096" y="4096" /> -->
<!--
quality options (cubemap size):
- low (256)
- medium (512)
- high/1k (1024)
- 2k (2048)
- 4k (4096)
- 8k (8192)
tilt specifies the dome tilt angle in degrees from the horizontal
-->
<Viewport name="fisheye">
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<FisheyeProjection fov="180" quality="1k" tilt="27.0">
<Background r="0.1" g="0.1" b="0.1" a="1.0" />
</FisheyeProjection>
</Viewport>
</Window>
<Window fullScreen="false" name="GUI" tags="GUI" draw3d="false">
<Stereo type="none" />
<Size x="1024" y="1024" />
<Pos x="50" y="50" />
<Viewport>
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<PlanarProjection>
<FOV down="25.267007923362" left="40.0" right="40.0" up="25.267007923362" />
<Orientation heading="0.0" pitch="0.0" roll="0.0" />
</PlanarProjection>
</Viewport>
</Window>
</Node>
<User eyeSeparation="0.06">
<Pos x="0.0" y="0.0" z="0.0" />
</User>
</Cluster>

70
config/single_gui.json Normal file
View File

@@ -0,0 +1,70 @@
{
"version": 1,
"masteraddress": "localhost",
"externalcontrolport": 20500,
"settings": {
"display": {
"swapinterval": 0
}
},
"nodes": [
{
"address": "localhost",
"port": 20401,
"windows": [
{
"name": "GUI",
"tags": [ "GUI" ],
"fullscreen": false,
"draw3d": false,
"stereo": "none",
"pos": { "x": 50, "y": 50 },
"size": { "x": 1280, "y": 720 },
"viewports": [
{
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "PlanarProjection",
"fov": {
"hfov": 80.0,
"vfov": 50.534015846724
},
"orientation": { "yaw": 0.0, "pitch": 0.0, "roll": 0.0 }
}
}
]
},
{
"name": "OpenSpace",
"fullscreen": false,
"draw2d": false,
"stereo": "none",
"pos": { "x": 50, "y": 50 },
"size": { "x": 1280, "y": 720 },
"viewports": [
{
"tracked": true,
"pos": { "x": 0.0, "y": 0.0 },
"size": { "x": 1.0, "y": 1.0 },
"projection": {
"type": "PlanarProjection",
"fov": {
"hfov": 80.0,
"vfov": 50.534015846724
},
"orientation": { "yaw": 0.0, "pitch": 0.0, "roll": 0.0 }
}
}
]
}
]
}
],
"users": [
{
"eyeseparation": 0.065,
"pos": { "x": 0.0, "y": 0.0, "z": 0.0 }
}
]
}

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" ?>
<Cluster masterAddress="localhost" externalControlPort="20500">
<Settings>
<Display swapInterval="0" />
</Settings>
<Node address="localhost" port="20401">
<Window fullScreen="false" name="OpenSpace" draw2d="false">
<Stereo type="none" />
<Size x="1280" y="720" />
<Pos x="50" y="50" />
<Viewport tracked="true">
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<PlanarProjection>
<FOV down="25.267007923362" left="40.0" right="40.0" up="25.267007923362" />
<Orientation heading="0.0" pitch="0.0" roll="0.0" />
</PlanarProjection>
</Viewport>
</Window>
<Window fullScreen="false" name="GUI" tags="GUI" draw3d="false">
<Stereo type="none" />
<Size x="1280" y="720" />
<Pos x="50" y="50" />
<Viewport>
<Pos x="0.0" y="0.0" />
<Size x="1.0" y="1.0" />
<PlanarProjection>
<FOV down="25.267007923362" left="40.0" right="40.0" up="25.267007923362" />
<Orientation heading="0.0" pitch="0.0" roll="0.0" />
</PlanarProjection>
</Viewport>
</Window>
</Node>
<User eyeSeparation="0.065">
<Pos x="0.0" y="0.0" z="0.0" />
</User>
</Cluster>

Some files were not shown because too many files have changed in this diff Show More