From 63e389ebdcc78dccd3db2efa4c56a3e95ae39a94 Mon Sep 17 00:00:00 2001 From: shingen75 Date: Fri, 15 Sep 2023 21:51:23 +0900 Subject: [PATCH] Added Qt port related files in src/platforms ( guiqt.cpp and qglmainwindow.h --- src/CMakeLists.txt | 483 +++++++++------ src/platform/guiqt.cpp | 1126 ++++++++++++++++++++++++++++++++++ src/platform/qglmainwindow.h | 485 +++++++++++++++ 3 files changed, 1923 insertions(+), 171 deletions(-) create mode 100644 src/platform/guiqt.cpp create mode 100644 src/platform/qglmainwindow.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0e9129f4..26630072 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,16 @@ endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +if(USE_QT_GUI) + #Including QT + set(CMAKE_AUTOMOC ON) + find_package(Qt5 COMPONENTS Core Gui Widgets OpenGL REQUIRED) + include (../extlib/tinyxml2/CMakeLists.txt) + + #including code from PoCAE + set(GitPath ${CMAKE_CURRENT_LIST_DIR}/../..) +endif() + # solvespace dependencies add_library(slvs_deps INTERFACE) target_include_directories(slvs_deps INTERFACE SYSTEM @@ -34,7 +44,7 @@ target_link_libraries(slvs_deps INTERFACE ${ZLIB_LIBRARY} ${PNG_LIBRARY} ${FREETYPE_LIBRARY} - ${CAIRO_LIBRARIES} + #${CAIRO_LIBRARIES} mimalloc-static) if(Backtrace_FOUND) @@ -111,68 +121,131 @@ set(every_platform_SOURCES set(solvespace_core_gl_SOURCES solvespace.cpp) -add_library(solvespace-core STATIC - dsc.h - expr.h - polygon.h - sketch.h - solvespace.h - ui.h - platform/platform.h - render/render.h - render/gl3shader.h - srf/surface.h - bsp.cpp - clipboard.cpp - confscreen.cpp - constraint.cpp - constrainteq.cpp - describescreen.cpp - draw.cpp - drawconstraint.cpp - drawentity.cpp - entity.cpp - export.cpp - exportstep.cpp - exportvector.cpp - expr.cpp - file.cpp - generate.cpp - graphicswin.cpp - group.cpp - groupmesh.cpp - importdxf.cpp - importidf.cpp - importmesh.cpp - mesh.cpp - modify.cpp - mouse.cpp - polyline.cpp - polygon.cpp - resource.cpp - request.cpp - style.cpp - system.cpp - textscreens.cpp - textwin.cpp - toolbar.cpp - ttf.cpp - undoredo.cpp - util.cpp - view.cpp - platform/platform.cpp - platform/gui.cpp - render/render.cpp - render/render2d.cpp - srf/boolean.cpp - srf/curve.cpp - srf/merge.cpp - srf/ratpoly.cpp - srf/raycast.cpp - srf/shell.cpp - srf/surface.cpp - srf/surfinter.cpp - srf/triangulate.cpp) +if (NOT USE_QT_GUI) + add_library(solvespace-core STATIC + dsc.h + expr.h + polygon.h + sketch.h + solvespace.h + ui.h + platform/platform.h + render/render.h + render/gl3shader.h + render/render2d.cpp + srf/surface.h + bsp.cpp + clipboard.cpp + confscreen.cpp + constraint.cpp + constrainteq.cpp + describescreen.cpp + draw.cpp + drawconstraint.cpp + drawentity.cpp + entity.cpp + export.cpp + exportstep.cpp + exportvector.cpp + expr.cpp + file.cpp + generate.cpp + graphicswin.cpp + group.cpp + groupmesh.cpp + importdxf.cpp + importidf.cpp + importmesh.cpp + mesh.cpp + modify.cpp + mouse.cpp + polyline.cpp + polygon.cpp + resource.cpp + request.cpp + style.cpp + system.cpp + textscreens.cpp + textwin.cpp + toolbar.cpp + ttf.cpp + undoredo.cpp + util.cpp + view.cpp + platform/platform.cpp + platform/gui.cpp + render/render.cpp + srf/boolean.cpp + srf/curve.cpp + srf/merge.cpp + srf/ratpoly.cpp + srf/raycast.cpp + srf/shell.cpp + srf/surface.cpp + srf/surfinter.cpp + srf/triangulate.cpp) +else() + add_library(solvespace-core STATIC + dsc.h + expr.h + polygon.h + sketch.h + solvespace.h + ui.h + platform/platform.h + render/render.h + srf/surface.h + bsp.cpp + clipboard.cpp + confscreen.cpp + constraint.cpp + constrainteq.cpp + describescreen.cpp + draw.cpp + drawconstraint.cpp + drawentity.cpp + entity.cpp + export.cpp + exportstep.cpp + exportvector.cpp + expr.cpp + file.cpp + generate.cpp + graphicswin.cpp + group.cpp + groupmesh.cpp + importdxf.cpp + importidf.cpp + importmesh.cpp + mesh.cpp + modify.cpp + mouse.cpp + polyline.cpp + polygon.cpp + resource.cpp + request.cpp + style.cpp + system.cpp + textscreens.cpp + textwin.cpp + toolbar.cpp + ttf.cpp + undoredo.cpp + util.cpp + view.cpp + platform/platform.cpp + platform/gui.cpp + render/render.cpp + srf/boolean.cpp + srf/curve.cpp + srf/merge.cpp + srf/ratpoly.cpp + srf/raycast.cpp + srf/shell.cpp + srf/surface.cpp + srf/surfinter.cpp + srf/triangulate.cpp) +endif() target_link_libraries(solvespace-core PUBLIC slvs_deps) @@ -262,128 +335,188 @@ endif() # solvespace graphical executable if(ENABLE_GUI) - add_executable(solvespace WIN32 MACOSX_BUNDLE - ${solvespace_core_gl_SOURCES} - platform/entrygui.cpp - $) + if(NOT USE_QT_GUI) + add_executable(solvespace WIN32 MACOSX_BUNDLE + ${solvespace_core_gl_SOURCES} + platform/entrygui.cpp + $) - add_dependencies(solvespace - resources) + add_dependencies(solvespace + resources) - target_link_libraries(solvespace - PRIVATE - solvespace-core - ${OPENGL_LIBRARIES}) + target_link_libraries(solvespace + PRIVATE + solvespace-core + ${OPENGL_LIBRARIES}) - # OpenGL version - if(OPENGL STREQUAL 3) - target_sources(solvespace PRIVATE - render/gl3shader.cpp - render/rendergl3.cpp) - elseif(OPENGL STREQUAL 1) - target_sources(solvespace PRIVATE - render/rendergl1.cpp) - else() - message(FATAL_ERROR "Unsupported OpenGL version ${OPENGL}") - endif() + # OpenGL version + if(OPENGL STREQUAL 3) + target_sources(solvespace PRIVATE + render/gl3shader.cpp + render/rendergl3.cpp) + elseif(OPENGL STREQUAL 1) + target_sources(solvespace PRIVATE + render/rendergl1.cpp) + else() + message(FATAL_ERROR "Unsupported OpenGL version ${OPENGL}") + endif() - # Platform-specific - if(WIN32) - target_sources(solvespace PRIVATE - platform/guiwin.cpp) + # Platform-specific + if(WIN32) + target_sources(solvespace PRIVATE + platform/guiwin.cpp) - target_link_libraries(solvespace PRIVATE comctl32) - elseif(APPLE) - target_compile_options(solvespace PRIVATE -fobjc-arc) - target_compile_definitions(solvespace PRIVATE GL_SILENCE_DEPRECATION) + target_link_libraries(solvespace PRIVATE comctl32) + elseif(APPLE) + target_compile_options(solvespace PRIVATE -fobjc-arc) + target_compile_definitions(solvespace PRIVATE GL_SILENCE_DEPRECATION) - target_sources(solvespace PRIVATE - platform/guimac.mm) - set_target_properties(solvespace PROPERTIES - OUTPUT_NAME SolveSpace - XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES" - XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.solvespace" - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - elseif(EMSCRIPTEN) - set(SHELL ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/emshell.html) - set(LINK_FLAGS - --bind --shell-file ${SHELL} - --no-heap-copy -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s ASYNCIFY=1 - -s DYNCALLS=1 -s ASSERTIONS=1 - -s TOTAL_STACK=33554432 -s TOTAL_MEMORY=134217728) + target_sources(solvespace PRIVATE + platform/guimac.mm) + set_target_properties(solvespace PROPERTIES + OUTPUT_NAME SolveSpace + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.solvespace" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + elseif(EMSCRIPTEN) + set(SHELL ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/emshell.html) + set(LINK_FLAGS + --bind --shell-file ${SHELL} + --no-heap-copy -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s ASYNCIFY=1 + -s DYNCALLS=1 -s ASSERTIONS=1 + -s TOTAL_STACK=33554432 -s TOTAL_MEMORY=134217728) - get_target_property(resource_names resources NAMES) - foreach(resource ${resource_names}) - list(APPEND LINK_FLAGS --preload-file ${resource}) - endforeach() + get_target_property(resource_names resources NAMES) + foreach(resource ${resource_names}) + list(APPEND LINK_FLAGS --preload-file ${resource}) + endforeach() - if(CMAKE_BUILD_TYPE STREQUAL Debug) - list(APPEND LINK_FLAGS - --emrun --emit-symbol-map - -s DEMANGLE_SUPPORT=1 - -s SAFE_HEAP=1) - endif() + if(CMAKE_BUILD_TYPE STREQUAL Debug) + list(APPEND LINK_FLAGS + --emrun --emit-symbol-map + -s DEMANGLE_SUPPORT=1 + -s SAFE_HEAP=1) + endif() - target_sources(solvespace PRIVATE - platform/guihtml.cpp) + target_sources(solvespace PRIVATE + platform/guihtml.cpp) - string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}") - set_target_properties(solvespace PROPERTIES - LINK_FLAGS "${LINK_FLAGS}") - set_source_files_properties(platform/guihtml.cpp PROPERTIES - OBJECT_DEPENDS ${SHELL}) + string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}") + set_target_properties(solvespace PROPERTIES + LINK_FLAGS "${LINK_FLAGS}") + set_source_files_properties(platform/guihtml.cpp PROPERTIES + OBJECT_DEPENDS ${SHELL}) - add_custom_command( - TARGET solvespace POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/solvespaceui.css - ${EXECUTABLE_OUTPUT_PATH}/solvespaceui.css - COMMENT "Copying UI stylesheet" - VERBATIM) - add_custom_command( - TARGET solvespace POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/solvespaceui.js - ${EXECUTABLE_OUTPUT_PATH}/solvespaceui.js - COMMENT "Copying UI script solvespaceui.js" - VERBATIM) - add_custom_command( - TARGET solvespace POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/filemanagerui.js - ${EXECUTABLE_OUTPUT_PATH}/filemanagerui.js - COMMENT "Copying UI script filemanagerui.sj" - VERBATIM) - else() - target_sources(solvespace PRIVATE - platform/guigtk.cpp) + add_custom_command( + TARGET solvespace POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/solvespaceui.css + ${EXECUTABLE_OUTPUT_PATH}/solvespaceui.css + COMMENT "Copying UI stylesheet" + VERBATIM) + add_custom_command( + TARGET solvespace POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/solvespaceui.js + ${EXECUTABLE_OUTPUT_PATH}/solvespaceui.js + COMMENT "Copying UI script solvespaceui.js" + VERBATIM) + add_custom_command( + TARGET solvespace POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/filemanagerui.js + ${EXECUTABLE_OUTPUT_PATH}/filemanagerui.js + COMMENT "Copying UI script filemanagerui.sj" + VERBATIM) + else() + target_sources(solvespace PRIVATE + platform/guigtk.cpp) - target_include_directories(solvespace PRIVATE SYSTEM - ${GTKMM_INCLUDE_DIRS} - ${JSONC_INCLUDE_DIRS} - ${FONTCONFIG_INCLUDE_DIRS}) - target_link_directories(solvespace PRIVATE - ${GTKMM_LIBRARY_DIRS} - ${JSONC_LIBRARY_DIRS} - ${FONTCONFIG_LIBRARY_DIRS}) - target_link_libraries(solvespace PRIVATE - ${GTKMM_LIBRARIES} - ${JSONC_LIBRARIES} - ${FONTCONFIG_LIBRARIES}) - endif() + target_include_directories(solvespace PRIVATE SYSTEM + ${GTKMM_INCLUDE_DIRS} + ${JSONC_INCLUDE_DIRS} + ${FONTCONFIG_INCLUDE_DIRS}) + target_link_directories(solvespace PRIVATE + ${GTKMM_LIBRARY_DIRS} + ${JSONC_LIBRARY_DIRS} + ${FONTCONFIG_LIBRARY_DIRS}) + target_link_libraries(solvespace PRIVATE + ${GTKMM_LIBRARIES} + ${JSONC_LIBRARIES} + ${FONTCONFIG_LIBRARIES}) + endif() - if(MSVC) - set_target_properties(solvespace PROPERTIES - LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF") - endif() + if(MSVC) + set_target_properties(solvespace PROPERTIES + LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF") + endif() + endif() + if(USE_QT_GUI) + add_executable(solvespaceQt WIN32 MACOSX_BUNDLE + ${solvespace_core_gl_SOURCES} + platform/entrygui.cpp + $) + + add_dependencies(solvespaceQt + resources) + + target_link_libraries(solvespaceQt + PRIVATE + solvespace-core + ${OPENGL_LIBRARIES}) + + # OpenGL version + if(OPENGL STREQUAL 3) + target_sources(solvespaceQt PRIVATE + render/gl3shader.cpp + render/rendergl3.cpp) + elseif(OPENGL STREQUAL 1) + target_sources(solvespaceQt PRIVATE + render/rendergl1.cpp) + else() + message(FATAL_ERROR "Unsupported OpenGL version ${OPENGL}") + endif() + + # Platform-specific + target_sources(solvespaceQt PRIVATE + #platform/guiwin.cpp + platform/guiqt.cpp + platform/QGLMainWindow.h + ${TinyXmlIncludes} + ) + + target_link_libraries(solvespaceQt PRIVATE #comctl32 + Qt5::Core + Qt5::Gui + Qt5::Widgets + Qt5::OpenGL + ) + set(QT_DEBUG_ENV "${_qt5_install_prefix}/../../bin\\\;") + set(QT_RELEASE_ENV "${_qt5_install_prefix}/../../bin\\\;") + set(QT_PLUGIN_PATH "${_qt5_install_prefix}/../../plugins\\\;") + GenUserProp(solvespaceQt ${QT_DEBUG_ENV} ${QT_RELEASE_ENV} ${QT_PLUGIN_PATH}) + if(MSVC) + set_target_properties(solvespaceQt PROPERTIES + LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF") + endif() + endif() endif() # solvespace headless library -add_library(solvespace-headless STATIC EXCLUDE_FROM_ALL - ${solvespace_core_gl_SOURCES} - platform/guinone.cpp - render/rendercairo.cpp) +if(NOT USE_QT_GUI) + add_library(solvespace-headless STATIC EXCLUDE_FROM_ALL + ${solvespace_core_gl_SOURCES} + platform/guinone.cpp + render/rendercairo.cpp + ) +else() + add_library(solvespace-headless STATIC EXCLUDE_FROM_ALL + ${solvespace_core_gl_SOURCES} + ) +endif() + + target_compile_definitions(solvespace-headless PRIVATE HEADLESS) @@ -419,10 +552,18 @@ endif() # solvespace unix package if(NOT (WIN32 OR APPLE OR EMSCRIPTEN)) - if(ENABLE_GUI) - install(TARGETS solvespace - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + if(NOT USE_QT_GUI) + if(ENABLE_GUI) + install(TARGETS solvespace + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + else() + if(ENABLE_GUI) + install(TARGETS solvespaceQt + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() endif() + if(ENABLE_CLI) install(TARGETS solvespace-cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/platform/guiqt.cpp b/src/platform/guiqt.cpp new file mode 100644 index 00000000..91e63504 --- /dev/null +++ b/src/platform/guiqt.cpp @@ -0,0 +1,1126 @@ + +#include +#include +#include "solvespace.h" +#include "platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +// FOR TESTING +#include + + +namespace SolveSpace +{ + namespace Platform + { + /*********** Fatal Error */ + + void FatalError(const std::string& message) + { + QMessageBox::StandardButton buttonPressed = (QMessageBox::StandardButton)QMessageBox::critical(NULL, QString("Fatal Error"), QString::fromStdString(message), + QMessageBox::StandardButton::Ok); + + abort(); + } + /*********** Message Dialog */ + + class MessageDialogImplQt final : public MessageDialog { + + public: + QMessageBox* messageBoxQ; + + MessageDialogImplQt() { + messageBoxQ = new QMessageBox(0); + } + + ~MessageDialogImplQt() { + delete messageBoxQ; + } +#undef ERROR + void SetType(Type type) override { + + switch (type) { + case Type::INFORMATION: + messageBoxQ->setIcon(QMessageBox::Information); + break; + case Type::QUESTION: + messageBoxQ->setIcon(QMessageBox::Question); + break; + case Type::WARNING: + messageBoxQ->setIcon(QMessageBox::Warning); + break; + case Type::ERROR: + messageBoxQ->setIcon(QMessageBox::Critical); + break; + } + } + + void SetTitle(std::string title) override { + + messageBoxQ->setWindowTitle(QString::fromStdString(title)); + } + + void SetMessage(std::string message) override { + + messageBoxQ->setText(QString::fromStdString(message)); + } + + void SetDescription(std::string description) override { + + messageBoxQ->setInformativeText(QString::fromStdString(description)); + } + + void AddButton(std::string label, Response response, bool isDefault = false) override { + switch (response) + { + case Response::CANCEL: + messageBoxQ->addButton(QMessageBox::StandardButton::Cancel); + break; + case Response::NO: + messageBoxQ->addButton(QMessageBox::StandardButton::No); + break; + case Response::YES: + messageBoxQ->addButton(QMessageBox::StandardButton::Yes); + break; + case Response::OK: + messageBoxQ->addButton(QMessageBox::StandardButton::Ok); + break; + case Response::NONE: + messageBoxQ->addButton(QMessageBox::StandardButton::Ignore); + break; + } + + } + + Response RunModal() { + //return Response::CANCEL; + QMessageBox::StandardButton responseQ = (QMessageBox::StandardButton)messageBoxQ->exec(); + + switch (responseQ) + { + case QMessageBox::StandardButton::Cancel: + return Response::CANCEL; + case QMessageBox::StandardButton::No: + return Response::NO; + case QMessageBox::StandardButton::Yes: + return Response::YES; + case QMessageBox::StandardButton::Ok: + return Response::OK; + default: // NONE + return Response::NONE; + } + + return Response::NONE; + } + + }; + + MessageDialogRef CreateMessageDialog(WindowRef parentWindow) { + std::shared_ptr dialog = std::make_shared(); + //dialog->mbp.hwndOwner = std::static_pointer_cast(parentWindow)->hWindow; + return dialog; + } + + + /*********** File Dilaog */ + class FileDialogImplQt final : public FileDialog, public QObject { + + public: + bool isSaveDialog; + QStringList filters; + QFileDialog* fileDialogQ; + + FileDialogImplQt() { + fileDialogQ = new QFileDialog(0); + connect(fileDialogQ, &QFileDialog::filterSelected, this, &FileDialogImplQt::updateDefaultSuffix); + isSaveDialog = false; + } + + ~FileDialogImplQt() { + fileDialogQ->disconnect(); + delete fileDialogQ; + } + + void updateDefaultSuffix(QString filter) { + fileDialogQ->setDefaultSuffix(filter); + } + + std::string replaceSubstringInString(std::string str, std::string substr1, + std::string substr2) { + for(size_t index = str.find(substr1, 0); + index != std::string::npos && substr1.length(); + index = str.find(substr1, index + substr2.length())) + str.replace(index, substr1.length(), substr2); + return str; + } + + std::vector tokanize(const std::string& str, const std::string& delimeters) + { + std::vectortokens; + + // Skip delimeters at begining + std::string::size_type lastPos = str.find_first_not_of(delimeters, 0); + // first non delimeter + std::string::size_type pos = str.find_first_of(delimeters, lastPos); + + while (pos != std::string::npos || lastPos != std::string::npos) + { + // Token found, insert into tokens + tokens.push_back(str.substr(lastPos, pos - lastPos)); + // Skip delimeters + lastPos = str.find_first_not_of(delimeters, pos); + // find the next non delimeter + pos = str.find_first_of(delimeters, lastPos); + } + return tokens; + } + + void SetTitle(std::string title) { + fileDialogQ->setWindowTitle(QString::fromStdString(title)); + } + + void SetCurrentName(std::string name) { + fileDialogQ->selectFile(QString::fromStdString(name)); + } + + Platform::Path GetFilename() { +#if defined WIN32 + std::cout << "IN WINDOWS" << std::endl; +#endif +#if defined UNIX + std::cout << "IN UNIX" << std::endl; +#endif + Platform::Path filePath; + QString suffix = tokanize(filters[0].toStdString(), ".")[1].c_str(); + fileDialogQ->setDefaultSuffix(suffix); + QStringList selectedFiles = fileDialogQ->selectedFiles(); + + if (true == selectedFiles.isEmpty()) + return filePath; + + std::string pathUnixStyle = selectedFiles.at(0).toStdString(); // return first selection. + if ('\\' == QDir::separator()) + filePath.raw = replaceSubstringInString(pathUnixStyle, "/", "\\"); + else + { + filePath.raw = pathUnixStyle; + } + return filePath; + } + + void SetFilename(Platform::Path path) { + fileDialogQ->selectFile(QString::fromStdString(path.raw)); + } + + void SuggestFilename(Platform::Path path) { + fileDialogQ->selectFile(QString::fromStdString(path.FileStem())); + } + + void AddFilter(std::string name, std::vector extensions) override { + std::string desc, patterns; + for (auto& extension : extensions) { + std::string pattern = "*." + extension; + if (!desc.empty()) desc += " "; + desc += pattern; + } + filters.append(QString::fromStdString(desc)); + } + + void AddFilter(const FileFilter& filter) { + AddFilter(filter.name, filter.extensions); + } + + void AddFilters(const std::vector& filters) { + for (auto& filter : filters) + { + AddFilter(filter); + } + } + + std::string GetExtension() + { + return fileDialogQ->selectedNameFilter().toStdString(); + } + + void SetExtension(std::string extension) + { + fileDialogQ->selectNameFilter(QString::fromStdString(extension)); + } + + void FreezeChoices(SettingsRef settings, const std::string& key) + { + settings->FreezeString("Dialog_" + key + "_Folder", + fileDialogQ->directory().absolutePath().toStdString()); + settings->FreezeString("Dialog_" + key + "_Filter",GetExtension()); + } + + void ThawChoices(SettingsRef settings, const std::string &key) + { + fileDialogQ->setDirectory(QDir(QString::fromStdString(settings->ThawString("Dialog_" + key + "_Folder")))); + SetExtension(settings->ThawString("Dialog_" + key + "_Filter")); + } + + + bool RunModal() override { + + if (isSaveDialog) { + SetTitle("Save file"); + fileDialogQ->setAcceptMode(QFileDialog::AcceptSave); + } + else { + SetTitle("Open file"); + fileDialogQ->setAcceptMode(QFileDialog::AcceptOpen); + } + + fileDialogQ->setNameFilters(filters); + return(fileDialogQ->exec()); + + } + + + }; + + FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) { + std::shared_ptr dialog = std::make_shared(); + dialog->isSaveDialog = false; + return dialog; + } + + FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) { + std::shared_ptr dialog = std::make_shared(); + dialog->isSaveDialog = true; + return dialog; + } + + /*******Timer Class ***************************/ + + class TimerImplQt final : public Timer, public QObject + { + public: + TimerImplQt() {} + + void RunAfter(unsigned milliseconds) override { + #if 0 + QEventLoop loop; + QTimer::singleShot(milliseconds, &loop, &QEventLoop::quit); + loop.exec(); + if (onTimeout) { + onTimeout(); + } + #endif + QTimer::singleShot(milliseconds, this, &TimerImplQt::runOnTimeOut); + } + void runOnTimeOut() { + if(onTimeout) { + onTimeout(); + } + } + }; + + TimerRef CreateTimer() { + std::shared_ptr timer = std::make_shared(); + return timer; + } + + /**********Menu Item*****************/ + + class MenuItemImplQt final : public MenuItem , public QObject { + + public: + QAction* actionItemQ; + // MenuItemImplQt must set this pointer in order to control exculsivity + QActionGroup* actionGroupItemQ; + + MenuItemImplQt() { + actionItemQ = new QAction; + actionGroupItemQ = 0; + connect(actionItemQ, &QAction::triggered, this, &MenuItemImplQt:: onTriggered); + } + + ~MenuItemImplQt() { + actionItemQ->disconnect(); + onTrigger = nullptr; + } + + void Clear() { + + actionItemQ->disconnect(); + } + + void SetAccelerator(KeyboardEvent accel) override { + + QString accelKey; + + if (accel.key == KeyboardEvent::Key::CHARACTER) { + if (accel.chr == '\t') { + accelKey = "Tab"; + } + else if (accel.chr == '\x1b') { + accelKey = "Escape"; + } + else if (accel.chr == '\x7f') { + accelKey = "Del"; + } + else { + accelKey = accel.chr; + } + } + else if (accel.key == KeyboardEvent::Key::FUNCTION) { + accelKey = "F" + QString::number(accel.num ); + } + + QString accelMods; + + if (accel.controlDown) { + accelMods = "Ctrl"; + } + + if (accel.shiftDown) { + accelMods += (true == accelMods.isEmpty()) ? "Shift" : "+Shift"; + } + + QKeySequence keySequence; + + if (false == accelMods.isEmpty()) { + keySequence = QString(accelMods + "+" + accelKey); + } + else { + keySequence = QString(accelKey); + } + + actionItemQ->setShortcut(keySequence); + } + + void SetIndicator(Indicator type) override { + + switch (type) + { + case Indicator::NONE: + actionItemQ->setCheckable(false); + actionGroupItemQ->removeAction(actionItemQ); + break; + case Indicator::CHECK_MARK: + actionItemQ->setCheckable(true); + actionGroupItemQ->setExclusive(false); + actionGroupItemQ->addAction(actionItemQ); + break; + case Indicator::RADIO_MARK: + actionItemQ->setCheckable(true); + actionGroupItemQ->setExclusive(true); + actionGroupItemQ->addAction(actionItemQ); + break; + + } + } + + void SetEnabled(bool enabled) override { + actionItemQ->setEnabled(enabled); + } + + void SetActive(bool active) override { + actionItemQ->setChecked(active); + } + + public slots: + void onTriggered(bool value) + { + if (onTrigger) + this->onTrigger(); + + } + + }; + + /**********************Menu *************************/ + class MenuImplQt final : public Menu, public QObject { + + public: + QMenu* menuQ; + QActionGroup* menuActionGroupQ; + bool hasParent; + std::vector> menuItems; + std::vector> subMenus; + + MenuImplQt(bool hasParentParam = true) { + menuQ = new QMenu(); + + connect(menuQ, &QMenu::aboutToShow, this, &MenuImplQt::menuAboutToShowEvent); + + // The menu action group is needed for menu items that are checkable and are exclusive + // The exclusive ( radio button like check, only one the group can be checked at a time) + menuActionGroupQ = new QActionGroup(menuQ); + hasParent = hasParentParam; + } + + + MenuImplQt(QMenu* menuQParam) { + menuQ = menuQParam; + // The menu action group is needed for menu items that are checkable and are exclusive + // The exclusive ( radio button like check, only one the group can be checked at a time) + menuActionGroupQ = new QActionGroup(menuQ); + hasParent = true; + } + + ~MenuImplQt() + { + menuQ->disconnect(); + this->Clear(); + } + + + std::shared_ptr AddItem(const std::string& label, std::function onTrigger, + bool /*mnemonics*/) override { + + std::shared_ptr menuItem = std::make_shared(); + menuItem->actionGroupItemQ = menuActionGroupQ; + menuItem->onTrigger = onTrigger; + menuItem->actionItemQ->setText(QString::fromStdString(label)); + // I do not think anything is needed for mnemonics flag. + // the shortcut key sequence is set in the menuItem class and Qt acts accordingly + // with no further activation ... I think + menuItems.push_back(menuItem); + menuQ->addAction(menuItem->actionItemQ); + return menuItem; + } + + std::shared_ptr AddSubMenu(const std::string& label) override { + + std::shared_ptr subMenu = std::make_shared(menuQ->addMenu(QString::fromStdString(label))); + subMenus.push_back(subMenu); + return subMenu; + } + + void AddSeparator() override { + + menuQ->addSeparator(); + } + + void PopUp() override { + + if (true == this->hasParent) + return; + + menuQ->popup(menuQ->mapFromGlobal(QCursor::pos())); + menuQ->exec(); + } + + void Clear() override { + + menuQ->disconnect(); + menuQ->clear(); + + for(auto menuItem : menuItems) { + menuItem->Clear(); + } + + for(auto subMenu : subMenus) { + subMenu->Clear(); + } + + } + + public slots: + void menuAboutToShowEvent() + { + std::cout.imbue(std::locale::classic()); + std::setlocale(LC_ALL, "ja_JP.UTF-8"); + std::cout << "Menu " << this->menuQ->title().toStdString() << " *** about to show" << std::endl; + this->menuQ->repaint(); + QRect rect = this->menuQ->geometry(); + QRect rect2 = rect; + std::cout << "Geometry of Menu ;" << rect.x() << "," << rect.y() << "," << rect.width() << "," << rect.height() << std::endl; + + if (true == this->menuQ->isVisible()) + { + std::cout << " MENU VISIBLE " << std::endl; + } + else + { + std::cout << "CALLING SHOW" << std::endl; + this->menuQ->show(); + } + } + }; + + MenuRef CreateMenu() { + return std::make_shared(false); // false says -> menu has not Qtwidget as a parent + } + + /******************Menu Bar********************************/ + + class MenuBarImpQt final : public MenuBar { + + public: + QMenuBar* menuBarQ; + std::vector> subMenus; + std::shared_ptr showPropertyBrowserMenuItem; + + MenuBarImpQt() { + menuBarQ = new QMenuBar; + this->menuBarQ->setNativeMenuBar(false); + showPropertyBrowserMenuItem = nullptr; + } + + + void findShowPropertyBrowserMenuItem() { + // loop through the menus + // go thorugh each item and check the key sequence + // it should be the one with the tab key sequence + for(size_t ii = 0; ii < subMenus.size(); ++ii) { + + std::vector> menuItems = subMenus[ii]->menuItems; + + for(size_t jj = 0; jj < menuItems.size(); ++jj) { + + std::shared_ptr menuItem = menuItems[jj]; + + if(menuItem->actionItemQ->shortcut() == Qt::Key_Tab) { + + showPropertyBrowserMenuItem = menuItem; + } + } + } + + } + + ~MenuBarImpQt() { + this->Clear(); + } + + std::shared_ptr AddSubMenu(const std::string& label) override { + + std::shared_ptr menu = std::make_shared(); + menuBarQ->addMenu(menu->menuQ); + menu->menuQ->setTitle(QString::fromStdString(label)); + subMenus.push_back(menu); + return menu; + } + + void Clear() override { + menuBarQ->clear(); + for(auto subMenu : subMenus) { + subMenu->Clear(); + } + } + }; + + std::shared_ptr gMenuBarQt = nullptr; + + MenuBarRef GetOrCreateMainMenu(bool* unique) { + + *unique = false; + //return std::make_shared(); + if(gMenuBarQt == nullptr) { + gMenuBarQt = std::make_shared(); + + } + else { + gMenuBarQt->menuBarQ->clear(); + gMenuBarQt->Clear(); + } + + return gMenuBarQt; + } + + /***********Window ***************/ + + + class WindowImplQt final : public Window, public QObject { + public: + QtGLMainWindow* windowGLQ; + Window::Kind windowKind; + std::shared_ptr parentWindow; + //std::shared_ptr menuBarQt; + + WindowImplQt(Window::Kind kind , std::shared_ptr parent = nullptr) + : windowKind(kind), + parentWindow(parent) + { + windowGLQ = new QtGLMainWindow((parentWindow == nullptr)? nullptr : parentWindow->windowGLQ); + connect(windowGLQ->GetGlWidget(), &GLWidget::mouseEventOccuredSignal, this, &WindowImplQt::runOnMouseEvent); + connect(windowGLQ->GetGlWidget(), &GLWidget::keyEventOccuredSignal, this, &WindowImplQt::runOnKeyboardEvent); + connect(windowGLQ->GetGlWidget(), &GLWidget::renderOccuredSignal, this, &WindowImplQt::runOnRender); + connect(windowGLQ->GetGlWidget(), &GLWidget::editingDoneSignal, this, &WindowImplQt::runOnEditingDone); + connect(windowGLQ, &QtGLMainWindow::windowClosedSignal, this, &WindowImplQt::runOnClose); + connect(windowGLQ->GetGlWidget(), &GLWidget::tabKeyPressed, this, &WindowImplQt::processTabKeyPressed); + + } + + ~WindowImplQt() { + + } + + void runOnMouseEvent(SolveSpace::Platform::MouseEvent mouseEvent){ + onMouseEvent(mouseEvent); + } + + void runOnKeyboardEvent(SolveSpace::Platform::KeyboardEvent keyEvent){ + onKeyboardEvent(keyEvent); + } + + void runOnRender(){ + onRender(); + } + + void runOnEditingDone(std::string text) { + onEditingDone(text); + } + + void runOnClose(){ + onClose(); + } + + // Returns physical display DPI. + double GetPixelDensity() override { + return windowGLQ->GetPixelDensity(); + } + + // Returns raster graphics and coordinate scale (already applied on the platform side), + // i.e. size of logical pixel in physical pixels, or device pixel ratio. + double GetDevicePixelRatio() override { + return windowGLQ->GetDevicePixelRatio(); + } + + // Returns (fractional) font scale, to be applied on top of (integral) device pixel ratio. + double GetDeviceFontScale() { + return GetPixelDensity() / GetDevicePixelRatio() / 96.0; + } + + bool IsVisible() override { + return windowGLQ->IsVisible(); + } + + void SetVisible(bool visible) override { + windowGLQ->setVisible(visible); + windowGLQ->raise(); + } + + void Focus() override { + windowGLQ->setFocus(); + } + + bool IsFullScreen() override { + return windowGLQ->isFullScreen(); + } + + void SetFullScreen(bool fullScreen) override { + if (true == fullScreen) + windowGLQ->setWindowState(Qt::WindowFullScreen); + else + windowGLQ->setWindowState(Qt::WindowNoState); //The window has no state set (in normal state). + + } + + void SetTitle(const std::string& title) override { + windowGLQ->setWindowTitle(QString::fromStdString(title)); + } + + void SetMenuBar(MenuBarRef menuBar) override { + // Cast this menu bar to a Qt + //menuBarQt = std::static_pointer_cast(menuBar); + //windowGLQ->setMenuBar(menuBarQt->menuBarQ); + + if(windowGLQ->menuBar() != gMenuBarQt->menuBarQ) { + windowGLQ->setMenuBar(gMenuBarQt->menuBarQ); + gMenuBarQt->findShowPropertyBrowserMenuItem(); + } + + + } + + void GetContentSize(double* width, double* height) override { + + windowGLQ->GetContentSize(width, height); + } + + void SetMinContentSize(double width, double height) override { + int w = width; + int h = height; + windowGLQ->setGeometry(50,50,w, h); + } + + void FreezePosition(SettingsRef settings, const std::string& key) override { + + if (!(windowGLQ->isVisible())) return; + + int left, top, width, height; + #if 0 + QPoint topLeftPoint = windowGLQ->geometry().topLeft(); + left = topLeftPoint.x(); + top = topLeftPoint.y(); + #endif + QPoint windowPos = windowGLQ->pos(); + left = windowPos.x(); + top = windowPos.y(); + width = windowGLQ->geometry().width(); + height = windowGLQ->geometry().height(); + bool isMaximized = windowGLQ->isMaximized(); + + settings->FreezeInt(key + "_Left", left); + settings->FreezeInt(key + "_Top", top); + settings->FreezeInt(key + "_Width", width); + settings->FreezeInt(key + "_Height", height); + settings->FreezeBool(key + "_Maximized", isMaximized); + } + + void ThawPosition(SettingsRef settings, const std::string& key) override { + int left = 100, top = 100, width = 0, height = 0; + + left = settings->ThawInt(key + "_Left", left); + top = settings->ThawInt(key + "_Top", top); + width = settings->ThawInt(key + "_Width", width); + height = settings->ThawInt(key + "_Height", height); + + if(width != 0 && height != 0) { + windowGLQ->move(left, top); + windowGLQ->resize(width, height); + } + + if (settings->ThawBool(key + "_Maximized", false)) { +// windowGLQ->SetFullScreen(true); + windowGLQ->setWindowState(Qt::WindowMaximized); + } + } + + void SetCursor(Cursor cursor) override { + + switch (cursor) + { + case Cursor::POINTER: + windowGLQ->setCursor(Qt::ArrowCursor); + break; + case Cursor::HAND: + windowGLQ->setCursor(Qt::PointingHandCursor); + break; + default: + windowGLQ->setCursor(Qt::ArrowCursor); + } + } + + void SetTooltip(const std::string& text, double x, double y, + double width, double height) override { + windowGLQ->SetTooltip(text, x, y, width, height); + } + + bool IsEditorVisible() override { + return windowGLQ->IsEditorVisible(); + } + + void ShowEditor(double x, double y, double fontHeight, double minWidth, + bool isMonospace, const std::string& text) override { + windowGLQ->ShowEditor(x, y, fontHeight, minWidth, isMonospace, text); + } + + void HideEditor() override { + windowGLQ->HideEditor(); + } + + void SetScrollbarVisible(bool visible) override { + windowGLQ->SetScrollbarVisible(visible); + } + + void ConfigureScrollbar(double min, double max, double pageSize) override { + windowGLQ->ConfigureScrollbar(min, max, pageSize); + } + + double GetScrollbarPosition() override { + return windowGLQ->GetScrollbarPosition(); + } + + void SetScrollbarPosition(double pos) override { + windowGLQ->SetScrollbarPosition(pos); + } + + void Invalidate() override { + windowGLQ->Invalidate(); + } + + QtGLMainWindow* getQtGLMainWindow() { + return windowGLQ; + } + + public slots: + + void processTabKeyPressed() { + + if(windowGLQ->menuBar() != gMenuBarQt->menuBarQ && gMenuBarQt->showPropertyBrowserMenuItem != nullptr) { + gMenuBarQt->showPropertyBrowserMenuItem->actionItemQ->setChecked(false); + gMenuBarQt->showPropertyBrowserMenuItem->onTriggered(false); + } + } + + }; + + std::shared_ptr gGrahicWindowQt = nullptr; + +#undef CreateWindow + WindowRef CreateWindow(Window::Kind kind, + WindowRef parentWindow) { + + std::shared_ptr window; + + if(Window::Kind::TOOL == kind) { + window = std::make_shared(kind, gGrahicWindowQt); + } else { + window = std::make_shared(kind, nullptr); + gGrahicWindowQt =std::static_pointer_cast(window); + } + return window; + } + + // 3DConnexion support. + void Open3DConnexion() { + + } + void Close3DConnexion() { + + } + void Request3DConnexionEventsForWindow(WindowRef window) { + + } + + /************** Settings *******************************/ +//#ifndef XMLCheckResult +// #define XMLCheckResult(a_eResult) if (a_eResult != tinyxml2::XMLError::XML_SUCCESS) { printf("Error: %i\n", a_eResult); } +//#endif + + class SettingsImpTinyXml final : public Settings { + public: + tinyxml2::XMLDocument xmlDoc; + tinyxml2::XMLNode* pRoot; + + SettingsImpTinyXml(){ + tinyxml2::XMLError eResult = xmlDoc.LoadFile("solvespace.conf"); + // XMLCheckResult(eResult); + if (eResult != tinyxml2::XMLError::XML_SUCCESS) { + pRoot = xmlDoc.NewElement("Root"); + xmlDoc.InsertFirstChild(pRoot); + tinyxml2::XMLError eResult = xmlDoc.SaveFile("solvespace.conf"); + } + else + pRoot = xmlDoc.FirstChild(); + } + + + ~SettingsImpTinyXml(){ + } + + void FreezeInt(const std::string& key, uint32_t value) override { + tinyxml2::XMLElement* pElement = pRoot->FirstChildElement(key.c_str()); + if (pElement == nullptr) { + pElement = xmlDoc.NewElement(key.c_str()); + pRoot->InsertEndChild(pElement); + } + pElement->SetText(value); + tinyxml2::XMLError eResult = xmlDoc.SaveFile("solvespace.conf"); + // XMLCheckResult(eResult); + } + + uint32_t ThawInt(const std::string& key, uint32_t defaultValue = 0) override { + int iOutInt = defaultValue; + tinyxml2::XMLNode* pRoot2 = xmlDoc.FirstChild(); + if(pRoot2 != nullptr) { + tinyxml2::XMLElement* pElement2 = pRoot2->FirstChildElement(key.c_str()); + if (pElement2 != nullptr) { + tinyxml2::XMLError eResult = pElement2->QueryIntText(&iOutInt); + } + else { + // std::cout << "Error: " << tinyxml2::XMLError::XML_ERROR_PARSING_ELEMENT; + // XMLCheckResult(eResult); + } + } + else { + // std::cout << "Error: " << tinyxml2::XMLError::XML_ERROR_FILE_READ_ERROR; + } + return iOutInt; + } + + void FreezeFloat(const std::string& key, double value) override{ + tinyxml2::XMLElement* pElement = pRoot->FirstChildElement(key.c_str()); + if (pElement == nullptr) { + pElement = xmlDoc.NewElement(key.c_str()); + pRoot->InsertEndChild(pElement); + } + pElement->SetText(value); + tinyxml2::XMLError eResult = xmlDoc.SaveFile("solvespace.conf"); + // XMLCheckResult(eResult); + } + + double ThawFloat(const std::string& key, double defaultValue = 0.0) override { + double iOutFloat = defaultValue; + tinyxml2::XMLNode* pRoot2 = xmlDoc.FirstChild(); + if(pRoot2 != nullptr) { + tinyxml2::XMLElement* pElement2 = pRoot2->FirstChildElement(key.c_str()); + if (pElement2 != nullptr) { + tinyxml2::XMLError eResult = pElement2->QueryDoubleText(&iOutFloat); + } + else { + // std::cout << "Error: " << tinyxml2::XMLError::XML_ERROR_PARSING_ELEMENT; + // XMLCheckResult(eResult); + } + } + else { + // std::cout << "Error: " << tinyxml2::XMLError::XML_ERROR_FILE_READ_ERROR; + } + return iOutFloat; + } + + void FreezeString(const std::string& key, const std::string& value) override { + tinyxml2::XMLElement* pElement = pRoot->FirstChildElement(key.c_str()); + if (pElement == nullptr) { + pElement = xmlDoc.NewElement(key.c_str()); + pRoot->InsertEndChild(pElement); + } + pElement->SetText(value.c_str()); + tinyxml2::XMLError eResult = xmlDoc.SaveFile("solvespace.conf"); + // XMLCheckResult(eResult); + } + + std::string ThawString(const std::string& key, const std::string& defaultValue = "") override { + std::string iOutString = defaultValue; + tinyxml2::XMLNode* pRoot2 = xmlDoc.FirstChild(); + if(pRoot2 != nullptr) { + tinyxml2::XMLElement* pElement2 = pRoot2->FirstChildElement(key.c_str()); + if(pElement2 != nullptr && pElement2->GetText() != nullptr) { + iOutString = pElement2->GetText(); + } + else { + //std::cout << "Error: " << tinyxml2::XMLError::XML_ERROR_PARSING_ELEMENT; + } + } + else { + // std::cout << "Error: " << tinyxml2::XMLError::XML_ERROR_FILE_READ_ERROR; + } + return iOutString; + #if 0 + std::string iOutString = defaultValue; + tinyxml2::XMLNode* pRoot2 = xmlDoc.FirstChild(); + if (pRoot2 == nullptr) + std::cout << "Error: " << tinyxml2::XMLError::XML_ERROR_FILE_READ_ERROR; + tinyxml2::XMLElement* pElement2 = pRoot2->FirstChildElement(key.c_str()); + if (pElement2 == nullptr) + std::cout << "Error: " << tinyxml2::XMLError::XML_ERROR_PARSING_ELEMENT; + iOutString = pElement2->GetText(); + return iOutString; + #endif + } + + }; + + SettingsRef GetSettings() + { + static std::shared_ptr settings; + if (!settings) { + settings = std::make_shared(); + } + return settings; + } + + std::vector GetFontFiles() { +#if WIN32 + std::vector fonts; + + std::wstring fontsDirW(MAX_PATH, '\0'); + fontsDirW.resize(GetWindowsDirectoryW(&fontsDirW[0], fontsDirW.length())); + fontsDirW += L"\\fonts\\"; + Platform::Path fontsDir = Platform::Path::From(SolveSpace::Platform::Narrow(fontsDirW)); + + WIN32_FIND_DATAW wfd; + HANDLE h = FindFirstFileW((fontsDirW + L"*").c_str(), &wfd); + while (h != INVALID_HANDLE_VALUE) { + fonts.push_back(fontsDir.Join(SolveSpace::Platform::Narrow(wfd.cFileName))); + if (!FindNextFileW(h, &wfd)) break; + } + + return fonts; +#endif +#if UNIX + std::vector fonts; + + // fontconfig is already initialized by GTK + FcPattern* pat = FcPatternCreate(); + FcObjectSet* os = FcObjectSetBuild(FC_FILE, (char*)0); + FcFontSet* fs = FcFontList(0, pat, os); + + for (int i = 0; i < fs->nfont; i++) { + FcChar8* filenameFC = FcPatternFormat(fs->fonts[i], (const FcChar8*)"%{file}"); + fonts.push_back(Platform::Path::From((const char*)filenameFC)); + FcStrFree(filenameFC); + } + + FcFontSetDestroy(fs); + FcObjectSetDestroy(os); + FcPatternDestroy(pat); + + return fonts; + +#endif + + return std::vector(); + } + + void OpenInBrowser(const std::string& url) { + + std::string urlTemp = url; + + if (urlTemp.find("http") == std::string::npos) + { + urlTemp = QApplication::applicationDirPath().toStdString(); + urlTemp += "/" + url; + + } + + if (false == QDesktopServices::openUrl(QUrl(urlTemp.c_str(), QUrl::TolerantMode))) + { + std::string errorStr = "Error: unable to open URL path : " + urlTemp; + QMessageBox::critical(0,QString("Manual Open Error"),QString::fromStdString(errorStr),QMessageBox::Ok); + } + } + + QApplication* gQApp; + + std::vector InitGui(int argc, char** argv) { + gQApp = new QApplication(argc, argv); + std::vector args = SolveSpace::Platform::InitCli(argc, argv); + return args; + } + + void RunGui() { + QString filePath = QString("./styleSheets/darkorange.qss"); + QFile File(filePath); + bool opened = File.open(QFile::ReadOnly); + + if (opened == false) + { + std::cout << "failed to open qss" << std::endl; + } + else + { + QString StyleSheet = QLatin1String(File.readAll()); + gQApp->setStyleSheet(StyleSheet); + } + gQApp->exec(); + } + void ExitGui() { +// gQApp->exit; + gQApp->quit(); + } + void ClearGui() { + delete gQApp; + } + } +} diff --git a/src/platform/qglmainwindow.h b/src/platform/qglmainwindow.h new file mode 100644 index 00000000..3b914272 --- /dev/null +++ b/src/platform/qglmainwindow.h @@ -0,0 +1,485 @@ +#ifndef QGlMainWindow_h +#define QGlMainWindow_h + + +#include + +#include +#include +#include +#include +//#include +//#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace SolveSpace::Platform; + +class GLWidget : public QOpenGLWidget, public QOpenGLFunctions_1_0 +{ + Q_OBJECT + +public: + GLWidget(QWidget* parent = 0) + : QOpenGLWidget(parent) { + + entry = new QLineEdit(this); + entry->setGeometry(QRect(0, 0, 200, 40)); + QFont entryFont("Arial", 16); + entry->setFont(entryFont); + entry->setVisible(false); + connect(entry, SIGNAL(returnPressed()), this, SLOT(entryFinished())); + this->setFocusPolicy(Qt::FocusPolicy::StrongFocus); + this->setMouseTracking(true); + } + + ~GLWidget() + { + delete entry; + } + + void startEditing(int x, int y, int fontHeight, int minWidth, bool isMonoSpace, + const std::string& val) { + + entry->setGeometry(QRect(x, y, minWidth, fontHeight)); + entry->setText(QString::fromStdString(val)); + + if (!entry->isVisible()) { + entry->show(); + entry->setFocus(); + entry->setCursorPosition(0); + } + } + + void stopEditing() { + + if (entry->isVisible()) { + entry->hide(); + this->setFocus(); + } + } + + + QSize minimumSizeHint() const { + return QSize(50, 50); + } + + QSize sizeHint() const { + return QSize(400, 400); + } + +protected slots: + + void entryFinished() + { + std::string entryText = entry->text().toStdString(); + emit editingDoneSignal(entryText); + } + + +protected: + void initializeGL() { + initializeOpenGLFunctions(); + } + + void paintGL() { + + if(true == QOpenGLContext::currentContext()->isValid()) + emit renderOccuredSignal(); + } + + void resizeGL(int width, int height) { + // end of remove + if(true == QOpenGLContext::currentContext()->isValid()) + emit renderOccuredSignal(); + } + + //----Mouse Events-------- + void wheelEvent(QWheelEvent* event) + { + int wheelDelta=120; + slvMouseEvent.button = MouseEvent::Button::NONE; + slvMouseEvent.type = MouseEvent::Type::SCROLL_VERT; + if (!event->angleDelta().isNull()) + slvMouseEvent.scrollDelta = (double)event->delta() / (double)wheelDelta; + + emit mouseEventOccuredSignal(slvMouseEvent); + } + + void mouseReleaseEvent(QMouseEvent* event) override{ + this->updateSlvSpaceMouseEvent(event); + QWidget::mouseReleaseEvent(event); + } + + virtual void mousePressEvent(QMouseEvent* event) override{ + + this->updateSlvSpaceMouseEvent(event); + QWidget::mousePressEvent(event); + + } + + void mouseMoveEvent(QMouseEvent* event) override{ + + this->updateSlvSpaceMouseEvent(event); + QWidget::mouseMoveEvent(event); + } + + void updateSlvSpaceMouseEvent(QMouseEvent* event) + { + slvMouseEvent.shiftDown = false; + slvMouseEvent.controlDown = false; + + switch (event->type()) + { + case QEvent::MouseMove: + slvMouseEvent.type = MouseEvent::Type::MOTION; + break; + case QEvent::MouseButtonPress: + slvMouseEvent.type = MouseEvent::Type::PRESS; + break; + case QEvent::MouseButtonDblClick: + slvMouseEvent.type = MouseEvent::Type::DBL_PRESS; + break; + case QEvent::MouseButtonRelease: + slvMouseEvent.type = MouseEvent::Type::RELEASE; + slvMouseEvent.button = MouseEvent::Button::NONE; + break; + case QEvent::Wheel: + slvMouseEvent.type = MouseEvent::Type::SCROLL_VERT; + //slvMouseEvent.scrollDelta = event-> + break; + case QEvent::Leave: + slvMouseEvent.type = MouseEvent::Type::LEAVE; + slvMouseEvent.button = MouseEvent::Button::NONE; + break; + default: + slvMouseEvent.type = MouseEvent::Type::RELEASE; + slvMouseEvent.button = MouseEvent::Button::NONE; + } + + Qt::MouseButtons buttonState = (slvMouseEvent.type == MouseEvent::Type::MOTION) ? event->buttons() : event->button(); + + switch (buttonState) + { + case Qt::MouseButton::LeftButton: + slvMouseEvent.button = MouseEvent::Button::LEFT; + break; + case Qt::MouseButton::RightButton: + slvMouseEvent.button = MouseEvent::Button::RIGHT; + break; + case Qt::MouseButton::MidButton: + slvMouseEvent.button = MouseEvent::Button::MIDDLE; + break; + default: + slvMouseEvent.button = MouseEvent::Button::NONE; + //slvMouseEvent.button = slvMouseEvent.button; + } + + + slvMouseEvent.x = event->pos().x(); + slvMouseEvent.y = event->pos().y(); + Qt::KeyboardModifiers keyModifier = QGuiApplication::keyboardModifiers(); + + switch (keyModifier) + { + case Qt::ShiftModifier: + slvMouseEvent.shiftDown = true; + break; + case Qt::ControlModifier: + slvMouseEvent.controlDown = true; + break; + } + + emit mouseEventOccuredSignal(slvMouseEvent); + } + //----Keyboard Events-------- + + /* + * Need to override the event method to catch the tab key during a key press event + * Qt does not send a key press event when the tab key is presed. To get that + * we need to overwrite the event functon and check if the tab key was presed during a key press event + * otherwise we send the rest of the event back to the widget for further processes + */ + + bool event(QEvent* e) + { + if(e->type() == QEvent::KeyPress) { + + QKeyEvent* keyEvent = static_cast(e); + + if(keyEvent->key() == Qt::Key_Tab) { + emit tabKeyPressed(); + } + } else { + + QWidget::event(e); + } + + + return true; + } + + + void keyPressEvent(QKeyEvent* keyEvent) + { + slvKeyEvent.type = KeyboardEvent::Type::PRESS; + updateSlvSpaceKeyEvent(keyEvent); + QWidget::keyPressEvent(keyEvent); + } + + + void keyReleaseEvent(QKeyEvent* keyEvent) + { + slvKeyEvent.type = KeyboardEvent::Type::RELEASE; + updateSlvSpaceKeyEvent(keyEvent); + QWidget::keyReleaseEvent(keyEvent); + } + + void updateSlvSpaceKeyEvent(QKeyEvent* event) + { + slvKeyEvent.key = KeyboardEvent::Key(-1); + slvKeyEvent.shiftDown = false; + slvKeyEvent.controlDown = false; + if (event->modifiers() == Qt::ShiftModifier) + slvKeyEvent.shiftDown = true; + if (event->modifiers() == Qt::ControlModifier) + slvKeyEvent.controlDown = true; + + if (event->key() >= Qt::Key_F1 && event->key() <= Qt::Key_F35) { + slvKeyEvent.key = KeyboardEvent::Key::FUNCTION; + slvKeyEvent.num = event->key() - Qt::Key_F1 + 1; + } + if (event->key() >= Qt::Key_Space && event->key() <= Qt::Key_yen) { + slvKeyEvent.key = KeyboardEvent::Key::CHARACTER; + QString keyLower = event->text().toLower(); + slvKeyEvent.chr = keyLower.toUcs4().at(0); + } + + emit keyEventOccuredSignal(slvKeyEvent); + } + + void SetTooltip(const std::string& text, double x, double y, + double width, double height) { + this->setToolTip(QString::fromStdString(text)); + QPoint pos(x, y); + QToolTip::showText(pos, toolTip()); + } +signals: + void mouseEventOccuredSignal(SolveSpace::Platform::MouseEvent mouseEvent); + void keyEventOccuredSignal(SolveSpace::Platform::KeyboardEvent keyEvent); + void renderOccuredSignal(); + void editingDoneSignal(std::string); + void tabKeyPressed(); + +public: + + QLineEdit* entry; + SolveSpace::Platform::MouseEvent slvMouseEvent; + SolveSpace::Platform::KeyboardEvent slvKeyEvent; +}; + + +class QtGLMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + QtGLMainWindow(QWidget* parent = 0) + :QMainWindow(parent) { + + glWidget = new GLWidget; + scrollArea = new QScrollArea(this); + gridLayout = new QGridLayout(); + glWidget->setLayout(gridLayout); + //scrollArea->setWidget(glWidget); + //scrollArea->setWidgetResizable(true); + this->setCentralWidget(glWidget); + //void QLayout::setContentsMargins(int left, int top, int right, int bottom) + this->wheelDelta=120; + + } + + ~QtGLMainWindow() + { + delete glWidget; + delete scrollArea; + } + + #if 0 //ADAM CHECK SCROLL + void resizeEvent(QResizeEvent* event) + { + int glWidth = glWidget->size().width(); + int glHeight = glWidget->size().height(); + int scrollAreaWidth = scrollArea->size().width(); + int scrollAreaHeight = scrollArea->size().height(); + + QWidget::resizeEvent(event); + + } + #endif + + void SetScrollbarVisible(bool visible) { + + //bool resizable = scrollArea->widgetResizable(); + #if 1 + if (true == visible) + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + else + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + #endif + } + + void ConfigureScrollbar(double min, double max, double pageSize) { + + scrollArea->verticalScrollBar()->setMinimum((int)min); + scrollArea->verticalScrollBar()->setMaximum((int)max); + scrollArea->verticalScrollBar()->setPageStep((int)pageSize); + } + + double GetScrollbarPosition() { + + return (double) scrollArea->verticalScrollBar()->value(); + } + + void SetScrollbarPosition(double pos) { + + return scrollArea->verticalScrollBar()->setValue((int)pos); + } + + void Invalidate() { + + glWidget->update(); + } + + void StartEditing(int x, int y, int fontHeight, int minWidth, bool isMonoSpace, + const std::string& val) { + + glWidget->startEditing(x, y, fontHeight, minWidth, isMonoSpace, val); + } + + void StopEditing(){ + glWidget->stopEditing(); + } + + GLWidget* GetGlWidget() { + return this->glWidget; + } + + + // Returns physical display DPI. + double GetPixelDensity() { + return QGuiApplication::primaryScreen()->logicalDotsPerInch(); + } + + // Returns raster graphics and coordinate scale (already applied on the platform side), + // i.e. size of logical pixel in physical pixels, or device pixel ratio. + double GetDevicePixelRatio() { + return this->GetGlWidget()->devicePixelRatio(); + } + + // Returns (fractional) font scale, to be applied on top of (integral) device pixel ratio. + double GetDeviceFontScale() { + return GetPixelDensity() / GetDevicePixelRatio() / 96.0; + } + + bool IsVisible() { + return this->isVisible(); + } + + void SetVisible(bool visible) { + this->setVisible(visible); + } + + void Focus() { + this->setFocus(); + } + + bool IsFullScreen() { + return this->isFullScreen(); + } + + void SetFullScreen(bool fullScreen) { + if (true == fullScreen) + this->setWindowState(Qt::WindowFullScreen); + else + this->setWindowState(Qt::WindowNoState); //The window has no state set (in normal state). + + } + + void SetTitle(const std::string& title) { + this->setWindowTitle(QString::fromStdString(title)); + } + + + void GetContentSize(double* width, double* height) { + + double pixelRatio = GetDevicePixelRatio(); + QRect rc = this->GetGlWidget()->geometry(); + *width = rc.width() / pixelRatio; + *height = rc.height() / pixelRatio; + } + + void SetMinContentSize(double width, double height) { + this->resize((int)width, (int)height); + this->GetGlWidget()->resize(width, height); + } + + void SetTooltip(const std::string& text, double x, double y, + double width, double height) { + this->glWidget->setToolTip(QString::fromStdString(text)); + } + + bool IsEditorVisible() { + + return this->glWidget->entry->isVisible(); + } + + void ShowEditor(double x, double y, double fontHeight, double minWidth, + bool isMonospace, const std::string& text) { + + // font size from Solvespace is very small and hard to see + // hard coded 20 for height and 100 for width + // using Arial font of size 16 + // font decalred and set to the entry QLabel in the constructor + // Raed Marwan + this->glWidget->startEditing(x, y, 20, 100, isMonospace, text); + } + + void HideEditor() { + + this->glWidget->stopEditing(); + } + + +protected: + + void closeEvent(QCloseEvent* ev) + { + emit windowClosedSignal(); + ev->ignore(); + } + +signals: + void windowClosedSignal(); + +private: + GLWidget* glWidget; + QScrollArea* scrollArea; + QGridLayout* gridLayout; + double wheelDelta; + +}; + + +#endif