diff --git a/.editorconfig b/.editorconfig index 302e4a894..73a35e247 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*] charset = utf-8 -end_of_line = lf +end_of_line = insert_final_newline = true vc_generate_documentation_comments = doxygen_slash_star diff --git a/.github/workflows/cmake_linux.yml b/.github/workflows/cmake_linux.yml index 59dc38fa1..7b2dba677 100644 --- a/.github/workflows/cmake_linux.yml +++ b/.github/workflows/cmake_linux.yml @@ -13,6 +13,7 @@ jobs: steps: - uses: actions/checkout@v2 + - run: sudo apt-get update - run: sudo apt-get install libx11-dev libx11-xcb-dev libxcb-util-dev libxcb-cursor-dev libxcb-keysyms1-dev libxcb-xkb-dev libxkbcommon-dev libxkbcommon-x11-dev libfontconfig1-dev libcairo2-dev libfreetype6-dev libpango1.0-dev - uses: ./.github/actions/cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 1364ba173..518f62c09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,9 @@ cmake_minimum_required(VERSION 3.5) if(NOT PROJECT_NAME) project(vstgui) set(VSTGUI_MAIN_PROJECT_BUILD 1) - set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9) + if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9) + endif() if(CMAKE_CONFIGURATION_TYPES) set(CMAKE_CONFIGURATION_TYPES Debug Release ReleaseLTO) endif() diff --git a/README.md b/README.md index a7d54c7c1..014ec6b72 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,14 @@ VSTGUI is a user interface toolkit mainly for audio plug-ins (VST, AAX, AudioUni ## System requirements Supported OS: -- Microsoft Windows 7-10 -- Apple macOS 10.9-11.0 -- Apple iOS 8-14 +- Microsoft Windows 10/11 +- Apple macOS 10.9-12 +- Apple iOS 9-15 - Linux (Preview) Supported IDE: -- Visual Studio 2015/2017/2019 -- minimum Xcode 7.3 +- Visual Studio 2015/2017/2019/2022 +- minimum Xcode 10.1 - Qt Creator - Visual Studio Code diff --git a/vstgui/.clang-format b/vstgui/.clang-format index 6290ffeb9..84b8b1680 100644 --- a/vstgui/.clang-format +++ b/vstgui/.clang-format @@ -9,8 +9,9 @@ AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Inline +AllowShortFunctionsOnASingleLine: true AllowShortIfStatementsOnASingleLine: false +AllowShortLambdasOnASingleLine: Inline AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false @@ -41,7 +42,7 @@ BreakBeforeInheritanceComma: true BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeComma BreakAfterJavaFieldAnnotations: false -BreakInheritanceList: BeforeComma +BreakInheritanceList: AfterComma BreakStringLiterals: true ColumnLimit: 100 # CommentPragmas: '^ IWYU pragma:' diff --git a/vstgui/.vscode/settings.json b/vstgui/.vscode/settings.json index 0a7405ac6..9d890f35f 100644 --- a/vstgui/.vscode/settings.json +++ b/vstgui/.vscode/settings.json @@ -51,6 +51,5 @@ "files.insertFinalNewline": true, "files.trimFinalNewlines": true, "files.trimTrailingWhitespace": true, - "editor.formatOnSave": true, "editor.insertSpaces": false } \ No newline at end of file diff --git a/vstgui/CMakeLists.txt b/vstgui/CMakeLists.txt index b5b51a608..96f598dca 100644 --- a/vstgui/CMakeLists.txt +++ b/vstgui/CMakeLists.txt @@ -6,7 +6,9 @@ if(NOT PROJECT_NAME) set(VSTGUI_MAIN_PROJECT_BUILD 1) endif() -set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9) +if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9) +endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/") @@ -95,7 +97,7 @@ if(NOT CMAKE_CONFIGURATION_TYPES) endif() if(VSTGUI_MAIN_PROJECT_BUILD) - message("Building only vstgui") + message(STATUS "Building only vstgui") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<$:${CMAKE_BINARY_DIR}/Debug/>$<$:${CMAKE_BINARY_DIR}/Release/>$<$:${CMAKE_BINARY_DIR}/ReleaseLTO/> ) @@ -126,10 +128,6 @@ if(NOT VSTGUI_STANDALONE AND VSTGUI_STANDALONE_EXAMPLES) set(VSTGUI_STANDALONE_EXAMPLES OFF) endif() -if(NOT DEFINED VSTGUI_ENABLE_DEPRECATED_METHODS) - option(VSTGUI_ENABLE_DEPRECATED_METHODS "Enable VSTGUI deprecated methods" ON) -endif() - if(NOT DEFINED VSTGUI_TOOLS) option(VSTGUI_TOOLS "Build VSTGUI Tools" ON) endif() @@ -153,9 +151,3 @@ if(hasParent) set(VSTGUI_COMPILE_DEFINITIONS ${VSTGUI_COMPILE_DEFINITIONS} PARENT_SCOPE) set(VSTGUI_LTO_COMPILER_FLAGS ${VSTGUI_LTO_COMPILER_FLAGS} PARENT_SCOPE) endif() - -if(VSTGUI_ENABLE_DEPRECATED_METHODS) - target_compile_definitions(vstgui PUBLIC VSTGUI_ENABLE_DEPRECATED_METHODS=1) -else() - target_compile_definitions(vstgui PUBLIC VSTGUI_ENABLE_DEPRECATED_METHODS=0) -endif() diff --git a/vstgui/cmake/modules/vstgui_init.cmake b/vstgui/cmake/modules/vstgui_init.cmake index 8990c6820..d4e1da05e 100644 --- a/vstgui/cmake/modules/vstgui_init.cmake +++ b/vstgui/cmake/modules/vstgui_init.cmake @@ -1,5 +1,17 @@ cmake_minimum_required(VERSION 3.5) +if(NOT DEFINED VSTGUI_ENABLE_DEPRECATED_METHODS) + option(VSTGUI_ENABLE_DEPRECATED_METHODS "Enable VSTGUI deprecated methods" ON) +endif() + +if(NOT DEFINED VSTGUI_ENABLE_XMLPARSER) + option(VSTGUI_ENABLE_XMLPARSER "Enable building deprecated Expat based XML Parser" ON) +endif() + +if(NOT DEFINED VSTGUI_ENABLE_OPENGL_SUPPORT) + option(VSTGUI_ENABLE_OPENGL_SUPPORT "Enable OpenGL support" ON) +endif() + ########################################################################################## if(UNIX AND NOT CMAKE_HOST_APPLE) set(LINUX TRUE CACHE INTERNAL "VSTGUI linux platform") @@ -36,6 +48,7 @@ if(MSVC) set(VSTGUI_LTO_LINKER_FLAGS "/LTCG") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /IGNORE:4221") endif() @@ -43,6 +56,30 @@ endif() set(VSTGUI_COMPILE_DEFINITIONS_DEBUG "${VSTGUI_COMPILE_DEFINITIONS_DEBUG};VSTGUI_LIVE_EDITING;DEBUG") set(VSTGUI_COMPILE_DEFINITIONS_RELEASE "${VSTGUI_COMPILE_DEFINITIONS_RELEASE};NDEBUG;RELEASE") +if(VSTGUI_ENABLE_DEPRECATED_METHODS) + set(VSTGUI_COMPILE_DEFINITIONS_DEBUG "${VSTGUI_COMPILE_DEFINITIONS_DEBUG};VSTGUI_ENABLE_DEPRECATED_METHODS=1") + set(VSTGUI_COMPILE_DEFINITIONS_RELEASE "${VSTGUI_COMPILE_DEFINITIONS_RELEASE};VSTGUI_ENABLE_DEPRECATED_METHODS=1") +else() + set(VSTGUI_COMPILE_DEFINITIONS_DEBUG "${VSTGUI_COMPILE_DEFINITIONS_DEBUG};VSTGUI_ENABLE_DEPRECATED_METHODS=0") + set(VSTGUI_COMPILE_DEFINITIONS_RELEASE "${VSTGUI_COMPILE_DEFINITIONS_RELEASE};VSTGUI_ENABLE_DEPRECATED_METHODS=0") +endif() + +if(VSTGUI_ENABLE_XMLPARSER) + set(VSTGUI_COMPILE_DEFINITIONS_DEBUG "${VSTGUI_COMPILE_DEFINITIONS_DEBUG};VSTGUI_ENABLE_XML_PARSER=1") + set(VSTGUI_COMPILE_DEFINITIONS_RELEASE "${VSTGUI_COMPILE_DEFINITIONS_RELEASE};VSTGUI_ENABLE_XML_PARSER=1") +else() + set(VSTGUI_COMPILE_DEFINITIONS_DEBUG "${VSTGUI_COMPILE_DEFINITIONS_DEBUG};VSTGUI_ENABLE_XML_PARSER=0") + set(VSTGUI_COMPILE_DEFINITIONS_RELEASE "${VSTGUI_COMPILE_DEFINITIONS_RELEASE};VSTGUI_ENABLE_XML_PARSER=0") +endif() + +if(VSTGUI_ENABLE_OPENGL_SUPPORT) + set(VSTGUI_COMPILE_DEFINITIONS_DEBUG "${VSTGUI_COMPILE_DEFINITIONS_DEBUG};VSTGUI_OPENGL_SUPPORT=1") + set(VSTGUI_COMPILE_DEFINITIONS_RELEASE "${VSTGUI_COMPILE_DEFINITIONS_RELEASE};VSTGUI_OPENGL_SUPPORT=1") +else() + set(VSTGUI_COMPILE_DEFINITIONS_DEBUG "${VSTGUI_COMPILE_DEFINITIONS_DEBUG};VSTGUI_OPENGL_SUPPORT=0") + set(VSTGUI_COMPILE_DEFINITIONS_RELEASE "${VSTGUI_COMPILE_DEFINITIONS_RELEASE};VSTGUI_OPENGL_SUPPORT=0") +endif() + set(VSTGUI_COMPILE_DEFINITIONS PRIVATE $<$:${VSTGUI_COMPILE_DEFINITIONS_DEBUG}> $<$:${VSTGUI_COMPILE_DEFINITIONS_RELEASE}> diff --git a/vstgui/doxygen/doc.h b/vstgui/doxygen/doc.h index 0973a0b15..063dc4449 100644 --- a/vstgui/doxygen/doc.h +++ b/vstgui/doxygen/doc.h @@ -50,10 +50,9 @@ to your IDE project and add a search path to the parent of the root folder of vs \par On macOS, you need to link to the following Frameworks: - Accelerate -- Carbon - Cocoa -- OpenGL - QuartzCore +- OpenGL (Optional) \par On iOS, you need to link to the following Frameworks: diff --git a/vstgui/doxygen/page_changes.h b/vstgui/doxygen/page_changes.h index bfecc133f..832024608 100644 --- a/vstgui/doxygen/page_changes.h +++ b/vstgui/doxygen/page_changes.h @@ -11,7 +11,6 @@ - @ref new_stuff @n - @ref code_changes @n - @ref hidpi_support @n -- @ref cocoa_support @n - @ref ios_support @n - @subpage page_previous_new_stuff @@ -23,6 +22,13 @@ It's recommended to start new projects with version 4 while old projects should @section new_stuff New Stuff +@subsection version4_11 Version 4.11 + +- Using DirectComposition on Windows now with support for CLayeredViewContainer +- Removed 32-bit Carbon support +- Reworked event handling, please see @ref code_changes_4_10_to_4_11 +- Reworked unit test framework to be able to debug the tests + @subsection version4_10 Version 4.10 - VSTGUI now needs to be initialized and terminated explicitly. See VSTGUI::init() and VSTGUI::exit(). @@ -110,6 +116,36 @@ Note: All current deprecated methods will be removed in the next version. So mak @section code_changes Changes for existing VSTGUI code +@subsection code_changes_4_10_to_4_11 VSTGUI 4.10 -> VSTGUI 4.11 + +Changes due to event handling rework: +- IKeyboardHook changed its methods. If you inherit from it, you need to adopt to the new methods or use OldKeyboardHookAdapter +- IMouseObserver changed a few of its methods. If you inherit from it, you need to adopt to the new methods or use OldMouseObserverAdapter +- CViewContainer::onWheel is now marked final, you cannot inherit this method, please override the new CView::onMouseWheelEvent instead if you need to handle mouse wheel events in a custom view container +- DragEventData has changed it's modifiers type from CButtonState to Modifiers +- CView::hitTest uses an Event now instead of a CButtonState (the method with a CButtonState still works but is deprecated) +- CControl::checkDefaultValue(CButtonState) was removed and replaced by a generic method which uses +the static function CControl::CheckDefaultValueEventFunc to reset a control to its default value + +CView has the following new methods: +- dispatchEvent +- onMouseDownEvent +- onMouseMoveEvent +- onMouseUpEvent +- onMouseCancelEvent +- onMouseEnterEvent +- onMouseExitEvent +- onMouseWheelEvent +- onZoomGestureEvent +- onKeyboardEvent + +Which replaces the following old methods: +- onKeyDown +- onKeyUp +- onWheel + +The old mouse methods (onMouseDown, onMouseUp, onMouseMoved, etc) are still supported but should be replaced with the new methods in the long run. + @subsection code_changes_4_9_to_4_10 VSTGUI 4.9 -> VSTGUI 4.10 - one has to use VSTGUI::init() before using VSTGUI and VSTGUI::exit() after use @@ -199,13 +235,6 @@ Note: All current deprecated methods will be removed in the next version. So mak - HiDPI is supported on OSX, iOS and Windows (with Direct2D backend) - Due to platform differences one need to call frame->setZoom (scaleFactor) on Windows, while on OSX and iOS this is not needed. -@section cocoa_support Cocoa notes - -- To get cocoa support your minimum required Mac OS X version is 10.6. -- In 32 bit Cocoa and Carbon are available. You can switch between them with CFrame::setCocoaMode(bool state). You must do this before creating the CFrame. -- In 64 bit only Cocoa is available. -- The pSystemWindow pointer in the CFrame constructor must be a NSView not a NSWindow. - @section ios_support iOS support notes - VSTGUI supports iOS 7 and later diff --git a/vstgui/lib/CMakeLists.txt b/vstgui/lib/CMakeLists.txt index 3e6784dff..a0e473b0c 100644 --- a/vstgui/lib/CMakeLists.txt +++ b/vstgui/lib/CMakeLists.txt @@ -18,6 +18,8 @@ set(${target}_common_sources cbitmapfilter.cpp cbitmapfilter.h cbuttonstate.h + cclipboard.cpp + cclipboard.h ccolor.cpp ccolor.h cdatabrowser.cpp @@ -35,6 +37,7 @@ set(${target}_common_sources cfont.h cframe.cpp cframe.h + cgradient.cpp cgradient.h cgradientview.cpp cgradientview.h @@ -128,6 +131,9 @@ set(${target}_common_sources cvstguitimer.h dragging.h dispatchlist.h + events.cpp + events.h + finally.h genericstringlistdatabrowsersource.cpp genericstringlistdatabrowsersource.h idatabrowserdelegate.h @@ -142,9 +148,12 @@ set(${target}_common_sources pixelbuffer.h pixelbuffer.cpp platform/iplatformbitmap.h + platform/iplatformfileselector.h platform/iplatformfont.h platform/iplatformframe.h platform/iplatformframecallback.h + platform/iplatformgradient.h + platform/iplatformgraphicspath.h platform/iplatformopenglview.h platform/iplatformoptionmenu.h platform/iplatformresourceinputstream.h @@ -165,6 +174,7 @@ set(${target}_common_sources platform/common/genericoptionmenu.h platform/common/generictextedit.cpp platform/common/generictextedit.h + platform/common/gradientbase.h platform/common/stb_textedit.h vstguibase.h vstguidebug.cpp @@ -177,12 +187,6 @@ set(${target}_common_sources ########################################################################################## set(${target}_mac_sources - platform/mac/carbon/hiviewframe.cpp - platform/mac/carbon/hiviewframe.h - platform/mac/carbon/hiviewoptionmenu.cpp - platform/mac/carbon/hiviewoptionmenu.h - platform/mac/carbon/hiviewtextedit.cpp - platform/mac/carbon/hiviewtextedit.h platform/mac/caviewlayer.h platform/mac/caviewlayer.mm platform/mac/cfontmac.h @@ -205,11 +209,13 @@ set(${target}_mac_sources platform/mac/cocoa/nsviewframe.mm platform/mac/cocoa/nsviewoptionmenu.h platform/mac/cocoa/nsviewoptionmenu.mm + platform/mac/cocoa/objcclassbuilder.h platform/mac/macclipboard.h platform/mac/macclipboard.mm platform/mac/macfactory.h platform/mac/macfactory.mm platform/mac/macfileselector.mm + platform/mac/macfileselector.h platform/mac/macglobals.cpp platform/mac/macglobals.h platform/mac/macstring.h @@ -228,16 +234,22 @@ set(${target}_mac_sources set(${target}_win32_sources platform/win32/direct2d/d2dbitmap.cpp platform/win32/direct2d/d2dbitmap.h + platform/win32/direct2d/d2dbitmapcache.cpp + platform/win32/direct2d/d2dbitmapcache.h platform/win32/direct2d/d2ddrawcontext.cpp platform/win32/direct2d/d2ddrawcontext.h platform/win32/direct2d/d2dfont.cpp platform/win32/direct2d/d2dfont.h + platform/win32/direct2d/d2dgradient.cpp + platform/win32/direct2d/d2dgradient.h platform/win32/direct2d/d2dgraphicspath.cpp platform/win32/direct2d/d2dgraphicspath.h platform/win32/win32bitmapbase.h platform/win32/win32dll.h platform/win32/win32datapackage.cpp platform/win32/win32datapackage.h + platform/win32/win32directcomposition.cpp + platform/win32/win32directcomposition.h platform/win32/win32dragging.cpp platform/win32/win32dragging.h platform/win32/win32factory.cpp @@ -254,7 +266,10 @@ set(${target}_win32_sources platform/win32/win32support.h platform/win32/win32textedit.cpp platform/win32/win32textedit.h + platform/win32/win32viewlayer.cpp + platform/win32/win32viewlayer.h platform/win32/winfileselector.cpp + platform/win32/winfileselector.h platform/win32/winstring.cpp platform/win32/winstring.h platform/win32/wintimer.cpp @@ -279,6 +294,7 @@ set(${target}_linux_sources platform/linux/x11dragging.cpp platform/linux/x11dragging.h platform/linux/x11fileselector.cpp + platform/linux/x11fileselector.h platform/linux/x11frame.cpp platform/linux/x11frame.h platform/linux/x11platform.cpp @@ -311,7 +327,7 @@ endif() add_library(${target} STATIC ${${target}_sources}) target_compile_definitions(${target} ${VSTGUI_COMPILE_DEFINITIONS}) -vstgui_set_cxx_version(${target} 14) +vstgui_set_cxx_version(${target} 17) vstgui_source_group_by_folder(${target}) if(LINUX) @@ -323,3 +339,28 @@ if(LINUX) target_include_directories(${target} PRIVATE ${FONTCONFIG_INCLUDE_DIRS}) target_link_libraries(${target} PRIVATE ${LINUX_LIBRARIES}) endif() + +if(CMAKE_HOST_APPLE) + target_compile_options(${target} PRIVATE -Wall -Werror) + set(PLATFORM_LIBRARIES + "-framework Cocoa" + "-framework QuartzCore" + "-framework Accelerate" + ) + if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 11.0) + set(PLATFORM_LIBRARIES + ${PLATFORM_LIBRARIES} + "-framework UniformTypeIdentifiers" + ) + endif() + if(VSTGUI_ENABLE_OPENGL_SUPPORT) + set(PLATFORM_LIBRARIES + ${PLATFORM_LIBRARIES} + "-framework OpenGL" + ) + endif() + target_link_libraries(${target} + PUBLIC + ${PLATFORM_LIBRARIES} + ) +endif() diff --git a/vstgui/lib/algorithm.h b/vstgui/lib/algorithm.h index bda8820a3..7fdd9d8dc 100644 --- a/vstgui/lib/algorithm.h +++ b/vstgui/lib/algorithm.h @@ -27,7 +27,7 @@ template Optional indexOfTest (Iter first, Iter last, Proc p) { auto it = std::find_if (first, last, p); - if (first == last) + if (it == last) return {}; return {static_cast (std::distance (first, it))}; } diff --git a/vstgui/lib/animation/animator.cpp b/vstgui/lib/animation/animator.cpp index 14cdc020a..a03c3cd31 100644 --- a/vstgui/lib/animation/animator.cpp +++ b/vstgui/lib/animation/animator.cpp @@ -197,7 +197,8 @@ Timer* Timer::gInstance = nullptr; class Animation : public NonAtomicReferenceCounted { public: - Animation (CView* view, const std::string& name, IAnimationTarget* at, ITimingFunction* t, DoneFunction&& notification); + Animation (CView* view, const std::string& name, IAnimationTarget* at, ITimingFunction* t, + DoneFunction&& notification, bool notifyOnCancel); ~Animation () noexcept override; std::string name; @@ -205,21 +206,21 @@ class Animation : public NonAtomicReferenceCounted IAnimationTarget* animationTarget; ITimingFunction* timingFunction; DoneFunction notification; - uint64_t startTime; - float lastPos; - bool done; + uint64_t startTime {0}; + float lastPos {-1.}; + bool done {false}; + bool notifyOnCancel; }; //----------------------------------------------------------------------------- -Animation::Animation (CView* view, const std::string& name, IAnimationTarget* at, ITimingFunction* t, DoneFunction&& notification) +Animation::Animation (CView* view, const std::string& name, IAnimationTarget* at, + ITimingFunction* t, DoneFunction&& notification, bool notifyOnCancel) : name (name) , view (view) , animationTarget (at) , timingFunction (t) , notification (std::move (notification)) -, startTime (0) -, lastPos (-1) -, done (false) +, notifyOnCancel (notifyOnCancel) { } @@ -260,12 +261,15 @@ Animator::~Animator () noexcept } //----------------------------------------------------------------------------- -void Animator::addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target, ITimingFunction* timingFunction, DoneFunction notification) +void Animator::addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target, + ITimingFunction* timingFunction, DoneFunction notification, + bool notifyOnCancel) { if (pImpl->animations.empty ()) Detail::Timer::addAnimator (this); removeAnimation (view, name); - pImpl->animations.add (makeOwned (view, name, target, timingFunction, std::move (notification))); + pImpl->animations.add (makeOwned (view, name, target, timingFunction, + std::move (notification), notifyOnCancel)); #if DEBUG_LOG DebugPrint ("new animation added: %p - %s\n", view, name); #endif @@ -302,7 +306,8 @@ void Animator::removeAnimation (CView* view, IdStringPtr name) animation->done = true; animation->animationTarget->animationFinished (view, name, true); } - animation->notification = nullptr; + if (!animation->notifyOnCancel) + animation->notification = nullptr; pImpl->animations.remove (animation); } }); diff --git a/vstgui/lib/animation/animator.h b/vstgui/lib/animation/animator.h index f25fc6197..4aaba439b 100644 --- a/vstgui/lib/animation/animator.h +++ b/vstgui/lib/animation/animator.h @@ -34,10 +34,12 @@ class Animator : public NonAtomicReferenceCounted /** adds an animation. Animation and timingFunction is now owned by the animator. An already running animation for view with name will be canceled. - The notification function will be called when the animation has finished. + The notification function will be called when the animation has finished or on cancelation + of the animation if notifyOnCancel is true (new in 4.11) */ void addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target, - ITimingFunction* timingFunction, DoneFunction notification = nullptr); + ITimingFunction* timingFunction, DoneFunction notification = nullptr, + bool notifyOnCancel = false); /** removes an animation. If animation has the IReference interface forget() will be called otherwise it is deleted. diff --git a/vstgui/lib/cclipboard.cpp b/vstgui/lib/cclipboard.cpp new file mode 100644 index 000000000..ba3359d02 --- /dev/null +++ b/vstgui/lib/cclipboard.cpp @@ -0,0 +1,104 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "cclipboard.h" +#include "platform/platformfactory.h" + +#include + +namespace VSTGUI { +namespace CClipboardDetail { + +//----------------------------------------------------------------------------- +template +struct StringDataPackage : IDataPackage +{ + StringDataPackage (std::string_view str) : str (str) {} + + uint32_t getCount () const final { return 1; } + uint32_t getDataSize (uint32_t index) const final + { + return static_cast (str.size ()); + } + Type getDataType (uint32_t index) const final { return AsFile ? Type::kFilePath : Type::kText; } + uint32_t getData (uint32_t index, const void*& buffer, Type& type) const final + { + buffer = str.data (); + type = getDataType (index); + return getDataSize (index); + } + + std::string str; +}; + +//----------------------------------------------------------------------------- +template +Optional getString (IDataPackage* cb) +{ + for (auto i = 0u, count = cb->getCount (); i < count; i++) + { + if (cb->getDataType (i) == (AsFile ? IDataPackage::Type::kFilePath + : IDataPackage::Type::kText)) + { + IDataPackage::Type type; + const void* data = nullptr; + auto size = cb->getData (i, data, type); + if (size > 0) + { + return {UTF8String (std::string (static_cast (data), size))}; + } + } + } + return {}; +} + +} // CClipboardDetail + +//----------------------------------------------------------------------------- +SharedPointer CClipboard::get () +{ + return getPlatformFactory ().getClipboard (); +} + +//----------------------------------------------------------------------------- +bool CClipboard::set (const SharedPointer& data) +{ + return getPlatformFactory ().setClipboard (data); +} + +//----------------------------------------------------------------------------- +bool CClipboard::setString (UTF8StringPtr str) +{ + return set (makeOwned> ( + std::string_view (str, strlen (str)))); +} + +//----------------------------------------------------------------------------- +bool CClipboard::setFilePath (UTF8StringPtr str) +{ + return set (makeOwned> ( + std::string_view (str, strlen (str)))); +} + +//----------------------------------------------------------------------------- +Optional CClipboard::getString () +{ + if (auto cb = get ()) + { + return CClipboardDetail::getString (cb); + } + return {}; +} + +//----------------------------------------------------------------------------- +Optional CClipboard::getFilePath () +{ + if (auto cb = get ()) + { + return CClipboardDetail::getString (cb); + } + return {}; +} + +} // VSTGUI diff --git a/vstgui/lib/cclipboard.h b/vstgui/lib/cclipboard.h new file mode 100644 index 000000000..c0fba2307 --- /dev/null +++ b/vstgui/lib/cclipboard.h @@ -0,0 +1,32 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "cstring.h" +#include "idatapackage.h" +#include "optional.h" + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +struct CClipboard +{ + /** get the global clipboard data */ + static SharedPointer get (); + /** set the global clipboard data */ + static bool set (const SharedPointer& data); + + /** get the string from the global clipboard if it exists */ + static Optional getString (); + /** get the file path from the global clipboard if it exists */ + static Optional getFilePath (); + + /** set the string of the global clipboard */ + static bool setString (UTF8StringPtr str); + /** set the file path of the global clipboard */ + static bool setFilePath (UTF8StringPtr str); +}; + +} // VSTGUI diff --git a/vstgui/lib/cdatabrowser.cpp b/vstgui/lib/cdatabrowser.cpp index 5efde14bd..ab1d246a3 100644 --- a/vstgui/lib/cdatabrowser.cpp +++ b/vstgui/lib/cdatabrowser.cpp @@ -36,7 +36,7 @@ class CDataBrowserView : public CView, public IFocusDrawing, public IDropTarget void onDragLeave (DragEventData data) override; bool onDrop (DragEventData data) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& keyCode) override; CRect getRowBounds (int32_t row); void invalidateRow (int32_t row); @@ -160,10 +160,11 @@ bool CDataBrowser::removed (CView* parent) return CScrollView::removed (parent); } -//----------------------------------------------------------------------------------------------- -int32_t CDataBrowser::onKeyDown (VstKeyCode& keyCode) +//------------------------------------------------------------------------ +void CDataBrowser::onKeyboardEvent (KeyboardEvent& event) { - return dbView ? dbView->onKeyDown (keyCode) : -1; + if (dbView) + dbView->onKeyboardEvent (event); } //----------------------------------------------------------------------------- @@ -1054,25 +1055,25 @@ DragOperation CDataBrowserView::onDragMove (DragEventData data) } //----------------------------------------------------------------------------------------------- -int32_t CDataBrowserView::onKeyDown (VstKeyCode& keyCode) +void CDataBrowserView::onKeyboardEvent (KeyboardEvent& event) { - int32_t res = db->dbOnKeyDown (keyCode, browser); - if (res != -1) - return res; - if (keyCode.modifier != 0) - return -1; - if (keyCode.virt == VKEY_UP || keyCode.virt == VKEY_DOWN || keyCode.virt == VKEY_PAGEUP || keyCode.virt == VKEY_PAGEDOWN) + db->dbOnKeyboardEvent (event, browser); + if (event.consumed || event.type != EventType::KeyDown) + return; + if (!event.modifiers.empty ()) + return; + if (event.virt == VirtualKey::Up || event.virt == VirtualKey::Down || event.virt == VirtualKey::PageUp || event.virt == VirtualKey::PageDown) { int32_t numRows = db->dbGetNumRows (browser); int32_t selRow = browser->getSelectedRow (); int32_t changeRow = 0; - if (keyCode.virt == VKEY_UP) + if (event.virt == VirtualKey::Up) changeRow = -1; - else if (keyCode.virt == VKEY_DOWN) + else if (event.virt == VirtualKey::Down) changeRow = 1; - else if (keyCode.virt == VKEY_PAGEUP) + else if (event.virt == VirtualKey::PageUp) changeRow = (int32_t) (-browser->getHeight () / db->dbGetRowHeight (browser)); - else if (keyCode.virt == VKEY_PAGEDOWN) + else if (event.virt == VirtualKey::PageDown) changeRow = (int32_t) (browser->getHeight () / db->dbGetRowHeight (browser)); int32_t newSelRow = selRow + changeRow; newSelRow = std::min (newSelRow, numRows); @@ -1085,9 +1086,8 @@ int32_t CDataBrowserView::onKeyDown (VstKeyCode& keyCode) CRect rect = getRowBounds (newSelRow); browser->makeRectVisible (rect); } - return 1; + event.consumed = true; } - return -1; } //----------------------------------------------------------------------------------------------- diff --git a/vstgui/lib/cdatabrowser.h b/vstgui/lib/cdatabrowser.h index b45a7d531..788c00fcc 100644 --- a/vstgui/lib/cdatabrowser.h +++ b/vstgui/lib/cdatabrowser.h @@ -105,7 +105,7 @@ class CDataBrowser : public CScrollView void setViewSize (const CRect& size, bool invalid) override; void setWantsFocus (bool state) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; protected: ~CDataBrowser () noexcept override; diff --git a/vstgui/lib/cfileselector.cpp b/vstgui/lib/cfileselector.cpp index 1a0514636..8d4c36ba5 100644 --- a/vstgui/lib/cfileselector.cpp +++ b/vstgui/lib/cfileselector.cpp @@ -2,6 +2,8 @@ // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE +#include "platform/iplatformfileselector.h" +#include "platform/platformfactory.h" #include "cfileselector.h" #include "cframe.h" #include "cstring.h" @@ -10,17 +12,29 @@ namespace VSTGUI { //----------------------------------------------------------------------------- -CFileExtension::CFileExtension (const UTF8String& inDescription, const UTF8String& inExtension, const UTF8String& inMimeType, int32_t inMacType, const UTF8String& inUti) -: macType (inMacType) +struct CFileExtension::Impl : PlatformFileExtension +{ +}; + +//----------------------------------------------------------------------------- +CFileExtension::CFileExtension () +{ + impl = std::make_unique (); +} + +//----------------------------------------------------------------------------- +CFileExtension::CFileExtension (const UTF8String& inDescription, const UTF8String& inExtension, + const UTF8String& inMimeType, int32_t inMacType, + const UTF8String& inUti) +: CFileExtension () { init (inDescription, inExtension, inMimeType, inUti); } //----------------------------------------------------------------------------- -CFileExtension::CFileExtension (const CFileExtension& ext) -: macType (ext.macType) +CFileExtension::CFileExtension (const CFileExtension& ext) : CFileExtension () { - init (ext.description, ext.extension, ext.mimeType, ext.uti); + *impl = *ext.impl; } //----------------------------------------------------------------------------- @@ -33,26 +47,22 @@ CFileExtension::CFileExtension (CFileExtension&& ext) noexcept } //----------------------------------------------------------------------------- -CFileExtension& CFileExtension::operator=(CFileExtension&& ext) noexcept +CFileExtension& CFileExtension::operator= (CFileExtension&& ext) noexcept { - description = std::move (ext.description); - extension = std::move (ext.extension); - mimeType = std::move (ext.mimeType); - uti = std::move (ext.uti); - macType = ext.macType; - ext.macType = 0; + std::swap (impl, ext.impl); return *this; } //----------------------------------------------------------------------------- -void CFileExtension::init (const UTF8String& inDescription, const UTF8String& inExtension, const UTF8String& inMimeType, const UTF8String& inUti) +void CFileExtension::init (const UTF8String& inDescription, const UTF8String& inExtension, + const UTF8String& inMimeType, const UTF8String& inUti) { - description = inDescription; - extension = inExtension; - mimeType = inMimeType; - uti = inUti; + impl->description = inDescription; + impl->extension = inExtension; + impl->mimeType = inMimeType; + impl->uti = inUti; - if (description == nullptr && !extension.empty ()) + if (impl->description == nullptr && !impl->extension.empty ()) { // TODO: query system for file type description // Win32: AssocGetPerceivedType @@ -64,20 +74,66 @@ void CFileExtension::init (const UTF8String& inDescription, const UTF8String& in bool CFileExtension::operator== (const CFileExtension& ext) const { bool result = false; - result = extension == ext.extension; + result = impl->extension == ext.impl->extension; if (!result) - result = mimeType == ext.mimeType; + result = impl->mimeType == ext.impl->mimeType; if (!result) - result = uti == ext.uti; - if (!result && macType != 0 && ext.macType != 0) - result = (macType == ext.macType); + result = impl->uti == ext.impl->uti; + if (!result && impl->macType != 0 && ext.impl->macType != 0) + result = (impl->macType == ext.impl->macType); return result; } +//----------------------------------------------------------------------------- +const UTF8String& CFileExtension::getDescription () const +{ + return impl->description; +} + +//----------------------------------------------------------------------------- +const UTF8String& CFileExtension::getExtension () const +{ + return impl->extension; +} + +//----------------------------------------------------------------------------- +const UTF8String& CFileExtension::getMimeType () const +{ + return impl->mimeType; +} + +//----------------------------------------------------------------------------- +const UTF8String& CFileExtension::getUTI () const +{ + return impl->uti; +} + +//----------------------------------------------------------------------------- +int32_t CFileExtension::getMacType () const +{ + return impl->macType; +} + +//----------------------------------------------------------------------------- +CFileExtension::CFileExtension (const PlatformFileExtension& ext) : CFileExtension () +{ + impl->description = ext.description; + impl->extension = ext.extension; + impl->mimeType = ext.mimeType; + impl->uti = ext.uti; + impl->macType = ext.macType; +} + +//----------------------------------------------------------------------------- +const PlatformFileExtension& CFileExtension::getPlatformFileExtension () const +{ + return *impl; +} + //----------------------------------------------------------------------------- const CFileExtension& CNewFileSelector::getAllFilesExtension () { - static CFileExtension allFilesExtension ("All Files", ""); + static CFileExtension allFilesExtension (PlatformAllFilesExtension); return allFilesExtension; } @@ -87,14 +143,19 @@ IdStringPtr CNewFileSelector::kSelectEndMessage = "CNewFileSelector Select End M //----------------------------------------------------------------------------- // CNewFileSelector Implementation //----------------------------------------------------------------------------- -CNewFileSelector::CNewFileSelector (CFrame* frame) -: frame (frame) -, title (nullptr) -, initialPath (nullptr) -, defaultSaveName (nullptr) -, defaultExtension (nullptr) -, allowMultiFileSelection (false) +struct CNewFileSelector::Impl : PlatformFileSelectorConfig +{ + PlatformFileSelectorPtr platformFileSelector; + CFrame* frame {nullptr}; + std::vector result; +}; + +//----------------------------------------------------------------------------- +CNewFileSelector::CNewFileSelector (PlatformFileSelectorPtr&& platformFileSelector, CFrame* parent) { + impl = std::make_unique (); + impl->platformFileSelector = std::move (platformFileSelector); + impl->frame = parent; } //----------------------------------------------------------------------------- @@ -110,133 +171,147 @@ bool CNewFileSelector::run (CBaseObject* delegate) #endif return false; } - if (frame) - frame->onStartLocalEventLoop (); - return runInternal (delegate); + if (impl->frame) + impl->frame->onStartLocalEventLoop (); + + impl->doneCallback = [this, del = shared (delegate)] (std::vector&& files) { + impl->result = std::move (files); + del->notify (this, CNewFileSelector::kSelectEndMessage); + }; + + setBit (impl->flags, PlatformFileSelectorFlags::RunModal, false); + return impl->platformFileSelector->run (*impl); } //----------------------------------------------------------------------------- -void CNewFileSelector::cancel () +bool CNewFileSelector::run (CallbackFunc&& callback) { - cancelInternal (); + if (impl->frame) + impl->frame->onStartLocalEventLoop (); + + impl->doneCallback = [Self = shared (this), + cb = std::move (callback)] (std::vector&& files) { + Self->impl->result = std::move (files); + cb (Self); + }; + + setBit (impl->flags, PlatformFileSelectorFlags::RunModal, false); + return impl->platformFileSelector->run (*impl); } //----------------------------------------------------------------------------- -bool CNewFileSelector::runModal () +void CNewFileSelector::cancel () { - if (frame) - frame->onStartLocalEventLoop (); - return runModalInternal (); + impl->platformFileSelector->cancel (); } //----------------------------------------------------------------------------- -class CNewFileSelectorCallback : public CBaseObject +bool CNewFileSelector::runModal () { -public: - CNewFileSelectorCallback (CNewFileSelector::CallbackFunc&& callback) : callbackFunc (std::move (callback)) {} - ~CNewFileSelectorCallback () noexcept override = default; -private: - CMessageResult notify (CBaseObject* sender, IdStringPtr message) override - { - if (message == CNewFileSelector::kSelectEndMessage) - { - callbackFunc (dynamic_cast (sender)); - return kMessageNotified; - } - return kMessageUnknown; - } - - CNewFileSelector::CallbackFunc callbackFunc; -}; + if (impl->frame) + impl->frame->onStartLocalEventLoop (); + setBit (impl->flags, PlatformFileSelectorFlags::RunModal, true); + impl->doneCallback = [&] (std::vector&& files) { + impl->result = std::move (files); + }; -//----------------------------------------------------------------------------- -bool CNewFileSelector::run (CallbackFunc&& callback) -{ - if (frame) - frame->onStartLocalEventLoop (); - auto fsCallback = makeOwned (std::move (callback)); - return runInternal (fsCallback); + return impl->platformFileSelector->run (*impl); } //----------------------------------------------------------------------------- void CNewFileSelector::setTitle (const UTF8String& inTitle) { - title = inTitle; + impl->title = inTitle; } //----------------------------------------------------------------------------- void CNewFileSelector::setInitialDirectory (const UTF8String& path) { - initialPath = path; + impl->initialPath = path; } //----------------------------------------------------------------------------- void CNewFileSelector::setDefaultSaveName (const UTF8String& name) { - defaultSaveName = name; + impl->defaultSaveName = name; } //----------------------------------------------------------------------------- void CNewFileSelector::setAllowMultiFileSelection (bool state) { - allowMultiFileSelection = state; + setBit (impl->flags, PlatformFileSelectorFlags::MultiFileSelection, state); } //----------------------------------------------------------------------------- void CNewFileSelector::setDefaultExtension (const CFileExtension& extension) { - if (defaultExtension) + if (impl->defaultExtension != PlatformNoFileExtension) { - #if DEBUG - DebugPrint ("VSTGUI Warning: It's not allowed to set a default extension twice on a CFileSelector instance\n"); - #endif +#if DEBUG + DebugPrint ("VSTGUI Warning: It's not allowed to set a default extension twice on a " + "CFileSelector instance\n"); +#endif return; } - bool found = false; - FileExtensionList::const_iterator it = extensions.begin (); - while (it != extensions.end ()) - { - if ((*it) == extension) - { - defaultExtension = &(*it); - found = true; - break; - } - ++it; - } - if (!found) - { + auto it = std::find (impl->extensions.begin (), impl->extensions.end (), + extension.getPlatformFileExtension ()); + if (it == impl->extensions.end ()) addFileExtension (extension); - setDefaultExtension (extension); - } + impl->defaultExtension = extension.getPlatformFileExtension (); } //----------------------------------------------------------------------------- void CNewFileSelector::addFileExtension (const CFileExtension& extension) { - extensions.emplace_back (extension); + impl->extensions.emplace_back (extension.getPlatformFileExtension ()); } //----------------------------------------------------------------------------- void CNewFileSelector::addFileExtension (CFileExtension&& extension) { - extensions.emplace_back (std::move (extension)); + impl->extensions.emplace_back (std::move (extension.getPlatformFileExtension ())); } //----------------------------------------------------------------------------- uint32_t CNewFileSelector::getNumSelectedFiles () const { - return static_cast (result.size ()); + return static_cast (impl->result.size ()); } //----------------------------------------------------------------------------- UTF8StringPtr CNewFileSelector::getSelectedFile (uint32_t index) const { - if (index < result.size ()) - return result[index]; + if (index < impl->result.size ()) + return impl->result[index]; return nullptr; } -} // VSTGUI +//------------------------------------------------------------------------ +CNewFileSelector* CNewFileSelector::create (CFrame* parent, Style style) +{ + PlatformFileSelectorStyle platformStyle; + switch (style) + { + case Style::kSelectFile: + platformStyle = PlatformFileSelectorStyle::SelectFile; + break; + case Style::kSelectDirectory: + platformStyle = PlatformFileSelectorStyle::SelectDirectory; + break; + case Style::kSelectSaveFile: + platformStyle = PlatformFileSelectorStyle::SelectSaveFile; + break; + default: + vstgui_assert (false); + return nullptr; + } + if (auto platformSelector = getPlatformFactory ().createFileSelector ( + platformStyle, parent ? parent->getPlatformFrame () : nullptr)) + { + return new CNewFileSelector (std::move (platformSelector), parent); + } + return nullptr; +} +} // VSTGUI diff --git a/vstgui/lib/cfileselector.h b/vstgui/lib/cfileselector.h index 8bd748d28..1b7d99c6f 100644 --- a/vstgui/lib/cfileselector.h +++ b/vstgui/lib/cfileselector.h @@ -6,9 +6,8 @@ #include "vstguifwd.h" #include "cstring.h" -#include -#include #include +#include namespace VSTGUI { @@ -19,28 +18,33 @@ namespace VSTGUI { class CFileExtension { public: - CFileExtension (const UTF8String& description, const UTF8String& extension, const UTF8String& mimeType = "", int32_t macType = 0, const UTF8String& uti = ""); + CFileExtension (const UTF8String& description, const UTF8String& extension, + const UTF8String& mimeType = "", int32_t macType = 0, + const UTF8String& uti = ""); CFileExtension (const CFileExtension& ext); ~CFileExtension () noexcept; - const UTF8String& getDescription () const { return description; } - const UTF8String& getExtension () const { return extension; } - const UTF8String& getMimeType () const { return mimeType; } - const UTF8String& getUTI () const { return uti; } - int32_t getMacType () const { return macType; } + const UTF8String& getDescription () const; + const UTF8String& getExtension () const; + const UTF8String& getMimeType () const; + const UTF8String& getUTI () const; + int32_t getMacType () const; bool operator== (const CFileExtension& ext) const; //----------------------------------------------------------------------------- CFileExtension (CFileExtension&& ext) noexcept; - CFileExtension& operator=(CFileExtension&& ext) noexcept; + CFileExtension& operator= (CFileExtension&& ext) noexcept; + + CFileExtension (const PlatformFileExtension&); + const PlatformFileExtension& getPlatformFileExtension () const; + protected: - void init (const UTF8String& description, const UTF8String& extension, const UTF8String& mimeType, const UTF8String& uti); - - UTF8String description; - UTF8String extension; - UTF8String mimeType; - UTF8String uti; - int32_t macType; + CFileExtension (); + void init (const UTF8String& description, const UTF8String& extension, + const UTF8String& mimeType, const UTF8String& uti); + + struct Impl; + std::unique_ptr impl; }; //----------------------------------------------------------------------------- @@ -81,7 +85,7 @@ CMessageResult MyClass::notify (CBaseObject* sender, IdStringPtr message) @endcode */ //----------------------------------------------------------------------------- -class CNewFileSelector : public CBaseObject +class CNewFileSelector final : public CBaseObject { public: enum Style { @@ -100,6 +104,8 @@ class CNewFileSelector : public CBaseObject /** create a new instance */ static CNewFileSelector* create (CFrame* parent = nullptr, Style style = kSelectFile); + CNewFileSelector (PlatformFileSelectorPtr&& platformFileSelector, CFrame* parent); + using CallbackFunc = std::function; bool run (CallbackFunc&& callback); /** the delegate will get a kSelectEndMessage throu the notify method where the sender is this CNewFileSelector object */ @@ -145,25 +151,12 @@ class CNewFileSelector : public CBaseObject static IdStringPtr kSelectEndMessage; //----------------------------------------------------------------------------- - CLASS_METHODS_NOCOPY(CNewFileSelector, CBaseObject) + CLASS_METHODS_NOCOPY (CNewFileSelector, CBaseObject) protected: - explicit CNewFileSelector (CFrame* frame = nullptr); ~CNewFileSelector () noexcept override; - virtual bool runInternal (CBaseObject* delegate) = 0; - virtual void cancelInternal () = 0; - virtual bool runModalInternal () = 0; - - CFrame* frame; - UTF8String title; - UTF8String initialPath; - UTF8String defaultSaveName; - const CFileExtension* defaultExtension; - bool allowMultiFileSelection; - - using FileExtensionList = std::list; - FileExtensionList extensions; - std::vector result; + struct Impl; + std::unique_ptr impl; }; } // VSTGUI diff --git a/vstgui/lib/cfont.cpp b/vstgui/lib/cfont.cpp index 8a48f1ab9..37d5addf1 100644 --- a/vstgui/lib/cfont.cpp +++ b/vstgui/lib/cfont.cpp @@ -12,57 +12,105 @@ namespace VSTGUI { //----------------------------------------------------------------------------- // Global Fonts //----------------------------------------------------------------------------- +struct GlobalFonts +{ + SharedPointer systemFont; + SharedPointer normalFontVeryBig; + SharedPointer normalFontBig; + SharedPointer normalFont; + SharedPointer normalFontSmall; + SharedPointer normalFontSmaller; + SharedPointer normalFontVerySmall; + SharedPointer symbolFont; +}; +static GlobalFonts globalFonts; + +//----------------------------------------------------------------------------- +CFontRef kSystemFont = nullptr; +CFontRef kNormalFontVeryBig = nullptr; +CFontRef kNormalFontBig = nullptr; +CFontRef kNormalFont = nullptr; +CFontRef kNormalFontSmall = nullptr; +CFontRef kNormalFontSmaller = nullptr; +CFontRef kNormalFontVerySmall = nullptr; +CFontRef kSymbolFont = nullptr; + +//----------------------------------------------------------------------------- +void CFontDesc::init () +{ #if MAC - #if TARGET_OS_IPHONE - static CFontDesc gSystemFont ("Helvetica", 12); - static CFontDesc gNormalFontVeryBig ("ArialMT", 18); - static CFontDesc gNormalFontBig ("ArialMT", 14); - static CFontDesc gNormalFont ("ArialMT", 12); - static CFontDesc gNormalFontSmall ("ArialMT", 11); - static CFontDesc gNormalFontSmaller ("ArialMT", 10); - static CFontDesc gNormalFontVerySmall ("ArialMT", 9); - static CFontDesc gSymbolFont ("Symbol", 12); - #else - static CFontDesc gSystemFont ("Lucida Grande", 12); - static CFontDesc gNormalFontVeryBig ("Arial", 18); - static CFontDesc gNormalFontBig ("Arial", 14); - static CFontDesc gNormalFont ("Arial", 12); - static CFontDesc gNormalFontSmall ("Arial", 11); - static CFontDesc gNormalFontSmaller ("Arial", 10); - static CFontDesc gNormalFontVerySmall ("Arial", 9); - static CFontDesc gSymbolFont ("Symbol", 12); - #endif +#if TARGET_OS_IPHONE + globalFonts.systemFont = makeOwned ("Helvetica", 12); + globalFonts.normalFontVeryBig = makeOwned ("ArialMT", 18); + globalFonts.normalFontBig = makeOwned ("ArialMT", 14); + globalFonts.normalFont = makeOwned ("ArialMT", 12); + globalFonts.normalFontSmall = makeOwned ("ArialMT", 11); + globalFonts.normalFontSmaller = makeOwned ("ArialMT", 10); + globalFonts.normalFontVerySmall = makeOwned ("ArialMT", 9); + globalFonts.symbolFont = makeOwned ("Symbol", 12); +#else + globalFonts.systemFont = makeOwned ("Lucida Grande", 12); + globalFonts.normalFontVeryBig = makeOwned ("Arial", 18); + globalFonts.normalFontBig = makeOwned ("Arial", 14); + globalFonts.normalFont = makeOwned ("Arial", 12); + globalFonts.normalFontSmall = makeOwned ("Arial", 11); + globalFonts.normalFontSmaller = makeOwned ("Arial", 10); + globalFonts.normalFontVerySmall = makeOwned ("Arial", 9); + globalFonts.symbolFont = makeOwned ("Symbol", 12); +#endif #elif WINDOWS - static CFontDesc gSystemFont ("Arial", 12); - static CFontDesc gNormalFontVeryBig ("Arial", 18); - static CFontDesc gNormalFontBig ("Arial", 14); - static CFontDesc gNormalFont ("Arial", 12); - static CFontDesc gNormalFontSmall ("Arial", 11); - static CFontDesc gNormalFontSmaller ("Arial", 10); - static CFontDesc gNormalFontVerySmall ("Arial", 9); - static CFontDesc gSymbolFont ("Symbol", 13); + globalFonts.systemFont = makeOwned ("Arial", 12); + globalFonts.normalFontVeryBig = makeOwned ("Arial", 18); + globalFonts.normalFontBig = makeOwned ("Arial", 14); + globalFonts.normalFont = makeOwned ("Arial", 12); + globalFonts.normalFontSmall = makeOwned ("Arial", 11); + globalFonts.normalFontSmaller = makeOwned ("Arial", 10); + globalFonts.normalFontVerySmall = makeOwned ("Arial", 9); + globalFonts.symbolFont = makeOwned ("Symbol", 13); #else - static CFontDesc gSystemFont ("Arial", 12); - static CFontDesc gNormalFontVeryBig ("Arial", 18); - static CFontDesc gNormalFontBig ("Arial", 14); - static CFontDesc gNormalFont ("Arial", 12); - static CFontDesc gNormalFontSmall ("Arial", 11); - static CFontDesc gNormalFontSmaller ("Arial", 10); - static CFontDesc gNormalFontVerySmall ("Arial", 9); - static CFontDesc gSymbolFont ("Symbol", 13); + globalFonts.systemFont = makeOwned ("Arial", 12); + globalFonts.normalFontVeryBig = makeOwned ("Arial", 18); + globalFonts.normalFontBig = makeOwned ("Arial", 14); + globalFonts.normalFont = makeOwned ("Arial", 12); + globalFonts.normalFontSmall = makeOwned ("Arial", 11); + globalFonts.normalFontSmaller = makeOwned ("Arial", 10); + globalFonts.normalFontVerySmall = makeOwned ("Arial", 9); + globalFonts.symbolFont = makeOwned ("Symbol", 13); #endif + kSystemFont = globalFonts.systemFont; + kNormalFontVeryBig = globalFonts.normalFontVeryBig; + kNormalFontBig = globalFonts.normalFontBig; + kNormalFont = globalFonts.normalFont; + kNormalFontSmall = globalFonts.normalFontSmall; + kNormalFontSmaller = globalFonts.normalFontSmaller; + kNormalFontVerySmall = globalFonts.normalFontVerySmall; + kSymbolFont = globalFonts.symbolFont; +} -const CFontRef kSystemFont = &gSystemFont; -const CFontRef kNormalFontVeryBig = &gNormalFontVeryBig; -const CFontRef kNormalFontBig = &gNormalFontBig; -const CFontRef kNormalFont = &gNormalFont; -const CFontRef kNormalFontSmall = &gNormalFontSmall; -const CFontRef kNormalFontSmaller = &gNormalFontSmaller; -const CFontRef kNormalFontVerySmall = &gNormalFontVerySmall; -const CFontRef kSymbolFont = &gSymbolFont; +//----------------------------------------------------------------------------- +void CFontDesc::cleanup () +{ + globalFonts.systemFont = nullptr; + globalFonts.normalFontVeryBig = nullptr; + globalFonts.normalFontBig = nullptr; + globalFonts.normalFont = nullptr; + globalFonts.normalFontSmall = nullptr; + globalFonts.normalFontSmaller = nullptr; + globalFonts.normalFontVerySmall = nullptr; + globalFonts.symbolFont = nullptr; + + kSystemFont = nullptr; + kNormalFontVeryBig = nullptr; + kNormalFontBig = nullptr; + kNormalFont = nullptr; + kNormalFontSmall = nullptr; + kNormalFontSmaller = nullptr; + kNormalFontVerySmall = nullptr; + kSymbolFont = nullptr; +} //----------------------------------------------------------------------------- // CFontDesc Implementation @@ -91,7 +139,10 @@ CFontDesc::CFontDesc (const CFontDesc& font) } //------------------------------------------------------------------------ -CFontDesc::~CFontDesc () noexcept = default; +CFontDesc::~CFontDesc () noexcept +{ + vstgui_assert (getNbReference () == 0, "Always use shared pointers with CFontDesc!"); +} //----------------------------------------------------------------------------- void CFontDesc::beforeDelete () @@ -167,17 +218,4 @@ bool CFontDesc::operator == (const CFontDesc& f) const return true; } -//----------------------------------------------------------------------------- -void CFontDesc::cleanup () -{ - gSystemFont.freePlatformFont (); - gNormalFontVeryBig.freePlatformFont (); - gNormalFontBig.freePlatformFont (); - gNormalFont.freePlatformFont (); - gNormalFontSmall.freePlatformFont (); - gNormalFontSmaller.freePlatformFont (); - gNormalFontVerySmall.freePlatformFont (); - gSymbolFont.freePlatformFont (); -} - } // VSTGUI diff --git a/vstgui/lib/cfont.h b/vstgui/lib/cfont.h index 0eaceeda2..5588e6626 100644 --- a/vstgui/lib/cfont.h +++ b/vstgui/lib/cfont.h @@ -59,7 +59,8 @@ class CFontDesc : public AtomicReferenceCounted virtual CFontDesc& operator= (const CFontDesc&); virtual bool operator== (const CFontDesc&) const; virtual bool operator!= (const CFontDesc& other) const { return !(*this == other);} - + + static void init (); static void cleanup (); protected: @@ -75,13 +76,13 @@ class CFontDesc : public AtomicReferenceCounted //----------------------------------------------------------------------------- // Global fonts //----------------------------------------------------------------------------- -extern const CFontRef kSystemFont; -extern const CFontRef kNormalFontVeryBig; -extern const CFontRef kNormalFontBig; -extern const CFontRef kNormalFont; -extern const CFontRef kNormalFontSmall; -extern const CFontRef kNormalFontSmaller; -extern const CFontRef kNormalFontVerySmall; -extern const CFontRef kSymbolFont; +extern CFontRef kSystemFont; +extern CFontRef kNormalFontVeryBig; +extern CFontRef kNormalFontBig; +extern CFontRef kNormalFont; +extern CFontRef kNormalFontSmall; +extern CFontRef kNormalFontSmaller; +extern CFontRef kNormalFontVerySmall; +extern CFontRef kSymbolFont; } // VSTGUI diff --git a/vstgui/lib/cframe.cpp b/vstgui/lib/cframe.cpp index a6383b438..0061fb990 100644 --- a/vstgui/lib/cframe.cpp +++ b/vstgui/lib/cframe.cpp @@ -3,6 +3,8 @@ // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "cframe.h" +#include "events.h" +#include "finally.h" #include "coffscreencontext.h" #include "ctooltipsupport.h" #include "cinvalidrectlist.h" @@ -23,7 +25,7 @@ namespace VSTGUI { IdStringPtr kMsgNewFocusView = "kMsgNewFocusView"; IdStringPtr kMsgOldFocusView = "kMsgOldFocusView"; -#define DEBUG_MOUSE_VIEWS 0//DEBUG +#define DEBUG_MOUSE_VIEWS (DEBUG && 0) //------------------------------------------------------------------------ struct CFrame::CollectInvalidRects @@ -136,7 +138,7 @@ CFrame::CFrame (const CRect& inSize, VSTGUIEditorInterface* inEditor) : CViewCon //----------------------------------------------------------------------------- void CFrame::beforeDelete () { - clearMouseViews (CPoint (0, 0), 0, false); + clearMouseViews (CPoint (0, 0), Modifiers (), false); clearModalViewSessions (); @@ -182,7 +184,7 @@ void CFrame::beforeDelete () //----------------------------------------------------------------------------- void CFrame::close () { - clearMouseViews (CPoint (0, 0), 0, false); + clearMouseViews (CPoint (0, 0), Modifiers (), false); clearModalViewSessions (); @@ -249,8 +251,8 @@ bool CFrame::setZoom (double zoomFactor) CGraphicsTransform currentTransform = getTransform (); CCoord origWidth = getWidth () / currentTransform.m11; CCoord origHeight = getHeight () / currentTransform.m22; - CCoord newWidth = std::round (origWidth * zoomFactor); - CCoord newHeight = std::round (origHeight * zoomFactor); + CCoord newWidth = origWidth * zoomFactor; + CCoord newHeight = origHeight * zoomFactor; setAutosizingEnabled (false); setTransform (CGraphicsTransform ().scale (zoomFactor, zoomFactor)); if (!setSize (newWidth, newHeight)) @@ -337,20 +339,21 @@ void CFrame::drawRect (CDrawContext* pContext, const CRect& updateRect) } //----------------------------------------------------------------------------- -void CFrame::clearMouseViews (const CPoint& where, const CButtonState& buttons, bool callMouseExit) +void CFrame::clearMouseViews (const CPoint& where, Modifiers modifiers, bool callMouseExit) { - CPoint lp; auto it = pImpl->mouseViews.rbegin (); while (it != pImpl->mouseViews.rend ()) { if (callMouseExit) { - lp = where; - lp = (*it)->translateToLocal (lp); - (*it)->onMouseExited (lp, buttons); - #if DEBUG_MOUSE_VIEWS - DebugPrint ("mouseExited : %p[%d,%d]\n", (*it), (int)lp.x, (int)lp.y); - #endif + MouseExitEvent exitEvent; + exitEvent.modifiers = modifiers; + exitEvent.mousePosition = (*it)->translateToLocal (where, true); + dispatchEvent ((*it), exitEvent); +#if DEBUG_MOUSE_VIEWS + DebugPrint ("mouseExited : %p[%d,%d]\n", (*it), (int)exitEvent.mousePosition.x, + (int)exitEvent.mousePosition.y); +#endif } if (pImpl->tooltips) pImpl->tooltips->onMouseExited ((*it)); @@ -387,12 +390,11 @@ void CFrame::removeFromMouseViews (CView* view) } //----------------------------------------------------------------------------- -void CFrame::checkMouseViews (const CPoint& where, const CButtonState& buttons) +void CFrame::checkMouseViews (const MouseEvent& event) { if (getMouseDownView ()) return; - CPoint lp; - CView* mouseView = getViewAt (where, GetViewOptions ().deep ().mouseEnabled ().includeViewContainer ()); + CView* mouseView = getViewAt (event.mousePosition, GetViewOptions ().deep ().mouseEnabled ().includeViewContainer ()); CView* currentMouseView = pImpl->mouseViews.empty () == false ? pImpl->mouseViews.back () : nullptr; if (currentMouseView == mouseView) return; // no change @@ -407,21 +409,38 @@ void CFrame::checkMouseViews (const CPoint& where, const CButtonState& buttons) if (mouseView == nullptr || mouseView == this) { - clearMouseViews (where, buttons); + clearMouseViews (event.mousePosition, event.modifiers); return; } + + auto callMouseExitForView = [this, &event] (CView* view) { + MouseExitEvent exitEvent (event); + exitEvent.mousePosition = view->translateToLocal (exitEvent.mousePosition, true); + dispatchEvent (view, exitEvent); + callMouseObserverMouseExited (view); +#if DEBUG_MOUSE_VIEWS + DebugPrint ("mouseExited : %p[%d,%d]\n", view, (int)exitEvent.mousePosition.x, + (int)exitEvent.mousePosition.y); +#endif + }; + + auto callMouseEnterForView = [this, &event] (CView* view) { + MouseEnterEvent enterEvent (event); + enterEvent.mousePosition = view->translateToLocal (enterEvent.mousePosition, true); + dispatchEvent (view, enterEvent); + callMouseObserverMouseEntered (view); +#if DEBUG_MOUSE_VIEWS + DebugPrint ("mouseEntered : %p[%d,%d]\n", view, (int)enterEvent.mousePosition.x, + (int)enterEvent.mousePosition.y); +#endif + }; + CViewContainer* vc = currentMouseView ? currentMouseView->asViewContainer () : nullptr; // if the currentMouseView is not a view container, we know that the new mouseView won't be a child of it and that all other // views in the list are viewcontainers if (vc == nullptr && currentMouseView) { - lp = where; - lp = currentMouseView->translateToLocal (lp); - currentMouseView->onMouseExited (lp, buttons); - callMouseObserverMouseExited (currentMouseView); - #if DEBUG_MOUSE_VIEWS - DebugPrint ("mouseExited : %p[%d,%d]\n", currentMouseView, (int)lp.x, (int)lp.y); - #endif + callMouseExitForView (currentMouseView); currentMouseView->forget (); pImpl->mouseViews.remove (currentMouseView); } @@ -433,13 +452,7 @@ void CFrame::checkMouseViews (const CPoint& where, const CButtonState& buttons) return; if (vc->isChild (mouseView, true) == false) { - lp = where; - lp = vc->translateToLocal (lp); - vc->onMouseExited (lp, buttons); - callMouseObserverMouseExited (vc); - #if DEBUG_MOUSE_VIEWS - DebugPrint ("mouseExited : %p[%d,%d]\n", vc, (int)lp.x, (int)lp.y); - #endif + callMouseExitForView (vc); vc->forget (); pImpl->mouseViews.erase (--it.base ()); } @@ -463,13 +476,7 @@ void CFrame::checkMouseViews (const CPoint& where, const CButtonState& buttons) ++it2; while (it2 != pImpl->mouseViews.end ()) { - lp = where; - lp = (*it2)->translateToLocal (lp); - (*it2)->onMouseEntered (lp, buttons); - callMouseObserverMouseEntered ((*it2)); - #if DEBUG_MOUSE_VIEWS - DebugPrint ("mouseEntered : %p[%d,%d]\n", (*it2), (int)lp.x, (int)lp.y); - #endif + callMouseEnterForView (*it2); ++it2; } } @@ -488,233 +495,283 @@ void CFrame::checkMouseViews (const CPoint& where, const CButtonState& buttons) auto it2 = pImpl->mouseViews.begin (); while (it2 != pImpl->mouseViews.end ()) { - lp = where; - lp = (*it2)->translateToLocal (lp); - (*it2)->onMouseEntered (lp, buttons); - callMouseObserverMouseEntered ((*it2)); - #if DEBUG_MOUSE_VIEWS - DebugPrint ("mouseEntered : %p[%d,%d]\n", (*it2), (int)lp.x, (int)lp.y); - #endif + callMouseEnterForView (*it2); ++it2; } } } //------------------------------------------------------------------------ -bool CFrame::hitTestSubViews (const CPoint& where, const CButtonState& buttons) +bool CFrame::hitTestSubViews (const CPoint& where, const Event& event) { if (auto modalView = getModalView ()) { CPoint where2 (where); getTransform ().inverse ().transform (where2); - if (modalView->isVisible () && modalView->getMouseEnabled () && modalView->hitTest (where2, buttons)) + if (modalView->isVisible () && modalView->getMouseEnabled () && modalView->hitTest (where2, event)) { if (auto viewContainer = modalView->asViewContainer ()) { - return viewContainer->hitTestSubViews (where2, buttons); + return viewContainer->hitTestSubViews (where2, event); } return true; } return false; } - return CViewContainer::hitTestSubViews (where, buttons); + return CViewContainer::hitTestSubViews (where, event); } //----------------------------------------------------------------------------- -CMouseEventResult CFrame::onMouseDown (CPoint &where, const CButtonState& buttons) +void CFrame::dispatchEvent (CView* view, Event& event) { - CPoint where2 (where); - getTransform ().inverse ().transform (where2); + view->dispatchEvent (event); +} - if (pImpl->tooltips) - pImpl->tooltips->onMouseDown (where2); +//----------------------------------------------------------------------------- +void CFrame::dispatchEventToChildren (Event& event) +{ + CView::dispatchEvent (event); +} - CMouseEventResult result = callMouseObserverMouseDown (where, buttons); - if (result != kMouseEventNotHandled) - return result; +//----------------------------------------------------------------------------- +void CFrame::dispatchKeyboardEvent (KeyboardEvent& event) +{ + dispatchKeyboardEventToHooks (event); + if (event.consumed) + return; + + if (pImpl->focusView) + { + CBaseObjectGuard og (pImpl->focusView); + if (pImpl->focusView->getMouseEnabled ()) + dispatchEvent (pImpl->focusView, event); + if (event.consumed) + return; + CView* parent = pImpl->focusView->getParentView (); + while (parent && parent != this) + { + if (parent->getMouseEnabled ()) + { + dispatchEvent (parent, event); + if (event.consumed) + return; + } + parent = parent->getParentView (); + } + } + if (auto modalView = getModalView ()) + { + CBaseObjectGuard og (modalView); + dispatchEvent (modalView, event); + if (event.consumed) + return; + } + if (event.type != EventType::KeyUp && event.virt == VirtualKey::Tab) + { + if (event.modifiers.empty () || event.modifiers.is (ModifierKey::Shift)) + { + if (advanceNextFocusView (pImpl->focusView, event.modifiers.is (ModifierKey::Shift))) + event.consumed = true; + } + } +} + +//------------------------------------------------------------------------ +void CFrame::dispatchMouseDownEvent (MouseDownEvent& event) +{ + auto originMousePosition = event.mousePosition; + auto transformedMousePosition = event.mousePosition; + getTransform ().inverse ().transform (transformedMousePosition); + if (auto tooltips = pImpl->tooltips) + tooltips->onMouseDown (transformedMousePosition); + + event.mousePosition = transformedMousePosition; + callMouseObserverOtherMouseEvent (event); + if (event.consumed) + return; + event.mousePosition = originMousePosition; - // reset views setMouseDownView (nullptr); if (pImpl->focusView && dynamic_cast (pImpl->focusView)) setFocusView (nullptr); - if (auto modalView = getModalView ()) + if (auto modalView = shared (getModalView ())) { - CBaseObjectGuard rg (modalView); - if (modalView->isVisible () && modalView->getMouseEnabled ()) { - result = modalView->callMouseListener (MouseListenerCall::MouseDown, where2, buttons); - if (result == kMouseEventNotHandled || result == kMouseEventNotImplemented) - result = modalView->onMouseDown (where2, buttons); - if (result == kMouseEventHandled) + event.mousePosition = transformedMousePosition; +#if VSTGUI_ENABLE_DEPRECATED_METHODS + auto buttonState = buttonStateFromMouseEvent (event); + auto result = modalView->callMouseListener (MouseListenerCall::MouseDown, event.mousePosition, buttonState); + if (result != kMouseEventNotHandled && result != kMouseEventNotImplemented) { - setMouseDownView (modalView); - return kMouseEventHandled; + event.consumed = true; + return; } - return result; +#endif + dispatchEvent (modalView, event); + if (event.consumed) + setMouseDownView (modalView); } + return; } - else - return CViewContainer::onMouseDown (where, buttons); - return kMouseEventNotHandled; + dispatchEventToChildren (event); } -//----------------------------------------------------------------------------- -CMouseEventResult CFrame::onMouseUp (CPoint &where, const CButtonState& buttons) +//------------------------------------------------------------------------ +void CFrame::dispatchMouseMoveEvent (MouseMoveEvent& event) { - CMouseEventResult result = CViewContainer::onMouseUp (where, buttons); - CButtonState modifiers = buttons & (kShift | kControl | kAlt | kApple); - checkMouseViews (where, modifiers); - return result; -} + auto originMousePosition = event.mousePosition; + auto transformedMousePosition = event.mousePosition; + getTransform ().inverse ().transform (transformedMousePosition); -//----------------------------------------------------------------------------- -CMouseEventResult CFrame::onMouseMoved (CPoint &where, const CButtonState& buttons) -{ - CPoint where2 (where); - getTransform ().inverse ().transform (where2); - - if (pImpl->tooltips) - pImpl->tooltips->onMouseMoved (where2); + if (auto tooltips = pImpl->tooltips) + tooltips->onMouseMoved (transformedMousePosition); - checkMouseViews (where, buttons); + checkMouseViews (event); - CMouseEventResult result = callMouseObserverMouseMoved (where, buttons); - if (result != kMouseEventNotHandled) - return result; + event.mousePosition = transformedMousePosition; + callMouseObserverOtherMouseEvent (event); + if (event.consumed) + return; + event.mousePosition = originMousePosition; - if (auto modalView = getModalView ()) + if (auto modalView = shared (getModalView ())) { - CBaseObjectGuard rg (modalView); - result = modalView->callMouseListener (MouseListenerCall::MouseMoved, where2, buttons); - if (result == kMouseEventNotHandled || result == kMouseEventNotImplemented) - result = modalView->onMouseMoved (where2, buttons); + if (modalView->isVisible () && modalView->getMouseEnabled ()) + { + event.mousePosition = transformedMousePosition; +#if VSTGUI_ENABLE_DEPRECATED_METHODS + auto buttonState = buttonStateFromMouseEvent (event); + auto result = modalView->callMouseListener (MouseListenerCall::MouseMoved, event.mousePosition, buttonState); + if (result != kMouseEventNotHandled && result != kMouseEventNotImplemented) + { + event.consumed = true; + return; + } +#endif + dispatchEvent (modalView, event); + } } else + dispatchEventToChildren (event); + if (event.consumed == false) { - result = CViewContainer::onMouseMoved (where, buttons); - } - if (result == kMouseEventNotHandled) - { - CButtonState buttons2 = (buttons & (kShift | kControl | kAlt | kApple)); + event.buttonState.clear (); auto it = pImpl->mouseViews.rbegin (); while (it != pImpl->mouseViews.rend ()) { - CPoint p (where2); + CPoint p (transformedMousePosition); auto view = *it; if (auto parent = view->getParentView ()) - parent->translateToLocal (p); - result = view->onMouseMoved (p, buttons2); - if (result == kMouseEventHandled) + parent->translateToLocal (p, true); + event.mousePosition = p; + dispatchEvent (view, event); + if (event.consumed) break; ++it; } } - return result; -} - -//----------------------------------------------------------------------------- -CMouseEventResult CFrame::onMouseExited (CPoint &where, const CButtonState& buttons) -{ // this should only get called from the platform implementation - - if (getMouseDownView () == nullptr) - { - clearMouseViews (where, buttons); - if (pImpl->tooltips) - pImpl->tooltips->hideTooltip (); - } - - return kMouseEventHandled; } -//----------------------------------------------------------------------------- -int32_t CFrame::onKeyDown (VstKeyCode& keyCode) +//------------------------------------------------------------------------ +void CFrame::dispatchMouseUpEvent (MouseUpEvent& event) { - int32_t result = keyboardHooksOnKeyDown (keyCode); + auto transformedMousePosition = event.mousePosition; + getTransform ().inverse ().transform (transformedMousePosition); + + auto f = finally ([this] () { setMouseDownView (nullptr); }); - if (result == -1 && pImpl->focusView) + callMouseObserverOtherMouseEvent (event); + if (event.consumed) + return; + + if (auto modalView = shared (getModalView ())) { - CBaseObjectGuard og (pImpl->focusView); - if (pImpl->focusView->getMouseEnabled ()) - result = pImpl->focusView->onKeyDown (keyCode); - if (result == -1) + if (modalView->isVisible () && modalView->getMouseEnabled ()) { - CView* parent = pImpl->focusView->getParentView (); - while (parent && parent != this && result == -1) + event.mousePosition = transformedMousePosition; +#if VSTGUI_ENABLE_DEPRECATED_METHODS + auto buttonState = buttonStateFromMouseEvent (event); + auto result = modalView->callMouseListener (MouseListenerCall::MouseUp, event.mousePosition, buttonState); + if (result != kMouseEventNotHandled && result != kMouseEventNotImplemented) { - if (parent->getMouseEnabled ()) - result = parent->onKeyDown (keyCode); - parent = parent->getParentView (); + event.consumed = true; + return; } +#endif + dispatchEvent (modalView, event); } + return; } + dispatchEventToChildren (event); +} - if (result == -1) - { - if (auto modalView = getModalView ()) +//------------------------------------------------------------------------ +void CFrame::dispatchMouseEvent (MouseEvent& event) +{ + if (event.type == EventType::MouseMove) + dispatchMouseMoveEvent (castMouseMoveEvent (event)); + else if (event.type == EventType::MouseDown) + dispatchMouseDownEvent (castMouseDownEvent (event)); + else if (event.type == EventType::MouseUp) + dispatchMouseUpEvent (castMouseUpEvent (event)); + else if (event.type == EventType::MouseEnter) + {} + else if (event.type == EventType::MouseExit) + { + if (getMouseDownView () == nullptr) { - CBaseObjectGuard og (modalView); - result = modalView->onKeyDown (keyCode); + clearMouseViews (event.mousePosition, event.modifiers); + if (pImpl->tooltips) + pImpl->tooltips->hideTooltip (); } + event.consumed = true; } - - if (result == -1 && keyCode.virt == VKEY_TAB) + else { - if (keyCode.modifier == 0 || keyCode.modifier == MODIFIER_SHIFT) - result = advanceNextFocusView (pImpl->focusView, (keyCode.modifier & MODIFIER_SHIFT) ? true : false) ? 1 : -1; + vstgui_assert (false); } - - return result; } //----------------------------------------------------------------------------- -int32_t CFrame::onKeyUp (VstKeyCode& keyCode) +void CFrame::dispatchEvent (Event& event) { - int32_t result = keyboardHooksOnKeyUp (keyCode); + Impl::PostEventHandler peh (*pImpl); + CollectInvalidRects cir (this); - if (result == -1 && pImpl->focusView) + if (auto mouseEvent = asMouseEvent (event)) { - if (pImpl->focusView->getMouseEnabled ()) - result = pImpl->focusView->onKeyUp (keyCode); - if (result == -1) - { - CView* parent = pImpl->focusView->getParentView (); - while (parent && parent != this && result == -1) - { - if (parent->getMouseEnabled ()) - result = parent->onKeyUp (keyCode); - parent = parent->getParentView (); - } - } + dispatchMouseEvent (*mouseEvent); + return; } - - if (result == -1) + if (auto keyEvent = asKeyboardEvent (event)) { - if (auto modalView = getModalView ()) - result = modalView->onKeyUp (keyCode); + dispatchKeyboardEvent (*keyEvent); + return; } - return result; -} + auto mousePosEvent = asMousePositionEvent (event); + CPoint mousePosition; + if (mousePosEvent) + mousePosition = mousePosEvent->mousePosition; -//------------------------------------------------------------------------ -bool CFrame::onWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &distance, const CButtonState &buttons) -{ - if (auto modalView = getModalView ()) - { - CPoint where2 (where); - getTransform ().inverse ().transform (where2); - return modalView->onWheel (where2, axis, distance, buttons); - } + auto modalView = getModalView (); + if (modalView && mousePosEvent) + getTransform ().inverse ().transform (mousePosEvent->mousePosition); + + if (modalView) + dispatchEvent (modalView, event); + else + dispatchEventToChildren (event); - bool result = false; - if (getMouseDownView () == nullptr) + if (mousePosEvent) { - result = CViewContainer::onWheel (where, axis, distance, buttons); - checkMouseViews (where, buttons); + MouseEvent mouseEvent; + mouseEvent.mousePosition = mousePosEvent->mousePosition; + mouseEvent.modifiers = mousePosEvent->modifiers; + checkMouseViews (mouseEvent); } - return result; } //----------------------------------------------------------------------------- @@ -919,7 +976,7 @@ void CFrame::initModalViewSession (const ModalViewSession& session) { onMouseCancel (); } - clearMouseViews (CPoint (0, 0), 0, true); + clearMouseViews (CPoint (0, 0), Modifiers (), true); if (auto container = session.view->asViewContainer ()) container->advanceNextFocusView (nullptr, false); else @@ -929,7 +986,10 @@ void CFrame::initModalViewSession (const ModalViewSession& session) { CPoint where; getCurrentMouseLocation (where); - checkMouseViews (where, getCurrentMouseButtons ()); + MouseEvent mouseEvent; + mouseEvent.mousePosition = where; + // TODO: modifiers and mouse button state + checkMouseViews (mouseEvent); } } @@ -1263,7 +1323,7 @@ bool CFrame::removeAll (bool withForget) pImpl->focusView = nullptr; } pImpl->activeFocusView = nullptr; - clearMouseViews (CPoint (0, 0), 0, false); + clearMouseViews (CPoint (0, 0), Modifiers (), false); return CViewContainer::removeAll (withForget); } @@ -1483,29 +1543,14 @@ void CFrame::unregisterKeyboardHook (IKeyboardHook* hook) } //----------------------------------------------------------------------------- -int32_t CFrame::keyboardHooksOnKeyDown (const VstKeyCode& key) +void CFrame::dispatchKeyboardEventToHooks (KeyboardEvent& event) { - int32_t result = -1; - pImpl->keyboardHooks.forEachReverse ([&] (IKeyboardHook* hook) { - if (result <= 0) - { - result = hook->onKeyDown (key, this); - } - }); - return result; -} - -//----------------------------------------------------------------------------- -int32_t CFrame::keyboardHooksOnKeyUp (const VstKeyCode& key) -{ - int32_t result = -1; - pImpl->keyboardHooks.forEachReverse ([&] (IKeyboardHook* hook) { - if (result <= 0) - { - result = hook->onKeyUp (key, this); - } - }); - return result; + pImpl->keyboardHooks.forEachReverse ( + [&] (IKeyboardHook* hook) { + hook->onKeyboardEvent (event, this); + return event.consumed; + }, + [] (bool consumed) { return consumed; }); } //----------------------------------------------------------------------------- @@ -1547,7 +1592,9 @@ void CFrame::unregisterMouseObserver (IMouseObserver* observer) //----------------------------------------------------------------------------- void CFrame::callMouseObserverMouseEntered (CView* view) { +#if VSTGUI_ENABLE_DEPRECATED_METHODS view->callMouseListenerEnteredExited (true); +#endif pImpl->mouseObservers.forEach ([&] (IMouseObserver* observer) { observer->onMouseEntered (view, this); }); @@ -1559,45 +1606,16 @@ void CFrame::callMouseObserverMouseExited (CView* view) pImpl->mouseObservers.forEach ([&] (IMouseObserver* observer) { observer->onMouseExited (view, this); }); +#if VSTGUI_ENABLE_DEPRECATED_METHODS view->callMouseListenerEnteredExited (false); +#endif } //----------------------------------------------------------------------------- -CMouseEventResult CFrame::callMouseObserverMouseDown (const CPoint& _where, const CButtonState& buttons) -{ - CMouseEventResult eventResult = kMouseEventNotHandled; - if (pImpl->mouseObservers.empty ()) - return eventResult; - - CPoint where (_where); - getTransform ().inverse ().transform (where); - - pImpl->mouseObservers.forEach ([&] (IMouseObserver* observer) { - CMouseEventResult result2 = observer->onMouseDown (this, where, buttons); - if (result2 != kMouseEventNotHandled) - eventResult = result2; - }); - - return eventResult; -} - -//----------------------------------------------------------------------------- -CMouseEventResult CFrame::callMouseObserverMouseMoved (const CPoint& _where, const CButtonState& buttons) +void CFrame::callMouseObserverOtherMouseEvent (MouseEvent& event) { - CMouseEventResult eventResult = kMouseEventNotHandled; - if (pImpl->mouseObservers.empty ()) - return eventResult; - - CPoint where (_where); - getTransform ().inverse ().transform (where); - - pImpl->mouseObservers.forEach ([&] (IMouseObserver* observer) { - CMouseEventResult result2 = observer->onMouseMoved (this, where, buttons); - if (result2 == kMouseEventHandled) - eventResult = kMouseEventHandled; - }); - - return eventResult; + pImpl->mouseObservers.forEach ( + [&] (IMouseObserver* observer) { observer->onMouseEvent (event, this); }); } //------------------------------------------------------------------------ @@ -1644,53 +1662,9 @@ bool CFrame::platformDrawRect (CDrawContext* context, const CRect& rect) } //----------------------------------------------------------------------------- -CMouseEventResult CFrame::platformOnMouseDown (CPoint& where, const CButtonState& buttons) -{ - if (!getMouseEnabled ()) - return kMouseEventNotHandled; - Impl::PostEventHandler peh (*pImpl); - CollectInvalidRects cir (this); - return onMouseDown (where, buttons); -} - -//----------------------------------------------------------------------------- -CMouseEventResult CFrame::platformOnMouseMoved (CPoint& where, const CButtonState& buttons) -{ - if (!getMouseEnabled ()) - return kMouseEventNotHandled; - Impl::PostEventHandler peh (*pImpl); - CollectInvalidRects cir (this); - return onMouseMoved (where, buttons); -} - -//----------------------------------------------------------------------------- -CMouseEventResult CFrame::platformOnMouseUp (CPoint& where, const CButtonState& buttons) +void CFrame::platformOnEvent (Event& event) { - if (!getMouseEnabled ()) - return kMouseEventNotHandled; - Impl::PostEventHandler peh (*pImpl); - CollectInvalidRects cir (this); - return onMouseUp (where, buttons); -} - -//----------------------------------------------------------------------------- -CMouseEventResult CFrame::platformOnMouseExited (CPoint& where, const CButtonState& buttons) -{ - if (!getMouseEnabled ()) - return kMouseEventNotHandled; - Impl::PostEventHandler peh (*pImpl); - CollectInvalidRects cir (this); - return onMouseExited (where, buttons); -} - -//----------------------------------------------------------------------------- -bool CFrame::platformOnMouseWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &distance, const CButtonState &buttons) -{ - if (!getMouseEnabled ()) - return false; - Impl::PostEventHandler peh (*pImpl); - CollectInvalidRects cir (this); - return onWheel (where, axis, distance, buttons); + dispatchEvent (event); } //----------------------------------------------------------------------------- @@ -1700,7 +1674,6 @@ DragOperation CFrame::platformOnDragEnter (DragEventData data) return DragOperation::None; Impl::PostEventHandler peh (*pImpl); CollectInvalidRects cir (this); - data.modifiers = data.modifiers.getModifierState (); return getDropTarget ()->onDragEnter (data); } @@ -1711,7 +1684,6 @@ DragOperation CFrame::platformOnDragMove (DragEventData data) return DragOperation::None; Impl::PostEventHandler peh (*pImpl); CollectInvalidRects cir (this); - data.modifiers = data.modifiers.getModifierState (); return getDropTarget ()->onDragMove (data); } @@ -1722,7 +1694,6 @@ void CFrame::platformOnDragLeave (DragEventData data) return; Impl::PostEventHandler peh (*pImpl); CollectInvalidRects cir (this); - data.modifiers = data.modifiers.getModifierState (); getDropTarget ()->onDragLeave (data); } @@ -1733,30 +1704,9 @@ bool CFrame::platformOnDrop (DragEventData data) return false; Impl::PostEventHandler peh (*pImpl); CollectInvalidRects cir (this); - data.modifiers = data.modifiers.getModifierState (); return getDropTarget ()->onDrop (data); } -//----------------------------------------------------------------------------- -bool CFrame::platformOnKeyDown (VstKeyCode& keyCode) -{ - if (!getMouseEnabled ()) - return false; - Impl::PostEventHandler peh (*pImpl); - CollectInvalidRects cir (this); - return onKeyDown (keyCode) == 1; -} - -//----------------------------------------------------------------------------- -bool CFrame::platformOnKeyUp (VstKeyCode& keyCode) -{ - if (!getMouseEnabled ()) - return false; - Impl::PostEventHandler peh (*pImpl); - CollectInvalidRects cir (this); - return onKeyUp (keyCode) == 1; -} - //----------------------------------------------------------------------------- void CFrame::platformOnActivate (bool state) { @@ -1810,21 +1760,23 @@ void CFrame::platformOnTouchEvent (ITouchEvent& event) { if (e.second.targetIsSingleTouch) { - CButtonState buttons (kLButton); CPoint where (e.second.location); target->frameToLocal (where); switch (e.second.state) { case ITouchEvent::kMoved: { - CMouseEventResult result = target->onMouseMoved (where, buttons); - if (result == kMouseMoveEventHandledButDontNeedMoreEvents) + MouseMoveEvent moveEvent (where, MouseButton::Left); + dispatchEvent (target, moveEvent); + if (moveEvent.ignoreFollowUpMoveAndUpEvents ()) { - event.unsetTouchTarget(e.first, target); - if (target->hitTest (where, buttons) == false) + event.unsetTouchTarget (e.first, target); + MouseMoveEvent mouseMoveEvent (where, MouseButton::Left); + if (target->hitTest (where, mouseMoveEvent) == false) { // when the touch goes out of the target and it tells us to - const_cast (e.second).state = ITouchEvent::kBegan; + const_cast (e.second).state = + ITouchEvent::kBegan; hasBeganTouch = true; } } @@ -1832,14 +1784,20 @@ void CFrame::platformOnTouchEvent (ITouchEvent& event) } case ITouchEvent::kCanceled: { - if (target->onMouseCancel () != kMouseEventHandled) - target->onMouseUp (where, buttons); + MouseCancelEvent cancelEvent; + dispatchEvent (target, cancelEvent); + if (cancelEvent.consumed == false) + { + MouseUpEvent upEvent (where, MouseButton::Left); + dispatchEvent (target, upEvent); + } event.unsetTouchTarget (e.first, target); break; } case ITouchEvent::kEnded: { - target->onMouseUp (where, buttons); + MouseUpEvent upEvent (where, MouseButton::Left); + dispatchEvent (target, upEvent); event.unsetTouchTarget (e.first, target); break; } @@ -1852,7 +1810,8 @@ void CFrame::platformOnTouchEvent (ITouchEvent& event) } else { - if (std::find (targetDispatched.begin (), targetDispatched.end (), target) == targetDispatched.end ()) + if (std::find (targetDispatched.begin (), targetDispatched.end (), target) == + targetDispatched.end ()) { target->onTouchEvent (event); targetDispatched.emplace_back (target); @@ -1869,11 +1828,11 @@ void CFrame::platformOnTouchEvent (ITouchEvent& event) if (CView* focusView = getFocusView ()) { if (dynamic_cast (focusView)) - setFocusView (0); + setFocusView (nullptr); } for (const auto& e : event) { - if (e.second.target == 0 && e.second.state == ITouchEvent::kBegan) + if (e.second.target == nullptr && e.second.state == ITouchEvent::kBegan) { findSingleTouchEventTarget (const_cast (e.second)); } @@ -1974,4 +1933,61 @@ void CFrame::CollectInvalidRects::addRect (const CRect& rect) } } +#if VSTGUI_ENABLE_DEPRECATED_METHODS +//----------------------------------------------------------------------------- +void OldMouseObserverAdapter::onMouseEvent (MouseEvent& event, CFrame* frame) +{ + if (event.type == EventType::MouseDown) + { + auto res = onMouseDown (frame, event.mousePosition, buttonStateFromMouseEvent (event)); + if (res == kMouseEventHandled) + event.consumed = true; + if (res == kMouseDownEventHandledButDontNeedMovedOrUpEvents) + { + event.consumed = true; + castMouseDownEvent (event).ignoreFollowUpMoveAndUpEvents (true); + } + } + else if (event.type == EventType::MouseMove) + { + auto res = onMouseMoved (frame, event.mousePosition, buttonStateFromMouseEvent (event)); + if (res == kMouseEventHandled) + event.consumed = true; + if (res == kMouseMoveEventHandledButDontNeedMoreEvents) + { + event.consumed = true; + castMouseMoveEvent (event).ignoreFollowUpMoveAndUpEvents (true); + } + } +} + +//----------------------------------------------------------------------------- +CMouseEventResult OldMouseObserverAdapter::onMouseMoved (CFrame* frame, const CPoint& where, const CButtonState& buttons) +{ + return kMouseEventNotHandled; +} + +//----------------------------------------------------------------------------- +CMouseEventResult OldMouseObserverAdapter::onMouseDown (CFrame* frame, const CPoint& where, const CButtonState& buttons) +{ + return kMouseEventNotHandled; +} + +//----------------------------------------------------------------------------- +void OldKeyboardHookAdapter::onKeyboardEvent (KeyboardEvent& event, CFrame* frame) +{ + auto vstKeyCode = toVstKeyCode (event); + if (event.type == EventType::KeyDown) + { + if (onKeyDown (vstKeyCode, frame) != -1) + event.consumed = true; + } + else + { + if (onKeyUp (vstKeyCode, frame) != -1) + event.consumed = true; + } +} +#endif + } // VSTGUI diff --git a/vstgui/lib/cframe.h b/vstgui/lib/cframe.h index 01309797d..3869352a4 100644 --- a/vstgui/lib/cframe.h +++ b/vstgui/lib/cframe.h @@ -202,7 +202,7 @@ class CFrame final : public CViewContainer, public IPlatformFrameCallback CView* getViewAt (const CPoint& where, const GetViewOptions& options = GetViewOptions ()) const override; CViewContainer* getContainerAt (const CPoint& where, const GetViewOptions& options = GetViewOptions ().deep ()) const override; bool getViewsAt (const CPoint& where, ViewList& views, const GetViewOptions& options = GetViewOptions ().deep ()) const override; - bool hitTestSubViews (const CPoint& where, const CButtonState& buttons = -1) override; + bool hitTestSubViews (const CPoint& where, const Event& event) override; CPoint& frameToLocal (CPoint& point) const override { return point; } CPoint& localToFrame (CPoint& point) const override { return point; } @@ -210,14 +210,8 @@ class CFrame final : public CViewContainer, public IPlatformFrameCallback bool attached (CView* parent) override; void draw (CDrawContext* pContext) override; void drawRect (CDrawContext* pContext, const CRect& updateRect) override; - CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override; - bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; - int32_t onKeyUp (VstKeyCode& keyCode) override; void setViewSize (const CRect& rect, bool invalid = true) override; + void dispatchEvent (Event& event) override; VSTGUIEditorInterface* getEditor () const override; IPlatformFrame* getPlatformFrame () const; @@ -234,37 +228,29 @@ class CFrame final : public CViewContainer, public IPlatformFrameCallback ~CFrame () noexcept override = default; void beforeDelete () override; - - void checkMouseViews (const CPoint& where, const CButtonState& buttons); - void clearMouseViews (const CPoint& where, const CButtonState& buttons, bool callMouseExit = true); + + void checkMouseViews (const MouseEvent& event); + void clearMouseViews (const CPoint& where, Modifiers modifiers, bool callMouseExit = true); void removeFromMouseViews (CView* view); void setCollectInvalidRects (CollectInvalidRects* collectInvalidRects); // keyboard hooks - int32_t keyboardHooksOnKeyDown (const VstKeyCode& key); - int32_t keyboardHooksOnKeyUp (const VstKeyCode& key); + void dispatchKeyboardEventToHooks (KeyboardEvent& event); // mouse observers void callMouseObserverMouseEntered (CView* view); void callMouseObserverMouseExited (CView* view); - CMouseEventResult callMouseObserverMouseDown (const CPoint& where, const CButtonState& buttons); - CMouseEventResult callMouseObserverMouseMoved (const CPoint& where, const CButtonState& buttons); + void callMouseObserverOtherMouseEvent (MouseEvent& event); void dispatchNewScaleFactor (double newScaleFactor); // platform frame bool platformDrawRect (CDrawContext* context, const CRect& rect) override; - CMouseEventResult platformOnMouseDown (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult platformOnMouseMoved (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult platformOnMouseUp (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult platformOnMouseExited (CPoint& where, const CButtonState& buttons) override; - bool platformOnMouseWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; + void platformOnEvent (Event& event) override; DragOperation platformOnDragEnter (DragEventData data) override; DragOperation platformOnDragMove (DragEventData data) override; void platformOnDragLeave (DragEventData data) override; bool platformOnDrop (DragEventData data) override; - bool platformOnKeyDown (VstKeyCode& keyCode) override; - bool platformOnKeyUp (VstKeyCode& keyCode) override; void platformOnActivate (bool state) override; void platformOnWindowActivate (bool state) override; void platformScaleFactorChanged (double newScaleFactor) override; @@ -278,6 +264,13 @@ class CFrame final : public CViewContainer, public IPlatformFrameCallback #endif void initModalViewSession (const ModalViewSession& session); void clearModalViewSessions (); + void dispatchKeyboardEvent (KeyboardEvent& event); + void dispatchMouseEvent (MouseEvent& event); + void dispatchMouseDownEvent (MouseDownEvent& event); + void dispatchMouseMoveEvent (MouseMoveEvent& event); + void dispatchMouseUpEvent (MouseUpEvent& event); + void dispatchEvent (CView* view, Event& event); + void dispatchEventToChildren (Event& event); struct Impl; Impl* pImpl {nullptr}; @@ -314,12 +307,22 @@ class IMouseObserver virtual ~IMouseObserver() noexcept = default; virtual void onMouseEntered (CView* view, CFrame* frame) = 0; virtual void onMouseExited (CView* view, CFrame* frame) = 0; - /** a mouse move event happend on the frame at position where. If the observer handles this, the event won't be propagated further */ - virtual CMouseEventResult onMouseMoved (CFrame* frame, const CPoint& where, const CButtonState& buttons) { return kMouseEventNotHandled; } - /** a mouse down event happend on the frame at position where. If the observer handles this, the event won't be propagated further */ - virtual CMouseEventResult onMouseDown (CFrame* frame, const CPoint& where, const CButtonState& buttons) { return kMouseEventNotHandled; } + virtual void onMouseEvent (MouseEvent& event, CFrame* frame) = 0; }; +#if VSTGUI_ENABLE_DEPRECATED_METHODS +//----------------------------------------------------------------------------- +class OldMouseObserverAdapter : public IMouseObserver +{ +public: + void onMouseEntered (CView* view, CFrame* frame) override {} + void onMouseExited (CView* view, CFrame* frame) override {} + void onMouseEvent (MouseEvent& event, CFrame* frame) override; + virtual CMouseEventResult onMouseMoved (CFrame* frame, const CPoint& where, const CButtonState& buttons); + virtual CMouseEventResult onMouseDown (CFrame* frame, const CPoint& where, const CButtonState& buttons); +}; +#endif + //----------------------------------------------------------------------------- // IKeyboardHook Declaration //! @brief generic keyboard hook interface for CFrame @@ -329,12 +332,22 @@ class IKeyboardHook { public: virtual ~IKeyboardHook () noexcept = default; - - /** should return 1 if no further key down processing should apply, otherwise -1 */ + + /** the event will not be dispatched further if it is consumed. */ + virtual void onKeyboardEvent (KeyboardEvent& event, CFrame* frame) = 0; +}; + +#if VSTGUI_ENABLE_DEPRECATED_METHODS +//------------------------------------------------------------------------ +class OldKeyboardHookAdapter : public IKeyboardHook +{ +public: virtual int32_t onKeyDown (const VstKeyCode& code, CFrame* frame) = 0; - /** should return 1 if no further key up processing should apply, otherwise -1 */ virtual int32_t onKeyUp (const VstKeyCode& code, CFrame* frame) = 0; +private: + void onKeyboardEvent (KeyboardEvent& event, CFrame* frame) override; }; +#endif //----------------------------------------------------------------------------- // IViewAddedRemovedObserver Declaration diff --git a/vstgui/lib/cgradient.cpp b/vstgui/lib/cgradient.cpp new file mode 100644 index 000000000..e23d9c0fe --- /dev/null +++ b/vstgui/lib/cgradient.cpp @@ -0,0 +1,65 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "cgradient.h" +#include "platform/iplatformgradient.h" +#include "platform/platformfactory.h" + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +CGradient* CGradient::create (const GradientColorStopMap& colorStopMap) +{ + if (auto pg = getPlatformFactory ().createGradient ()) + { + pg->setColorStops (colorStopMap); + return new CGradient (std::move (pg)); + } + return nullptr; +} + +//----------------------------------------------------------------------------- +CGradient* CGradient::create (double color1Start, double color2Start, const CColor& color1, + const CColor& color2) +{ + GradientColorStopMap map; + map.emplace (color1Start, color1); + map.emplace (color2Start, color2); + return create (map); +} + +//----------------------------------------------------------------------------- +CGradient::CGradient (PlatformGradientPtr&& platformGradient) +: platformGradient (std::move (platformGradient)) +{ +} + +//----------------------------------------------------------------------------- +CGradient::~CGradient () noexcept = default; + +//----------------------------------------------------------------------------- +void CGradient::addColorStop (double start, const CColor& color) +{ + addColorStop (std::make_pair (start, color)); +} + +//----------------------------------------------------------------------------- +void CGradient::addColorStop (const GradientColorStop& colorStop) +{ + platformGradient->addColorStop (colorStop); +} + +//----------------------------------------------------------------------------- +const GradientColorStopMap& CGradient::getColorStops () const +{ + return platformGradient->getColorStops (); +} + +//----------------------------------------------------------------------------- +const PlatformGradientPtr& CGradient::getPlatformGradient () const +{ + return platformGradient; +} + +} // VSTGUI diff --git a/vstgui/lib/cgradient.h b/vstgui/lib/cgradient.h index c0dcfb4cc..30253c921 100644 --- a/vstgui/lib/cgradient.h +++ b/vstgui/lib/cgradient.h @@ -18,48 +18,26 @@ namespace VSTGUI { class CGradient : public AtomicReferenceCounted { public: - using ColorStopMap = std::multimap; - - static CGradient* create (const ColorStopMap& colorStopMap); - static CGradient* create (double color1Start, double color2Start, const CColor& color1, const CColor& color2) - { - ColorStopMap map; - map.emplace (color1Start, color1); - map.emplace (color2Start, color2); - return create (map); - } + static CGradient* create (const GradientColorStopMap& colorStopMap); + static CGradient* create (double color1Start, double color2Start, const CColor& color1, const CColor& color2); + CGradient (PlatformGradientPtr&& platformGradient); + ~CGradient () noexcept override; + //----------------------------------------------------------------------------- /// @name Member Access //----------------------------------------------------------------------------- //@{ - void addColorStop (double start, const CColor& color) - { - addColorStop (std::make_pair (start, color)); - } - - virtual void addColorStop (const std::pair& colorStop) - { - colorStops.emplace (colorStop); - } - - virtual void addColorStop (std::pair&& colorStop) - { - colorStops.emplace (std::move (colorStop)); - } + void addColorStop (double start, const CColor& color); + void addColorStop (const GradientColorStop& colorStop); - const ColorStopMap& getColorStops () const { return colorStops; } + const GradientColorStopMap& getColorStops () const; //@} + + const PlatformGradientPtr& getPlatformGradient () const; protected: - CGradient (double color1Start, double color2Start, const CColor& color1, const CColor& color2) - { - addColorStop (color1Start, color1); - addColorStop (color2Start, color2); - } - explicit CGradient (const ColorStopMap& colorStopMap) : colorStops (colorStopMap) {} - - ColorStopMap colorStops; + PlatformGradientPtr platformGradient; }; } // VSTGUI diff --git a/vstgui/lib/cgraphicspath.cpp b/vstgui/lib/cgraphicspath.cpp index dcad078bb..9ca5c162b 100644 --- a/vstgui/lib/cgraphicspath.cpp +++ b/vstgui/lib/cgraphicspath.cpp @@ -1,17 +1,19 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE -#include "cgraphicspath.h" #include "cdrawcontext.h" +#include "cgradient.h" +#include "cgraphicspath.h" #include "cgraphicstransform.h" +#include "platform/iplatformgraphicspath.h" namespace VSTGUI { //----------------------------------------------------------------------------- void CGraphicsPath::addRoundRect (const CRect& size, CCoord radius) { - if (radius == 0.) + if (radius <= 0.) { addRect (size); return; @@ -23,18 +25,18 @@ void CGraphicsPath::addRoundRect (const CRect& size, CCoord radius) const CCoord top = rect2.top; const CCoord bottom = rect2.bottom; - beginSubpath (CPoint (right-radius, top)); + beginSubpath (CPoint (right - radius, top)); addArc (CRect (right - 2.0 * radius, top, right, top + 2.0 * radius), 270., 360., true); - addArc (CRect (right - 2.0 * radius, bottom - 2.0 *radius, right, bottom), 0., 90., true); + addArc (CRect (right - 2.0 * radius, bottom - 2.0 * radius, right, bottom), 0., 90., true); addArc (CRect (left, bottom - 2.0 * radius, left + 2.0 * radius, bottom), 90., 180., true); addArc (CRect (left, top, left + 2.0 * radius, top + 2.0 * radius), 180., 270., true); closeSubpath (); } //----------------------------------------------------------------------------- -void CGraphicsPath::addPath (const CGraphicsPath& path, CGraphicsTransform* transformation) +void CGraphicsPath::addPath (const CGraphicsPath& inPath, CGraphicsTransform* transformation) { - for (auto e : path.elements) + for (auto e : inPath.elements) { if (transformation) { @@ -42,13 +44,16 @@ void CGraphicsPath::addPath (const CGraphicsPath& path, CGraphicsTransform* tran { case Element::kArc: { - transformation->transform (e.instruction.arc.rect.left, e.instruction.arc.rect.right, e.instruction.arc.rect.top, e.instruction.arc.rect.bottom); + transformation->transform ( + e.instruction.arc.rect.left, e.instruction.arc.rect.right, + e.instruction.arc.rect.top, e.instruction.arc.rect.bottom); break; } case Element::kEllipse: case Element::kRect: { - transformation->transform (e.instruction.rect.left, e.instruction.rect.right, e.instruction.rect.top, e.instruction.rect.bottom); + transformation->transform (e.instruction.rect.left, e.instruction.rect.right, + e.instruction.rect.top, e.instruction.rect.bottom); break; } case Element::kBeginSubpath: @@ -59,9 +64,12 @@ void CGraphicsPath::addPath (const CGraphicsPath& path, CGraphicsTransform* tran } case Element::kBezierCurve: { - transformation->transform (e.instruction.curve.control1.x, e.instruction.curve.control1.y); - transformation->transform (e.instruction.curve.control2.x, e.instruction.curve.control2.y); - transformation->transform (e.instruction.curve.end.x, e.instruction.curve.end.y); + transformation->transform (e.instruction.curve.control1.x, + e.instruction.curve.control1.y); + transformation->transform (e.instruction.curve.control2.x, + e.instruction.curve.control2.y); + transformation->transform (e.instruction.curve.end.x, + e.instruction.curve.end.y); break; } case Element::kCloseSubpath: @@ -119,7 +127,8 @@ void CGraphicsPath::addLine (const CPoint& to) } //----------------------------------------------------------------------------- -void CGraphicsPath::addBezierCurve (const CPoint& control1, const CPoint& control2, const CPoint& end) +void CGraphicsPath::addBezierCurve (const CPoint& control1, const CPoint& control2, + const CPoint& end) { Element e; e.type = Element::kBezierCurve; @@ -149,4 +158,166 @@ void CGraphicsPath::closeSubpath () dirty (); } +//----------------------------------------------------------------------------- +CGraphicsPath::CGraphicsPath (const PlatformGraphicsPathFactoryPtr& factory, + PlatformGraphicsPathPtr&& path) +: factory (factory), path (std::move (path)) +{ +} + +//----------------------------------------------------------------------------- +CGraphicsPath::~CGraphicsPath () noexcept {} + +//----------------------------------------------------------------------------- +CGradient* CGraphicsPath::createGradient (double color1Start, double color2Start, + const CColor& color1, const CColor& color2) +{ + return CGradient::create (color1Start, color2Start, color1, color2); +} + +//----------------------------------------------------------------------------- +void CGraphicsPath::makePlatformGraphicsPath (PlatformGraphicsPathFillMode fillMode) +{ + path = factory->createPath (fillMode); + if (!path) + return; + for (const auto& e : elements) + { + switch (e.type) + { + case Element::kArc: + { + path->addArc (rect2CRect (e.instruction.arc.rect), e.instruction.arc.startAngle, + e.instruction.arc.endAngle, e.instruction.arc.clockwise); + break; + } + case Element::kEllipse: + { + path->addEllipse (rect2CRect (e.instruction.rect)); + break; + } + case Element::kRect: + { + path->addRect (rect2CRect (e.instruction.rect)); + break; + } + case Element::kLine: + { + path->addLine (point2CPoint (e.instruction.point)); + break; + } + case Element::kBezierCurve: + { + path->addBezierCurve (point2CPoint (e.instruction.curve.control1), + point2CPoint (e.instruction.curve.control2), + point2CPoint (e.instruction.curve.end)); + break; + } + case Element::kBeginSubpath: + { + path->beginSubpath (point2CPoint (e.instruction.point)); + break; + } + case Element::kCloseSubpath: + { + path->closeSubpath (); + break; + } + } + } + path->finishBuilding (); +} + +//----------------------------------------------------------------------------- +bool CGraphicsPath::ensurePlatformGraphicsPathValid (PlatformGraphicsPathFillMode fillMode) +{ + if (path == nullptr || (path->getFillMode () != PlatformGraphicsPathFillMode::Ignored && + path->getFillMode () != fillMode)) + { + makePlatformGraphicsPath (fillMode); + } + return path != nullptr; +} + +//----------------------------------------------------------------------------- +void CGraphicsPath::dirty () +{ + path = nullptr; +} + +//----------------------------------------------------------------------------- +bool CGraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, CGraphicsTransform* transform) +{ + ensurePlatformGraphicsPathValid (evenOddFilled ? PlatformGraphicsPathFillMode::Alternate + : PlatformGraphicsPathFillMode::Winding); + return path ? path->hitTest (p, evenOddFilled, transform) : false; +} + +//----------------------------------------------------------------------------- +CPoint CGraphicsPath::getCurrentPosition () +{ + CPoint res; + if (!elements.empty ()) + { + const auto& e = elements.back (); + switch (e.type) + { + case Element::kBeginSubpath: + { + res = point2CPoint (e.instruction.point); + break; + } + case Element::kCloseSubpath: + { + // TODO: find opening point + break; + } + case Element::kArc: + { + // TODO: calculate end point + break; + } + case Element::kEllipse: + { + res = {e.instruction.rect.left + + (e.instruction.rect.right - e.instruction.rect.left) / 2., + e.instruction.rect.bottom}; + break; + } + case Element::kRect: + { + res = rect2CRect (e.instruction.rect).getTopLeft (); + break; + } + case Element::kLine: + { + res = point2CPoint (e.instruction.point); + break; + } + case Element::kBezierCurve: + { + res = point2CPoint (e.instruction.curve.end); + break; + } + } + } + return res; +} + +//----------------------------------------------------------------------------- +CRect CGraphicsPath::getBoundingBox () +{ + ensurePlatformGraphicsPathValid (path ? path->getFillMode () + : PlatformGraphicsPathFillMode::Winding); + return path ? path->getBoundingBox () : CRect (); +} + +//----------------------------------------------------------------------------- +const PlatformGraphicsPathPtr& + CGraphicsPath::getPlatformPath (PlatformGraphicsPathFillMode fillMode) +{ + ensurePlatformGraphicsPathValid (fillMode); + return path; +} + } // VSTGUI diff --git a/vstgui/lib/cgraphicspath.h b/vstgui/lib/cgraphicspath.h index d68b702e2..ed4b4e252 100644 --- a/vstgui/lib/cgraphicspath.h +++ b/vstgui/lib/cgraphicspath.h @@ -30,7 +30,8 @@ class CGraphicsPath : public AtomicReferenceCounted * @param color2 the second color of the gradient * @return a new gradient object */ - virtual CGradient* createGradient (double color1Start, double color2Start, const CColor& color1, const CColor& color2) = 0; + CGradient* createGradient (double color1Start, double color2Start, const CColor& color1, + const CColor& color2); //@} //----------------------------------------------------------------------------- @@ -38,21 +39,21 @@ class CGraphicsPath : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ /** add an arc to the path. Begins a new subpath if no elements were added before. */ - virtual void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise); + void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise); /** add an ellipse to the path. Begins a new subpath if no elements were added before. */ - virtual void addEllipse (const CRect& rect); + void addEllipse (const CRect& rect); /** add a rectangle to the path. Begins a new subpath if no elements were added before. */ - virtual void addRect (const CRect& rect); + void addRect (const CRect& rect); /** add another path to the path. Begins a new subpath if no elements were added before. */ - virtual void addPath (const CGraphicsPath& path, CGraphicsTransform* transformation = nullptr); + void addPath (const CGraphicsPath& path, CGraphicsTransform* transformation = nullptr); /** add a line to the path. A subpath must begin before */ - virtual void addLine (const CPoint& to); + void addLine (const CPoint& to); /** add a bezier curve to the path. A subpath must begin before */ - virtual void addBezierCurve (const CPoint& control1, const CPoint& control2, const CPoint& end); + void addBezierCurve (const CPoint& control1, const CPoint& control2, const CPoint& end); /** begin a new subpath. */ - virtual void beginSubpath (const CPoint& start); + void beginSubpath (const CPoint& start); /** close a subpath. A straight line will be added from the current point to the start point. */ - virtual void closeSubpath (); + void closeSubpath (); inline void beginSubpath (CCoord x, CCoord y) { @@ -79,21 +80,29 @@ class CGraphicsPath : public AtomicReferenceCounted /// @name Hit Testing //----------------------------------------------------------------------------- //@{ - virtual bool hitTest (const CPoint& p, bool evenOddFilled = false, CGraphicsTransform* transform = nullptr) = 0; + bool hitTest (const CPoint& p, bool evenOddFilled = false, + CGraphicsTransform* transform = nullptr); //@} //----------------------------------------------------------------------------- /// @name States //----------------------------------------------------------------------------- //@{ - virtual CPoint getCurrentPosition () = 0; - virtual CRect getBoundingBox () = 0; + CPoint getCurrentPosition (); + CRect getBoundingBox (); //@} - + + CGraphicsPath (const PlatformGraphicsPathFactoryPtr& factory, + PlatformGraphicsPathPtr&& path = nullptr); + ~CGraphicsPath () noexcept override; + + const PlatformGraphicsPathPtr& getPlatformPath (PlatformGraphicsPathFillMode fillMode); + protected: - CGraphicsPath () {} + void makePlatformGraphicsPath (PlatformGraphicsPathFillMode fillMode); + bool ensurePlatformGraphicsPathValid (PlatformGraphicsPathFillMode fillMode); - virtual void dirty () = 0; // platform object should be released + void dirty (); /// @cond ignore @@ -142,12 +151,17 @@ class CGraphicsPath : public AtomicReferenceCounted } instruction; }; - inline void CRect2Rect (const CRect& rect, CGraphicsPath::Rect& r) const {r.left = rect.left;r.right = rect.right;r.top = rect.top;r.bottom = rect.bottom;} - inline void CPoint2Point (const CPoint& point, CGraphicsPath::Point& p) const {p.x = point.x;p.y = point.y;} + static void CRect2Rect (const CRect& rect, CGraphicsPath::Rect& r) {r.left = rect.left;r.right = rect.right;r.top = rect.top;r.bottom = rect.bottom;} + static void CPoint2Point (const CPoint& point, CGraphicsPath::Point& p) {p.x = point.x;p.y = point.y;} + static CRect rect2CRect (const Rect& r) { return CRect (r.left, r.top, r.right, r.bottom); } + static CPoint point2CPoint (const Point& p) { return CPoint (p.x, p.y); } /// @endcond using ElementList = std::vector; ElementList elements; + + PlatformGraphicsPathFactoryPtr factory; + PlatformGraphicsPathPtr path; }; } // VSTGUI diff --git a/vstgui/lib/cinvalidrectlist.h b/vstgui/lib/cinvalidrectlist.h index 53c77b0da..2e5bb426e 100644 --- a/vstgui/lib/cinvalidrectlist.h +++ b/vstgui/lib/cinvalidrectlist.h @@ -25,6 +25,7 @@ struct CInvalidRectList void clear () { list.clear (); } const RectList& data () const { return list; } + bool empty () const { return list.empty (); } private: RectList list; diff --git a/vstgui/lib/clayeredviewcontainer.cpp b/vstgui/lib/clayeredviewcontainer.cpp index dd6c509d4..628d7ff92 100644 --- a/vstgui/lib/clayeredviewcontainer.cpp +++ b/vstgui/lib/clayeredviewcontainer.cpp @@ -5,6 +5,7 @@ #include "clayeredviewcontainer.h" #include "cframe.h" #include "cdrawcontext.h" +#include "coffscreencontext.h" #include "platform/iplatformframe.h" namespace VSTGUI { @@ -187,7 +188,10 @@ void CLayeredViewContainer::setAlphaValue (float alpha) //----------------------------------------------------------------------------- void CLayeredViewContainer::drawRect (CDrawContext* pContext, const CRect& updateRect) { - if (layer) + auto drawsIntoBitmap = false; + if (auto offscreenContext = dynamic_cast (pContext)) + drawsIntoBitmap = offscreenContext->getBitmap () != nullptr; + if (layer && !drawsIntoBitmap) layer->draw (pContext, updateRect); else CViewContainer::drawRect (pContext, updateRect); diff --git a/vstgui/lib/controls/cbuttons.cpp b/vstgui/lib/controls/cbuttons.cpp index 5aa4b6dea..36bc5fb61 100644 --- a/vstgui/lib/controls/cbuttons.cpp +++ b/vstgui/lib/controls/cbuttons.cpp @@ -7,6 +7,7 @@ #include "../cbitmap.h" #include "../cframe.h" #include "../cgraphicspath.h" +#include "../events.h" #include "../platform/iplatformfont.h" #include @@ -103,18 +104,19 @@ CMouseEventResult COnOffButton::onMouseCancel () } //------------------------------------------------------------------------ -int32_t COnOffButton::onKeyDown (VstKeyCode& keyCode) +void COnOffButton::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.virt == VKEY_RETURN && keyCode.modifier == 0) + if (event.type != EventType::KeyDown) + return; + if (event.virt == VirtualKey::Return && event.modifiers.empty ()) { value = (value == getMax ()) ? getMin () : getMax (); invalid (); beginEdit (); valueChanged (); endEdit (); - return 1; + event.consumed = true; } - return -1; } //------------------------------------------------------------------------ @@ -263,34 +265,30 @@ CMouseEventResult CKickButton::onMouseMoved (CPoint& where, const CButtonState& } //------------------------------------------------------------------------ -int32_t CKickButton::onKeyDown (VstKeyCode& keyCode) +void CKickButton::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.modifier == 0 && keyCode.virt == VKEY_RETURN) + if (event.modifiers.empty () && event.virt == VirtualKey::Return) { - if (value != getMax ()) + if (event.type == EventType::KeyDown) { - beginEdit (); - value = getMax (); + if (value != getMax ()) + { + beginEdit (); + value = getMax (); + invalid (); + valueChanged (); + } + event.consumed = true; + } + else if (event.type == EventType::KeyUp && isEditing ()) + { + value = getMin (); invalid (); valueChanged (); + endEdit (); + event.consumed = true; } - return 1; } - return -1; -} - -//------------------------------------------------------------------------ -int32_t CKickButton::onKeyUp (VstKeyCode& keyCode) -{ - if (keyCode.modifier == 0 && keyCode.virt == VKEY_RETURN) - { - value = getMin (); - invalid (); - valueChanged (); - endEdit (); - return 1; - } - return -1; } //------------------------------------------------------------------------ @@ -670,18 +668,18 @@ CMouseEventResult CCheckBox::onMouseUp (CPoint& where, const CButtonState& butto } //------------------------------------------------------------------------ -int32_t CCheckBox::onKeyDown (VstKeyCode& keyCode) +void CCheckBox::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.virt == VKEY_RETURN && keyCode.modifier == 0) + if (event.type == EventType::KeyDown && event.virt == VirtualKey::Return && + event.modifiers.empty ()) { value = (value < getMax ()) ? getMax () : getMin (); invalid (); beginEdit (); valueChanged (); endEdit (); - return 1; + event.consumed = true; } - return -1; } //------------------------------------------------------------------------ @@ -1029,9 +1027,11 @@ CMouseEventResult CTextButton::onMouseMoved (CPoint& where, const CButtonState& } //------------------------------------------------------------------------ -int32_t CTextButton::onKeyDown (VstKeyCode& keyCode) +void CTextButton::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.modifier == 0 && keyCode.virt == VKEY_RETURN) + if (event.type != EventType::KeyDown) + return; + if (event.modifiers.empty () && event.virt == VirtualKey::Return) { if (style == kKickStyle) { @@ -1058,15 +1058,8 @@ int32_t CTextButton::onKeyDown (VstKeyCode& keyCode) valueChanged (); endEdit (); } - return 1; + event.consumed = true; } - return -1; -} - -//------------------------------------------------------------------------ -int32_t CTextButton::onKeyUp (VstKeyCode& keyCode) -{ - return -1; } } // VSTGUI diff --git a/vstgui/lib/controls/cbuttons.h b/vstgui/lib/controls/cbuttons.h index 3f5712dcf..cc80ff4ad 100644 --- a/vstgui/lib/controls/cbuttons.h +++ b/vstgui/lib/controls/cbuttons.h @@ -40,7 +40,7 @@ class COnOffButton : public CControl CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseCancel () override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; bool sizeToFit () override; CLASS_METHODS(COnOffButton, CControl) @@ -104,7 +104,7 @@ class CCheckBox : public CControl CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseCancel () override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; bool sizeToFit () override; void setBackground (CBitmap *background) override; bool getFocusPath (CGraphicsPath& outPath) override; @@ -146,8 +146,7 @@ class CKickButton : public CControl, public IMultiBitmapControl CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseCancel () override; - int32_t onKeyDown (VstKeyCode& keyCode) override; - int32_t onKeyUp (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; bool sizeToFit () override; @@ -238,8 +237,7 @@ class CTextButton : public CControl CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseCancel () override; - int32_t onKeyDown (VstKeyCode& keyCode) override; - int32_t onKeyUp (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; CLASS_METHODS(CTextButton, CControl) protected: diff --git a/vstgui/lib/controls/ccontrol.cpp b/vstgui/lib/controls/ccontrol.cpp index 37d164bc4..91999719f 100644 --- a/vstgui/lib/controls/ccontrol.cpp +++ b/vstgui/lib/controls/ccontrol.cpp @@ -4,10 +4,12 @@ #include "ccontrol.h" #include "icontrollistener.h" +#include "../events.h" #include "../cframe.h" #include "../cgraphicspath.h" #include "../cvstguitimer.h" #include "../dispatchlist.h" +#include "../iviewlistener.h" #include #define VSTGUI_CCONTROL_LOG_EDITING 0 //DEBUG @@ -15,7 +17,7 @@ namespace VSTGUI { //------------------------------------------------------------------------ -struct CControl::Impl +struct CControl::Impl : ViewEventListenerAdapter { using SubListenerDispatcher = DispatchList; @@ -26,6 +28,28 @@ struct CControl::Impl float vmax {1.f}; float wheelInc {0.1f}; int32_t editing {0}; + + void viewOnEvent (CView* view, Event& event) override + { + if (event.type != EventType::MouseDown) + return; + auto control = static_cast (view); + auto& mouseDownEvent = castMouseDownEvent (event); + if (CControl::CheckDefaultValueEventFunc (control, mouseDownEvent)) + { + auto defValue = control->getDefaultValue (); + if (defValue != control->getValue ()) + { + control->beginEdit (); + control->setValue (defValue); + control->valueChanged (); + control->endEdit (); + control->setDirty (); + } + mouseDownEvent.consumed = true; + mouseDownEvent.ignoreFollowUpMoveAndUpEvents (true); + } + } }; //------------------------------------------------------------------------ @@ -44,6 +68,7 @@ CControl::CControl (const CRect& size, IControlListener* listener, int32_t tag, setTransparency (false); setMouseEnabled (true); setBackground (pBackground); + registerViewEventListener (impl.get ()); } //------------------------------------------------------------------------ @@ -59,10 +84,14 @@ CControl::CControl (const CControl& c) impl->vmin = c.impl->vmin; impl->vmax = c.impl->vmax; impl->wheelInc = c.impl->wheelInc; + registerViewEventListener (impl.get ()); } //------------------------------------------------------------------------ -CControl::~CControl () noexcept = default; +CControl::~CControl () noexcept +{ + unregisterViewEventListener (impl.get ()); +} //------------------------------------------------------------------------ void CControl::registerControlListener (IControlListener* subListener) @@ -136,10 +165,6 @@ float CControl::getDefaultValue (void) const return impl->defaultValue; } -//------------------------------------------------------------------------ -int32_t CControl::kZoomModifier = kShift; -int32_t CControl::kDefaultValueModifier = kControl; - //------------------------------------------------------------------------ void CControl::setTag (int32_t val) { @@ -265,6 +290,7 @@ void CControl::bounceValue () value = getMin (); } +#if VSTGUI_ENABLE_DEPRECATED_METHODS //------------------------------------------------------------------------ CControl::CheckDefaultValueFuncT CControl::CheckDefaultValueFunc = [] (CControl*, CButtonState button) { @@ -275,29 +301,25 @@ CControl::CheckDefaultValueFuncT CControl::CheckDefaultValueFunc = [] (CControl* #endif }; -//----------------------------------------------------------------------------- -bool CControl::checkDefaultValue (CButtonState button) -{ - if (CheckDefaultValueFunc (this, button)) - { - float defValue = getDefaultValue (); - if (defValue != getValue ()) +#endif // VSTGUI_ENABLE_DEPRECATED_METHODS + +//------------------------------------------------------------------------ +CControl::CheckDefaultValueEventFuncT CControl::CheckDefaultValueEventFunc = + [] (CControl* c, MouseDownEvent& event) { +#if VSTGUI_ENABLE_DEPRECATED_METHODS + if (event.buttonState.isLeft ()) { - // begin of edit parameter - beginEdit (); - - setValue (defValue); - valueChanged (); - - // end of edit parameter - endEdit (); - - setDirty (); + return CheckDefaultValueFunc (c, buttonStateFromMouseEvent (event)); } - return true; - } - return false; -} +#else +#if TARGET_OS_IPHONE + return event.buttonState.isLeft () && event.clickCount == 2; +#else + return event.buttonState.isLeft () && event.modifiers.is (ModifierKey::Control); +#endif // TARGET_OS_IPHONE +#endif // VSTGUI_ENABLE_DEPRECATED_METHODS + return false; + }; //------------------------------------------------------------------------ bool CControl::drawFocusOnTop () @@ -322,6 +344,7 @@ bool CControl::getFocusPath (CGraphicsPath& outPath) return true; } +#if VSTGUI_ENABLE_DEPRECATED_METHODS //----------------------------------------------------------------------------- int32_t CControl::mapVstKeyModifier (int32_t vstModifier) { @@ -336,6 +359,7 @@ int32_t CControl::mapVstKeyModifier (int32_t vstModifier) modifiers |= kControl; return modifiers; } +#endif //------------------------------------------------------------------------ //------------------------------------------------------------------------ diff --git a/vstgui/lib/controls/ccontrol.h b/vstgui/lib/controls/ccontrol.h index f3c45a349..2bee36b53 100644 --- a/vstgui/lib/controls/ccontrol.h +++ b/vstgui/lib/controls/ccontrol.h @@ -52,7 +52,6 @@ class CControl : public CView, public IFocusDrawing virtual float getDefaultValue () const; virtual void bounceValue (); - virtual bool checkDefaultValue (CButtonState button); /** notifies listener and dependent objects */ virtual void valueChanged (); @@ -96,22 +95,30 @@ class CControl : public CView, public IFocusDrawing bool drawFocusOnTop () override; bool getFocusPath (CGraphicsPath& outPath) override; + using CheckDefaultValueEventFuncT = bool (*) (CControl*, MouseDownEvent&); + /** Function to check if a mouse down event should reset the value to its default value for a + *control. Per default this checks for a left mouse down button and the control modifier key. */ + static CheckDefaultValueEventFuncT CheckDefaultValueEventFunc; + /** zoom modifier key, per default is the shift key */ - static int32_t kZoomModifier; - /** default value modifier key, per default is the control key */ - static int32_t kDefaultValueModifier; + inline static int32_t kZoomModifier = kShift; + +#if VSTGUI_ENABLE_DEPRECATED_METHODS + /** \deprecated default value modifier key, per default is the control key */ + inline static int32_t kDefaultValueModifier = kControl; using CheckDefaultValueFuncT = bool (*) (CControl*, CButtonState); - /** Function to check if the button state is the state to set the control value to its default - * value. The default implementation uses the kDefaultValueModifier (see above). Use this to - * change this to double click per example. But consider to change this to the same behaviour as - * the host you are running in for best user experience. */ + /** \deprecated Function to check if the button state is the state to set the control value to + * its default value. The default implementation uses the kDefaultValueModifier (see above). Use + * this to change this to double click per example. But consider to change this to the same + * behaviour as the host you are running in for best user experience. */ static CheckDefaultValueFuncT CheckDefaultValueFunc; +#endif CLASS_METHODS_VIRTUAL(CControl, CView) protected: ~CControl () noexcept override; - static int32_t mapVstKeyModifier (int32_t vstModifier); + VSTGUI_DEPRECATED (static int32_t mapVstKeyModifier (int32_t vstModifier);) IControlListener* listener; int32_t tag; diff --git a/vstgui/lib/controls/cfontchooser.cpp b/vstgui/lib/controls/cfontchooser.cpp index cc31cf895..6587c6822 100644 --- a/vstgui/lib/controls/cfontchooser.cpp +++ b/vstgui/lib/controls/cfontchooser.cpp @@ -293,9 +293,9 @@ bool CFontChooser::attached (CView* parent) } //----------------------------------------------------------------------------- -int32_t CFontChooser::onKeyDown (VstKeyCode& keyCode) +void CFontChooser::onKeyboardEvent (KeyboardEvent& event) { - return fontBrowser->onKeyDown (keyCode); + fontBrowser->onKeyboardEvent (event); } } // VSTGUI diff --git a/vstgui/lib/controls/cfontchooser.h b/vstgui/lib/controls/cfontchooser.h index 2f92b1f5f..56fa14d39 100644 --- a/vstgui/lib/controls/cfontchooser.h +++ b/vstgui/lib/controls/cfontchooser.h @@ -73,7 +73,7 @@ class CFontChooser : public CViewContainer, public IControlListener, public Gene void dbSelectionChanged (int32_t selectedRow, GenericStringListDataBrowserSource* source) override; void valueChanged (CControl* pControl) override; bool attached (CView* parent) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; IFontChooserDelegate* delegate; CDataBrowser* fontBrowser; diff --git a/vstgui/lib/controls/cknob.cpp b/vstgui/lib/controls/cknob.cpp index d3a962683..9c0017a96 100644 --- a/vstgui/lib/controls/cknob.cpp +++ b/vstgui/lib/controls/cknob.cpp @@ -8,6 +8,7 @@ #include "../cframe.h" #include "../cgraphicspath.h" #include "../cvstguitimer.h" +#include "../events.h" #include namespace VSTGUI { @@ -104,12 +105,6 @@ CMouseEventResult CKnobBase::onMouseDown (CPoint& where, const CButtonState& but invalidMouseWheelEditTimer (this); beginEdit (); - if (checkDefaultValue (buttons)) - { - endEdit (); - return kMouseDownEventHandledButDontNeedMovedOrUpEvents; - } - auto& mouseState = getMouseEditingState (); mouseState.firstPoint = where; mouseState.lastPoint (-1, -1); @@ -232,18 +227,15 @@ CMouseEventResult CKnobBase::onMouseMoved (CPoint& where, const CButtonState& bu } //------------------------------------------------------------------------ -bool CKnobBase::onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float &distance, const CButtonState &buttons) +void CKnobBase::onMouseWheelEvent (MouseWheelEvent& event) { - if (!getMouseEnabled ()) - return false; - onMouseWheelEditing (this); float v = getValueNormalized (); - if (buttons & kZoomModifier) - v += 0.1f * distance * getWheelInc (); + if (buttonStateFromEventModifiers (event.modifiers) & kZoomModifier) + v += 0.1f * static_cast (event.deltaY) * getWheelInc (); else - v += distance * getWheelInc (); + v += static_cast (event.deltaY) * getWheelInc (); setValueNormalized (v); if (isDirty ()) @@ -251,25 +243,27 @@ bool CKnobBase::onWheel (const CPoint& where, const CMouseWheelAxis& axis, const invalid (); valueChanged (); } - return true; + event.consumed = true; } //------------------------------------------------------------------------ -int32_t CKnobBase::onKeyDown (VstKeyCode& keyCode) +void CKnobBase::onKeyboardEvent (KeyboardEvent& event) { - switch (keyCode.virt) + if (event.type != EventType::KeyDown) + return; + switch (event.virt) { - case VKEY_UP : - case VKEY_RIGHT : - case VKEY_DOWN : - case VKEY_LEFT : + case VirtualKey::Up : + case VirtualKey::Right : + case VirtualKey::Down : + case VirtualKey::Left : { float distance = 1.f; - if (keyCode.virt == VKEY_DOWN || keyCode.virt == VKEY_LEFT) + if (event.virt == VirtualKey::Down || event.virt == VirtualKey::Left) distance = -distance; float v = getValueNormalized (); - if (mapVstKeyModifier (keyCode.modifier) & kZoomModifier) + if (buttonStateFromEventModifiers (event.modifiers) & kZoomModifier) v += 0.1f * distance * getWheelInc (); else v += distance * getWheelInc (); @@ -278,28 +272,23 @@ int32_t CKnobBase::onKeyDown (VstKeyCode& keyCode) if (isDirty ()) { invalid (); - - // begin of edit parameter beginEdit (); - valueChanged (); - - // end of edit parameter endEdit (); } - return 1; + event.consumed = true; } - case VKEY_ESCAPE: + case VirtualKey::Escape: { if (isEditing ()) { onMouseCancel (); - return 1; + event.consumed = true; } break; } + default: return; } - return -1; } //------------------------------------------------------------------------ diff --git a/vstgui/lib/controls/cknob.h b/vstgui/lib/controls/cknob.h index 45b16a42b..cc69611cc 100644 --- a/vstgui/lib/controls/cknob.h +++ b/vstgui/lib/controls/cknob.h @@ -35,8 +35,8 @@ class CKnobBase : public CControl, protected CMouseWheelEditingSupport //@} // overrides - bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onMouseWheelEvent (MouseWheelEvent& event) override; + void onKeyboardEvent (KeyboardEvent& event) override; void setViewSize (const CRect &rect, bool invalid = true) override; bool sizeToFit () override; void setMin (float val) override; diff --git a/vstgui/lib/controls/clistcontrol.cpp b/vstgui/lib/controls/clistcontrol.cpp index 888d61a6f..b129495f2 100644 --- a/vstgui/lib/controls/clistcontrol.cpp +++ b/vstgui/lib/controls/clistcontrol.cpp @@ -7,6 +7,7 @@ #include "../cframe.h" #include "../cgraphicspath.h" #include "../cscrollview.h" +#include "../events.h" #include "clistcontrol.h" #include @@ -22,6 +23,7 @@ struct CListControl::Impl std::vector rowDescriptions; Optional hoveredRow {}; bool doHoverCheck {false}; + CCoord minHeight {0.}; }; //------------------------------------------------------------------------ @@ -98,6 +100,9 @@ void CListControl::recalculateLayout () impl->doHoverCheck |= (impl->rowDescriptions[row].flags & CListControlRowDesc::Hoverable) != 0; } + if (impl->minHeight > 0 && height < impl->minHeight) + height = impl->minHeight; + auto viewSize = getViewSize (); if (viewSize.getHeight () != height) { @@ -198,6 +203,43 @@ void CListControl::drawRect (CDrawContext* context, const CRect& updateRect) //------------------------------------------------------------------------ bool CListControl::attached (CView* parent) { + if (auto scrollView = dynamic_cast (parent->getParentView ())) + { + impl->minHeight = scrollView->calculateOptimalContainerSize ().getHeight (); + struct SizeListener : ViewListenerAdapter + { + SizeListener (CListControl* listControl, CScrollView* scrollView) + : control (listControl), scrollView (scrollView) + { + listControl->registerViewListener (this); + scrollView->registerViewListener (this); + } + ~SizeListener () noexcept + { + control->unregisterViewListener (this); + scrollView->unregisterViewListener (this); + } + void viewSizeChanged (CView* view, const CRect& oldSize) override + { + if (view != scrollView) + return; + control->impl->minHeight = + scrollView->calculateOptimalContainerSize ().getHeight (); + control->recalculateLayout (); + } + void viewWillDelete (CView* view) override + { + if (view == control || view == scrollView) + delete this; + } + void viewAttached (CView* view) override {} + void viewRemoved (CView* view) override {} + + CListControl* control {nullptr}; + CScrollView* scrollView {nullptr}; + }; + new SizeListener (this, scrollView); + } recalculateLayout (); return CControl::attached (parent); } @@ -351,48 +393,51 @@ bool CListControl::rowSelectable (int32_t row) const } //------------------------------------------------------------------------ -int32_t CListControl::onKeyDown (VstKeyCode& keyCode) +void CListControl::onKeyboardEvent (KeyboardEvent& event) { - if (getMouseEnabled () && keyCode.character == 0) + if (event.type != EventType::KeyDown) + return; + if (getMouseEnabled () && event.character == 0) { int32_t newRow = getIntValue (); - switch (keyCode.virt) + switch (event.virt) { - case VKEY_HOME: + default: return; + case VirtualKey::Home: { - if (keyCode.modifier != 0) + if (!event.modifiers.empty ()) break; newRow = getMinRowIndex (); if (!rowSelectable (newRow)) newRow = getNextSelectableRow (newRow, 1); break; } - case VKEY_END: + case VirtualKey::End: { - if (keyCode.modifier != 0) + if (!event.modifiers.empty ()) break; newRow = getMaxRowIndex (); if (!rowSelectable (newRow)) newRow = getNextSelectableRow (newRow, -1); break; } - case VKEY_UP: + case VirtualKey::Up: { - if (keyCode.modifier != 0) + if (!event.modifiers.empty ()) break; newRow = getNextSelectableRow (newRow, -1); break; } - case VKEY_DOWN: + case VirtualKey::Down: { - if (keyCode.modifier != 0) + if (!event.modifiers.empty ()) break; newRow = getNextSelectableRow (newRow, 1); break; } - case VKEY_PAGEUP: + case VirtualKey::PageUp: { - if (keyCode.modifier != 0) + if (!event.modifiers.empty ()) break; auto vr = getVisibleViewSize (); auto rr = getRowRect (newRow); @@ -403,13 +448,16 @@ int32_t CListControl::onKeyDown (VstKeyCode& keyCode) if (auto scrollView = dynamic_cast (parent->getParentView ())) { scrollView->makeRectVisible (*rr); - return onKeyDown (keyCode); + onKeyboardEvent (event); + return; } } } vr.top += 2; if (auto firstVisibleRow = getRowAtPoint (vr.getTopLeft ())) { + while (!rowSelectable (*firstVisibleRow)) + *firstVisibleRow += 1; if (*firstVisibleRow == getIntValue ()) { vr.offset (0, -vr.getHeight ()); @@ -427,9 +475,9 @@ int32_t CListControl::onKeyDown (VstKeyCode& keyCode) newRow = getNextSelectableRow (newRow, -1); break; } - case VKEY_PAGEDOWN: + case VirtualKey::PageDown: { - if (keyCode.modifier != 0) + if (!event.modifiers.empty ()) break; auto vr = getVisibleViewSize (); auto rr = getRowRect (newRow); @@ -440,13 +488,16 @@ int32_t CListControl::onKeyDown (VstKeyCode& keyCode) if (auto scrollView = dynamic_cast (parent->getParentView ())) { scrollView->makeRectVisible (*rr); - return onKeyDown (keyCode); + onKeyboardEvent (event); + return; } } } vr.bottom -= 2; if (auto lastVisibleRow = getRowAtPoint (vr.getBottomLeft ())) { + while (!rowSelectable (*lastVisibleRow)) + *lastVisibleRow -= 1; if (*lastVisibleRow == getIntValue ()) { vr.offset (0, vr.getHeight ()); @@ -481,10 +532,9 @@ int32_t CListControl::onKeyDown (VstKeyCode& keyCode) scrollView->makeRectVisible (*rowRect); } } - return 1; + event.consumed = true; } } - return -1; } //------------------------------------------------------------------------ diff --git a/vstgui/lib/controls/clistcontrol.h b/vstgui/lib/controls/clistcontrol.h index 9b2d0b1da..d4e6d773e 100644 --- a/vstgui/lib/controls/clistcontrol.h +++ b/vstgui/lib/controls/clistcontrol.h @@ -56,7 +56,7 @@ class CListControl final : public CControl CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; void setViewSize (const CRect& rect, bool invalid = true) override; bool drawFocusOnTop () override; bool getFocusPath (CGraphicsPath& outPath) override; diff --git a/vstgui/lib/controls/cmoviebutton.cpp b/vstgui/lib/controls/cmoviebutton.cpp index cdbbc43b0..fb60b7149 100644 --- a/vstgui/lib/controls/cmoviebutton.cpp +++ b/vstgui/lib/controls/cmoviebutton.cpp @@ -5,6 +5,7 @@ #include "cmoviebutton.h" #include "../cdrawcontext.h" #include "../cbitmap.h" +#include "../events.h" namespace VSTGUI { @@ -136,18 +137,19 @@ CMouseEventResult CMovieButton::onMouseCancel () } //------------------------------------------------------------------------ -int32_t CMovieButton::onKeyDown (VstKeyCode& keyCode) +void CMovieButton::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.virt == VKEY_RETURN && keyCode.modifier == 0) + if (event.type != EventType::KeyDown || event.modifiers.empty () == false) + return; + if (event.virt == VirtualKey::Return) { value = (value == getMax ()) ? getMin () : getMax (); invalid (); beginEdit (); valueChanged (); endEdit (); - return 1; + event.consumed = true; } - return -1; } //----------------------------------------------------------------------------------------------- diff --git a/vstgui/lib/controls/cmoviebutton.h b/vstgui/lib/controls/cmoviebutton.h index e51774e2b..ef2a3189f 100644 --- a/vstgui/lib/controls/cmoviebutton.h +++ b/vstgui/lib/controls/cmoviebutton.h @@ -26,7 +26,7 @@ class CMovieButton : public CControl, public IMultiBitmapControl CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseCancel () override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; bool sizeToFit () override; void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); } diff --git a/vstgui/lib/controls/coptionmenu.cpp b/vstgui/lib/controls/coptionmenu.cpp index 042a62e26..415bd1df1 100644 --- a/vstgui/lib/controls/coptionmenu.cpp +++ b/vstgui/lib/controls/coptionmenu.cpp @@ -6,18 +6,42 @@ #include "../cbitmap.h" #include "../cframe.h" #include "../cstring.h" +#include "../events.h" #include "../platform/iplatformoptionmenu.h" #include "../platform/iplatformframe.h" namespace VSTGUI { +//------------------------------------------------------------------------ +struct CMenuItem::Impl +{ + UTF8String title; + UTF8String keyCode; + SharedPointer submenu; + SharedPointer icon; + int32_t flags {0}; + int32_t keyModifiers {0}; + VirtualKey virtualKey {VirtualKey::None}; + int32_t tag {-1}; +}; + //------------------------------------------------------------------------ // CMenuItem //------------------------------------------------------------------------ /*! @class CMenuItem Defines an item of a VSTGUI::COptionMenu */ + +//------------------------------------------------------------------------ +CMenuItem::CMenuItem () +{ + impl = std::make_unique (); +} + +//------------------------------------------------------------------------ +CMenuItem::~CMenuItem () noexcept = default; + //------------------------------------------------------------------------ /** * CMenuItem constructor. @@ -29,8 +53,9 @@ Defines an item of a VSTGUI::COptionMenu */ //------------------------------------------------------------------------ CMenuItem::CMenuItem (const UTF8String& inTitle, const UTF8String& inKeycode, int32_t inKeyModifiers, CBitmap* inIcon, int32_t inFlags) -: flags (inFlags) +: CMenuItem () { + impl->flags = inFlags; setTitle (inTitle); setKey (inKeycode, inKeyModifiers); setIcon (inIcon); @@ -45,6 +70,7 @@ CMenuItem::CMenuItem (const UTF8String& inTitle, const UTF8String& inKeycode, in */ //------------------------------------------------------------------------ CMenuItem::CMenuItem (const UTF8String& inTitle, COptionMenu* inSubmenu, CBitmap* inIcon) +: CMenuItem () { setTitle (inTitle); setSubmenu (inSubmenu); @@ -59,6 +85,7 @@ CMenuItem::CMenuItem (const UTF8String& inTitle, COptionMenu* inSubmenu, CBitmap */ //------------------------------------------------------------------------ CMenuItem::CMenuItem (const UTF8String& inTitle, int32_t inTag) +: CMenuItem () { setTitle (inTitle); setTag (inTag); @@ -71,12 +98,13 @@ CMenuItem::CMenuItem (const UTF8String& inTitle, int32_t inTag) */ //------------------------------------------------------------------------ CMenuItem::CMenuItem (const CMenuItem& item) -: flags (item.flags) +: CMenuItem () { + impl->flags = item.impl->flags; setTitle (item.getTitle ()); setIcon (item.getIcon ()); - if (item.getVirtualKeyCode ()) - setVirtualKey (item.getVirtualKeyCode (), item.getKeyModifiers ()); + if (item.getVirtualKey () != VirtualKey::None) + setVirtualKey (item.getVirtualKey (), item.getKeyModifiers ()); else setKey (item.getKeycode (), item.getKeyModifiers ()); setTag (item.getTag ()); @@ -86,64 +114,145 @@ CMenuItem::CMenuItem (const CMenuItem& item) //------------------------------------------------------------------------ void CMenuItem::setTitle (const UTF8String& inTitle) { - title = inTitle; + impl->title = inTitle; } //------------------------------------------------------------------------ void CMenuItem::setKey (const UTF8String& inKeycode, int32_t inKeyModifiers) { - keyCode = inKeycode; - keyModifiers = inKeyModifiers; - virtualKeyCode = 0; + impl->keyCode = inKeycode; + impl->keyModifiers = inKeyModifiers; + impl->virtualKey = VirtualKey::None; } +#if VSTGUI_ENABLE_DEPRECATED_METHODS //------------------------------------------------------------------------ void CMenuItem::setVirtualKey (int32_t inVirtualKeyCode, int32_t inKeyModifiers) { setKey (nullptr, inKeyModifiers); - virtualKeyCode = inVirtualKeyCode; + impl->virtualKey = fromVstVirtualKey (inVirtualKeyCode); +} + +//------------------------------------------------------------------------ +int32_t CMenuItem::getVirtualKeyCode () const +{ + return toVstVirtualKey (impl->virtualKey); +} +#endif + +//------------------------------------------------------------------------ +void CMenuItem::setVirtualKey (VirtualKey inVirtualKey, int32_t inKeyModifiers) +{ + setKey (nullptr, inKeyModifiers); + impl->virtualKey = inVirtualKey; } //------------------------------------------------------------------------ void CMenuItem::setSubmenu (COptionMenu* inSubmenu) { - submenu = inSubmenu; + impl->submenu = inSubmenu; } //------------------------------------------------------------------------ void CMenuItem::setIcon (CBitmap* inIcon) { - icon = inIcon; + impl->icon = inIcon; } //------------------------------------------------------------------------ void CMenuItem::setTag (int32_t t) { - tag = t; + impl->tag = t; } //------------------------------------------------------------------------ void CMenuItem::setEnabled (bool state) { - setBit (flags, kDisabled, !state); + setBit (impl->flags, kDisabled, !state); } //------------------------------------------------------------------------ void CMenuItem::setChecked (bool state) { - setBit (flags, kChecked, state); + setBit (impl->flags, kChecked, state); } //------------------------------------------------------------------------ void CMenuItem::setIsTitle (bool state) { - setBit (flags, kTitle, state); + setBit (impl->flags, kTitle, state); } //------------------------------------------------------------------------ void CMenuItem::setIsSeparator (bool state) { - setBit (flags, kSeparator, state); + setBit (impl->flags, kSeparator, state); +} + +//------------------------------------------------------------------------ +bool CMenuItem::isEnabled () const +{ + return !hasBit (impl->flags, kDisabled); +} + +//------------------------------------------------------------------------ +bool CMenuItem::isChecked () const +{ + return hasBit (impl->flags, kChecked); +} + +//------------------------------------------------------------------------ +bool CMenuItem::isTitle () const +{ + return hasBit (impl->flags, kTitle); +} + +//------------------------------------------------------------------------ +bool CMenuItem::isSeparator () const +{ + return hasBit (impl->flags, kSeparator); +} + +//------------------------------------------------------------------------ +const UTF8String& CMenuItem::getTitle () const +{ + return impl->title; +} + +//------------------------------------------------------------------------ +int32_t CMenuItem::getKeyModifiers () const +{ + return impl->keyModifiers; +} + +//------------------------------------------------------------------------ +const UTF8String& CMenuItem::getKeycode () const +{ + return impl->keyCode; +} + +//------------------------------------------------------------------------ +VirtualKey CMenuItem::getVirtualKey () const +{ + return impl->virtualKey; +} + +//------------------------------------------------------------------------ +COptionMenu* CMenuItem::getSubmenu () const +{ + return impl->submenu; +} + +//------------------------------------------------------------------------ +CBitmap* CMenuItem::getIcon () const +{ + return impl->icon; +} + +//------------------------------------------------------------------------ +int32_t CMenuItem::getTag () const +{ + return impl->tag; } //------------------------------------------------------------------------ @@ -159,6 +268,7 @@ CCommandMenuItem::CCommandMenuItem (Desc&& args) , commandName (std::move (args.commandName)) , itemTarget (std::move (args.target)) { + setTag (args.tag); } //------------------------------------------------------------------------ @@ -168,6 +278,7 @@ CCommandMenuItem::CCommandMenuItem (const Desc& args) , commandName (args.commandName) , itemTarget (args.target) { + setTag (args.tag); } //------------------------------------------------------------------------ @@ -313,21 +424,22 @@ void COptionMenu::unregisterOptionMenuListener (IOptionMenuListener* listener) } //------------------------------------------------------------------------ -int32_t COptionMenu::onKeyDown (VstKeyCode& keyCode) +void COptionMenu::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.modifier == 0 && keyCode.character == 0) + if (event.type != EventType::KeyUp && event.modifiers.empty () && event.character == 0) { - if (keyCode.virt == VKEY_RETURN) + if (event.virt == VirtualKey::Return) { auto self = shared (this); getFrame ()->doAfterEventProcessing ([self] () { self->doPopup (); }); - return 1; + event.consumed = true; + return; } if (!(style & (kMultipleCheckStyle & ~kCheckStyle))) { - if (keyCode.virt == VKEY_UP) + if (event.virt == VirtualKey::Up) { int32_t value = (int32_t)getValue ()-1; if (value >= 0) @@ -345,9 +457,10 @@ int32_t COptionMenu::onKeyDown (VstKeyCode& keyCode) invalid (); } } - return 1; + event.consumed = true; + return; } - if (keyCode.virt == VKEY_DOWN) + if (event.virt == VirtualKey::Down) { int32_t value = (int32_t)getValue ()+1; if (value < getNbEntries ()) @@ -365,11 +478,12 @@ int32_t COptionMenu::onKeyDown (VstKeyCode& keyCode) invalid (); } } - return 1; + event.consumed = true; + return; } } } - return CParamDisplay::onKeyDown (keyCode); + CParamDisplay::onKeyboardEvent (event); } //------------------------------------------------------------------------ diff --git a/vstgui/lib/controls/coptionmenu.h b/vstgui/lib/controls/coptionmenu.h index 115bbd19a..b0acc7747 100644 --- a/vstgui/lib/controls/coptionmenu.h +++ b/vstgui/lib/controls/coptionmenu.h @@ -49,8 +49,8 @@ class CMenuItem : public CBaseObject virtual void setSubmenu (COptionMenu* submenu); /** set keycode and key modifiers of menu item */ virtual void setKey (const UTF8String& keyCode, int32_t keyModifiers = 0); - /** set virtual keycode and key modifiers of menu item */ - virtual void setVirtualKey (int32_t virtualKeyCode, int32_t keyModifiers = 0); + /** set virtual key and key modifiers of menu item */ + virtual void setVirtualKey (VirtualKey virtualKey, int32_t keyModifiers = 0); /** set menu item enabled state */ virtual void setEnabled (bool state = true); /** set menu item checked state */ @@ -65,42 +65,41 @@ class CMenuItem : public CBaseObject virtual void setTag (int32_t tag); /** returns whether the item is enabled or not */ - bool isEnabled () const { return !hasBit (flags, kDisabled); } + bool isEnabled () const; /** returns whether the item is checked or not */ - bool isChecked () const { return hasBit (flags, kChecked); } + bool isChecked () const; /** returns whether the item is a title item or not */ - bool isTitle () const { return hasBit (flags, kTitle); } + bool isTitle () const; /** returns whether the item is a separator or not */ - bool isSeparator () const { return hasBit (flags, kSeparator); } + bool isSeparator () const; /** returns the title of the item */ - const UTF8String& getTitle () const { return title; } + const UTF8String& getTitle () const; /** returns the key modifiers of the item */ - int32_t getKeyModifiers () const { return keyModifiers; } + int32_t getKeyModifiers () const; /** returns the keycode of the item */ - const UTF8String& getKeycode () const { return keyCode; } - /** returns the virtual keycode of the item */ - int32_t getVirtualKeyCode () const { return virtualKeyCode; } + const UTF8String& getKeycode () const; + /** returns the virtual key of the item */ + VirtualKey getVirtualKey () const; /** returns the submenu of the item */ - COptionMenu* getSubmenu () const { return submenu; } + COptionMenu* getSubmenu () const; /** returns the icon of the item */ - CBitmap* getIcon () const { return icon; } + CBitmap* getIcon () const; /** returns the tag of the item */ - int32_t getTag () const { return tag; } + int32_t getTag () const; //@} +#if VSTGUI_ENABLE_DEPRECATED_METHODS + int32_t getVirtualKeyCode () const; + virtual void setVirtualKey (int32_t virtualKeyCode, int32_t keyModifiers = 0); +#endif //------------------------------------------------------------------------ protected: - ~CMenuItem () noexcept override = default; - - UTF8String title; - UTF8String keyCode; - SharedPointer submenu; - SharedPointer icon; - int32_t flags {0}; - int32_t keyModifiers {0}; - int32_t virtualKeyCode {0}; - int32_t tag {-1}; + CMenuItem (); + ~CMenuItem () noexcept override; + + struct Impl; + std::unique_ptr impl; }; //----------------------------------------------------------------------------- @@ -121,7 +120,8 @@ class CCommandMenuItem : public CMenuItem SharedPointer icon; int32_t keyModifiers {0}; int32_t flags {kNoFlags}; - + int32_t tag {-1}; + Desc () = default; ~Desc () noexcept = default; @@ -141,14 +141,13 @@ class CCommandMenuItem : public CMenuItem { } - Desc (const UTF8String& title, int32_t tag, - ICommandMenuItemTarget* target = nullptr, - const UTF8String& commandCategory = nullptr, - const UTF8String& commandName = nullptr) + Desc (const UTF8String& title, int32_t tag, ICommandMenuItemTarget* target = nullptr, + const UTF8String& commandCategory = nullptr, const UTF8String& commandName = nullptr) : title (title) , commandCategory (commandCategory) , commandName (commandName) , target (target) + , tag (tag) { } @@ -312,7 +311,7 @@ class COptionMenu : public CParamDisplay void draw (CDrawContext* pContext) override; CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; void takeFocus () override; void looseFocus () override; diff --git a/vstgui/lib/controls/cscrollbar.cpp b/vstgui/lib/controls/cscrollbar.cpp index e82a99cf6..8737873ed 100644 --- a/vstgui/lib/controls/cscrollbar.cpp +++ b/vstgui/lib/controls/cscrollbar.cpp @@ -9,6 +9,7 @@ #include "../cframe.h" #include "../cgraphicspath.h" #include "../cdrawcontext.h" +#include "../events.h" namespace VSTGUI { @@ -304,27 +305,28 @@ void CScrollbar::onVisualChange () } //------------------------------------------------------------------------ -bool CScrollbar::onWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &_distance, const CButtonState &buttons) +void CScrollbar::onMouseWheelEvent (MouseWheelEvent& event) { if (scrollerLength == 0 || !getMouseEnabled ()) - return false; + return; - if (buttons != 0 && !(buttons & (kShift|kMouseWheelInverted))) - return false; + if (!event.modifiers.empty () && !(event.modifiers.has (ModifierKey::Shift) && + event.flags & MouseWheelEvent::DirectionInvertedFromDevice)) + return; - if (direction == kHorizontal && axis == kMouseWheelAxisY) - return false; + float distance = 0.f; + if (direction == kHorizontal) + distance = static_cast (event.deltaX); + else + distance = static_cast (event.deltaY); - if (direction == kVertical && axis == kMouseWheelAxisX) - return false; + if (distance == 0.f) + return; - float distance = _distance; - if (direction == kHorizontal && axis == kMouseWheelAxisY) - distance *= -1; - if (buttons & kMouseWheelInverted) + if (event.flags & MouseWheelEvent::DirectionInvertedFromDevice) distance *= -1; - if (buttons & kShift) + if (event.modifiers.has (ModifierKey::Shift)) value -= 0.1f * distance * getWheelInc (); else value -= distance * getWheelInc (); @@ -336,7 +338,7 @@ bool CScrollbar::onWheel (const CPoint &where, const CMouseWheelAxis &axis, cons valueChanged (); invalid (); } - return true; + event.consumed = true; } //----------------------------------------------------------------------------- diff --git a/vstgui/lib/controls/cscrollbar.h b/vstgui/lib/controls/cscrollbar.h index d9916b0ca..2a20ede6e 100644 --- a/vstgui/lib/controls/cscrollbar.h +++ b/vstgui/lib/controls/cscrollbar.h @@ -53,7 +53,7 @@ class CScrollbar : public CControl // overwrite void draw (CDrawContext* pContext) override; - bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; + void onMouseWheelEvent (MouseWheelEvent& event) override; CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; diff --git a/vstgui/lib/controls/csegmentbutton.cpp b/vstgui/lib/controls/csegmentbutton.cpp index 22073f242..306e687bc 100644 --- a/vstgui/lib/controls/csegmentbutton.cpp +++ b/vstgui/lib/controls/csegmentbutton.cpp @@ -6,6 +6,7 @@ #include "../cdrawcontext.h" #include "../cframe.h" #include "../cgraphicspath.h" +#include "../events.h" #include namespace VSTGUI { @@ -358,59 +359,60 @@ CMouseEventResult CSegmentButton::onMouseDown (CPoint& where, const CButtonState } //----------------------------------------------------------------------------- -int32_t CSegmentButton::onKeyDown (VstKeyCode& keyCode) +void CSegmentButton::onKeyboardEvent (KeyboardEvent& event) { - int32_t result = -1; - if (selectionMode != SelectionMode::kMultiple && keyCode.modifier == 0 && - keyCode.character == 0) + if (event.type != EventType::KeyDown || event.modifiers.empty () == false || + event.character != 0) + return; + if (selectionMode != SelectionMode::kMultiple) { uint32_t newIndex = getSegmentIndex (getValueNormalized ()); uint32_t oldIndex = newIndex; - switch (keyCode.virt) + switch (event.virt) { - case VKEY_LEFT: + case VirtualKey::Left: { if (style == Style::kHorizontal && newIndex > 0) newIndex--; else if (style == Style::kHorizontalInverse && newIndex < segments.size () - 1) newIndex++; - result = 1; + event.consumed = true; break; } - case VKEY_RIGHT: + case VirtualKey::Right: { if (style == Style::kHorizontal && newIndex < segments.size () - 1) newIndex++; else if (style == Style::kHorizontalInverse && newIndex > 0) newIndex--; - result = 1; + event.consumed = true; break; } - case VKEY_UP: + case VirtualKey::Up: { if (style == Style::kVertical && newIndex > 0) newIndex--; else if (style == Style::kVerticalInverse && newIndex < segments.size () - 1) newIndex++; - result = 1; + event.consumed = true; break; } - case VKEY_DOWN: + case VirtualKey::Down: { if (style == Style::kVertical && newIndex < segments.size () - 1) newIndex++; else if (style == Style::kVerticalInverse && newIndex > 0) newIndex--; - result = 1; + event.consumed = true; break; } + default: return; } if (newIndex != oldIndex) { setSelectedSegment (newIndex); } } - return result; } //----------------------------------------------------------------------------- @@ -438,6 +440,8 @@ void CSegmentButton::drawRect (CDrawContext* pContext, const CRect& dirtyRect) CRect r (getViewSize ()); r.inset (lineWidth / 2., lineWidth / 2.); path = owned (pContext->createGraphicsPath ()); + if (!path) + return; path->addRoundRect (r, getRoundRadius ()); } pContext->setDrawMode (kAntiAliasing); @@ -521,9 +525,8 @@ uint32_t CSegmentButton::getSegmentIndex (float value) const if (value < 0.f || value > 1.f) return kPushBack; - const auto segmentIndex = static_cast (segments.size () - 1) * value; - const auto segmentIndexRounded = static_cast (segmentIndex + 0.5f); - return segmentIndexRounded; + return std::min (static_cast (segments.size () - 1), + static_cast (value * (segments.size ()))); } //----------------------------------------------------------------------------- diff --git a/vstgui/lib/controls/csegmentbutton.h b/vstgui/lib/controls/csegmentbutton.h index df68b15bf..508de0e4d 100644 --- a/vstgui/lib/controls/csegmentbutton.h +++ b/vstgui/lib/controls/csegmentbutton.h @@ -129,7 +129,7 @@ class CSegmentButton : public CControl bool attached (CView *parent) override; void setViewSize (const CRect& rect, bool invalid = true) override; CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; void draw (CDrawContext* pContext) override; void drawRect (CDrawContext* pContext, const CRect& dirtyRect) override; bool drawFocusOnTop () override; diff --git a/vstgui/lib/controls/cslider.cpp b/vstgui/lib/controls/cslider.cpp index 12fe21139..5ae640e3c 100644 --- a/vstgui/lib/controls/cslider.cpp +++ b/vstgui/lib/controls/cslider.cpp @@ -6,6 +6,7 @@ #include "../cdrawcontext.h" #include "../cgraphicspath.h" #include "../cvstguitimer.h" +#include "../events.h" #include "cslider.h" #include @@ -380,16 +381,6 @@ CMouseEventResult CSliderBase::onMouseDown (CPoint& where, const CButtonState& b impl->mePreviousVal = getMin () - 1; impl->meOldButton = buttons; - if ((getEffectiveSliderMode () == CSliderMode::RelativeTouch && - handleRect.pointInside (where)) || - getEffectiveSliderMode () != CSliderMode::RelativeTouch) - { - if (checkDefaultValue (buttons)) - { - return kMouseDownEventHandledButDontNeedMovedOrUpEvents; - } - } - if (getEffectiveSliderMode () == CSliderMode::Ramp && !handleRect.pointInside (where)) { impl->rampTimer = owned (new CVSTGUITimer ([this] (CVSTGUITimer*) { doRamping (); }, 16)); @@ -504,28 +495,23 @@ CMouseEventResult CSliderBase::onMouseMoved (CPoint& where, const CButtonState& } //------------------------------------------------------------------------ -bool CSliderBase::onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, - const CButtonState& buttons) +void CSliderBase::onMouseWheelEvent (MouseWheelEvent& event) { - if (!getMouseEnabled ()) - return false; - - if ((isStyleHorizontal () && axis == kMouseWheelAxisY) || - (!isStyleHorizontal () && axis == kMouseWheelAxisX)) - return false; + auto distance = isStyleHorizontal () ? event.deltaX : event.deltaY; + if (distance == 0.) + return; onMouseWheelEditing (this); - float _distance = distance; if (isStyleHorizontal ()) - _distance *= -1.f; + distance *= -1.; if (isInverseStyle ()) - _distance *= -1.f; + distance *= -1.; float normValue = getValueNormalized (); - if (buttons & kZoomModifier) - normValue += 0.1f * _distance * getWheelInc (); + if (buttonStateFromEventModifiers (event.modifiers) & kZoomModifier) + normValue += 0.1f * static_cast (distance) * getWheelInc (); else - normValue += _distance * getWheelInc (); + normValue += static_cast (distance) * getWheelInc (); setValueNormalized (normValue); @@ -536,31 +522,33 @@ bool CSliderBase::onWheel (const CPoint& where, const CMouseWheelAxis& axis, con valueChanged (); } - return true; + event.consumed = true; } //------------------------------------------------------------------------ -int32_t CSliderBase::onKeyDown (VstKeyCode& keyCode) +void CSliderBase::onKeyboardEvent (KeyboardEvent& event) { - switch (keyCode.virt) + if (event.type != EventType::KeyDown) + return; + switch (event.virt) { - case VKEY_UP: - case VKEY_RIGHT: - case VKEY_DOWN: - case VKEY_LEFT: + case VirtualKey::Up: [[fallthrough]]; + case VirtualKey::Right: [[fallthrough]]; + case VirtualKey::Down: [[fallthrough]]; + case VirtualKey::Left: { float distance = 1.f; bool isInverse = isInverseStyle (); - if ((keyCode.virt == VKEY_DOWN && !isInverse) || - (keyCode.virt == VKEY_UP && isInverse) || - (keyCode.virt == VKEY_LEFT && !isInverse) || - (keyCode.virt == VKEY_RIGHT && isInverse)) + if ((event.virt == VirtualKey::Down && !isInverse) || + (event.virt == VirtualKey::Up && isInverse) || + (event.virt == VirtualKey::Left && !isInverse) || + (event.virt == VirtualKey::Right && isInverse)) { distance = -distance; } float normValue = getValueNormalized (); - if (mapVstKeyModifier (keyCode.modifier) & kZoomModifier) + if (buttonStateFromEventModifiers (event.modifiers) & kZoomModifier) normValue += 0.1f * distance * getWheelInc (); else normValue += distance * getWheelInc (); @@ -579,19 +567,19 @@ int32_t CSliderBase::onKeyDown (VstKeyCode& keyCode) // end of edit parameter endEdit (); } - return 1; + event.consumed = true; } - case VKEY_ESCAPE: + case VirtualKey::Escape: { if (isEditing ()) { onMouseCancel (); - return 1; + event.consumed = true; } break; } + default: break; } - return -1; } //------------------------------------------------------------------------ diff --git a/vstgui/lib/controls/cslider.h b/vstgui/lib/controls/cslider.h index 009a75aa7..74c5b2168 100644 --- a/vstgui/lib/controls/cslider.h +++ b/vstgui/lib/controls/cslider.h @@ -63,9 +63,8 @@ class CSliderBase : public CControl, protected CMouseWheelEditingSupport CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseCancel () override; - bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, - const CButtonState& buttons) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onMouseWheelEvent (MouseWheelEvent& event) override; + void onKeyboardEvent (KeyboardEvent& event) override; void setViewSize (const CRect& rect, bool invalid) override; diff --git a/vstgui/lib/controls/csplashscreen.cpp b/vstgui/lib/controls/csplashscreen.cpp index 031a92e57..cfbe370aa 100644 --- a/vstgui/lib/controls/csplashscreen.cpp +++ b/vstgui/lib/controls/csplashscreen.cpp @@ -8,6 +8,7 @@ #include "../cframe.h" #include "../animation/animations.h" #include "../animation/timingfunctions.h" +#include "../events.h" namespace VSTGUI { @@ -109,11 +110,17 @@ void CSplashScreen::draw (CDrawContext *pContext) } //------------------------------------------------------------------------ -bool CSplashScreen::hitTest (const CPoint& where, const CButtonState& buttons) +bool CSplashScreen::hitTest (const CPoint& where, const Event& event) { - bool result = CView::hitTest (where, buttons); - if (result && !(buttons & kLButton)) - return false; + bool result = CView::hitTest (where, event); + if (result) + { + if (auto mouseEvent = asMouseEvent (event)) + { + if (!mouseEvent->buttonState.isLeft ()) + return false; + } + } return result; } diff --git a/vstgui/lib/controls/csplashscreen.h b/vstgui/lib/controls/csplashscreen.h index 6fd83828a..61b7ccdd2 100644 --- a/vstgui/lib/controls/csplashscreen.h +++ b/vstgui/lib/controls/csplashscreen.h @@ -23,7 +23,7 @@ class CSplashScreen : public CControl, public IControlListener CSplashScreen (const CSplashScreen& splashScreen); void draw (CDrawContext*) override; - bool hitTest (const CPoint& where, const CButtonState& buttons = -1) override; + bool hitTest (const CPoint& where, const Event& event) override; //----------------------------------------------------------------------------- /// @name CSplashScreen Methods diff --git a/vstgui/lib/controls/cswitch.cpp b/vstgui/lib/controls/cswitch.cpp index c4d9515de..c90f89ef2 100644 --- a/vstgui/lib/controls/cswitch.cpp +++ b/vstgui/lib/controls/cswitch.cpp @@ -6,6 +6,7 @@ #include "../cdrawcontext.h" #include "../cbitmap.h" #include "../cvstguitimer.h" +#include "../events.h" namespace VSTGUI { @@ -81,11 +82,6 @@ CMouseEventResult CSwitchBase::onMouseDown (CPoint& where, const CButtonState& b beginEdit (); - if (checkDefaultValue (buttons)) - { - endEdit (); - return kMouseDownEventHandledButDontNeedMovedOrUpEvents; - } mouseStartValue = getValue (); return onMouseMoved (where, buttons); } @@ -212,36 +208,34 @@ float CVerticalSwitch::calcNormFromPoint (const CPoint& where) const } //------------------------------------------------------------------------ -int32_t CVerticalSwitch::onKeyDown (VstKeyCode& keyCode) +void CVerticalSwitch::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.modifier == 0) + if (event.type != EventType::KeyDown || event.modifiers.empty () == false) + return; + float norm = getValueNormalized (); + int32_t currentIndex = normalizedToIndex (norm); + if (event.virt == VirtualKey::Up && currentIndex > 0) { - float norm = getValueNormalized (); - int32_t currentIndex = normalizedToIndex (norm); - if (keyCode.virt == VKEY_UP && currentIndex > 0) - { - --currentIndex; - norm = indexToNormalized (currentIndex); - value = (getMax () - getMin ()) * norm + getMin (); - bounceValue (); - } - if (keyCode.virt == VKEY_DOWN && currentIndex < (getNumSubPixmaps () - 1)) - { - ++currentIndex; - norm = indexToNormalized (currentIndex); - value = (getMax () - getMin ()) * norm + getMin (); - bounceValue (); - } - if (isDirty ()) - { - invalid (); - beginEdit (); - valueChanged (); - endEdit (); - return 1; - } + --currentIndex; + norm = indexToNormalized (currentIndex); + value = (getMax () - getMin ()) * norm + getMin (); + bounceValue (); + } + if (event.virt == VirtualKey::Down && currentIndex < (getNumSubPixmaps () - 1)) + { + ++currentIndex; + norm = indexToNormalized (currentIndex); + value = (getMax () - getMin ()) * norm + getMin (); + bounceValue (); + } + if (isDirty ()) + { + invalid (); + beginEdit (); + valueChanged (); + endEdit (); + event.consumed = true; } - return -1; } //------------------------------------------------------------------------ @@ -308,37 +302,34 @@ float CHorizontalSwitch::calcNormFromPoint (const CPoint& where) const static_cast (getNumSubPixmaps () - 1); } -//------------------------------------------------------------------------ -int32_t CHorizontalSwitch::onKeyDown (VstKeyCode& keyCode) +void CHorizontalSwitch::onKeyboardEvent(KeyboardEvent &event) { - if (keyCode.modifier == 0) + if (event.type != EventType::KeyDown || event.modifiers.empty () == false) + return; + float norm = getValueNormalized (); + int32_t currentIndex = normalizedToIndex (norm); + if (event.virt == VirtualKey::Left && currentIndex > 0) { - float norm = getValueNormalized (); - int32_t currentIndex = normalizedToIndex (norm); - if (keyCode.virt == VKEY_LEFT && currentIndex > 0) - { - --currentIndex; - norm = indexToNormalized (currentIndex); - value = (getMax () - getMin ()) * norm + getMin (); - bounceValue (); - } - if (keyCode.virt == VKEY_RIGHT && currentIndex < (getNumSubPixmaps () - 1)) - { - ++currentIndex; - norm = indexToNormalized (currentIndex); - value = (getMax () - getMin ()) * norm + getMin (); - bounceValue (); - } - if (isDirty ()) - { - invalid (); - beginEdit (); - valueChanged (); - endEdit (); - return 1; - } + --currentIndex; + norm = indexToNormalized (currentIndex); + value = (getMax () - getMin ()) * norm + getMin (); + bounceValue (); + } + if (event.virt == VirtualKey::Right && currentIndex < (getNumSubPixmaps () - 1)) + { + ++currentIndex; + norm = indexToNormalized (currentIndex); + value = (getMax () - getMin ()) * norm + getMin (); + bounceValue (); + } + if (isDirty ()) + { + invalid (); + beginEdit (); + valueChanged (); + endEdit (); + event.consumed = true; } - return -1; } //------------------------------------------------------------------------ @@ -515,54 +506,51 @@ CMouseEventResult CRockerSwitch::onMouseMoved (CPoint& where, const CButtonState } //------------------------------------------------------------------------ -int32_t CRockerSwitch::onKeyDown (VstKeyCode& keyCode) +void CRockerSwitch::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.modifier == 0) + if (event.modifiers.empty () == false) + return; + if (event.type == EventType::KeyDown) { - if (style & kHorizontal && (keyCode.virt == VKEY_LEFT || keyCode.virt == VKEY_RIGHT)) + if (style & kHorizontal && + (event.virt == VirtualKey::Left || event.virt == VirtualKey::Right)) { - value = keyCode.virt == VKEY_LEFT ? getMin () : getMax (); + value = event.virt == VirtualKey::Left ? getMin () : getMax (); invalid (); beginEdit (); valueChanged (); - return 1; + event.consumed = true; } - if (style & kVertical && (keyCode.virt == VKEY_UP || keyCode.virt == VKEY_DOWN)) + if (style & kVertical && (event.virt == VirtualKey::Up || event.virt == VirtualKey::Down)) { - value = keyCode.virt == VKEY_UP ? getMin () : getMax (); + value = event.virt == VirtualKey::Up ? getMin () : getMax (); invalid (); beginEdit (); valueChanged (); - return 1; + event.consumed = true; } } - return -1; -} - -//------------------------------------------------------------------------ -int32_t CRockerSwitch::onKeyUp (VstKeyCode& keyCode) -{ - if (keyCode.modifier == 0) + else if (event.type == EventType::KeyUp) { - if (keyCode.virt == VKEY_LEFT || keyCode.virt == VKEY_RIGHT) + if ((style & kHorizontal && + (event.virt == VirtualKey::Left || event.virt == VirtualKey::Right)) || + (style & kVertical && (event.virt == VirtualKey::Up || event.virt == VirtualKey::Down))) { value = (getMax () - getMin ()) / 2.f + getMin (); invalid (); valueChanged (); endEdit (); - - return 1; + event.consumed = true; } } - return -1; } //------------------------------------------------------------------------ -bool CRockerSwitch::onWheel (const CPoint& where, const CMouseWheelAxis& axis, - const float& distance, const CButtonState& buttons) +void CRockerSwitch::onMouseWheelEvent (MouseWheelEvent& event) { - if (!getMouseEnabled ()) - return false; + auto distance = event.deltaY; + if (distance == 0.) + return; if (distance > 0) value = getMin (); @@ -582,7 +570,7 @@ bool CRockerSwitch::onWheel (const CPoint& where, const CMouseWheelAxis& axis, resetValueTimer->stop (); resetValueTimer->start (); - return true; + event.consumed = true; } //------------------------------------------------------------------------ diff --git a/vstgui/lib/controls/cswitch.h b/vstgui/lib/controls/cswitch.h index 20c7ce172..5a854dcde 100644 --- a/vstgui/lib/controls/cswitch.h +++ b/vstgui/lib/controls/cswitch.h @@ -77,7 +77,7 @@ class CVerticalSwitch : public CSwitchBase CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps, CCoord heightOfOneImage, int32_t iMaxPositions, CBitmap* background, const CPoint& offset = CPoint (0, 0)); CVerticalSwitch (const CVerticalSwitch& vswitch); - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; CLASS_METHODS(CVerticalSwitch, CControl) protected: @@ -100,7 +100,7 @@ class CHorizontalSwitch : public CSwitchBase CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps, CCoord heightOfOneImage, int32_t iMaxPositions, CBitmap* background, const CPoint& offset = CPoint (0, 0)); CHorizontalSwitch (const CHorizontalSwitch& hswitch); - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; CLASS_METHODS(CHorizontalSwitch, CControl) protected: @@ -136,14 +136,13 @@ class CRockerSwitch : public CControl, public IMultiBitmapControl CRockerSwitch (const CRockerSwitch& rswitch); void draw (CDrawContext*) override; - bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; + void onMouseWheelEvent (MouseWheelEvent& event) override; CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseCancel () override; - int32_t onKeyDown (VstKeyCode& keyCode) override; - int32_t onKeyUp (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; bool sizeToFit () override; diff --git a/vstgui/lib/controls/ctextedit.cpp b/vstgui/lib/controls/ctextedit.cpp index b3320e67a..e884bd99d 100644 --- a/vstgui/lib/controls/ctextedit.cpp +++ b/vstgui/lib/controls/ctextedit.cpp @@ -5,6 +5,7 @@ #include "ctextedit.h" #include "itexteditlistener.h" #include "../cframe.h" +#include "../events.h" #include "../platform/iplatformframe.h" #include @@ -237,27 +238,26 @@ CMouseEventResult CTextEdit::onMouseDown (CPoint& where, const CButtonState& but } //------------------------------------------------------------------------ -int32_t CTextEdit::onKeyDown (VstKeyCode& keyCode) +void CTextEdit::onKeyboardEvent (KeyboardEvent& event) { - if (platformControl) + if (!platformControl || event.type != EventType::KeyDown) + return; + + if (event.virt == VirtualKey::Escape) { - if (keyCode.virt == VKEY_ESCAPE) - { - bWasReturnPressed = false; - platformControl->setText (text); - getFrame ()->setFocusView (nullptr); - looseFocus (); - return 1; - } - else if (keyCode.virt == VKEY_RETURN) - { - bWasReturnPressed = true; - getFrame ()->setFocusView (nullptr); - looseFocus (); - return 1; - } + bWasReturnPressed = false; + platformControl->setText (text); + getFrame ()->setFocusView (nullptr); + looseFocus (); + event.consumed = true; + } + else if (event.virt == VirtualKey::Return) + { + bWasReturnPressed = true; + getFrame ()->setFocusView (nullptr); + looseFocus (); + event.consumed = true; } - return -1; } //------------------------------------------------------------------------ @@ -296,21 +296,21 @@ void CTextEdit::platformLooseFocus (bool returnPressed) } //------------------------------------------------------------------------ -bool CTextEdit::platformOnKeyDown (const VstKeyCode& key) +void CTextEdit::platformOnKeyboardEvent (KeyboardEvent& event) { - if (dynamic_cast (getFrame ())->platformOnKeyDown (const_cast (key)) == 1) - return true; - if (key.virt == VKEY_RETURN) + dynamic_cast (getFrame ())->platformOnEvent (event); + if (event.consumed) + return; + if (event.virt == VirtualKey::Return) { platformLooseFocus (true); - return true; + event.consumed = true; } - else if (key.virt == VKEY_ESCAPE) + else if (event.virt == VirtualKey::Escape) { platformLooseFocus (false); - return true; + event.consumed = true; } - return false; } //------------------------------------------------------------------------ @@ -381,8 +381,7 @@ void CTextEdit::looseFocus () CBaseObjectGuard guard (this); - auto _platformControl = platformControl; - platformControl = nullptr; + auto _platformControl = std::move (platformControl); updateText (_platformControl); _platformControl = nullptr; diff --git a/vstgui/lib/controls/ctextedit.h b/vstgui/lib/controls/ctextedit.h index a8f9a4421..657b6640e 100644 --- a/vstgui/lib/controls/ctextedit.h +++ b/vstgui/lib/controls/ctextedit.h @@ -74,7 +74,7 @@ class CTextEdit : public CTextLabel, public IPlatformTextEditCallback void draw (CDrawContext* pContext) override; CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; void takeFocus () override; void looseFocus () override; @@ -104,7 +104,7 @@ class CTextEdit : public CTextLabel, public IPlatformTextEditCallback CRect platformGetVisibleSize () const override; CPoint platformGetTextInset () const override { return getTextInset (); } void platformLooseFocus (bool returnPressed) override; - bool platformOnKeyDown (const VstKeyCode& key) override; + void platformOnKeyboardEvent (KeyboardEvent& event) override; void platformTextDidChange () override; bool platformIsSecureTextEdit () override; diff --git a/vstgui/lib/controls/ctextlabel.h b/vstgui/lib/controls/ctextlabel.h index a763a68e0..b939fa73c 100644 --- a/vstgui/lib/controls/ctextlabel.h +++ b/vstgui/lib/controls/ctextlabel.h @@ -65,7 +65,9 @@ class CTextLabel : public CParamDisplay void freeText (); void calculateTruncatedText (); +#if VSTGUI_ENABLE_DEPRECATED_METHODS bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override { return false; } +#endif TextTruncateMode textTruncateMode; UTF8String text; diff --git a/vstgui/lib/controls/cxypad.cpp b/vstgui/lib/controls/cxypad.cpp index b38643176..dee7fee3f 100644 --- a/vstgui/lib/controls/cxypad.cpp +++ b/vstgui/lib/controls/cxypad.cpp @@ -4,6 +4,7 @@ #include "cxypad.h" #include "../cdrawcontext.h" +#include "../events.h" namespace VSTGUI { @@ -67,34 +68,32 @@ void CXYPad::drawBack (CDrawContext* context, CBitmap* newBack) } //------------------------------------------------------------------------ -CMouseEventResult CXYPad::onMouseDown (CPoint& where, const CButtonState& buttons) +void CXYPad::onMouseDownEvent (MouseDownEvent& event) { - if (buttons.isLeftButton ()) + if (event.buttonState.isLeft ()) { invalidMouseWheelEditTimer (this); mouseStartValue = getValue (); - mouseChangeStartPoint = where; + mouseChangeStartPoint = event.mousePosition; mouseChangeStartPoint.offset (-getViewSize ().left - getRoundRectRadius () / 2., -getViewSize ().top - getRoundRectRadius () / 2.); beginEdit (); - return onMouseMoved (where, buttons); + onMouseMove (event); } - return kMouseEventNotHandled; } //------------------------------------------------------------------------ -CMouseEventResult CXYPad::onMouseUp (CPoint& where, const CButtonState& buttons) +void CXYPad::onMouseUpEvent (MouseUpEvent& event) { if (isEditing ()) { endEdit (); - return kMouseEventHandled; + event.consumed = true; } - return kMouseEventNotHandled; } //------------------------------------------------------------------------ -CMouseEventResult CXYPad::onMouseCancel () +void CXYPad::onMouseCancelEvent (MouseCancelEvent &event) { if (isEditing ()) { @@ -105,60 +104,73 @@ CMouseEventResult CXYPad::onMouseCancel () invalid (); } endEdit (); + event.consumed = true; } - return kMouseEventHandled; } //------------------------------------------------------------------------ -CMouseEventResult CXYPad::onMouseMoved (CPoint& where, const CButtonState& buttons) +void CXYPad::onMouseMoveEvent (MouseMoveEvent& event) { - if (buttons.isLeftButton () && isEditing ()) + if (event.buttonState.isLeft () && isEditing ()) { - if (stopTrackingOnMouseExit) + onMouseMove (event); + } +} + +//------------------------------------------------------------------------ +void CXYPad::onMouseMove (MouseDownUpMoveEvent& event) +{ + auto where = event.mousePosition; + if (stopTrackingOnMouseExit) + { + if (!hitTest (where, event)) { - if (!hitTest (where, buttons)) - { - endEdit (); - return kMouseMoveEventHandledButDontNeedMoreEvents; - } + endEdit (); + event.ignoreFollowUpMoveAndUpEvents (true); + event.consumed = true; + return; } - float x, y; - CCoord width = getWidth() - getRoundRectRadius (); - CCoord height = getHeight() - getRoundRectRadius (); - where.offset (-getViewSize ().left - getRoundRectRadius () / 2., - -getViewSize ().top - getRoundRectRadius () / 2.); + } + float x, y; + CCoord width = getWidth() - getRoundRectRadius (); + CCoord height = getHeight() - getRoundRectRadius (); + where.offset (-getViewSize ().left - getRoundRectRadius () / 2., + -getViewSize ().top - getRoundRectRadius () / 2.); - x = (float)(where.x / width); - y = (float)(where.y / height); + x = (float)(where.x / width); + y = (float)(where.y / height); - boundValues (x, y); - setValue (calculateValue (x, y)); - if (isDirty ()) - { - valueChanged (); - invalid (); - } - lastMouseChangePoint = where; - return kMouseEventHandled; + boundValues (x, y); + setValue (calculateValue (x, y)); + if (isDirty ()) + { + valueChanged (); + invalid (); } - return kMouseEventNotHandled; + lastMouseChangePoint = where; + event.consumed = true; } //------------------------------------------------------------------------ -bool CXYPad::onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& _distance, - const CButtonState& buttons) +void CXYPad::onMouseWheelEvent (MouseWheelEvent& event) { float x, y; calculateXY (getValue (), x, y); - auto distance = _distance * getWheelInc (); - if (buttons & kMouseWheelInverted) - distance = -distance; - if (buttons & kShift) - distance *= 0.1f; - if (axis == kMouseWheelAxisX) - x += distance; - else - y += distance; + + auto distanceX = static_cast (event.deltaX) * getWheelInc (); + auto distanceY = static_cast (event.deltaY) * getWheelInc (); + if (event.flags & MouseWheelEvent::DirectionInvertedFromDevice) + { + distanceX *= -1.f; + distanceY *= -1.f; + } + if (event.modifiers.has (ModifierKey::Shift)) + { + distanceX *= 0.1f; + distanceY *= 0.1f; + } + x += distanceX; + y += distanceY; boundValues (x, y); onMouseWheelEditing (this); setValue (calculateValue (x, y)); @@ -167,25 +179,22 @@ bool CXYPad::onWheel (const CPoint& where, const CMouseWheelAxis& axis, const fl invalid (); valueChanged (); } - return true; + event.consumed = true; } //------------------------------------------------------------------------ -int32_t CXYPad::onKeyDown (VstKeyCode& keyCode) +void CXYPad::onKeyboardEvent (KeyboardEvent& event) { - switch (keyCode.virt) + if (event.type != EventType::KeyDown) + return; + if (event.virt == VirtualKey::Escape) { - case VKEY_ESCAPE: + if (isEditing ()) { - if (isEditing ()) - { - onMouseCancel (); - return 1; - } - break; + onMouseCancel (); + event.consumed = true; } } - return -1; } //------------------------------------------------------------------------ @@ -201,4 +210,16 @@ void CXYPad::boundValues (float& x, float& y) y = 1.f; } +//------------------------------------------------------------------------ +void CXYPad::setDefaultValue (float val) +{ + CControl::setDefaultValue (calculateValue (val, val)); +} + +//------------------------------------------------------------------------ +void CXYPad::setDefaultValues (float x, float y) +{ + CControl::setDefaultValue (calculateValue (x, y)); +} + } // VSTGUI diff --git a/vstgui/lib/controls/cxypad.h b/vstgui/lib/controls/cxypad.h index d5798f549..8ea5880d6 100644 --- a/vstgui/lib/controls/cxypad.h +++ b/vstgui/lib/controls/cxypad.h @@ -25,12 +25,17 @@ class CXYPad : public CParamDisplay, protected CMouseWheelEditingSupport void draw (CDrawContext* context) override; void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr) override; - CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseCancel () override; - bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onMouseDownEvent (MouseDownEvent& event) override; + void onMouseUpEvent (MouseUpEvent& event) override; + void onMouseMoveEvent (MouseMoveEvent& event) override; + void onMouseCancelEvent (MouseCancelEvent& event) override; + void onMouseWheelEvent (MouseWheelEvent& event) override; + void onKeyboardEvent (KeyboardEvent& event) override; + + /** set default value so that x and y default to val */ + void setDefaultValue (float val) override; + /** set default value for x and y */ + void setDefaultValues (float x, float y); static float calculateValue (float x, float y) { @@ -46,6 +51,8 @@ class CXYPad : public CParamDisplay, protected CMouseWheelEditingSupport } protected: + void onMouseMove (MouseDownUpMoveEvent& event); + void setMin (float val) override { } void setMax (float val) override { } diff --git a/vstgui/lib/cscrollview.cpp b/vstgui/lib/cscrollview.cpp index c638b663b..98602a11a 100644 --- a/vstgui/lib/cscrollview.cpp +++ b/vstgui/lib/cscrollview.cpp @@ -8,6 +8,7 @@ #include "cframe.h" #include "dragging.h" #include "controls/cscrollbar.h" +#include "events.h" #include /// @cond ignore @@ -340,6 +341,24 @@ CScrollView::CScrollView (const CScrollView& v) CViewContainer::addView (sc, nullptr); } +//----------------------------------------------------------------------------- +CRect CScrollView::calculateOptimalContainerSize () const +{ + auto size = getViewSize (); + size.originize (); + if (!(style & kDontDrawFrame)) + size.inset (1, 1); + if (!(style & kAutoHideScrollbars) && !(style & kOverlayScrollbars)) + { + if (style & kHorizontalScrollbar) + size.right -= scrollbarWidth; + if (style & kVerticalScrollbar) + size.bottom -= scrollbarWidth; + } + size.originize (); + return size; +} + //----------------------------------------------------------------------------- void CScrollView::recalculateSubViews () { @@ -746,17 +765,15 @@ void CScrollView::drawBackgroundRect (CDrawContext *pContext, const CRect& _upda } //----------------------------------------------------------------------------- -bool CScrollView::onWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &distance, const CButtonState &buttons) +void CScrollView::onMouseWheelEvent (MouseWheelEvent& event) { - bool result = CViewContainer::onWheel (where, axis, distance, buttons); - if (!result) - { - if (vsb && axis == kMouseWheelAxisY) - result = vsb->onWheel (where, axis, distance, buttons); - else if (hsb && axis == kMouseWheelAxisX) - result = hsb->onWheel (where, axis, distance, buttons); - } - return result; + CViewContainer::onMouseWheelEvent (event); + if (event.consumed) + return; + if (vsb && event.deltaY != 0.) + vsb->onMouseWheelEvent (event); + if (hsb && event.deltaX != 0.) + hsb->onMouseWheelEvent (event); } //----------------------------------------------------------------------------- diff --git a/vstgui/lib/cscrollview.h b/vstgui/lib/cscrollview.h index 93a09ba98..0e40f3608 100644 --- a/vstgui/lib/cscrollview.h +++ b/vstgui/lib/cscrollview.h @@ -82,6 +82,9 @@ class CScrollView : public CViewContainer, public IControlListener, public ViewL /** set scrollview to show rect */ virtual void makeRectVisible (const CRect& rect); + + /** calculate the maximum rect where the scrollbars are inactive */ + CRect calculateOptimalContainerSize () const; //@} // overwrite @@ -92,7 +95,7 @@ class CScrollView : public CViewContainer, public IControlListener, public ViewL CView* getView (uint32_t index) const override; bool changeViewZOrder (CView* view, uint32_t newIndex) override; void drawBackgroundRect (CDrawContext* pContext, const CRect& _updateRect) override; - bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; + void onMouseWheelEvent (MouseWheelEvent& event) override; void valueChanged (CControl* pControl) override; void setTransparency (bool val) override; void setBackgroundColor (const CColor& color) override; diff --git a/vstgui/lib/csplitview.cpp b/vstgui/lib/csplitview.cpp index 035685afa..c02a47029 100644 --- a/vstgui/lib/csplitview.cpp +++ b/vstgui/lib/csplitview.cpp @@ -5,6 +5,7 @@ #include "csplitview.h" #include "cframe.h" #include "cdrawcontext.h" +#include "events.h" #include "../uidescription/icontroller.h" #include @@ -19,13 +20,15 @@ class CSplitViewSeparatorView : public CViewContainer void drawRect (CDrawContext *pContext, const CRect& r) override; - CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; + void onMouseDownEvent (MouseDownEvent& event) override; + void onMouseUpEvent (MouseUpEvent& event) override; + void onMouseMoveEvent (MouseMoveEvent& event) override; - CMouseEventResult onMouseEntered (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override; - bool hitTestSubViews (const CPoint& where, const CButtonState& buttons = -1) override; + void mouseMoved (MouseEvent& event); + + void onMouseEnterEvent (MouseEnterEvent& event) override; + void onMouseExitEvent (MouseExitEvent& event) override; + bool hitTestSubViews (const CPoint& where, const Event& event) override; bool removed (CView* parent) override; protected: @@ -758,96 +761,114 @@ void CSplitViewSeparatorView::drawRect (CDrawContext *pContext, const CRect& r) } //------------------------------------------------------------------------ -bool CSplitViewSeparatorView::hitTestSubViews (const CPoint& where, const CButtonState& buttons) +bool CSplitViewSeparatorView::hitTestSubViews (const CPoint& where, const Event& event) { - return hitTest (where, buttons); + return hitTest (where, event); } //----------------------------------------------------------------------------- -CMouseEventResult CSplitViewSeparatorView::onMouseDown (CPoint& where, const CButtonState& buttons) +void CSplitViewSeparatorView::onMouseDownEvent (MouseDownEvent& event) { - if (CViewContainer::hitTestSubViews (where, buttons)) + if (CViewContainer::hitTestSubViews (event.mousePosition, event)) { - return CViewContainer::onMouseDown (where, buttons); + CViewContainer::onMouseDownEvent (event); } - if (buttons.isLeftButton ()) + else if (event.buttonState.isLeft ()) { setBit (flags, ISplitViewSeparatorDrawer::kMouseDown, true); - lastMousePos = where; + lastMousePos = event.mousePosition; startSize = getViewSize (); invalid (); - return onMouseMoved (where, buttons); + mouseMoved (event); } - return kMouseEventNotHandled; } //----------------------------------------------------------------------------- -CMouseEventResult CSplitViewSeparatorView::onMouseUp (CPoint& where, const CButtonState& buttons) +void CSplitViewSeparatorView::onMouseUpEvent (MouseUpEvent& event) { if (getMouseDownView ()) - return CViewContainer::onMouseUp (where, buttons); - if (hasBit (flags, ISplitViewSeparatorDrawer::kMouseDown)) + CViewContainer::onMouseUpEvent (event); + else if (hasBit (flags, ISplitViewSeparatorDrawer::kMouseDown)) { setBit (flags, ISplitViewSeparatorDrawer::kMouseDown, false); invalid (); - return kMouseEventHandled; + event.consumed = true; } - return kMouseEventNotHandled; } //----------------------------------------------------------------------------- -CMouseEventResult CSplitViewSeparatorView::onMouseMoved (CPoint& where, const CButtonState& buttons) +void CSplitViewSeparatorView::onMouseMoveEvent (MouseMoveEvent& event) { if (getMouseDownView ()) - return CViewContainer::onMouseMoved (where, buttons); + CViewContainer::onMouseMoveEvent (event); + else + mouseMoved (event); +} + +//----------------------------------------------------------------------------- +void CSplitViewSeparatorView::mouseMoved (MouseEvent& event) +{ if (hasBit (flags, ISplitViewSeparatorDrawer::kMouseDown)) { - if (where != lastMousePos) + if (event.mousePosition != lastMousePos) { CRect newSize (startSize); if (style == CSplitView::kHorizontal) - newSize.offset (where.x - lastMousePos.x, 0); + newSize.offset (event.mousePosition.x - lastMousePos.x, 0); else - newSize.offset (0, where.y - lastMousePos.y); + newSize.offset (0, event.mousePosition.y - lastMousePos.y); auto* splitView = static_cast (getParentView ()); if (splitView) splitView->requestNewSeparatorSize (this, newSize); } + event.consumed = true; } else if (!hasBit (flags, ISplitViewSeparatorDrawer::kMouseOver)) { - if (!CViewContainer::hitTestSubViews (where, buttons) && hitTest (where)) - onMouseEntered (where, buttons); + if (!CViewContainer::hitTestSubViews (event.mousePosition, event) && hitTest (event.mousePosition, event)) + { + MouseEnterEvent enterEvent (event.mousePosition, event.buttonState, event.modifiers); + onMouseEnterEvent (enterEvent); + if (enterEvent.consumed) + event.consumed = true; + } } else if (hasBit (flags, ISplitViewSeparatorDrawer::kMouseOver)) { - if (CViewContainer::hitTestSubViews (where, buttons)) - onMouseExited (where, buttons); + if (CViewContainer::hitTestSubViews (event.mousePosition, event)) + { + MouseExitEvent exitEvent (event.mousePosition, event.buttonState, event.modifiers); + onMouseExitEvent (exitEvent); + if (exitEvent.consumed) + event.consumed = true; + } } - return kMouseEventHandled; } //----------------------------------------------------------------------------- -CMouseEventResult CSplitViewSeparatorView::onMouseEntered (CPoint& where, const CButtonState& buttons) +void CSplitViewSeparatorView::onMouseEnterEvent (MouseEnterEvent& event) { - if (CViewContainer::hitTestSubViews (where, buttons)) - return kMouseEventHandled; + if (CViewContainer::hitTestSubViews (event.mousePosition, event)) + { + event.consumed = true; + return; + } setBit (flags, ISplitViewSeparatorDrawer::kMouseOver, true); invalid (); if (style == CSplitView::kHorizontal) getFrame ()->setCursor (kCursorHSize); else getFrame ()->setCursor (kCursorVSize); - return kMouseEventHandled; + event.consumed = true; } //----------------------------------------------------------------------------- -CMouseEventResult CSplitViewSeparatorView::onMouseExited (CPoint& where, const CButtonState& buttons) +void CSplitViewSeparatorView::onMouseExitEvent (MouseExitEvent& event) { setBit (flags, ISplitViewSeparatorDrawer::kMouseOver, false); invalid (); getFrame ()->setCursor (kCursorDefault); - return kMouseEventHandled; + event.consumed = true; } //----------------------------------------------------------------------------- diff --git a/vstgui/lib/cview.cpp b/vstgui/lib/cview.cpp index 7c2546a88..5dd5d6438 100644 --- a/vstgui/lib/cview.cpp +++ b/vstgui/lib/cview.cpp @@ -12,6 +12,7 @@ #include "idatapackage.h" #include "iviewlistener.h" #include "malloc.h" +#include "events.h" #include "animation/animator.h" #include "../uidescription/icontroller.h" #include "platform/iplatformframe.h" @@ -146,8 +147,6 @@ std::unique_ptr IdleViewUpdater::gInstance; } // CViewInternal -uint32_t CView::idleRate = 30; - /// @endcond UTF8StringPtr kDegreeSymbol = "\xC2\xB0"; @@ -178,12 +177,17 @@ struct CView::Impl { using ViewAttributes = std::unordered_map>; using ViewListenerDispatcher = DispatchList; - using ViewMouseListenerDispatcher = DispatchList; - + using ViewEventListenerDispatcher = DispatchList; + ViewAttributes attributes; std::unique_ptr viewListeners; + std::unique_ptr viewEventListeners; +#if VSTGUI_ENABLE_DEPRECATED_METHODS +#include "private/disabledeprecatedmessage.h" + using ViewMouseListenerDispatcher = DispatchList; std::unique_ptr viewMouseListener; - +#include "private/enabledeprecatedmessage.h" +#endif CRect size; int32_t viewFlags {0}; int32_t autosizeFlags {kAutosizeNone}; @@ -236,10 +240,12 @@ void CView::beforeDelete () }); vstgui_assert (pImpl->viewListeners->empty (), "View listeners not empty"); } +#if VSTGUI_ENABLE_DEPRECATED_METHODS if (pImpl->viewMouseListener) { vstgui_assert (pImpl->viewMouseListener->empty (), "View mouse listeners not empty"); } +#endif vstgui_assert (isAttached () == false, "View is still attached"); @@ -264,7 +270,6 @@ void CView::beforeDelete () CViewInternal::gNbCView--; CViewInternal::gViewList.remove (this); #endif - CBaseObject::beforeDelete (); } //----------------------------------------------------------------------------- @@ -353,11 +358,18 @@ void CView::setMouseEnabled (bool state) { setDirty (true); } + if (pImpl->viewListeners) + pImpl->viewListeners->forEach ( + [&] (IViewListener* listener) { listener->viewOnMouseEnabled (this, state); }); +#if VSTGUI_ENABLE_DEPRECATED_METHODS if (pImpl->viewMouseListener) { +#include "private/disabledeprecatedmessage.h" pImpl->viewMouseListener->forEach ( [&] (IViewMouseListener* listener) { listener->viewOnMouseEnabled (this, state); }); +#include "private/enabledeprecatedmessage.h" } +#endif } } @@ -463,6 +475,257 @@ bool CView::removed (CView* parent) return true; } +//------------------------------------------------------------------------ +void CView::onMouseDownEvent (MouseDownEvent& event) +{ + auto buttonState = buttonStateFromMouseEvent (event); + switch (onMouseDown (event.mousePosition, buttonState)) + { + case kMouseEventHandled: + { + event.consumed = true; + break; + } + case kMouseDownEventHandledButDontNeedMovedOrUpEvents: + { + event.consumed = true; + event.ignoreFollowUpMoveAndUpEvents (true); + break; + } + case kMouseEventNotHandled: [[fallthrough]]; + case kMouseEventNotImplemented: [[fallthrough]]; + case kMouseMoveEventHandledButDontNeedMoreEvents: break; + } +} + +//------------------------------------------------------------------------ +void CView::onMouseMoveEvent (MouseMoveEvent& event) +{ + auto buttonState = buttonStateFromMouseEvent (event); + switch (onMouseMoved (event.mousePosition, buttonState)) + { + case kMouseEventHandled: + { + event.consumed = true; + break; + } + case kMouseMoveEventHandledButDontNeedMoreEvents: + { + event.consumed = true; + event.ignoreFollowUpMoveAndUpEvents (true); + break; + } + case kMouseEventNotHandled: [[fallthrough]]; + case kMouseEventNotImplemented: [[fallthrough]]; + case kMouseDownEventHandledButDontNeedMovedOrUpEvents: break; + } +} + +//------------------------------------------------------------------------ +void CView::onMouseUpEvent (MouseUpEvent& event) +{ + auto buttonState = buttonStateFromMouseEvent (event); + switch (onMouseUp (event.mousePosition, buttonState)) + { + case kMouseEventHandled: + { + event.consumed = true; + break; + } + case kMouseDownEventHandledButDontNeedMovedOrUpEvents: [[fallthrough]]; + case kMouseEventNotHandled: [[fallthrough]]; + case kMouseEventNotImplemented: [[fallthrough]]; + case kMouseMoveEventHandledButDontNeedMoreEvents: break; + } +} + +//------------------------------------------------------------------------ +void CView::onMouseCancelEvent (MouseCancelEvent& event) +{ + switch (onMouseCancel ()) + { + case kMouseEventHandled: + { + event.consumed = true; + break; + } + case kMouseDownEventHandledButDontNeedMovedOrUpEvents: [[fallthrough]]; + case kMouseEventNotHandled: [[fallthrough]]; + case kMouseEventNotImplemented: [[fallthrough]]; + case kMouseMoveEventHandledButDontNeedMoreEvents: break; + } +} + +//------------------------------------------------------------------------ +void CView::onMouseEnterEvent (MouseEnterEvent& event) +{ + auto buttonState = buttonStateFromMouseEvent (event); + switch (onMouseEntered (event.mousePosition, buttonState)) + { + case kMouseEventHandled: + { + event.consumed = true; + break; + } + case kMouseDownEventHandledButDontNeedMovedOrUpEvents: [[fallthrough]]; + case kMouseEventNotHandled: [[fallthrough]]; + case kMouseEventNotImplemented: [[fallthrough]]; + case kMouseMoveEventHandledButDontNeedMoreEvents: break; + } +} + +//------------------------------------------------------------------------ +void CView::onMouseExitEvent (MouseExitEvent& event) +{ + auto buttonState = buttonStateFromMouseEvent (event); + switch (onMouseExited (event.mousePosition, buttonState)) + { + case kMouseEventHandled: + { + event.consumed = true; + break; + } + case kMouseDownEventHandledButDontNeedMovedOrUpEvents: [[fallthrough]]; + case kMouseEventNotHandled: [[fallthrough]]; + case kMouseEventNotImplemented: [[fallthrough]]; + case kMouseMoveEventHandledButDontNeedMoreEvents: break; + } +} + +//------------------------------------------------------------------------ +void CView::onMouseWheelEvent (MouseWheelEvent& event) +{ +#if VSTGUI_ENABLE_DEPRECATED_METHODS +#include "private/disabledeprecatedmessage.h" + auto buttons = buttonStateFromEventModifiers (event.modifiers); + if (event.flags & MouseWheelEvent::DirectionInvertedFromDevice) + buttons |= kMouseWheelInverted; + if (event.deltaX != 0.) + { + if (onWheel (event.mousePosition, kMouseWheelAxisX, static_cast (event.deltaX), buttons)) + event.consumed = true; + } + if (event.deltaY != 0.) + { + if (onWheel (event.mousePosition, kMouseWheelAxisY, static_cast (event.deltaY), buttons)) + event.consumed = true; + } +#include "private/enabledeprecatedmessage.h" +#endif +} + +//------------------------------------------------------------------------ +void CView::onKeyboardEvent (KeyboardEvent& event) +{ +#if VSTGUI_ENABLE_DEPRECATED_METHODS +#include "private/disabledeprecatedmessage.h" + auto keyCode = toVstKeyCode (event); + + switch (event.type) + { + case EventType::KeyDown: + { + if (onKeyDown (keyCode) == 1) + event.consumed = true; + break; + } + case EventType::KeyUp: + { + if (onKeyUp (keyCode) == 1) + event.consumed = true; + break; + } + default: + { + vstgui_assert (false); + break; + } + } +#include "private/enabledeprecatedmessage.h" +#endif +} + +//------------------------------------------------------------------------ +void CView::onZoomGestureEvent (ZoomGestureEvent& event) +{ +} + +//------------------------------------------------------------------------ +void CView::dispatchEvent (Event& event) +{ + if (pImpl->viewEventListeners) + { + pImpl->viewEventListeners->forEachReverse ( + [&] (IViewEventListener* listener) { + listener->viewOnEvent (this, event); + return event.consumed; + }, + [] (bool consumed) { return consumed; }); + if (event.consumed) + return; + } + + switch (event.type) + { + case EventType::MouseDown: + { + auto& mouseDownEvent = castMouseDownEvent (event); + onMouseDownEvent (mouseDownEvent); + break; + } + case EventType::MouseMove: + { + auto& mouseMoveEvent = castMouseMoveEvent (event); + onMouseMoveEvent (mouseMoveEvent); + break; + } + case EventType::MouseUp: + { + auto& mouseUpEvent = castMouseUpEvent (event); + onMouseUpEvent (mouseUpEvent); + break; + } + case EventType::MouseCancel: + { + auto& mouseCancelEvent = castMouseCancelEvent (event); + onMouseCancelEvent (mouseCancelEvent); + break; + } + case EventType::MouseEnter: + { + auto& mouseEnterEvent = castMouseEnterEvent (event); + onMouseEnterEvent (mouseEnterEvent); + break; + } + case EventType::MouseExit: + { + auto& mouseExitEvent = castMouseExitEvent (event); + onMouseExitEvent (mouseExitEvent); + break; + } + case EventType::MouseWheel: + { + auto& wheelEvent = castMouseWheelEvent (event); + onMouseWheelEvent (wheelEvent); + break; + } + case EventType::ZoomGesture: + { + auto& zoomGesture = castZoomGestureEvent (event); + onZoomGestureEvent (zoomGesture); + break; + } + case EventType::KeyUp: [[fallthrough]]; + case EventType::KeyDown: + { + auto& keyEvent = castKeyboardEvent (event); + onKeyboardEvent (keyEvent); + break; + } + case EventType::Unknown: vstgui_assert (false); break; + } +} + //----------------------------------------------------------------------------- /** * @param where mouse location of mouse down @@ -502,6 +765,26 @@ CMouseEventResult CView::onMouseCancel () return kMouseEventNotImplemented; } +//------------------------------------------------------------------------ +bool CView::hitTest (const CPoint& where, const Event& event) +{ +#if VSTGUI_ENABLE_DEPRECATED_METHODS +#include "private/disabledeprecatedmessage.h" + auto mouseEvent = asMouseEvent (event); + return hitTest (where, mouseEvent ? buttonStateFromMouseEvent (*mouseEvent) : -1); +#include "private/enabledeprecatedmessage.h" +#else + if (auto path = getHitTestPath ()) + { + CPoint p (where); + p.offset (-getViewSize ().left, -getViewSize ().top); + return path->hitTest (p); + } + return getMouseableArea ().pointInside (where); +#endif +} + +#if VSTGUI_ENABLE_DEPRECATED_METHODS //----------------------------------------------------------------------------- /** * @param where location @@ -518,6 +801,7 @@ bool CView::hitTest (const CPoint& where, const CButtonState& buttons) } return getMouseableArea ().pointInside (where); } +#endif //----------------------------------------------------------------------------- /** @@ -598,6 +882,7 @@ void CView::draw (CDrawContext* pContext) setDirty (false); } +#if VSTGUI_ENABLE_DEPRECATED_METHODS //------------------------------------------------------------------------ /** * @param where location @@ -631,7 +916,6 @@ int32_t CView::onKeyUp (VstKeyCode& keyCode) return -1; } -#if VSTGUI_ENABLE_DEPRECATED_METHODS //------------------------------------------------------------------------------ /** * a drag can only be started from within onMouseDown @@ -1007,18 +1291,23 @@ void CView::addAnimation (IdStringPtr name, Animation::IAnimationTarget* target, vstgui_assert (isAttached (), "to start an animation, the view needs to be attached"); if (auto frame = getFrame ()) { +#include "private/disabledeprecatedmessage.h" frame->getAnimator ()->addAnimation (this, name, target, timingFunction, notificationObject); +#include "private/enabledeprecatedmessage.h" } } #endif //----------------------------------------------------------------------------- -void CView::addAnimation (IdStringPtr name, Animation::IAnimationTarget* target, Animation::ITimingFunction* timingFunction, const Animation::DoneFunction& doneFunc) +void CView::addAnimation (IdStringPtr name, Animation::IAnimationTarget* target, + Animation::ITimingFunction* timingFunction, + const Animation::DoneFunction& doneFunc, bool callDoneOnCancel) { vstgui_assert (isAttached (), "to start an animation, the view needs to be attached"); if (auto frame = getFrame ()) { - frame->getAnimator ()->addAnimation (this, name, target, timingFunction, doneFunc); + frame->getAnimator ()->addAnimation (this, name, target, timingFunction, doneFunc, + callDoneOnCancel); } } @@ -1060,8 +1349,10 @@ void CView::dumpInfo () void CView::registerViewListener (IViewListener* listener) { if (!pImpl->viewListeners) + { pImpl->viewListeners = std::unique_ptr (new Impl::ViewListenerDispatcher); + } pImpl->viewListeners->add (listener); } @@ -1073,6 +1364,27 @@ void CView::unregisterViewListener (IViewListener* listener) pImpl->viewListeners->remove (listener); } +//----------------------------------------------------------------------------- +void CView::registerViewEventListener (IViewEventListener* listener) +{ + if (!pImpl->viewEventListeners) + { + pImpl->viewEventListeners = std::unique_ptr ( + new Impl::ViewEventListenerDispatcher); + } + pImpl->viewEventListeners->add (listener); +} + +//----------------------------------------------------------------------------- +void CView::unregisterViewEventListener (IViewEventListener* listener) +{ + if (!pImpl->viewEventListeners) + return; + pImpl->viewEventListeners->remove (listener); +} + +#if VSTGUI_ENABLE_DEPRECATED_METHODS +#include "private/disabledeprecatedmessage.h" //------------------------------------------------------------------------ void CView::registerViewMouseListener (IViewMouseListener* listener) { @@ -1089,6 +1401,7 @@ void CView::unregisterViewMouseListener (IViewMouseListener* listener) return; pImpl->viewMouseListener->remove (listener); } +#include "private/enabledeprecatedmessage.h" //----------------------------------------------------------------------------- CMouseEventResult CView::callMouseListener (MouseListenerCall type, CPoint pos, CButtonState buttons) @@ -1096,6 +1409,7 @@ CMouseEventResult CView::callMouseListener (MouseListenerCall type, CPoint pos, CMouseEventResult result = kMouseEventNotHandled; if (!pImpl->viewMouseListener) return result; +#include "private/disabledeprecatedmessage.h" pImpl->viewMouseListener->forEachReverse ( [&] (IViewMouseListener* l) { switch (type) @@ -1115,6 +1429,7 @@ CMouseEventResult CView::callMouseListener (MouseListenerCall type, CPoint pos, } return false; }); +#include "private/enabledeprecatedmessage.h" return result; } @@ -1123,13 +1438,16 @@ void CView::callMouseListenerEnteredExited (bool mouseEntered) { if (!pImpl->viewMouseListener) return; +#include "private/disabledeprecatedmessage.h" pImpl->viewMouseListener->forEachReverse ([&] (IViewMouseListener* l) { if (mouseEntered) l->viewOnMouseEntered (this); else l->viewOnMouseExited (this); }); +#include "private/enabledeprecatedmessage.h" } +#endif //----------------------------------------------------------------------------- SharedPointer CView::getDropTarget () diff --git a/vstgui/lib/cview.h b/vstgui/lib/cview.h index 7b5d7b7e9..c95d80cad 100644 --- a/vstgui/lib/cview.h +++ b/vstgui/lib/cview.h @@ -81,33 +81,41 @@ class CView : public CBaseObject bool isVisible () const { return hasViewFlag (kVisible) && getAlphaValue () > 0.f; } //@} + //----------------------------------------------------------------------------- + /// @name Event Handling Methods + //----------------------------------------------------------------------------- + //@{ + + /** dispatch an event + * + * the event is then dispatched to the event listeners and then to one of the event methods. + */ + virtual void dispatchEvent (Event& event); + + //@} + //----------------------------------------------------------------------------- /// @name Mouse Methods //----------------------------------------------------------------------------- //@{ /** called when a mouse down event occurs */ - virtual CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons); - /** called when a mouse up event occurs */ - virtual CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons); + virtual void onMouseDownEvent (MouseDownEvent& event); /** called when a mouse move event occurs */ - virtual CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons); + virtual void onMouseMoveEvent (MouseMoveEvent& event); + /** called when a mouse up event occurs */ + virtual void onMouseUpEvent (MouseUpEvent& event); /** called when mouse tracking should be canceled */ - virtual CMouseEventResult onMouseCancel (); + virtual void onMouseCancelEvent (MouseCancelEvent& event); /** called when the mouse enters this view */ - virtual CMouseEventResult onMouseEntered (CPoint& where, const CButtonState& buttons) {return kMouseEventNotImplemented;} + virtual void onMouseEnterEvent (MouseEnterEvent& event); /** called when the mouse leaves this view */ - virtual CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) {return kMouseEventNotImplemented;} - - void setHitTestPath (CGraphicsPath* path); - /** check if where hits this view */ - virtual bool hitTest (const CPoint& where, const CButtonState& buttons = -1); - - VSTGUI_DEPRECATED( - /** \deprecated never called anymore, please override the method below for wheel handling */ - virtual bool onWheel (const CPoint& where, const float& distance, const CButtonState& buttons) final { return false; }) - /** called if a mouse wheel event is happening over this view */ - virtual bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons); + virtual void onMouseExitEvent (MouseExitEvent& event); + + /** called when a mouse wheel event occurs */ + virtual void onMouseWheelEvent (MouseWheelEvent& event); + /** called when a zoom gesture event occurs */ + virtual void onZoomGestureEvent (ZoomGestureEvent& event); /** turn on/off mouse usage for this view */ virtual void setMouseEnabled (bool bEnable = true); @@ -115,14 +123,62 @@ class CView : public CBaseObject bool getMouseEnabled () const { return hasViewFlag (kMouseEnabled); } /** set the area in which the view reacts to the mouse */ - void setMouseableArea (const CRect& rect); + virtual void setMouseableArea (const CRect& rect); + /** get the area in which the view reacts to the mouse */ + CRect getMouseableArea () const; + + //----------------------------------------------------------------------------- + // The following non deprecated mouse methods will still work for the next few years, + // but you should if possible refactor your own code to use the newer event methods + //----------------------------------------------------------------------------- + /** do not use any longer. if possible refactor your classes to use the newer mouse event methods above. */ + virtual CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons); + /** do not use any longer. if possible refactor your classes to use the newer mouse event methods above. */ + virtual CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons); + /** do not use any longer. if possible refactor your classes to use the newer mouse event methods above. */ + virtual CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons); + /** do not use any longer. if possible refactor your classes to use the newer mouse event methods above. */ + virtual CMouseEventResult onMouseCancel (); + /** do not use any longer. if possible refactor your classes to use the newer mouse event methods above. */ + virtual CMouseEventResult onMouseEntered (CPoint& where, const CButtonState& buttons) {return kMouseEventNotImplemented;} + /** do not use any longer. if possible refactor your classes to use the newer mouse event methods above. */ + virtual CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) {return kMouseEventNotImplemented;} + + VSTGUI_DEPRECATED( + /** \deprecated never called anymore, please use onMouseWheelEvent instead */ + virtual bool onWheel (const CPoint& where, const float& distance, const CButtonState& buttons) final { return false; }) + VSTGUI_DEPRECATED_MSG( + /** \deprecated please use onMouseWheelEvent instead */ + virtual bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons);, "Use CView::onMouseWheelEvent instead") + VSTGUI_DEPRECATED( /** get the area in which the view reacts to the mouse */ CRect& getMouseableArea (CRect& rect) const;) - /** get the area in which the view reacts to the mouse */ - CRect getMouseableArea () const; //@} + //----------------------------------------------------------------------------- + /// @name Hit testing Methods + //----------------------------------------------------------------------------- + //@{ + void setHitTestPath (CGraphicsPath* path); + /** check if where hits this view + * + * the default behaviour is to return true if where is inside the view size of this view, but if you set a hit test path + * the path is checked if the point lies in its boundaries. + */ + virtual bool hitTest (const CPoint& where, const Event& event = noEvent ()); + + VSTGUI_DEPRECATED_MSG( + /** \deprecated check if where hits this view + * + * the default behaviour is to return true if where is inside the view size of this view, but if you set a hit test path + * the path is checked if the point lies in its boundaries. + */ + virtual bool hitTest (const CPoint& where, const CButtonState& buttons);, "Use the other hitTest method") + + //@} + + #if VSTGUI_TOUCH_EVENT_HANDLING //----------------------------------------------------------------------------- /// @name Touch Event Handling Methods @@ -144,18 +200,28 @@ class CView : public CBaseObject /** set a custom drop target */ void setDropTarget (const SharedPointer& dt); + VSTGUI_DEPRECATED( /** \deprecated start a drag operation. See CDropSource to create the source data package */ - VSTGUI_DEPRECATED(DragResult doDrag (IDataPackage* source, const CPoint& offset = CPoint (0, 0), CBitmap* dragBitmap = nullptr);) + DragResult doDrag (IDataPackage* source, const CPoint& offset = CPoint (0, 0), CBitmap* dragBitmap = nullptr);) //@} //----------------------------------------------------------------------------- /// @name Keyboard Methods //----------------------------------------------------------------------------- //@{ + + /** called when a keyboard event is dispatched to this view + * + * This happens normally only if the view is the focus view. + */ + virtual void onKeyboardEvent (KeyboardEvent& event); + + VSTGUI_DEPRECATED_MSG( /** called if a key down event occurs and this view has focus */ - virtual int32_t onKeyDown (VstKeyCode& keyCode); + virtual int32_t onKeyDown (VstKeyCode& keyCode);, "Use CView::onKeyboardEvent instead") + VSTGUI_DEPRECATED_MSG( /** called if a key up event occurs and this view has focus */ - virtual int32_t onKeyUp (VstKeyCode& keyCode); + virtual int32_t onKeyUp (VstKeyCode& keyCode);, "Use CView::onKeyboardEvent instead") //@} //----------------------------------------------------------------------------- @@ -297,8 +363,9 @@ class CView : public CBaseObject //@{ VSTGUI_DEPRECATED(void addAnimation (IdStringPtr name, Animation::IAnimationTarget* target, Animation::ITimingFunction* timingFunction, CBaseObject* notificationObject);) void addAnimation (IdStringPtr name, Animation::IAnimationTarget* target, - Animation::ITimingFunction* timingFunction, - const Animation::DoneFunction& doneFunc = nullptr); + Animation::ITimingFunction* timingFunction, + const Animation::DoneFunction& doneFunc = nullptr, + bool callDoneOnCancel = false); void removeAnimation (IdStringPtr name); void removeAllAnimations (); //@} @@ -316,7 +383,7 @@ class CView : public CBaseObject /** returns if the view wants idle callback or not */ bool wantsIdle () const { return hasViewFlag (kWantsIdle); } /** global idle rate in Hz, defaults to 30 Hz*/ - static uint32_t idleRate; + inline static uint32_t idleRate = 30; //@} /** whether this view wants to be informed if the window's active state changes */ @@ -332,9 +399,14 @@ class CView : public CBaseObject //@{ void registerViewListener (IViewListener* listener); void unregisterViewListener (IViewListener* listener); - - void registerViewMouseListener (IViewMouseListener* listener); - void unregisterViewMouseListener (IViewMouseListener* listener); + + void registerViewEventListener (IViewEventListener* listener); + void unregisterViewEventListener (IViewEventListener* listener); + + VSTGUI_DEPRECATED_MSG( + void registerViewMouseListener (IViewMouseListener* listener);, "Use registerViewListener instead") + VSTGUI_DEPRECATED_MSG( + void unregisterViewMouseListener (IViewMouseListener* listener);, "Use unregisterViewListener instead") //@} //----------------------------------------------------------------------------- @@ -356,6 +428,7 @@ class CView : public CBaseObject virtual CViewContainer* asViewContainer () { return nullptr; } virtual const CViewContainer* asViewContainer () const { return nullptr; } +#if VSTGUI_ENABLE_DEPRECATED_METHODS enum class MouseListenerCall { MouseDown, @@ -365,7 +438,8 @@ class CView : public CBaseObject }; CMouseEventResult callMouseListener (MouseListenerCall type, CPoint pos, CButtonState buttons); void callMouseListenerEnteredExited (bool mouseEntered); - +#endif + // overwrites CMessageResult notify (CBaseObject* sender, IdStringPtr message) override; void beforeDelete () override; diff --git a/vstgui/lib/cviewcontainer.cpp b/vstgui/lib/cviewcontainer.cpp index 558ee2aee..a64c6fda3 100644 --- a/vstgui/lib/cviewcontainer.cpp +++ b/vstgui/lib/cviewcontainer.cpp @@ -15,6 +15,8 @@ #include "controls/ccontrol.h" #include "dragging.h" #include "dispatchlist.h" +#include "events.h" +#include "finally.h" #include #include @@ -202,10 +204,16 @@ void CViewContainer::setMouseDownView (CView* view) // make sure the old mouse down view get a mouse cancel or if not implemented a mouse up if (auto cvc = mouseDownView->asViewContainer ()) cvc->setMouseDownView (nullptr); - else if (mouseDownView->onMouseCancel () == kMouseEventNotImplemented) + else { - CPoint p = mouseDownView->getViewSize ().getTopLeft () - CPoint (10, 10); - mouseDownView->onMouseUp (p, 0); + MouseCancelEvent cancelEvent; + mouseDownView->dispatchEvent (cancelEvent); + if (!cancelEvent.consumed) + { + MouseUpEvent upEvent; + upEvent.mousePosition = mouseDownView->getViewSize ().getTopLeft () - CPoint (10, 10); + mouseDownView->dispatchEvent (upEvent); + } } } setAttribute (kCViewContainerMouseDownViewAttribute, view); @@ -938,10 +946,10 @@ bool CViewContainer::checkUpdateRect (CView* view, const CRect& rect) //----------------------------------------------------------------------------- /** * @param where point - * @param buttons mouse button and modifier state + * @param event current event * @return true if any sub view accepts the hit */ -bool CViewContainer::hitTestSubViews (const CPoint& where, const CButtonState& buttons) +bool CViewContainer::hitTestSubViews (const CPoint& where, const Event& event) { CPoint where2 (where); where2.offset (-getViewSize ().left, -getViewSize ().top); @@ -950,11 +958,11 @@ bool CViewContainer::hitTestSubViews (const CPoint& where, const CButtonState& b for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it) { const auto& pV = *it; - if (pV && pV->isVisible () && pV->getMouseEnabled () && pV->hitTest (where2, buttons)) + if (pV && pV->isVisible () && pV->getMouseEnabled () && pV->hitTest (where2, event)) { if (auto container = pV->asViewContainer ()) { - if (container->hitTestSubViews (where2, buttons)) + if (container->hitTestSubViews (where2, event)) return true; } else @@ -964,47 +972,101 @@ bool CViewContainer::hitTestSubViews (const CPoint& where, const CButtonState& b return false; } -//----------------------------------------------------------------------------- -/** - * @param where point - * @param buttons mouse button and modifier state - * @return true if container accepts the hit - */ -bool CViewContainer::hitTest (const CPoint& where, const CButtonState& buttons) +#if VSTGUI_ENABLE_DEPRECATED_METHODS +//------------------------------------------------------------------------ +bool CViewContainer::onWheel (const CPoint& where, const CMouseWheelAxis& axis, + const float& distance, const CButtonState& buttons) { - CPoint where2 (where); - //return hitTestSubViews (where); would change default behavior - return CView::hitTest (where2, buttons); + return false; } +#endif -//----------------------------------------------------------------------------- -CMouseEventResult CViewContainer::onMouseDown (CPoint &where, const CButtonState& buttons) +//------------------------------------------------------------------------ +void CViewContainer::dispatchEventToSubViews (Event& event) { - // convert to relativ pos - CPoint where2 (where); - where2.offset (-getViewSize ().left, -getViewSize ().top); - getTransform ().inverse ().transform (where2); + if (auto mouseEvent = asMousePositionEvent (event)) + { + auto mousePos = mouseEvent->mousePosition; + auto f = finally ([&] () { mouseEvent->mousePosition = mousePos; }); + mouseEvent->mousePosition.offset (-getViewSize ().left, -getViewSize ().top); + getTransform ().inverse ().transform (mouseEvent->mousePosition); + for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; + ++it) + { + const auto& pV = *it; + if (pV && pV->isVisible () && pV->getMouseEnabled () && + pV->getMouseableArea ().pointInside (mouseEvent->mousePosition)) + { + pV->dispatchEvent (event); + if (!pV->getTransparency () || event.consumed) + return; + } + } + } +} + +//------------------------------------------------------------------------ +void CViewContainer::onMouseWheelEvent (MouseWheelEvent& event) +{ + dispatchEventToSubViews (event); +} + +//------------------------------------------------------------------------ +void CViewContainer::onZoomGestureEvent (ZoomGestureEvent& event) +{ + dispatchEventToSubViews (event); +} + +//------------------------------------------------------------------------ +void CViewContainer::onMouseDownEvent (MouseDownEvent& event) +{ + auto buttonState = buttonStateFromMouseEvent (event); + auto mouseResult = onMouseDown (event.mousePosition, buttonState); + if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented)) + { + event.consumed = true; + if (mouseResult == kMouseMoveEventHandledButDontNeedMoreEvents) + event.ignoreFollowUpMoveAndUpEvents (true); + return; + } + auto f = finally ([&, pos = event.mousePosition] () { event.mousePosition = pos; }); + event.mousePosition.offset (-getViewSize ().left, -getViewSize ().top); + getTransform ().inverse ().transform (event.mousePosition); for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it) { - auto pV = *it; - if (pV && pV->isVisible () && pV->getMouseEnabled () && pV->hitTest (where2, buttons)) + const auto& pV = *it; + if (pV && pV->isVisible () && pV->getMouseEnabled () && + pV->hitTest (event.mousePosition, event)) { - if (buttons & (kAlt | kShift | kControl | kApple | kRButton)) + if (!event.modifiers.empty ()) { - auto control = pV.cast (); - if (control && control->getListener ()) + if (auto control = pV.cast ()) { - if (control->getListener ()->controlModifierClicked (control, buttons) != 0) - return kMouseEventHandled; + if (auto listener = control->getListener ()) + { + if (listener->controlModifierClicked (control, buttonState) != 0) + { + event.consumed = true; + return; + } + } } } auto frame = getFrame (); auto previousFocusView = frame ? frame->getFocusView () : nullptr; - auto result = pV->callMouseListener (MouseListenerCall::MouseDown, where2, buttons); - if (result == kMouseEventNotHandled || result == kMouseEventNotImplemented) - result = pV->onMouseDown (where2, buttons); - if (result != kMouseEventNotHandled && result != kMouseEventNotImplemented) +#if VSTGUI_ENABLE_DEPRECATED_METHODS + mouseResult = pV->callMouseListener (MouseListenerCall::MouseDown, event.mousePosition, buttonState); + if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented)) + { + event.consumed = true; + if (mouseResult == kMouseMoveEventHandledButDontNeedMoreEvents) + event.ignoreFollowUpMoveAndUpEvents (true); + return; + } +#endif + pV->dispatchEvent (event); + if (event.consumed) { if (pV->getNbReference () >1) { @@ -1013,93 +1075,90 @@ CMouseEventResult CViewContainer::onMouseDown (CPoint &where, const CButtonState { getFrame ()->setFocusView (pV); } - - if (result == kMouseEventHandled) + if (!event.ignoreFollowUpMoveAndUpEvents ()) setMouseDownView (pV); } - return result; + return; } if (!pV->getTransparency ()) - return result; + return; } } - return kMouseEventNotHandled; } -//----------------------------------------------------------------------------- -CMouseEventResult CViewContainer::onMouseUp (CPoint &where, const CButtonState& buttons) +//------------------------------------------------------------------------ +void CViewContainer::onMouseMoveEvent (MouseMoveEvent& event) { - if (auto view = getMouseDownView ()) + auto buttonState = buttonStateFromMouseEvent (event); + auto mouseResult = onMouseMoved (event.mousePosition, buttonState); + if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented)) { - CBaseObjectGuard crg (view); - - CPoint where2 (where); - where2.offset (-getViewSize ().left, -getViewSize ().top); - getTransform ().inverse ().transform (where2); - auto mouseResult = view->callMouseListener (MouseListenerCall::MouseUp, where2, buttons); - if (mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented) - view->onMouseUp (where2, buttons); - clearMouseDownView (); - return kMouseEventHandled; + event.consumed = true; + if (mouseResult == kMouseMoveEventHandledButDontNeedMoreEvents) + event.ignoreFollowUpMoveAndUpEvents (true); + return; + } + if (auto view = shared (getMouseDownView ())) + { + auto f = finally ([&, pos = event.mousePosition] () { event.mousePosition = pos; }); + event.mousePosition.offset (-getViewSize ().left, -getViewSize ().top); + getTransform ().inverse ().transform (event.mousePosition); +#if VSTGUI_ENABLE_DEPRECATED_METHODS + mouseResult = view->callMouseListener (MouseListenerCall::MouseMoved, event.mousePosition, + buttonState); + if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented)) + { + event.consumed = true; + if (mouseResult == kMouseMoveEventHandledButDontNeedMoreEvents) + event.ignoreFollowUpMoveAndUpEvents (true); + return; + } +#endif + view->dispatchEvent (event); } - return kMouseEventNotHandled; } -//----------------------------------------------------------------------------- -CMouseEventResult CViewContainer::onMouseMoved (CPoint &where, const CButtonState& buttons) +//------------------------------------------------------------------------ +void CViewContainer::onMouseUpEvent (MouseUpEvent& event) { - if (auto view = getMouseDownView ()) + auto buttonState = buttonStateFromMouseEvent (event); + auto mouseResult = onMouseUp (event.mousePosition, buttonState); + if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented)) + { + event.consumed = true; + return; + } + if (auto view = shared (getMouseDownView ())) { - CBaseObjectGuard crg (view); - - CPoint where2 (where); - where2.offset (-getViewSize ().left, -getViewSize ().top); - getTransform ().inverse ().transform (where2); - auto mouseResult = view->callMouseListener (MouseListenerCall::MouseMoved, where2, buttons); - if (mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented) - mouseResult = view->onMouseMoved (where2, buttons); - if (mouseResult != kMouseEventHandled && mouseResult != kMouseEventNotImplemented) + auto f = finally ([&, pos = event.mousePosition] () { event.mousePosition = pos; }); + event.mousePosition.offset (-getViewSize ().left, -getViewSize ().top); + getTransform ().inverse ().transform (event.mousePosition); +#if VSTGUI_ENABLE_DEPRECATED_METHODS + mouseResult = + view->callMouseListener (MouseListenerCall::MouseUp, event.mousePosition, buttonState); + if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented)) { - clearMouseDownView (); - return kMouseEventNotHandled; + event.consumed = true; + return; } - return kMouseEventHandled; +#endif + view->dispatchEvent (event); + clearMouseDownView (); } - return kMouseEventNotHandled; } //----------------------------------------------------------------------------- -CMouseEventResult CViewContainer::onMouseCancel () +void CViewContainer::onMouseCancelEvent (MouseCancelEvent& event) { if (auto mouseDownView = getMouseDownView ()) { CBaseObjectGuard crg (mouseDownView); +#if VSTGUI_ENABLE_DEPRECATED_METHODS mouseDownView->callMouseListener (MouseListenerCall::MouseCancel, {}, 0); - auto result = mouseDownView->onMouseCancel (); +#endif + mouseDownView->dispatchEvent (event); clearMouseDownView (); - return result; - } - return kMouseEventHandled; -} - -//----------------------------------------------------------------------------- -bool CViewContainer::onWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &distance, const CButtonState &buttons) -{ - for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it) - { - const auto& pV = *it; - CPoint where2 (where); - where2.offset (-getViewSize ().left, -getViewSize ().top); - getTransform ().inverse ().transform (where2); - if (pV && pV->isVisible () && pV->getMouseEnabled () && pV->getMouseableArea ().pointInside (where2)) - { - if (pV->onWheel (where2, axis, distance, buttons)) - return true; - if (!pV->getTransparency ()) - return false; - } } - return false; } //----------------------------------------------------------------------------- @@ -1135,10 +1194,11 @@ void CViewContainer::onTouchEvent (ITouchEvent& event) { if (e.second.state == ITouchEvent::kBegan && e.second.target == 0) { - CButtonState buttons (kLButton + (e.second.tapCount > 1 ? kDoubleClick : 0)); CPoint where (e.second.location); frameToLocal (where); - if (view->hitTest (where, buttons)) + MouseDownEvent downEvent (where, MouseButton::Left); + downEvent.clickCount = e.second.tapCount; + if (view->hitTest (where, downEvent)) { view->onTouchEvent (event); break; @@ -1151,44 +1211,45 @@ void CViewContainer::onTouchEvent (ITouchEvent& event) } //----------------------------------------------------------------------------- -void CViewContainer::findSingleTouchEventTarget (ITouchEvent::Touch& event) +bool CViewContainer::findSingleTouchEventTarget (ITouchEvent::Touch& event) { vstgui_assert(event.target == 0); vstgui_assert(event.state == ITouchEvent::kBegan); - CButtonState buttons (kLButton + (event.tapCount > 1 ? kDoubleClick : 0)); CPoint where (event.location); frameToLocal (where); + + MouseDownEvent downEvent (where, MouseButton::Left); + downEvent.clickCount = event.tapCount; + ReverseViewIterator it (this); while (*it) { CView* view = *it; CBaseObjectGuard guard (view); - if (view->getMouseEnabled () && view->isVisible () && view->hitTest (where, buttons)) + if (view->getMouseEnabled () && view->isVisible () && view->hitTest (where, downEvent)) { if (auto container = view->asViewContainer ()) { - container->findSingleTouchEventTarget (event); - if (event.target != 0) - return; + if (container->findSingleTouchEventTarget (event)) + return true; } else { - CMouseEventResult result = view->onMouseDown (where, buttons); - if (result == kMouseEventHandled) + view->dispatchEvent (downEvent); + if (downEvent.ignoreFollowUpMoveAndUpEvents ()) + return true; + else if (downEvent.consumed) { event.target = view; event.targetIsSingleTouch = true; - return; - } - else if (result == kMouseDownEventHandledButDontNeedMovedOrUpEvents) - { - return; + return true; } } } it++; } + return false; } #endif diff --git a/vstgui/lib/cviewcontainer.h b/vstgui/lib/cviewcontainer.h index 6e474d67b..455eeb0bd 100644 --- a/vstgui/lib/cviewcontainer.h +++ b/vstgui/lib/cviewcontainer.h @@ -88,7 +88,7 @@ class CViewContainer : public CView /** change view z order position */ virtual bool changeViewZOrder (CView* view, uint32_t newIndex); - virtual bool hitTestSubViews (const CPoint& where, const CButtonState& buttons = -1); + virtual bool hitTestSubViews (const CPoint& where, const Event& event); /** enable or disable autosizing subviews. Per default this is enabled. */ virtual void setAutosizingEnabled (bool state); @@ -136,18 +136,22 @@ class CViewContainer : public CView // CView void draw (CDrawContext* pContext) override; void drawRect (CDrawContext* pContext, const CRect& updateRect) override; - CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; - CMouseEventResult onMouseCancel () override; - bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; - bool hitTest (const CPoint& where, const CButtonState& buttons = -1) override; + void onMouseDownEvent (MouseDownEvent& event) override; + void onMouseMoveEvent (MouseMoveEvent& event) override; + void onMouseUpEvent (MouseUpEvent& event) override; + void onMouseCancelEvent (MouseCancelEvent& event) override; + void onMouseWheelEvent (MouseWheelEvent& event) override; + void onZoomGestureEvent (ZoomGestureEvent& event) override; CMessageResult notify (CBaseObject* sender, IdStringPtr message) override; +#if VSTGUI_ENABLE_DEPRECATED_METHODS + bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, + const CButtonState& buttons) final; +#endif #if VSTGUI_TOUCH_EVENT_HANDLING virtual void onTouchEvent (ITouchEvent& event) override; virtual bool wantsMultiTouchEvents () const override { return true; } - virtual void findSingleTouchEventTarget (ITouchEvent::Touch& event); + virtual bool findSingleTouchEventTarget (ITouchEvent::Touch& event); #endif SharedPointer getDropTarget () override; @@ -179,48 +183,56 @@ class CViewContainer : public CView class Iterator { public: - explicit Iterator (const CViewContainer* container) : children (container->getChildren ()) { if (reverse) riterator = children.rbegin (); else iterator = children.begin (); } - Iterator (const Iterator& vi) : children (vi.children), iterator (vi.iterator), riterator (vi.riterator) {} - - Iterator& operator++ () + using IteratorType = typename std::conditional::type; + + explicit Iterator (const CViewContainer* container) : children (container->getChildren ()) { - if (reverse) - ++riterator; + if constexpr (reverse) + iterator = children.rbegin (); else - ++iterator; + iterator = children.begin (); + } + + explicit Iterator (const Iterator& vi) + : children (vi.children), iterator (vi.iterator) + { + } + + Iterator (Iterator&& o) : children (o.children), iterator (std::move (o.iterator)) + { + } + + Iterator& operator++ () + { + ++iterator; return *this; } - + Iterator operator++ (int) { Iterator old (*this); - if (reverse) - ++riterator; - else - ++iterator; + ++iterator; return old; } Iterator& operator-- () { - if (reverse) - --riterator; - else - --iterator; + --iterator; return *this; } CView* operator* () const { - if (reverse) - return riterator != children.rend () ? *riterator : nullptr; - return iterator != children.end () ? *iterator : nullptr; + if constexpr (reverse) + return (iterator == children.rend ()) ? nullptr : *iterator; + else + return (iterator == children.end ()) ? nullptr : *iterator; } protected: const ViewList& children; - ChildViewConstIterator iterator; - ChildViewConstReverseIterator riterator; + IteratorType iterator; }; //------------------------------------------- @@ -249,6 +261,8 @@ class CViewContainer : public CView const ViewList& getChildren () const; private: + void dispatchEventToSubViews (Event& event); + void clearMouseDownView (); CRect getLastDrawnFocus () const; void setLastDrawnFocus (CRect r); diff --git a/vstgui/lib/cvstguitimer.cpp b/vstgui/lib/cvstguitimer.cpp index 02f07851d..8d019e9c0 100644 --- a/vstgui/lib/cvstguitimer.cpp +++ b/vstgui/lib/cvstguitimer.cpp @@ -53,7 +53,6 @@ CVSTGUITimer::~CVSTGUITimer () noexcept = default; void CVSTGUITimer::beforeDelete () { stop (); - CBaseObject::beforeDelete (); } //----------------------------------------------------------------------------- diff --git a/vstgui/lib/dragging.h b/vstgui/lib/dragging.h index 0aaffe7f6..ef54675d6 100644 --- a/vstgui/lib/dragging.h +++ b/vstgui/lib/dragging.h @@ -6,7 +6,7 @@ #include "vstguifwd.h" #include "cbitmap.h" -#include "cbuttonstate.h" +#include "events.h" #include "cpoint.h" #include "idatapackage.h" #include @@ -71,7 +71,7 @@ struct DragEventData /** mouse position */ CPoint pos; /** key modifiers */ - CButtonState modifiers; + Modifiers modifiers; }; //------------------------------------------------------------------------ diff --git a/vstgui/lib/events.cpp b/vstgui/lib/events.cpp new file mode 100644 index 000000000..6ffaf797c --- /dev/null +++ b/vstgui/lib/events.cpp @@ -0,0 +1,76 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "events.h" +#include "platform/platformfactory.h" + +//------------------------------------------------------------------------ +namespace VSTGUI { +namespace EventPrivate { +static uint64_t counter = 0; +} // EventPrivate + +//------------------------------------------------------------------------ +Event::Event () noexcept +: id (++EventPrivate::counter), timestamp (getPlatformFactory ().getTicks ()) +{ +} + +//------------------------------------------------------------------------ +CButtonState buttonStateFromEventModifiers (const Modifiers& mods) +{ + CButtonState state; + if (mods.has (ModifierKey::Control)) + state |= kControl; + if (mods.has (ModifierKey::Shift)) + state |= kShift; + if (mods.has (ModifierKey::Alt)) + state |= kAlt; + return state; +} + +//------------------------------------------------------------------------ +CButtonState buttonStateFromMouseEvent (const MouseEvent& event) +{ + CButtonState state = buttonStateFromEventModifiers (event.modifiers); + if (event.buttonState.has (MouseButton::Left)) + state |= kLButton; + if (event.buttonState.has (MouseButton::Right)) + state |= kRButton; + if (event.buttonState.has (MouseButton::Middle)) + state |= kMButton; + if (event.buttonState.has (MouseButton::Fourth)) + state |= kButton4; + if (event.buttonState.has (MouseButton::Fifth)) + state |= kButton5; + if (auto downEvent = asMouseDownEvent (event)) + { + if (downEvent->clickCount > 1) + state |= kDoubleClick; + } + return state; +} + +#if VSTGUI_ENABLE_DEPRECATED_METHODS +//------------------------------------------------------------------------ +VstKeyCode toVstKeyCode (const KeyboardEvent& event) +{ + VstKeyCode keyCode {}; + keyCode.character = event.character; + keyCode.virt = toVstVirtualKey (event.virt); + if (event.modifiers.has (ModifierKey::Shift)) + keyCode.modifier |= MODIFIER_SHIFT; + if (event.modifiers.has (ModifierKey::Alt)) + keyCode.modifier |= MODIFIER_ALTERNATE; + if (event.modifiers.has (ModifierKey::Control)) + keyCode.modifier |= MODIFIER_CONTROL; + if (event.modifiers.has (ModifierKey::Super)) + keyCode.modifier |= MODIFIER_COMMAND; + return keyCode; +} +#endif + + +//------------------------------------------------------------------------ +} // VSTGUI diff --git a/vstgui/lib/events.h b/vstgui/lib/events.h new file mode 100644 index 000000000..d7b3dcd04 --- /dev/null +++ b/vstgui/lib/events.h @@ -0,0 +1,762 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "vstguifwd.h" +#include "cbuttonstate.h" +#include "cpoint.h" + +#if VSTGUI_ENABLE_DEPRECATED_METHODS +#include "vstkeycode.h" +#endif + +//------------------------------------------------------------------------ +namespace VSTGUI { + +//------------------------------------------------------------------------ +/** EventType + * @ingroup new_in_4_11 + */ +enum class EventType : uint32_t +{ + Unknown, + MouseDown, + MouseMove, + MouseUp, + MouseCancel, + MouseEnter, + MouseExit, + MouseWheel, + ZoomGesture, + KeyUp, + KeyDown, +}; + +//------------------------------------------------------------------------ +/** EventConsumeState + * @ingroup new_in_4_11 + */ +struct EventConsumeState +{ + static constexpr uint32_t NotHandled = 0; + static constexpr uint32_t Handled = 1; + static constexpr uint32_t Last = 2; + + void operator= (bool state) + { + if (state) + data |= Handled; + else + data &= ~Handled; + } + operator bool () { return data & Handled; } + + void reset () { data = NotHandled; } + + uint32_t data {NotHandled}; +}; + +//------------------------------------------------------------------------ +/** Event + * @ingroup new_in_4_11 + */ +struct Event +{ + Event () noexcept; + Event (const Event&) = delete; + Event& operator= (const Event&) = delete; + Event (Event&&) = default; + Event& operator= (Event&&) = default; + + /** Type */ + EventType type {EventType::Unknown}; + /** Unique ID*/ + uint64_t id; + /** Timestamp */ + uint64_t timestamp; + /** Consumed? If this is true, event dispatching is stopped. */ + EventConsumeState consumed; +}; + +//------------------------------------------------------------------------ +inline const Event& noEvent () +{ + static Event e; + return e; +} + +//------------------------------------------------------------------------ +/** Modifiers + * @ingroup new_in_4_11 + */ +struct Modifiers +{ + Modifiers () = default; + Modifiers (const Modifiers&) = default; + explicit Modifiers (ModifierKey modifier) : data (cast (modifier)) {} + + /** test if no modifier key is set */ + bool empty () const { return data == 0; } + /** test if modifier key is set */ + bool has (ModifierKey modifier) const { return data & cast (modifier); } + /** test if modifier key is set exclusively */ + bool is (ModifierKey modifier) const { return data == cast (modifier); } + /** test if the modifier keys are set exclusively */ + bool is (const std::initializer_list& modifiers) const + { + uint32_t d = 0; + for (auto& mod : modifiers) + d |= cast (mod); + return data == d; + } + /** test if modifier key is set */ + bool operator| (ModifierKey modifier) const { return has (modifier); } + /** test if modifier key is set exclusively */ + bool operator== (ModifierKey modifier) const { return is (modifier); } + + /** add a modifier key */ + void add (ModifierKey modifier) { data |= cast (modifier); } + /** remove a modifier key */ + void remove (ModifierKey modifier) { data &= ~cast (modifier); } + /** clear all modifiers */ + void clear () { data = 0; } + /** set to one modifier key */ + Modifiers& operator= (ModifierKey modifier) + { + data = cast (modifier); + return *this; + } + +private: + static uint32_t cast (ModifierKey mod) { return static_cast (mod); } + uint32_t data {0}; +}; + +//------------------------------------------------------------------------ +/** ModifierEvent + * @ingroup new_in_4_11 + */ +struct ModifierEvent : Event +{ + /** pressed modifiers */ + Modifiers modifiers {}; +}; + +//------------------------------------------------------------------------ +/** MousePositionEvent + * @ingroup new_in_4_11 + */ +struct MousePositionEvent : ModifierEvent +{ + CPoint mousePosition; +}; + +//------------------------------------------------------------------------ +/** MouseButton + * @ingroup new_in_4_11 + */ +enum class MouseButton : uint32_t +{ + None = 0, + Left = 1 << 1, + Middle = 1 << 2, + Right = 1 << 3, + Fourth = 1 << 4, + Fifth = 1 << 5, +}; + +//------------------------------------------------------------------------ +/** MouseEventButtonState + * @ingroup new_in_4_11 + */ +struct MouseEventButtonState +{ + bool isLeft () const { return data == MouseButton::Left; } + bool isMiddle () const { return data == MouseButton::Middle; } + bool isRight () const { return data == MouseButton::Right; } + bool is (MouseButton pos) const { return data == pos; } + bool isOther (uint32_t index) const { return data == static_cast (1 << index); } + bool has (MouseButton pos) const + { + return static_cast (static_cast (data) & static_cast (pos)); + } + bool empty () const { return data == MouseButton::None; } + + void add (MouseButton pos) + { + data = + static_cast (static_cast (data) | static_cast (pos)); + } + void set (MouseButton pos) { data = pos; } + void clear () { data = MouseButton::None; } + + MouseEventButtonState () = default; + MouseEventButtonState (const MouseEventButtonState&) = default; + MouseEventButtonState (MouseButton pos) { set (pos); } + + bool operator== (const MouseEventButtonState& other) const { return data == other.data; } + bool operator!= (const MouseEventButtonState& other) const { return data != other.data; } + +private: + MouseButton data {MouseButton::None}; +}; + +//------------------------------------------------------------------------ +/** MouseEvent + * @ingroup new_in_4_11 + */ +struct MouseEvent : MousePositionEvent +{ + MouseEventButtonState buttonState; +}; + +//------------------------------------------------------------------------ +/** MouseEnterEvent + * @ingroup new_in_4_11 + */ +struct MouseEnterEvent : MouseEvent +{ + MouseEnterEvent () { type = EventType::MouseEnter; } + MouseEnterEvent (CPoint pos, MouseEventButtonState buttons, Modifiers mods) + : MouseEnterEvent () + { + mousePosition = pos; + buttonState = buttons; + modifiers = mods; + } + MouseEnterEvent (const MouseEvent& e) + : MouseEnterEvent (e.mousePosition, e.buttonState, e.modifiers) + { + } +}; + +//------------------------------------------------------------------------ +/** MouseExitEvent + * @ingroup new_in_4_11 + */ +struct MouseExitEvent : MouseEvent +{ + MouseExitEvent () { type = EventType::MouseExit; } + MouseExitEvent (CPoint pos, MouseEventButtonState buttons, Modifiers mods) + : MouseExitEvent () + { + mousePosition = pos; + buttonState = buttons; + modifiers = mods; + } + MouseExitEvent (const MouseEvent& e) + : MouseExitEvent (e.mousePosition, e.buttonState, e.modifiers) + { + } +}; + +//------------------------------------------------------------------------ +/** MouseDownUpMoveEvent + * @ingroup new_in_4_11 + */ +struct MouseDownUpMoveEvent : MouseEvent +{ + uint32_t clickCount {0}; + + void ignoreFollowUpMoveAndUpEvents (bool state) + { + if (state) + consumed.data |= IgnoreFollowUpEventsMask; + else + consumed.data &= ~IgnoreFollowUpEventsMask; + } + + bool ignoreFollowUpMoveAndUpEvents () + { + return consumed.data & IgnoreFollowUpEventsMask; + } + + static constexpr uint32_t IgnoreFollowUpEvents = EventConsumeState::Last; + static constexpr uint32_t IgnoreFollowUpEventsMask = 1 << IgnoreFollowUpEvents; + +protected: + MouseDownUpMoveEvent () = default; + MouseDownUpMoveEvent (const CPoint& pos, MouseEventButtonState buttons, Modifiers mods = {}) + { + mousePosition = pos; + buttonState = buttons; + modifiers = mods; + } +}; + +//------------------------------------------------------------------------ +/** MouseDownEvent + * @ingroup new_in_4_11 + */ +struct MouseDownEvent : MouseDownUpMoveEvent +{ + MouseDownEvent () { type = EventType::MouseDown; } + MouseDownEvent (const CPoint& pos, MouseEventButtonState buttons) + : MouseDownUpMoveEvent (pos, buttons) + { + type = EventType::MouseDown; + } +}; + +//------------------------------------------------------------------------ +/** MouseUpEvent + * @ingroup new_in_4_11 + */ +struct MouseUpEvent : MouseDownUpMoveEvent +{ + MouseUpEvent () { type = EventType::MouseUp; } + MouseUpEvent (const CPoint& pos, MouseEventButtonState buttons) + : MouseDownUpMoveEvent (pos, buttons) + { + type = EventType::MouseUp; + } +}; + +//------------------------------------------------------------------------ +/** MouseMoveEvent + * @ingroup new_in_4_11 + */ +struct MouseMoveEvent : MouseDownUpMoveEvent +{ + MouseMoveEvent () { type = EventType::MouseMove; } + MouseMoveEvent (const CPoint& pos, MouseEventButtonState buttons = {}) + : MouseDownUpMoveEvent (pos, buttons) + { + type = EventType::MouseMove; + } + MouseMoveEvent (const MouseDownUpMoveEvent& other) + : MouseDownUpMoveEvent (other.mousePosition, other.buttonState, other.modifiers) + { + type = EventType::MouseMove; + } +}; + +//------------------------------------------------------------------------ +/** MouseCancelEvent + * @ingroup new_in_4_11 + */ +struct MouseCancelEvent : Event +{ + MouseCancelEvent () { type = EventType::MouseCancel; } +}; + +//------------------------------------------------------------------------ +/** MouseWheelEvent + * @ingroup new_in_4_11 + */ +struct MouseWheelEvent : MousePositionEvent +{ + enum Flags : uint32_t + { + /** deltaX and deltaY are inverted */ + DirectionInvertedFromDevice = 1 << 0, + /** indicates a precise scroll event where deltaX and deltaY are multiplied by 0.1. If you + * divide the deltas by 0.1 you will get exact pixel movement. + */ + PreciseDeltas = 1 << 1, + }; + CCoord deltaX {0.}; + CCoord deltaY {0.}; + + uint32_t flags {0}; + + MouseWheelEvent () { type = EventType::MouseWheel; } +}; + +//------------------------------------------------------------------------ +/** GestureEvent + * @ingroup new_in_4_11 + */ +struct GestureEvent : MousePositionEvent +{ + enum class Phase : uint32_t + { + Unknown, + Begin, + Changed, + End, + }; + + Phase phase {Phase::Unknown}; +}; + +//------------------------------------------------------------------------ +/** ZoomGestureEvent + * @ingroup new_in_4_11 + */ +struct ZoomGestureEvent : GestureEvent +{ + double zoom; + + ZoomGestureEvent () { type = EventType::ZoomGesture; } +}; + +//------------------------------------------------------------------------ +// Keyboard Events +//------------------------------------------------------------------------ +/** VirtualKey + * @ingroup new_in_4_11 + */ +enum class VirtualKey : uint32_t +{ + None = 0, + + Back, + Tab, + Clear, + Return, + Pause, + Escape, + Space, + Next, + End, + Home, + + Left, + Up, + Right, + Down, + PageUp, + PageDown, + + Select, + Print, + Enter, + Snapshot, + Insert, + Delete, + Help, + + NumPad0, + NumPad1, + NumPad2, + NumPad3, + NumPad4, + NumPad5, + NumPad6, + NumPad7, + NumPad8, + NumPad9, + + Multiply, + Add, + Separator, + Subtract, + Decimal, + Divide, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + NumLock, + Scroll, + + ShiftModifier, + ControlModifier, + AltModifier, + + Equals, + // DO NOT CHANGE THE ORDER ABOVE + +}; + +//------------------------------------------------------------------------ +/** ModifierKey + * @ingroup new_in_4_11 + */ +enum class ModifierKey : uint32_t +{ + /** the left or right shift key */ + Shift = 1 << 0, + /** the alternate key */ + Alt = 1 << 1, + /** the control key (Command key on macOS and control key on other platforms) */ + Control = 1 << 2, + /** the super key (Control key on macOS, Windows key on Windows and Super key on other + platforms)*/ + Super = 1 << 3, + + None = 0 +}; + +//------------------------------------------------------------------------ +/** KeyboardEvent + * @ingroup new_in_4_11 + */ +struct KeyboardEvent : ModifierEvent +{ + /** UTF-32 character */ + char32_t character {0}; + /** virtual key */ + VirtualKey virt {VirtualKey::None}; + /** indicates for a key down event if this is a repeated key down */ + bool isRepeat {false}; + + KeyboardEvent (EventType t = EventType::KeyDown) { type = t; } +}; + +//------------------------------------------------------------------------ +/** event as mouse position event or nullpointer if not a mouse position event + * @ingroup new_in_4_11 + */ +template , typename std::add_const_t, OutputT>::type> +inline MousePositionEventT* asMousePositionEvent (EventT& event) +{ + switch (event.type) + { + case EventType::ZoomGesture: [[fallthrough]]; + case EventType::MouseWheel: [[fallthrough]]; + case EventType::MouseDown: [[fallthrough]]; + case EventType::MouseMove: [[fallthrough]]; + case EventType::MouseUp: [[fallthrough]]; + case EventType::MouseEnter: [[fallthrough]]; + case EventType::MouseExit: return static_cast (&event); + default: break; + } + return nullptr; +} + +//------------------------------------------------------------------------ +/** event as mouse position event or nullpointer if not a mouse position event + * @ingroup new_in_4_11 + */ +template , typename std::add_const_t, OutputT>::type> +inline MouseEventT* asMouseEvent (EventT& event) +{ + switch (event.type) + { + case EventType::MouseDown: [[fallthrough]]; + case EventType::MouseMove: [[fallthrough]]; + case EventType::MouseUp: [[fallthrough]]; + case EventType::MouseEnter: [[fallthrough]]; + case EventType::MouseExit: return static_cast (&event); + default: break; + } + return nullptr; +} + +//------------------------------------------------------------------------ +/** event as mouse down event or nullpointer if not a mouse down event + * @ingroup new_in_4_11 + */ +template , typename std::add_const_t, OutputT>::type> +inline MouseDownEventT* asMouseDownEvent (EventT& event) +{ + switch (event.type) + { + case EventType::MouseDown: [[fallthrough]]; + case EventType::MouseMove: [[fallthrough]]; + case EventType::MouseUp: return static_cast (&event); + default: break; + } + return nullptr; +} + +//------------------------------------------------------------------------ +/** event as modifier event or nullpointer if not a modifier event + * @ingroup new_in_4_11 + */ +template , typename std::add_const_t, OutputT>::type> +inline ModifierEventT* asModifierEvent (EventT& event) +{ + switch (event.type) + { + case EventType::KeyDown: [[fallthrough]]; + case EventType::KeyUp: [[fallthrough]]; + case EventType::MouseEnter: [[fallthrough]]; + case EventType::MouseExit: [[fallthrough]]; + case EventType::MouseWheel: [[fallthrough]]; + case EventType::MouseDown: [[fallthrough]]; + case EventType::MouseMove: [[fallthrough]]; + case EventType::MouseUp: return static_cast (&event); + default: break; + } + return nullptr; +} + +//------------------------------------------------------------------------ +/** event as keyboard event or nullpointer if not a keyboard event + * @ingroup new_in_4_11 + */ +template , typename std::add_const_t, OutputT>::type> +inline KeyboardEventT* asKeyboardEvent (EventT& event) +{ + switch (event.type) + { + case EventType::KeyDown: [[fallthrough]]; + case EventType::KeyUp: return static_cast (&event); + default: break; + } + return nullptr; +} + +//------------------------------------------------------------------------ +/** cast event to a mouse position event + * @ingroup new_in_4_11 + */ +inline MousePositionEvent& castMousePositionEvent (Event& event) +{ + vstgui_assert (event.type >= EventType::MouseDown && event.type <= EventType::ZoomGesture); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse event + * @ingroup new_in_4_11 + */ +inline MouseEvent& castMouseEvent (Event& event) +{ + vstgui_assert (event.type >= EventType::MouseDown && event.type <= EventType::MouseExit); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse down event + * @ingroup new_in_4_11 + */ +inline MouseDownEvent& castMouseDownEvent (Event& event) +{ + vstgui_assert (event.type == EventType::MouseDown); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse move event + * @ingroup new_in_4_11 + */ +inline MouseMoveEvent& castMouseMoveEvent (Event& event) +{ + vstgui_assert (event.type == EventType::MouseMove); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse up event + * @ingroup new_in_4_11 + */ +inline MouseUpEvent& castMouseUpEvent (Event& event) +{ + vstgui_assert (event.type == EventType::MouseUp); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse enter event + * @ingroup new_in_4_11 + */ +inline MouseEnterEvent& castMouseEnterEvent (Event& event) +{ + vstgui_assert (event.type == EventType::MouseEnter); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse exit event + * @ingroup new_in_4_11 + */ +inline MouseExitEvent& castMouseExitEvent (Event& event) +{ + vstgui_assert (event.type == EventType::MouseExit); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse cancel event + * @ingroup new_in_4_11 + */ +inline MouseCancelEvent& castMouseCancelEvent (Event& event) +{ + vstgui_assert (event.type == EventType::MouseCancel); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse wheel event + * @ingroup new_in_4_11 + */ +inline MouseWheelEvent& castMouseWheelEvent (Event& event) +{ + vstgui_assert (event.type == EventType::MouseWheel); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a zoom gesture event + * @ingroup new_in_4_11 + */ +inline ZoomGestureEvent& castZoomGestureEvent (Event& event) +{ + vstgui_assert (event.type == EventType::ZoomGesture); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** cast event to a mouse wheel event + * @ingroup new_in_4_11 + */ +inline KeyboardEvent& castKeyboardEvent (Event& event) +{ + vstgui_assert (event.type == EventType::KeyDown || event.type == EventType::KeyUp); + return static_cast (event); +} + +//------------------------------------------------------------------------ +/** convert from new Modifiers to old CButtonState + * @ingroup new_in_4_11 + */ +CButtonState buttonStateFromEventModifiers (const Modifiers& mods); + +//------------------------------------------------------------------------ +/** convert from new MouseEvent to old CButtonState + * @ingroup new_in_4_11 + */ +CButtonState buttonStateFromMouseEvent (const MouseEvent& event); + +#if VSTGUI_ENABLE_DEPRECATED_METHODS +//------------------------------------------------------------------------ +/** helper function to convert from new VirtualKey to old VstVirtualKey + * + * returns 0 if key cannot be mapped + * @ingroup new_in_4_11 + */ +inline unsigned char toVstVirtualKey (VirtualKey key) +{ + auto k = static_cast (key); + if (k <= static_cast (VirtualKey::Equals)) + return static_cast (k); + return 0; +} + +inline VirtualKey fromVstVirtualKey (uint32_t key) +{ + auto k = static_cast (key); + if (k <= VirtualKey::Equals) + return k; + return VirtualKey::None; +} + +VstKeyCode toVstKeyCode (const KeyboardEvent& event); +#endif + +//------------------------------------------------------------------------ +} // VSTGUI diff --git a/vstgui/lib/finally.h b/vstgui/lib/finally.h new file mode 100644 index 000000000..f2ad292f7 --- /dev/null +++ b/vstgui/lib/finally.h @@ -0,0 +1,46 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "vstguifwd.h" + +//------------------------------------------------------------------------ +namespace VSTGUI { + +//------------------------------------------------------------------------ +template +class FinalAction +{ +public: + explicit FinalAction (Proc action) noexcept : action (std::move (action)) {} + FinalAction (FinalAction&& other) noexcept + { + action = std::move (other.action); + other.invoke (false); + } + FinalAction (const FinalAction&) = delete; + FinalAction& operator= (const FinalAction&) = delete; + FinalAction& operator= (FinalAction&&) = delete; + + ~FinalAction () noexcept + { + if (invoke) + action (); + } + +private: + Proc action; + bool invoke {true}; +}; + +//------------------------------------------------------------------------ +template +auto finally (F&& f) noexcept +{ + return FinalAction::type>::type> ( + std::forward (f)); +} +//------------------------------------------------------------------------ +} // VSTGUI diff --git a/vstgui/lib/genericstringlistdatabrowsersource.cpp b/vstgui/lib/genericstringlistdatabrowsersource.cpp index a6f95979b..fe426689d 100644 --- a/vstgui/lib/genericstringlistdatabrowsersource.cpp +++ b/vstgui/lib/genericstringlistdatabrowsersource.cpp @@ -193,16 +193,17 @@ void GenericStringListDataBrowserSource::dbDrawCell (CDrawContext* context, cons } //----------------------------------------------------------------------------- -int32_t GenericStringListDataBrowserSource::dbOnKeyDown (const VstKeyCode& _key, - CDataBrowser* browser) +void GenericStringListDataBrowserSource::dbOnKeyboardEvent (KeyboardEvent& event, CDataBrowser* browser) { - VstKeyCode key = _key; - if (key.virt == VKEY_SPACE) + if (event.type != EventType::KeyDown) + return; + + if (event.virt == VirtualKey::Space) { - key.virt = 0; - key.character = 0x20; + event.virt = VirtualKey::None; + event.character = 0x20; } - if (dataBrowser && key.virt == 0 && key.modifier == 0) + if (dataBrowser && event.virt == VirtualKey::None && event.modifiers.empty ()) { if (timer == nullptr) { @@ -214,7 +215,7 @@ int32_t GenericStringListDataBrowserSource::dbOnKeyDown (const VstKeyCode& _key, timer->stop (); timer->start (); } - keyDownFindString += static_cast (toupper (key.character)); + keyDownFindString += static_cast (toupper (event.character)); StringVector::const_iterator it = stringList->begin (); int32_t row = 0; while (it != stringList->end ()) @@ -224,13 +225,13 @@ int32_t GenericStringListDataBrowserSource::dbOnKeyDown (const VstKeyCode& _key, if (str == keyDownFindString) { dataBrowser->setSelectedRow (row, true); - return 1; + event.consumed = true; + return; } row++; ++it; } } - return -1; } //----------------------------------------------------------------------------- diff --git a/vstgui/lib/genericstringlistdatabrowsersource.h b/vstgui/lib/genericstringlistdatabrowsersource.h index 214275512..67509b3c1 100644 --- a/vstgui/lib/genericstringlistdatabrowsersource.h +++ b/vstgui/lib/genericstringlistdatabrowsersource.h @@ -92,7 +92,7 @@ class GenericStringListDataBrowserSource : public DataBrowserDelegateAdapter, pu { } - int32_t dbOnKeyDown (const VstKeyCode& key, CDataBrowser* browser) override; + void dbOnKeyboardEvent (KeyboardEvent& event, CDataBrowser* browser) override; void dbAttached (CDataBrowser* browser) override; void dbRemoved (CDataBrowser* browser) override; diff --git a/vstgui/lib/idatabrowserdelegate.h b/vstgui/lib/idatabrowserdelegate.h index 2459d9190..703c500af 100644 --- a/vstgui/lib/idatabrowserdelegate.h +++ b/vstgui/lib/idatabrowserdelegate.h @@ -5,7 +5,10 @@ #pragma once #include "dragging.h" +#if VSTGUI_ENABLE_DEPRECATED_METHODS #include "vstkeycode.h" +#include "events.h" +#endif //------------------------------------------------------------------------ namespace VSTGUI { @@ -105,7 +108,7 @@ class IDataBrowserDelegate /** @name Keyboard Handling */ /// @{ - virtual int32_t dbOnKeyDown (const VstKeyCode& key, CDataBrowser* browser) = 0; + virtual void dbOnKeyboardEvent (KeyboardEvent& event, CDataBrowser* browser) = 0; /// @} virtual ~IDataBrowserDelegate () noexcept = default; @@ -187,7 +190,17 @@ class DataBrowserDelegateAdapter : public IDataBrowserDelegate CDataBrowser* browser) override { } - int32_t dbOnKeyDown (const VstKeyCode& key, CDataBrowser* browser) override { return -1; } + void dbOnKeyboardEvent (KeyboardEvent& event, CDataBrowser* browser) override + { +#if VSTGUI_ENABLE_DEPRECATED_METHODS + if (dbOnKeyDown (toVstKeyCode (event), browser) != -1) + event.consumed = true; +#endif + } + +#if VSTGUI_ENABLE_DEPRECATED_METHODS + virtual int32_t dbOnKeyDown (const VstKeyCode& keyCode, CDataBrowser* browser) { return -1; } +#endif }; //------------------------------------------------------------------------ diff --git a/vstgui/lib/idependency.h b/vstgui/lib/idependency.h index 0e71ec34d..876853ea9 100644 --- a/vstgui/lib/idependency.h +++ b/vstgui/lib/idependency.h @@ -25,7 +25,7 @@ namespace VSTGUI { and that you must make sure that the dependent objects are alife while added as dependent. */ //---------------------------------------------------------------------------------------------------- -class IDependency +class [[deprecated("Please use listeners instead")]] IDependency { public: /** add a dependent object */ @@ -61,7 +61,7 @@ class IDependency int32_t deferChangeCount {0}; DeferedChangesSet deferedChanges; DependentList dependents; -} VSTGUI_DEPRECATED_ATTRIBUTE; +}; //---------------------------------------------------------------------------------------------------- inline void IDependency::addDependency (CBaseObject* obj) diff --git a/vstgui/lib/iviewlistener.h b/vstgui/lib/iviewlistener.h index 3d44a9b49..7a16dd532 100644 --- a/vstgui/lib/iviewlistener.h +++ b/vstgui/lib/iviewlistener.h @@ -19,47 +19,61 @@ class IViewListener public: virtual ~IViewListener () noexcept = default; + /** called when the view's size changed */ virtual void viewSizeChanged (CView* view, const CRect& oldSize) = 0; + /** called when a view was attached to a view hierarchy */ virtual void viewAttached (CView* view) = 0; + /** called when a view was removed from the view hierarchy */ virtual void viewRemoved (CView* view) = 0; + /** called when a view lost focus */ virtual void viewLostFocus (CView* view) = 0; + /** called when a view took focus */ virtual void viewTookFocus (CView* view) = 0; + /** called when a view is going to be destroyed */ virtual void viewWillDelete (CView* view) = 0; + /** called when a view's mouse handling is enabled or disabled + * @ingroup new_in_4_11 + */ + virtual void viewOnMouseEnabled (CView* view, bool state) = 0; }; //----------------------------------------------------------------------------- -/** @brief ViewContainer Listener Interface +/** @brief View Event Listener Interface + * + * @ingroup new_in_4_11 */ //----------------------------------------------------------------------------- -class IViewContainerListener +class IViewEventListener { public: - virtual ~IViewContainerListener () noexcept = default; + virtual ~IViewEventListener () noexcept = default; - virtual void viewContainerViewAdded (CViewContainer* container, CView* view) = 0; - virtual void viewContainerViewRemoved (CViewContainer* container, CView* view) = 0; - virtual void viewContainerViewZOrderChanged (CViewContainer* container, CView* view) = 0; - virtual void viewContainerTransformChanged (CViewContainer* container) = 0; + /** called on an event on a view + * + * whenever an event is dispatched to a view, the listener will be notified about it and can + * mark the event as consumed if necessary to prevent that the event is handled by the view and + * further dispatched in the view hierarchy. + */ + virtual void viewOnEvent (CView* view, Event& event) = 0; }; //----------------------------------------------------------------------------- -/** @brief View Mouse Listener Interface - * - * @ingroup new_in_4_7 +/** @brief ViewContainer Listener Interface */ //----------------------------------------------------------------------------- -class IViewMouseListener +class IViewContainerListener { public: - virtual ~IViewMouseListener () noexcept = default; + virtual ~IViewContainerListener () noexcept = default; - virtual CMouseEventResult viewOnMouseDown (CView* view, CPoint pos, CButtonState buttons) = 0; - virtual CMouseEventResult viewOnMouseUp (CView* view, CPoint pos, CButtonState buttons) = 0; - virtual CMouseEventResult viewOnMouseMoved (CView* view, CPoint pos, CButtonState buttons) = 0; - virtual CMouseEventResult viewOnMouseCancel (CView* view) = 0; - virtual void viewOnMouseEntered (CView* view) = 0; - virtual void viewOnMouseExited (CView* view) = 0; - virtual void viewOnMouseEnabled (CView* view, bool state) = 0; + /** called when a new view was added to the container */ + virtual void viewContainerViewAdded (CViewContainer* container, CView* view) = 0; + /** called when a view was removed from the container */ + virtual void viewContainerViewRemoved (CViewContainer* container, CView* view) = 0; + /** called when a view's z-order changed inside the container */ + virtual void viewContainerViewZOrderChanged (CViewContainer* container, CView* view) = 0; + /** called when the transform matrix of the container changed */ + virtual void viewContainerTransformChanged (CViewContainer* container) = 0; }; //----------------------------------------------------------------------------- @@ -75,6 +89,16 @@ class ViewListenerAdapter : public IViewListener void viewLostFocus (CView* view) override {} void viewTookFocus (CView* view) override {} void viewWillDelete (CView* view) override {} + void viewOnMouseEnabled (CView* view, bool state) override {} +}; + +//------------------------------------------------------------------------ +/** @brief View Event Listener Interface Adapter + */ +class ViewEventListenerAdapter : public IViewEventListener +{ +public: + void viewOnEvent (CView* view, Event& event) override {} }; //----------------------------------------------------------------------------- @@ -90,18 +114,38 @@ class ViewContainerListenerAdapter : public IViewContainerListener void viewContainerTransformChanged (CViewContainer* container) override {} }; +#if VSTGUI_ENABLE_DEPRECATED_METHODS //----------------------------------------------------------------------------- -/** @brief View Mouse Listener Interface Adapter +/** @brief View Mouse Listener Interface * * @ingroup new_in_4_7 */ //----------------------------------------------------------------------------- -class ViewMouseListenerAdapter : public IViewMouseListener +class [[deprecated ("Use IViewListener/IViewEventListener instead")]] IViewMouseListener { public: - CMouseEventResult viewOnMouseDown (CView* view, CPoint pos, CButtonState buttons) override - { - return kMouseEventNotImplemented; + virtual ~IViewMouseListener () noexcept = default; + + virtual CMouseEventResult viewOnMouseDown (CView* view, CPoint pos, CButtonState buttons) = 0; + virtual CMouseEventResult viewOnMouseUp (CView* view, CPoint pos, CButtonState buttons) = 0; + virtual CMouseEventResult viewOnMouseMoved (CView* view, CPoint pos, CButtonState buttons) = 0; + virtual CMouseEventResult viewOnMouseCancel (CView* view) = 0; + virtual void viewOnMouseEntered (CView* view) = 0; + virtual void viewOnMouseExited (CView* view) = 0; + virtual void viewOnMouseEnabled (CView* view, bool state) = 0; +}; + +#include "private/disabledeprecatedmessage.h" +//----------------------------------------------------------------------------- +/** @brief View Mouse Listener Interface Adapter + * + * @ingroup new_in_4_7 + */ +//----------------------------------------------------------------------------- +class [[deprecated ( + "Use ViewListenerAdapter/ViewEventListenerAdapter instead")]] ViewMouseListenerAdapter +: public IViewMouseListener {public: CMouseEventResult viewOnMouseDown ( + CView * view, CPoint pos, CButtonState buttons) override {return kMouseEventNotImplemented; } CMouseEventResult viewOnMouseUp (CView* view, CPoint pos, CButtonState buttons) override { @@ -116,5 +160,7 @@ class ViewMouseListenerAdapter : public IViewMouseListener void viewOnMouseExited (CView* view) override {} void viewOnMouseEnabled (CView* view, bool state) override {} }; +#include "private/enabledeprecatedmessage.h" +#endif // VSTGUI_ENABLE_DEPRECATED_METHODS } // VSTGUI diff --git a/vstgui/lib/pixelbuffer.cpp b/vstgui/lib/pixelbuffer.cpp index c25bf8521..e747c1223 100644 --- a/vstgui/lib/pixelbuffer.cpp +++ b/vstgui/lib/pixelbuffer.cpp @@ -3,6 +3,7 @@ // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "pixelbuffer.h" +#include "vstguibase.h" //------------------------------------------------------------------------ namespace VSTGUI { @@ -18,20 +19,30 @@ inline uint32_t shuffle (uint32_t input) static constexpr auto s3 = bs3 * 8; static constexpr auto s4 = bs4 * 8; - auto b1 = (input & 0xFF000000); - auto b2 = (input & 0x00FF0000); - auto b3 = (input & 0x0000FF00); - auto b4 = (input & 0x000000FF); + uint32_t b1 = (input & 0xFF000000); + uint32_t b2 = (input & 0x00FF0000); + uint32_t b3 = (input & 0x0000FF00); + uint32_t b4 = (input & 0x000000FF); #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4293) #endif - b1 = (s1 >= 0) ? b1 << s1 : b1 >> -s1; - b2 = (s2 >= 0) ? b2 << s2 : b2 >> -s2; - b3 = (s3 >= 0) ? b3 << s3 : b3 >> -s3; - b4 = (s4 >= 0) ? b4 << s4 : b4 >> -s4; + if constexpr (ByteOrder::kNativeByteOrder == ByteOrder::kLittleEndianByteOrder) + { + b1 = (s4 >= 0) ? b1 >> s4 : b1 << -s4; + b2 = (s3 >= 0) ? b2 >> s3 : b2 << -s3; + b3 = (s2 >= 0) ? b3 >> s2 : b3 << -s2; + b4 = (s1 >= 0) ? b4 >> s1 : b4 << -s1; + } + else + { + b1 = (s1 >= 0) ? b1 << s1 : b1 >> -s1; + b2 = (s2 >= 0) ? b2 << s2 : b2 >> -s2; + b3 = (s3 >= 0) ? b3 << s3 : b3 >> -s3; + b4 = (s4 >= 0) ? b4 << s4 : b4 >> -s4; + } #ifdef _MSC_VER #pragma warning(pop) @@ -63,7 +74,7 @@ inline void convert (uint8_t* buffer, uint32_t bytesPerRow, uint32_t width, uint } case Format::ABGR: { - *intPtr = shuffle<0, -2, 0, 2> (pixel); + *intPtr = shuffle<-2, 0, 2, 0> (pixel); break; } case Format::BGRA: @@ -73,7 +84,7 @@ inline void convert (uint8_t* buffer, uint32_t bytesPerRow, uint32_t width, uint } case Format::RGBA: { - *intPtr = shuffle<-3, 1, 1, 1> (pixel); + *intPtr = shuffle<-1, -1, -1, 3> (pixel); break; } } @@ -85,7 +96,7 @@ inline void convert (uint8_t* buffer, uint32_t bytesPerRow, uint32_t width, uint { case Format::ARGB: { - *intPtr = shuffle<0, -2, 0, 2> (pixel); + *intPtr = shuffle<-2, 0, 2, 0> (pixel); break; } case Format::ABGR: @@ -95,7 +106,7 @@ inline void convert (uint8_t* buffer, uint32_t bytesPerRow, uint32_t width, uint } case Format::BGRA: { - *intPtr = shuffle<-3, 1, 1, 1> (pixel); + *intPtr = shuffle<-1, -1, -1, 3> (pixel); break; } case Format::RGBA: @@ -112,7 +123,7 @@ inline void convert (uint8_t* buffer, uint32_t bytesPerRow, uint32_t width, uint { case Format::ARGB: { - *intPtr = shuffle<-3, 1, 1, 1> (pixel); + *intPtr = shuffle<-1, -1, -1, 3> (pixel); break; } case Format::ABGR: @@ -144,7 +155,7 @@ inline void convert (uint8_t* buffer, uint32_t bytesPerRow, uint32_t width, uint } case Format::ABGR: { - *intPtr = shuffle<-3, 1, 1, 1> (pixel); + *intPtr = shuffle<-1, -1, -1, 3> (pixel); break; } case Format::BGRA: @@ -154,7 +165,7 @@ inline void convert (uint8_t* buffer, uint32_t bytesPerRow, uint32_t width, uint } case Format::RGBA: { - *intPtr = shuffle<-2, 0, 2, 0> (pixel); + *intPtr = shuffle<0, -2, 0, 2> (pixel); break; } } diff --git a/vstgui/lib/platform/common/genericoptionmenu.cpp b/vstgui/lib/platform/common/genericoptionmenu.cpp index 6e2f0bd51..9188e6a20 100644 --- a/vstgui/lib/platform/common/genericoptionmenu.cpp +++ b/vstgui/lib/platform/common/genericoptionmenu.cpp @@ -15,6 +15,7 @@ #include "../../controls/coptionmenu.h" #include "../../controls/cscrollbar.h" #include "../../cvstguitimer.h" +#include "../../events.h" #include "../../idatabrowserdelegate.h" //------------------------------------------------------------------------ @@ -137,6 +138,7 @@ class DataSource : public DataBrowserDelegateAdapter, } }); } + void onMouseEvent (MouseEvent& event, CFrame* frame) override {} int32_t dbGetNumRows (CDataBrowser* browser) override { return menu->getNbEntries (); } int32_t dbGetNumColumns (CDataBrowser* browser) override { return 1; } @@ -169,60 +171,63 @@ class DataSource : public DataBrowserDelegateAdapter, } } - int32_t dbOnKeyDown (const VstKeyCode& key, CDataBrowser* browser) override + void dbOnKeyboardEvent (KeyboardEvent& event, CDataBrowser* browser) override { - if (key.character == 0 && key.modifier == 0) + if (event.type != EventType::KeyDown || event.character != 0 || !event.modifiers.empty ()) + return; + switch (event.virt) { - switch (key.virt) + default: return; + case VirtualKey::Down: { - case VKEY_DOWN: - { - alterSelection (browser->getSelectedRow (), 1); - return 1; - } - case VKEY_UP: - { - alterSelection (browser->getSelectedRow (), -1); - return 1; - } - case VKEY_ESCAPE: - { - clickCallback (menu, CDataBrowser::kNoSelection); - return 1; - } - case VKEY_RETURN: - case VKEY_ENTER: - { - if (clickCallback) - clickCallback (menu, browser->getSelectedRow ()); - return 1; - } - case VKEY_LEFT: + alterSelection (browser->getSelectedRow (), 1); + event.consumed = true; + return; + } + case VirtualKey::Up: + { + alterSelection (browser->getSelectedRow (), -1); + event.consumed = true; + return; + } + case VirtualKey::Escape: + { + clickCallback (menu, CDataBrowser::kNoSelection); + event.consumed = true; + return; + } + case VirtualKey::Return: + case VirtualKey::Enter: + { + if (clickCallback) + clickCallback (menu, browser->getSelectedRow ()); + event.consumed = true; + return; + } + case VirtualKey::Left: + { + if (parentDataSource) { - if (parentDataSource) - { - parentDataSource->closeSubMenu (); - return 1; - } - break; + parentDataSource->closeSubMenu (); + event.consumed = true; } - case VKEY_RIGHT: + return; + } + case VirtualKey::Right: + { + auto row = db->getSelectedRow (); + if (auto item = menu->getEntry (row)) { - auto row = db->getSelectedRow (); - if (auto item = menu->getEntry (row)) + if (auto subMenu = item->getSubmenu ()) { - if (auto subMenu = item->getSubmenu ()) - { - auto r = db->getCellBounds ({row, 0}); - openSubMenu (item, r); - return 1; - } + auto r = db->getCellBounds ({row, 0}); + openSubMenu (item, r); + event.consumed = true; } } - default: break; + return; } } - return -1; } CMouseEventResult dbOnMouseMoved (const CPoint& where, const CButtonState& buttons, int32_t row, @@ -568,12 +573,12 @@ struct GenericOptionMenu::Impl IGenericOptionMenuListener* listener {nullptr}; GenericOptionMenuTheme theme; Callback callback; - CButtonState initialButtons; + MouseEventButtonState initialButtonState; bool focusDrawingWasEnabled {false}; }; //------------------------------------------------------------------------ -GenericOptionMenu::GenericOptionMenu (CFrame* frame, CButtonState initialButtons, +GenericOptionMenu::GenericOptionMenu (CFrame* frame, MouseEventButtonState initialButtons, GenericOptionMenuTheme theme) { auto frameSize = frame->getViewSize (); @@ -582,15 +587,15 @@ GenericOptionMenu::GenericOptionMenu (CFrame* frame, CButtonState initialButtons impl = std::unique_ptr (new Impl); impl->frame = frame; - impl->initialButtons = initialButtons; impl->theme = theme; impl->container = new Impl::ContainerT (frameSize); impl->container->setZIndex (100); impl->container->setTransparency (true); - impl->container->registerViewMouseListener (this); + impl->container->registerViewEventListener (this); impl->modalViewSession = impl->frame->beginModalViewSession (impl->container); impl->focusDrawingWasEnabled = impl->frame->focusDrawingEnabled (); impl->frame->setFocusDrawingEnabled (false); + impl->initialButtonState = initialButtons; } //------------------------------------------------------------------------ @@ -616,66 +621,88 @@ void GenericOptionMenu::removeModalView (PlatformOptionMenuResult result) auto self = shared (this); impl->container->addAnimation ( - "OptionMenuDone", new AlphaValueAnimation (0.f, true), - new CubicBezierTimingFunction ( - CubicBezierTimingFunction::easyOut (impl->theme.menuAnimationTime)), - [self, result] (CView*, const IdStringPtr, IAnimationTarget*) { - if (!self->impl->container) - return; - auto callback = std::move (self->impl->callback); - self->impl->callback = nullptr; - self->impl->container->unregisterViewMouseListener (self); - if (self->impl->modalViewSession) - { + "OptionMenuDone", new AlphaValueAnimation (0.f, true), + new CubicBezierTimingFunction ( + CubicBezierTimingFunction::easyOut (impl->theme.menuAnimationTime)), + [self, result] (CView*, const IdStringPtr, IAnimationTarget*) { + if (!self->impl->container) + return; + auto callback = std::move (self->impl->callback); + self->impl->callback = nullptr; + self->impl->container->unregisterViewEventListener (self); + if (self->impl->modalViewSession) + { self->impl->frame->endModalViewSession (*self->impl->modalViewSession); self->impl->modalViewSession = {}; } - callback (self->impl->menu, result); - self->impl->frame->setFocusView (self->impl->menu); - self->impl->container = nullptr; - }); - } -} - -//------------------------------------------------------------------------ -CMouseEventResult GenericOptionMenu::viewOnMouseDown (CView* view, CPoint pos, CButtonState buttons) -{ - if (auto container = view->asViewContainer ()) - { - CViewContainer::ViewList views; - if (container->getViewsAt (pos, views, GetViewOptions ().deep ().includeInvisible ())) - { - return kMouseEventNotHandled; - } - auto self = shared (this); - self->removeModalView ({nullptr, -1}); - return kMouseDownEventHandledButDontNeedMovedOrUpEvents; + callback (self->impl->menu, result); + self->impl->frame->setFocusView (self->impl->menu); + self->impl->container = nullptr; + }); } - return kMouseEventNotHandled; } //------------------------------------------------------------------------ -CMouseEventResult GenericOptionMenu::viewOnMouseUp (CView* view, CPoint pos, CButtonState buttons) +void GenericOptionMenu::viewOnEvent (CView* view, Event& event) { - if (impl->initialButtons.isLeftButton () && buttons.isLeftButton ()) + if (event.type == EventType::MouseDown) { if (auto container = view->asViewContainer ()) { + auto& downEvent = castMouseDownEvent (event); CViewContainer::ViewList views; - if (container->getViewsAt (pos, views, GetViewOptions ().deep ().includeInvisible ())) + if (container->getViewsAt (downEvent.mousePosition, views, GetViewOptions ().deep ().includeInvisible ())) { - if (view->onMouseDown (pos, buttons) == kMouseEventHandled) - view->onMouseUp (pos, buttons); + return; } - else + auto self = shared (this); + self->removeModalView ({nullptr, -1}); + downEvent.ignoreFollowUpMoveAndUpEvents (true); + downEvent.consumed = true; + return; + } + } + else if (event.type == EventType::MouseUp) + { + auto& upEvent = castMouseUpEvent (event); + if (impl->initialButtonState == upEvent.buttonState && !impl->mouseUpTimer) + { + if (auto container = view->asViewContainer ()) { - auto self = shared (this); - self->removeModalView ({nullptr, -1}); - return kMouseDownEventHandledButDontNeedMovedOrUpEvents; + CViewContainer::ViewList views; + if (container->getViewsAt (upEvent.mousePosition, views, GetViewOptions ().deep ().includeInvisible ())) + { + auto pos = upEvent.mousePosition; + view->translateToGlobal (pos); + MouseDownEvent downEvent; + downEvent.buttonState = upEvent.buttonState; + downEvent.clickCount = 1; + for (auto& v : views) + { + downEvent.mousePosition = pos; + v->translateToLocal (downEvent.mousePosition); + v->dispatchEvent (downEvent); + if (downEvent.consumed) + { + upEvent.mousePosition = downEvent.mousePosition; + v->dispatchEvent (upEvent); + break; + } + } + event.consumed = true; + return; + } + else + { + auto self = shared (this); + self->removeModalView ({nullptr, -1}); + upEvent.ignoreFollowUpMoveAndUpEvents (true); + upEvent.consumed = true; + return; + } } } } - return kMouseEventNotHandled; } //------------------------------------------------------------------------ @@ -686,7 +713,7 @@ void GenericOptionMenu::popup (COptionMenu* optionMenu, const Callback& callback auto self = shared (this); auto clickCallback = [self] (COptionMenu* menu, int32_t index) { - self->impl->container->unregisterViewMouseListener (self); + self->impl->container->unregisterViewEventListener (self); self->removeModalView ({menu, index}); }; @@ -698,25 +725,26 @@ void GenericOptionMenu::popup (COptionMenu* optionMenu, const Callback& callback if (auto view = impl->frame->getViewAt (where, GetViewOptions ().deep ().includeInvisible ())) { - if (impl->initialButtons.getButtonState () != 0) + if (!impl->initialButtonState.empty ()) { - impl->frame->getCurrentMouseLocation (where); - view->translateToLocal (where); - view->onMouseMoved (where, impl->initialButtons); - impl->mouseUpTimer = makeOwned ( - [this, where, view] (CVSTGUITimer* timer) { - timer->stop (); - if (!impl->container || - impl->frame->getCurrentMouseButtons ().getButtonState () == 0) - return; - impl->container->registerViewMouseListener (this); - CPoint p (where); - view->translateToGlobal (p); - impl->frame->onMouseDown (p, impl->initialButtons); - }, - 200); + MouseMoveEvent moveEvent; + moveEvent.buttonState = impl->initialButtonState; + impl->frame->getCurrentMouseLocation (moveEvent.mousePosition); + view->translateToLocal (moveEvent.mousePosition); + view->dispatchEvent (moveEvent); } } + if (!impl->initialButtonState.empty ()) + { + impl->mouseUpTimer = makeOwned ( + [this] (CVSTGUITimer*) { + impl->mouseUpTimer = nullptr; + if (!impl->container || + impl->frame->getCurrentMouseButtons ().getButtonState () == 0) + return; + }, + 200); + } if (impl->listener) impl->listener->optionMenuPopupStarted (); } diff --git a/vstgui/lib/platform/common/genericoptionmenu.h b/vstgui/lib/platform/common/genericoptionmenu.h index 9795dd3a5..27580ccc9 100644 --- a/vstgui/lib/platform/common/genericoptionmenu.h +++ b/vstgui/lib/platform/common/genericoptionmenu.h @@ -8,6 +8,7 @@ #include "../../ccolor.h" #include "../../cfont.h" +#include "../../events.h" #include "../../iviewlistener.h" #include @@ -40,10 +41,12 @@ struct IGenericOptionMenuListener }; //------------------------------------------------------------------------ -class GenericOptionMenu : public IPlatformOptionMenu, public ViewMouseListenerAdapter +class GenericOptionMenu +: public IPlatformOptionMenu +, public ViewEventListenerAdapter { public: - GenericOptionMenu (CFrame* frame, CButtonState initialButtons, + GenericOptionMenu (CFrame* frame, MouseEventButtonState initialButtons, GenericOptionMenuTheme theme = {}); ~GenericOptionMenu () noexcept override; @@ -53,8 +56,7 @@ class GenericOptionMenu : public IPlatformOptionMenu, public ViewMouseListenerAd private: void removeModalView (PlatformOptionMenuResult result); - CMouseEventResult viewOnMouseDown (CView* view, CPoint pos, CButtonState buttons) override; - CMouseEventResult viewOnMouseUp (CView* view, CPoint pos, CButtonState buttons) override; + void viewOnEvent (CView* view, Event& event) override; struct Impl; std::unique_ptr impl; diff --git a/vstgui/lib/platform/common/generictextedit.cpp b/vstgui/lib/platform/common/generictextedit.cpp index 37ed76ea6..04e2c715e 100644 --- a/vstgui/lib/platform/common/generictextedit.cpp +++ b/vstgui/lib/platform/common/generictextedit.cpp @@ -13,6 +13,7 @@ #include "../../cframe.h" #include "../../cvstguitimer.h" #include "../../cdropsource.h" +#include "../../events.h" #include #include @@ -54,17 +55,11 @@ class STBTextEditView void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr) override; void setText (const UTF8String& txt) override; - int32_t onKeyDown (const VstKeyCode& code, CFrame* frame) override; - int32_t onKeyUp (const VstKeyCode& code, CFrame* frame) override; + void onKeyboardEvent (KeyboardEvent& event, CFrame* frame) override; void onMouseEntered (CView* view, CFrame* frame) override; void onMouseExited (CView* view, CFrame* frame) override; - CMouseEventResult onMouseMoved (CFrame* frame, - const CPoint& where, - const CButtonState& buttons) override; - CMouseEventResult onMouseDown (CFrame* frame, - const CPoint& where, - const CButtonState& buttons) override; + void onMouseEvent (MouseEvent& event, CFrame* frame) override; bool attached (CView* parent) override; bool removed (CView* parent) override; @@ -83,8 +78,7 @@ class STBTextEditView static int getLength (STBTextEditView* self); private: - using CTextLabel::onKeyDown; - using CTextLabel::onKeyUp; + using CTextLabel::onKeyboardEvent; using CTextLabel::onMouseEntered; using CTextLabel::onMouseExited; using CTextLabel::onMouseMoved; @@ -103,18 +97,21 @@ class STBTextEditView static constexpr auto BitCursorIsSet = 1 << 2; static constexpr auto BitCursorSizesValid = 1 << 3; static constexpr auto BitNotifyTextChange = 1 << 4; + static constexpr auto BitMouseDownHandling = 1 << 5; bool isRecursiveKeyEventGuard () const { return hasBit (flags, BitRecursiveKeyGuard); } bool isBlinkToggle () const { return hasBit (flags, BitBlinkToggle); } bool isCursorSet () const { return hasBit (flags, BitCursorIsSet); } bool cursorSizesValid () const { return hasBit (flags, BitCursorSizesValid); } bool notifyTextChange () const { return hasBit (flags, BitNotifyTextChange); } + bool mouseDownHandling () const { return hasBit (flags, BitMouseDownHandling); } void setRecursiveKeyEventGuard (bool state) { setBit (flags, BitRecursiveKeyGuard, state); } void setBlinkToggle (bool state) { setBit (flags, BitBlinkToggle, state); } void setCursorIsSet (bool state) { setBit (flags, BitCursorIsSet, state); } void setCursorSizesValid (bool state) { setBit (flags, BitCursorSizesValid, state); } void setNotifyTextChange (bool state) { setBit (flags, BitNotifyTextChange, state); } + void setMouseDownHandling (bool state) { setBit (flags, BitMouseDownHandling, state); } SharedPointer blinkTimer; IPlatformTextEditCallback* callback; @@ -264,49 +261,54 @@ bool STBTextEditView::callSTB (Proc proc) } //----------------------------------------------------------------------------- -int32_t STBTextEditView::onKeyDown (const VstKeyCode& code, CFrame* frame) +void STBTextEditView::onKeyboardEvent (KeyboardEvent& event, CFrame* frame) { + if (event.type == EventType::KeyUp) + return; + if (isRecursiveKeyEventGuard ()) - return -1; + return; auto selfGuard = SharedPointer (this); BitScopeToggleT br (flags, BitRecursiveKeyGuard); - if (callback->platformOnKeyDown (code)) - return 1; + callback->platformOnKeyboardEvent (event); + if (event.consumed) + return; - if (code.character == 0 && code.virt == 0) - return -1; + if (event.character == 0 && event.virt == VirtualKey::None) + return; - if (code.modifier == MODIFIER_CONTROL) + if (event.modifiers.is (ModifierKey::Control)) { - switch (code.character) + switch (event.character) { case 'a': { selectAll (); - return 1; + event.consumed = true; + return; } case 'x': { if (doCut ()) - return 1; - return -1; + event.consumed = true; + return; } case 'c': { if (doCopy ()) - return 1; - return -1; + event.consumed = true; + return; } case 'v': { if (doPaste ()) - return 1; - return -1; + event.consumed = true; + return; } } } - auto key = code.character; + auto key = event.character; if (key) { if (auto text = getFrame ()->getPlatformFrame ()->convertCurrentKeyEventToText ()) @@ -316,46 +318,95 @@ int32_t STBTextEditView::onKeyDown (const VstKeyCode& code, CFrame* frame) key = tmp[0]; #else if (text->length () != 1) - return -1; + return; key = text->getString ()[0]; #endif } } - if (code.virt) + if (event.virt != VirtualKey::None) { - switch (code.virt) + switch (event.virt) { - case VKEY_SPACE: + case VirtualKey::Space: { key = 0x20; break; } - case VKEY_TAB: + case VirtualKey::Tab: { - return -1; + return; } default: { - key = code.virt | VIRTUAL_KEY_BIT; + key = static_cast (event.virt) | VIRTUAL_KEY_BIT; break; } } } - if (code.modifier & MODIFIER_CONTROL) + if (event.modifiers.has (ModifierKey::Control)) key |= STB_TEXTEDIT_K_CONTROL; - if (code.modifier & MODIFIER_ALTERNATE) + if (event.modifiers.has (ModifierKey::Alt)) key |= STB_TEXTEDIT_K_ALT; - if (code.modifier & MODIFIER_SHIFT) + if (event.modifiers.has (ModifierKey::Shift)) key |= STB_TEXTEDIT_K_SHIFT; - return callSTB ([&]() { stb_textedit_key (this, &editState, key); }) ? 1 : -1; + if (callSTB ([&]() { stb_textedit_key (this, &editState, key); })) + event.consumed = true; } //----------------------------------------------------------------------------- -int32_t STBTextEditView::onKeyUp (const VstKeyCode& code, CFrame* frame) +void STBTextEditView::onMouseEvent (MouseEvent& event, CFrame* frame) { - return -1; + if (event.buttonState.isLeft () == false) + return; + + if (auto parent = getParentView ()) + { + auto where = event.mousePosition; + where = translateToLocal (where, true); + if (mouseDownHandling () || hitTest (where, event)) + { + where.x -= getViewSize ().left; + where.y -= getViewSize ().top; + switch (event.type) + { + case EventType::MouseDown: + { + setMouseDownHandling (true); + callSTB ([&] () { + stb_textedit_click (this, &editState, static_cast (where.x), + static_cast (where.y)); + }); + event.consumed = true; + break; + } + case EventType::MouseMove: + { + if (mouseDownHandling ()) + { + callSTB ([&] () { + stb_textedit_drag (this, &editState, static_cast (where.x), + static_cast (where.y)); + }); + event.consumed = true; + } + break; + } + case EventType::MouseUp: + { + if (mouseDownHandling ()) + { + event.consumed = true; + setMouseDownHandling (false); + } + break; + } + default: break; + } + } + } } +#if 0 //----------------------------------------------------------------------------- CMouseEventResult STBTextEditView::onMouseDown (CFrame* frame, const CPoint& _where, @@ -365,7 +416,7 @@ CMouseEventResult STBTextEditView::onMouseDown (CFrame* frame, if (auto parent = getParentView ()) { where = translateToLocal (where, true); - if (buttons.isLeftButton () && hitTest (where, buttons)) + if (buttons.isLeftButton () && hitTest (where, noEvent ())) { where.x -= getViewSize ().left; where.y -= getViewSize ().top; @@ -388,7 +439,7 @@ CMouseEventResult STBTextEditView::onMouseMoved (CFrame* frame, if (auto parent = getParentView ()) { where = translateToLocal (where, true); - if (buttons.isLeftButton () && hitTest (where, buttons)) + if (buttons.isLeftButton () && hitTest (where, noEvent ())) { where.x -= getViewSize ().left; where.y -= getViewSize ().top; @@ -401,6 +452,7 @@ CMouseEventResult STBTextEditView::onMouseMoved (CFrame* frame, } return kMouseEventNotHandled; } +#endif //----------------------------------------------------------------------------- void STBTextEditView::onMouseEntered (CView* view, CFrame* frame) diff --git a/vstgui/lib/platform/common/gradientbase.h b/vstgui/lib/platform/common/gradientbase.h new file mode 100644 index 000000000..d8c94ac3f --- /dev/null +++ b/vstgui/lib/platform/common/gradientbase.h @@ -0,0 +1,36 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../iplatformgradient.h" + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +class PlatformGradientBase : public IPlatformGradient +{ +public: + void setColorStops (const GradientColorStopMap& colorStops) override + { + map = colorStops; + changed (); + } + void addColorStop (const GradientColorStop& colorStop) override + { + map.emplace (colorStop); + changed (); + } + const GradientColorStopMap& getColorStops () const override + { + return map; + } + + virtual void changed () {} +private: + GradientColorStopMap map; +}; + +} // VSTGUI + diff --git a/vstgui/lib/platform/iplatformfileselector.h b/vstgui/lib/platform/iplatformfileselector.h new file mode 100644 index 000000000..a29ed4cfb --- /dev/null +++ b/vstgui/lib/platform/iplatformfileselector.h @@ -0,0 +1,83 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../cstring.h" +#include +#include + +//----------------------------------------------------------------------------- +namespace VSTGUI { + +//----------------------------------------------------------------------------- +struct PlatformFileExtension +{ + UTF8String description; + UTF8String extension; + UTF8String mimeType; + UTF8String uti; + int32_t macType {0}; +}; + +static PlatformFileExtension PlatformAllFilesExtension = {"All Files", "", "", "", 0}; +static PlatformFileExtension PlatformNoFileExtension = {}; + +//----------------------------------------------------------------------------- +enum class PlatformFileSelectorStyle : uint32_t +{ + SelectFile, + SelectDirectory, + SelectSaveFile, +}; + +//----------------------------------------------------------------------------- +enum class PlatformFileSelectorFlags : uint32_t +{ + MultiFileSelection = 1 << 0, + RunModal = 1 << 1, +}; + +//----------------------------------------------------------------------------- +struct PlatformFileSelectorConfig +{ + using CallbackFunc = std::function&&)>; + using FileExtensionList = std::vector; + + UTF8String title; + UTF8String initialPath; + UTF8String defaultSaveName; + FileExtensionList extensions; + PlatformFileExtension defaultExtension; + uint32_t flags {0}; + + CallbackFunc doneCallback; +}; + +//----------------------------------------------------------------------------- +class IPlatformFileSelector +{ +public: + virtual bool run (const PlatformFileSelectorConfig& config) = 0; + virtual bool cancel () = 0; + + virtual ~IPlatformFileSelector () noexcept = default; +}; + +//----------------------------------------------------------------------------- +inline bool operator== (const PlatformFileExtension& e1, const PlatformFileExtension& e2) +{ + return e1.macType == e2.macType && e1.uti == e2.uti && e1.mimeType == e2.mimeType && + e1.extension == e2.extension && e1.description == e2.description; +} + +//----------------------------------------------------------------------------- +inline bool operator!= (const PlatformFileExtension& e1, const PlatformFileExtension& e2) +{ + return e1.macType != e2.macType || e1.uti != e2.uti || e1.mimeType != e2.mimeType || + e1.extension != e2.extension || e1.description != e2.description; +} + +//----------------------------------------------------------------------------- +} // VSTGUI diff --git a/vstgui/lib/platform/iplatformframe.h b/vstgui/lib/platform/iplatformframe.h index 1b38c184f..2fe67166a 100644 --- a/vstgui/lib/platform/iplatformframe.h +++ b/vstgui/lib/platform/iplatformframe.h @@ -31,6 +31,8 @@ class IPlatformFrame : public AtomicReferenceCounted virtual bool getCurrentMousePosition (CPoint& mousePosition) const = 0; /** get current mouse buttons out of event stream */ virtual bool getCurrentMouseButtons (CButtonState& buttons) const = 0; + /** get current hardware modifier key state */ + virtual bool getCurrentModifiers (Modifiers& modifiers) const = 0; /** set mouse cursor shape */ virtual bool setMouseCursor (CCursorType type) = 0; diff --git a/vstgui/lib/platform/iplatformframecallback.h b/vstgui/lib/platform/iplatformframecallback.h index 3bb64bcdb..18b395933 100644 --- a/vstgui/lib/platform/iplatformframecallback.h +++ b/vstgui/lib/platform/iplatformframecallback.h @@ -15,7 +15,6 @@ namespace VSTGUI { //----------------------------------------------------------------------------- enum class PlatformType : int32_t { kHWND, // Windows HWND - kWindowRef, // macOS WindowRef (Carbon) kNSView, // macOS NSView kUIView, // iOS UIView kHWNDTopLevel, // Windows HWDN Top Level (non child) @@ -33,20 +32,13 @@ class IPlatformFrameCallback public: virtual bool platformDrawRect (CDrawContext* context, const CRect& rect) = 0; - virtual CMouseEventResult platformOnMouseDown (CPoint& where, const CButtonState& buttons) = 0; - virtual CMouseEventResult platformOnMouseMoved (CPoint& where, const CButtonState& buttons) = 0; - virtual CMouseEventResult platformOnMouseUp (CPoint& where, const CButtonState& buttons) = 0; - virtual CMouseEventResult platformOnMouseExited (CPoint& where, const CButtonState& buttons) = 0; - virtual bool platformOnMouseWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &distance, const CButtonState &buttons) = 0; + virtual void platformOnEvent (Event& event) = 0; virtual DragOperation platformOnDragEnter (DragEventData data) = 0; virtual DragOperation platformOnDragMove (DragEventData data) = 0; virtual void platformOnDragLeave (DragEventData data) = 0; virtual bool platformOnDrop (DragEventData data) = 0; - virtual bool platformOnKeyDown (VstKeyCode& keyCode) = 0; - virtual bool platformOnKeyUp (VstKeyCode& keyCode) = 0; - virtual void platformOnActivate (bool state) = 0; virtual void platformOnWindowActivate (bool state) = 0; diff --git a/vstgui/lib/platform/iplatformgradient.h b/vstgui/lib/platform/iplatformgradient.h new file mode 100644 index 000000000..ed4ff5525 --- /dev/null +++ b/vstgui/lib/platform/iplatformgradient.h @@ -0,0 +1,22 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../ccolor.h" + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +class IPlatformGradient +{ +public: + virtual void setColorStops (const GradientColorStopMap& colorStops) = 0; + virtual void addColorStop (const GradientColorStop& colorStop) = 0; + virtual const GradientColorStopMap& getColorStops () const = 0; + + virtual ~IPlatformGradient () noexcept = default; +}; + +} // VSTGUI diff --git a/vstgui/lib/platform/iplatformgraphicspath.h b/vstgui/lib/platform/iplatformgraphicspath.h new file mode 100644 index 000000000..76c4d421b --- /dev/null +++ b/vstgui/lib/platform/iplatformgraphicspath.h @@ -0,0 +1,64 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../ccolor.h" +#include "../cgraphicstransform.h" +#include "../cpoint.h" +#include "../crect.h" + +namespace VSTGUI { + +//------------------------------------------------------------------------ +enum class PlatformGraphicsPathFillMode : int32_t +{ + Winding, + Alternate, + Ignored +}; + +//------------------------------------------------------------------------ +class IPlatformGraphicsPathFactory +{ +public: + virtual PlatformGraphicsPathPtr createPath ( + PlatformGraphicsPathFillMode fillMode = PlatformGraphicsPathFillMode::Winding) = 0; + virtual PlatformGraphicsPathPtr createTextPath (const PlatformFontPtr& font, + UTF8StringPtr text) = 0; + + virtual ~IPlatformGraphicsPathFactory () noexcept = default; +}; + +//----------------------------------------------------------------------------- +class IPlatformGraphicsPath +{ +public: + /** add an arc to the path. Begins a new subpath if no elements were added before. */ + virtual void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) = 0; + /** add an ellipse to the path. Begins a new subpath if no elements were added before. */ + virtual void addEllipse (const CRect& rect) = 0; + /** add a rectangle to the path. Begins a new subpath if no elements were added before. */ + virtual void addRect (const CRect& rect) = 0; + /** add a line to the path. A subpath must begin before */ + virtual void addLine (const CPoint& to) = 0; + /** add a bezier curve to the path. A subpath must begin before */ + virtual void addBezierCurve (const CPoint& control1, const CPoint& control2, + const CPoint& end) = 0; + /** begin a new subpath. */ + virtual void beginSubpath (const CPoint& start) = 0; + /** close a subpath. A straight line will be added from the current point to the start point. */ + virtual void closeSubpath () = 0; + virtual void finishBuilding () = 0; + + virtual bool hitTest (const CPoint& p, bool evenOddFilled = false, + CGraphicsTransform* transform = nullptr) const = 0; + virtual CRect getBoundingBox () const = 0; + + virtual PlatformGraphicsPathFillMode getFillMode () const = 0; + + virtual ~IPlatformGraphicsPath () noexcept = default; +}; + +} // VSTGUI diff --git a/vstgui/lib/platform/iplatformtextedit.h b/vstgui/lib/platform/iplatformtextedit.h index f34c28827..224ce2a1c 100644 --- a/vstgui/lib/platform/iplatformtextedit.h +++ b/vstgui/lib/platform/iplatformtextedit.h @@ -29,7 +29,7 @@ class IPlatformTextEditCallback virtual CRect platformGetVisibleSize () const = 0; virtual CPoint platformGetTextInset () const = 0; virtual void platformLooseFocus (bool returnPressed) = 0; - virtual bool platformOnKeyDown (const VstKeyCode& key) = 0; + virtual void platformOnKeyboardEvent (KeyboardEvent& event) = 0; virtual void platformTextDidChange () = 0; virtual bool platformIsSecureTextEdit () = 0; diff --git a/vstgui/lib/platform/linux/cairocontext.cpp b/vstgui/lib/platform/linux/cairocontext.cpp index 0bf8cd828..c2d07eb37 100644 --- a/vstgui/lib/platform/linux/cairocontext.cpp +++ b/vstgui/lib/platform/linux/cairocontext.cpp @@ -4,6 +4,7 @@ #include "cairocontext.h" #include "../../cbitmap.h" +#include "../../cgradient.h" #include "cairobitmap.h" #include "cairogradient.h" #include "cairopath.h" @@ -443,7 +444,9 @@ void Context::clearRect (const CRect& rect) //----------------------------------------------------------------------------- CGraphicsPath* Context::createGraphicsPath () { - return new Path (cr); + if (!graphicsPathFactory) + graphicsPathFactory = std::make_shared (cr); + return new CGraphicsPath (graphicsPathFactory); } //----------------------------------------------------------------------------- @@ -457,12 +460,18 @@ CGraphicsPath* Context::createTextPath (const CFontRef font, UTF8StringPtr text) void Context::drawGraphicsPath (CGraphicsPath* path, CDrawContext::PathDrawMode mode, CGraphicsTransform* transformation) { - if (auto cairoPath = dynamic_cast (path)) + if (path) { + auto graphicsPath = dynamic_cast ( + path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored).get ()); + if (!graphicsPath) + return; if (auto cd = DrawBlock::begin (*this)) { - auto p = cairoPath->getPath ( - cr, needPixelAlignment (getDrawMode ()) ? &getCurrentTransform () : nullptr); + std::unique_ptr alignedPath; + if (needPixelAlignment (getDrawMode ())) + alignedPath = graphicsPath->copyPixelAlign (getCurrentTransform ()); + auto p = alignedPath ? alignedPath->getCairoPath () : graphicsPath->getCairoPath (); if (transformation) { cairo_matrix_t currentMatrix; @@ -506,13 +515,20 @@ void Context::fillLinearGradient (CGraphicsPath* path, const CGradient& gradient const CPoint& startPoint, const CPoint& endPoint, bool evenOdd, CGraphicsTransform* transformation) { - if (auto cairoPath = dynamic_cast (path)) + if (path) { - if (auto cairoGradient = dynamic_cast (&gradient)) + auto graphicsPath = dynamic_cast ( + path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored).get ()); + if (!graphicsPath) + return; + std::unique_ptr alignedPath; + if (needPixelAlignment (getDrawMode ())) + alignedPath = graphicsPath->copyPixelAlign (getCurrentTransform ()); + if (auto cairoGradient = dynamic_cast (gradient.getPlatformGradient ().get ())) { if (auto cd = DrawBlock::begin (*this)) { - auto p = cairoPath->getPath (cr); + auto p = alignedPath ? alignedPath->getCairoPath () : graphicsPath->getCairoPath (); cairo_append_path (cr, p); cairo_set_source (cr, cairoGradient->getLinearGradient (startPoint, endPoint)); if (evenOdd) diff --git a/vstgui/lib/platform/linux/cairocontext.h b/vstgui/lib/platform/linux/cairocontext.h index cf78bd259..5cc75f881 100644 --- a/vstgui/lib/platform/linux/cairocontext.h +++ b/vstgui/lib/platform/linux/cairocontext.h @@ -5,6 +5,7 @@ #pragma once #include "cairoutils.h" +#include "cairopath.h" #include "../../coffscreencontext.h" @@ -67,6 +68,8 @@ class Context : public COffscreenContext SurfaceHandle surface; ContextHandle cr; + + PlatformGraphicsPathFactoryPtr graphicsPathFactory; }; //------------------------------------------------------------------------ diff --git a/vstgui/lib/platform/linux/cairogradient.cpp b/vstgui/lib/platform/linux/cairogradient.cpp index 691b1ed93..f2abb35ad 100644 --- a/vstgui/lib/platform/linux/cairogradient.cpp +++ b/vstgui/lib/platform/linux/cairogradient.cpp @@ -6,45 +6,32 @@ //------------------------------------------------------------------------ namespace VSTGUI { - -//------------------------------------------------------------------------ -CGradient* CGradient::create (const ColorStopMap& colorStopMap) -{ - return new Cairo::Gradient (colorStopMap); -} - -//------------------------------------------------------------------------ namespace Cairo { //------------------------------------------------------------------------ -Gradient::Gradient (const ColorStopMap& colorStopMap) : CGradient (colorStopMap) -{ -} - -//------------------------------------------------------------------------ -Gradient::~Gradient () +Gradient::~Gradient () noexcept { - destroy (); + changed (); } //------------------------------------------------------------------------ -void Gradient::destroy () const +void Gradient::changed () { linearGradient.reset (); radialGradient.reset (); } //------------------------------------------------------------------------ -const PatternHandle& Gradient::getLinearGradient (CPoint start, CPoint end) const +const PatternHandle& Gradient::getLinearGradient (CPoint start, CPoint end) { if (!linearGradient || start != linearGradientStart || end != linearGradientEnd) { - destroy (); + changed (); linearGradientStart = start; linearGradientEnd = end; linearGradient = PatternHandle (cairo_pattern_create_linear (start.x, start.y, end.x, end.y)); - for (auto& it : this->colorStops) + for (auto& it : getColorStops ()) { cairo_pattern_add_color_stop_rgba ( linearGradient, it.first, it.second.normRed (), @@ -56,12 +43,12 @@ const PatternHandle& Gradient::getLinearGradient (CPoint start, CPoint end) cons } //------------------------------------------------------------------------ -const PatternHandle& Gradient::getRadialGradient () const +const PatternHandle& Gradient::getRadialGradient () { if (!radialGradient) { radialGradient = PatternHandle (cairo_pattern_create_radial (0, 0, 1, 0, 0, 1)); - for (auto& it : this->colorStops) + for (auto& it : getColorStops ()) { cairo_pattern_add_color_stop_rgba ( radialGradient, it.first, it.second.normRed (), diff --git a/vstgui/lib/platform/linux/cairogradient.h b/vstgui/lib/platform/linux/cairogradient.h index 45bb3c37e..d2a3fc896 100644 --- a/vstgui/lib/platform/linux/cairogradient.h +++ b/vstgui/lib/platform/linux/cairogradient.h @@ -1,10 +1,10 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #pragma once -#include "../../cgradient.h" +#include "../common/gradientbase.h" #include "../../cpoint.h" #include "cairoutils.h" #include @@ -14,38 +14,23 @@ namespace VSTGUI { namespace Cairo { //------------------------------------------------------------------------ -class Gradient : public CGradient +class Gradient : public PlatformGradientBase { public: - Gradient (const ColorStopMap& colorStopMap); - ~Gradient (); - - void addColorStop (const std::pair& colorStop) override - { - destroy (); - CGradient::addColorStop (colorStop); - } - -#if VSTGUI_RVALUE_REF_SUPPORT - void addColorStop (std::pair&& colorStop) override - { - destroy (); - CGradient::addColorStop (std::move (colorStop)); - } -#endif - - const PatternHandle& getLinearGradient (CPoint start, CPoint end) const; - const PatternHandle& getRadialGradient () const; + ~Gradient () noexcept override; + + const PatternHandle& getLinearGradient (CPoint start, CPoint end); + const PatternHandle& getRadialGradient (); private: - void destroy () const; + void changed () override; /* we want to calculate a normalized linear and radial gradiant */ - mutable PatternHandle linearGradient; - mutable PatternHandle radialGradient; + PatternHandle linearGradient; + PatternHandle radialGradient; - mutable CPoint linearGradientStart; - mutable CPoint linearGradientEnd; + CPoint linearGradientStart; + CPoint linearGradientEnd; }; //------------------------------------------------------------------------ diff --git a/vstgui/lib/platform/linux/cairopath.cpp b/vstgui/lib/platform/linux/cairopath.cpp index 7e095e892..bf54b6c59 100644 --- a/vstgui/lib/platform/linux/cairopath.cpp +++ b/vstgui/lib/platform/linux/cairopath.cpp @@ -2,204 +2,196 @@ // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE -#include "cairopath.h" #include "../../cgradient.h" #include "../../cgraphicstransform.h" #include "cairocontext.h" +#include "cairopath.h" //------------------------------------------------------------------------ namespace VSTGUI { namespace Cairo { +//----------------------------------------------------------------------------- +GraphicsPathFactory::GraphicsPathFactory (const ContextHandle& cr) : context (cr) {} + +//----------------------------------------------------------------------------- +PlatformGraphicsPathPtr + GraphicsPathFactory::createPath ([[maybe_unused]] PlatformGraphicsPathFillMode fillMode) +{ + return std::make_unique (context); +} + +//----------------------------------------------------------------------------- +PlatformGraphicsPathPtr GraphicsPathFactory::createTextPath (const PlatformFontPtr& font, + UTF8StringPtr text) +{ + return nullptr; +} + //------------------------------------------------------------------------ -Path::Path (const ContextHandle& cr) noexcept : cr (cr) {} +GraphicsPath::GraphicsPath (const ContextHandle& c) : context (c) +{ + cairo_save (context); + cairo_new_path (context); +} //------------------------------------------------------------------------ -Path::~Path () noexcept +GraphicsPath::~GraphicsPath () noexcept { - dirty (); + cairo_path_destroy (path); } //------------------------------------------------------------------------ -CGradient* Path::createGradient (double color1Start, double color2Start, - const VSTGUI::CColor& color1, const VSTGUI::CColor& color2) +void GraphicsPath::finishBuilding () { - return CGradient::create (color1Start, color2Start, color1, color2); + path = cairo_copy_path (context); + cairo_restore (context); + cairo_new_path (context); // clear path in context } //------------------------------------------------------------------------ -bool Path::hitTest (const CPoint& p, bool evenOddFilled, CGraphicsTransform* transform) +void GraphicsPath::addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) { - auto result = false; - if (auto cPath = getPath (cr)) + auto radiusX = (rect.right - rect.left) / 2.; + auto radiusY = (rect.bottom - rect.top) / 2.; + + auto centerX = static_cast (rect.left + radiusX); + auto centerY = static_cast (rect.top + radiusY); + + startAngle = radians (startAngle); + endAngle = radians (endAngle); + if (radiusX != radiusY) { - auto tp = p; - if (transform) - transform->transform (tp); - cairo_save (cr); - cairo_new_path (cr); - cairo_append_path (cr, cPath); - cairo_set_fill_rule (cr, evenOddFilled ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); - cairo_clip (cr); - result = cairo_in_clip (cr, tp.x, tp.y); - cairo_restore (cr); + startAngle = std::atan2 (std::sin (startAngle) * radiusX, std::cos (startAngle) * radiusY); + endAngle = std::atan2 (std::sin (endAngle) * radiusX, std::cos (endAngle) * radiusY); } - return result; + cairo_matrix_t matrix; + cairo_get_matrix (context, &matrix); + cairo_translate (context, centerX, centerY); + cairo_scale (context, radiusX, radiusY); + if (clockwise) + { + cairo_arc (context, 0, 0, 1, startAngle, endAngle); + } + else + { + cairo_arc_negative (context, 0, 0, 1, startAngle, endAngle); + } + cairo_set_matrix (context, &matrix); } //------------------------------------------------------------------------ -CPoint Path::getCurrentPosition () +void GraphicsPath::addEllipse (const CRect& rect) { - CPoint p; - if (auto cPath = getPath (cr)) - { - cairo_save (cr); - cairo_new_path (cr); - cairo_append_path (cr, cPath); - cairo_get_current_point (cr, &p.x, &p.y); - cairo_restore (cr); - } - return p; +#warning TODO: GraphicsPath::addEllipse } //------------------------------------------------------------------------ -CRect Path::getBoundingBox () +void GraphicsPath::addRect (const CRect& rect) { - CRect r; - if (auto cPath = getPath (cr)) - { - cairo_save (cr); - cairo_new_path (cr); - cairo_append_path (cr, cPath); - CPoint p1, p2; - cairo_path_extents (cr, &p1.x, &p1.y, &p2.x, &p2.y); - cairo_restore (cr); - r.setTopLeft (p1); - r.setBottomRight (p2); - } - return r; + cairo_rectangle (context, rect.left, rect.top, rect.getWidth (), rect.getHeight ()); } //------------------------------------------------------------------------ -void Path::dirty () +void GraphicsPath::addLine (const CPoint& to) { - if (path) - { - cairo_path_destroy (path); - path = nullptr; - } + cairo_line_to (context, to.x, to.y); +} + +//------------------------------------------------------------------------ +void GraphicsPath::addBezierCurve (const CPoint& control1, const CPoint& control2, + const CPoint& end) +{ + cairo_curve_to (context, control1.x, control1.y, control2.x, control2.y, end.x, end.y); +} + +//------------------------------------------------------------------------ +void GraphicsPath::beginSubpath (const CPoint& start) +{ + cairo_new_sub_path (context); + cairo_move_to (context, start.x, start.y); +} + +//------------------------------------------------------------------------ +void GraphicsPath::closeSubpath () +{ + cairo_close_path (context); } //------------------------------------------------------------------------ -cairo_path_t* Path::getPath (const ContextHandle& handle, const CGraphicsTransform* alignTm) +std::unique_ptr GraphicsPath::copyPixelAlign (const CGraphicsTransform& tm) { - if (alignTm) - dirty (); - if (!path) + auto result = std::make_unique (context); + cairo_append_path (context, path); + result->finishBuilding (); + auto rpath = result->path; + + auto align = [] (_cairo_path_data_t* data, int index, const CGraphicsTransform& tm) { + CPoint input (data[index].point.x, data[index].point.y); + auto output = Cairo::pixelAlign (tm, input); + data[index].point.x = output.x; + data[index].point.y = output.y; + }; + for (auto i = 0; i < rpath->num_data; i += rpath->data[i].header.length) { - cairo_new_path (handle); - for (auto& e : elements) + auto data = &rpath->data[i]; + switch (data->header.type) { - switch (e.type) + case CAIRO_PATH_MOVE_TO: + { + align (data, 1, tm); + break; + } + case CAIRO_PATH_LINE_TO: + { + align (data, 1, tm); + break; + } + case CAIRO_PATH_CURVE_TO: { - case Element::Type::kBeginSubpath: - { - cairo_new_sub_path (handle); - if (alignTm) - { - auto p = pixelAlign (*alignTm, - CPoint {e.instruction.point.x, e.instruction.point.y}); - cairo_move_to (handle, p.x, p.y); - } - else - cairo_move_to (handle, e.instruction.point.x, e.instruction.point.y); - break; - } - case Element::Type::kCloseSubpath: - { - cairo_close_path (handle); - break; - } - case Element::Type::kLine: - { - if (alignTm) - { - auto p = pixelAlign (*alignTm, - CPoint {e.instruction.point.x, e.instruction.point.y}); - cairo_line_to (handle, p.x, p.y); - } - else - cairo_line_to (handle, e.instruction.point.x, e.instruction.point.y); - break; - } - case Element::Type::kBezierCurve: - { - cairo_curve_to (handle, e.instruction.curve.control1.x, - e.instruction.curve.control1.y, e.instruction.curve.control2.x, - e.instruction.curve.control2.y, e.instruction.curve.end.x, - e.instruction.curve.end.y); - break; - } - case Element::Type::kRect: - { - if (alignTm) - { - auto r = pixelAlign ( - *alignTm, CRect {e.instruction.rect.left, e.instruction.rect.top, - e.instruction.rect.right, e.instruction.rect.bottom}); - cairo_rectangle (handle, r.left, r.top, r.getWidth (), r.getHeight ()); - } - else - { - cairo_rectangle (handle, e.instruction.rect.left, e.instruction.rect.top, - e.instruction.rect.right - e.instruction.rect.left, - e.instruction.rect.bottom - e.instruction.rect.top); - } - break; - } - case Element::Type::kEllipse: - { -#warning TODO: Implementation Element::Type::kEllipse - break; - } - case Element::Type::kArc: - { - auto radiusX = - (e.instruction.arc.rect.right - e.instruction.arc.rect.left) / 2.; - auto radiusY = - (e.instruction.arc.rect.bottom - e.instruction.arc.rect.top) / 2.; - - auto centerX = static_cast (e.instruction.arc.rect.left + radiusX); - auto centerY = static_cast (e.instruction.arc.rect.top + radiusY); - - double startAngle = radians (e.instruction.arc.startAngle); - double endAngle = radians (e.instruction.arc.endAngle); - if (radiusX != radiusY) - { - startAngle = atan2 (sin (startAngle) * radiusX, cos (startAngle) * radiusY); - endAngle = atan2 (sin (endAngle) * radiusX, cos (endAngle) * radiusY); - } - cairo_matrix_t matrix; - cairo_get_matrix (handle, &matrix); - cairo_translate (handle, centerX, centerY); - cairo_scale (handle, radiusX, radiusY); - if (e.instruction.arc.clockwise) - { - cairo_arc (handle, 0, 0, 1, startAngle, endAngle); - } - else - { - cairo_arc_negative (handle, 0, 0, 1, startAngle, endAngle); - } - cairo_set_matrix (handle, &matrix); - break; - } + align (data, 1, tm); + align (data, 2, tm); + align (data, 3, tm); + break; + } + case CAIRO_PATH_CLOSE_PATH: { break; } } - path = cairo_copy_path (handle); - cairo_new_path (handle); // clear path } - return path; + return result; +} + +//------------------------------------------------------------------------ +bool GraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, + CGraphicsTransform* transform) const +{ + auto tp = p; + if (transform) + transform->transform (tp); + cairo_save (context); + cairo_new_path (context); + cairo_append_path (context, path); + cairo_set_fill_rule (context, + evenOddFilled ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); + cairo_clip (context); + auto result = cairo_in_clip (context, tp.x, tp.y); + cairo_restore (context); + return result; +} + +//------------------------------------------------------------------------ +CRect GraphicsPath::getBoundingBox () const +{ + CRect r; + cairo_save (context); + cairo_new_path (context); + cairo_append_path (context, path); + CPoint p1, p2; + cairo_path_extents (context, &p1.x, &p1.y, &p2.x, &p2.y); + cairo_restore (context); + r.setTopLeft (p1); + r.setBottomRight (p2); + return r; } //------------------------------------------------------------------------ diff --git a/vstgui/lib/platform/linux/cairopath.h b/vstgui/lib/platform/linux/cairopath.h index 766b0e5dd..8aede58a7 100644 --- a/vstgui/lib/platform/linux/cairopath.h +++ b/vstgui/lib/platform/linux/cairopath.h @@ -1,10 +1,11 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #pragma once #include "../../cgraphicspath.h" +#include "../iplatformgraphicspath.h" #include "cairoutils.h" //------------------------------------------------------------------------ @@ -12,28 +13,49 @@ namespace VSTGUI { namespace Cairo { //------------------------------------------------------------------------ -class Path : public CGraphicsPath +class GraphicsPathFactory : public IPlatformGraphicsPathFactory { public: - Path (const ContextHandle& cr) noexcept; - ~Path () noexcept; + GraphicsPathFactory (const ContextHandle& cr); - cairo_path_t* getPath (const ContextHandle& handle, - const CGraphicsTransform* alignTransform = nullptr); + PlatformGraphicsPathPtr createPath (PlatformGraphicsPathFillMode fillMode) override; + PlatformGraphicsPathPtr createTextPath (const PlatformFontPtr& font, + UTF8StringPtr text) override; - CGradient* createGradient (double color1Start, double color2Start, const CColor& color1, - const CColor& color2) override; +private: + ContextHandle context; +}; - bool hitTest (const CPoint& p, bool evenOddFilled = false, - CGraphicsTransform* transform = 0) override; - CPoint getCurrentPosition () override; - CRect getBoundingBox () override; +//----------------------------------------------------------------------------- +class GraphicsPath : public IPlatformGraphicsPath +{ +public: + GraphicsPath (const ContextHandle& c); + ~GraphicsPath () noexcept; - void dirty () override; + cairo_path_t* getCairoPath () const { return path; } + std::unique_ptr copyPixelAlign (const CGraphicsTransform& tm); + + // IPlatformGraphicsPath + void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) override; + void addEllipse (const CRect& rect) override; + void addRect (const CRect& rect) override; + void addLine (const CPoint& to) override; + void addBezierCurve (const CPoint& control1, const CPoint& control2, + const CPoint& end) override; + void beginSubpath (const CPoint& start) override; + void closeSubpath () override; + void finishBuilding () override; + bool hitTest (const CPoint& p, bool evenOddFilled = false, + CGraphicsTransform* transform = nullptr) const override; + CRect getBoundingBox () const override; + PlatformGraphicsPathFillMode getFillMode () const override + { + return PlatformGraphicsPathFillMode::Ignored; + } -//------------------------------------------------------------------------ private: - ContextHandle cr; + ContextHandle context; cairo_path_t* path {nullptr}; }; diff --git a/vstgui/lib/platform/linux/linuxfactory.cpp b/vstgui/lib/platform/linux/linuxfactory.cpp index b004d27b2..202ed91a7 100644 --- a/vstgui/lib/platform/linux/linuxfactory.cpp +++ b/vstgui/lib/platform/linux/linuxfactory.cpp @@ -4,6 +4,7 @@ #include "cairobitmap.h" #include "cairofont.h" +#include "cairogradient.h" #include "cairocontext.h" #include "x11frame.h" #include "../iplatformframecallback.h" @@ -11,6 +12,7 @@ #include "../iplatformresourceinputstream.h" #include "linuxstring.h" #include "x11timer.h" +#include "x11fileselector.h" #include "linuxfactory.h" #include #include @@ -198,6 +200,20 @@ auto LinuxFactory::createOffscreenContext (const CPoint& size, double scaleFacto return nullptr; } +//----------------------------------------------------------------------------- +PlatformGradientPtr LinuxFactory::createGradient () const noexcept +{ + return std::make_unique (); +} + +//----------------------------------------------------------------------------- +PlatformFileSelectorPtr LinuxFactory::createFileSelector (PlatformFileSelectorStyle style, + IPlatformFrame* frame) const noexcept +{ + auto x11Frame = dynamic_cast (frame); + return X11::createFileSelector (style, x11Frame); +} + //----------------------------------------------------------------------------- const LinuxFactory* LinuxFactory::asLinuxFactory () const noexcept { diff --git a/vstgui/lib/platform/linux/linuxfactory.h b/vstgui/lib/platform/linux/linuxfactory.h index c36051911..306085c51 100644 --- a/vstgui/lib/platform/linux/linuxfactory.h +++ b/vstgui/lib/platform/linux/linuxfactory.h @@ -116,6 +116,19 @@ class LinuxFactory final : public IPlatformFactory COffscreenContextPtr createOffscreenContext (const CPoint& size, double scaleFactor = 1.) const noexcept final; + /** Create a platform gradient object + * @return platform gradient object or nullptr on failure + */ + PlatformGradientPtr createGradient () const noexcept final; + + /** Create a platform file selector + * @param style file selector style + * @param frame frame + * @return platform file selector or nullptr on failure + */ + PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, + IPlatformFrame* frame) const noexcept final; + const LinuxFactory* asLinuxFactory () const noexcept final; const MacFactory* asMacFactory () const noexcept final; const Win32Factory* asWin32Factory () const noexcept final; diff --git a/vstgui/lib/platform/linux/x11fileselector.cpp b/vstgui/lib/platform/linux/x11fileselector.cpp index ab80daafa..f727b8110 100644 --- a/vstgui/lib/platform/linux/x11fileselector.cpp +++ b/vstgui/lib/platform/linux/x11fileselector.cpp @@ -2,7 +2,7 @@ // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE -#include "../../cfileselector.h" +#include "x11fileselector.h" #include #include #include @@ -22,38 +22,35 @@ static constexpr auto kdialogpath = "/usr/bin/kdialog"; static constexpr auto zenitypath = "/usr/bin/zenity"; //------------------------------------------------------------------------ -struct FileSelector : CNewFileSelector +struct FileSelector : IPlatformFileSelector { - FileSelector (CFrame* parent, Style style) : CNewFileSelector (parent), style (style) - { - identifiyExDialogType (); - } + FileSelector (PlatformFileSelectorStyle style) : style (style) { identifiyExDialogType (); } - ~FileSelector () + ~FileSelector () noexcept { closeProcess (); } + + bool cancel () override { - closeProcess (); + closeProcess (); + return false; } - bool runInternal (CBaseObject* delegate) override + bool runDialog (const PlatformFileSelectorConfig& config) { - this->delegate = delegate; switch (exDialogType) { case ExDialogType::kdialog: - return runKDialog (); + return runKDialog (config); case ExDialogType::zenity: - return runZenity (); + return runZenity (config); case ExDialogType::none: break; } return false; } - void cancelInternal () override { closeProcess (); } - - bool runModalInternal () override + bool run (const PlatformFileSelectorConfig& config) override { - if (runInternal (nullptr)) + if (runDialog (config)) { std::string path; path.reserve (1024); @@ -67,6 +64,7 @@ struct FileSelector : CNewFileSelector path.append (buffer, count); } + std::vector result; if (count != -1) { if (! path.empty () && path[0] == '/') @@ -76,8 +74,11 @@ struct FileSelector : CNewFileSelector result.emplace_back (path); } } + if (config.doneCallback) + config.doneCallback (std::move (result)); + return true; } - return !result.empty (); + return false; } private: @@ -96,27 +97,29 @@ struct FileSelector : CNewFileSelector exDialogType = ExDialogType::kdialog; } - bool runKDialog () + bool runKDialog (const PlatformFileSelectorConfig& config) { std::vector args; args.reserve (16); args.push_back (kdialogpath); - if (style == Style::kSelectFile) { + if (style == PlatformFileSelectorStyle::SelectFile) + { args.push_back ("--getopenfilename"); args.push_back ("--separate-output"); } - else if (style == Style::kSelectSaveFile) + else if (style == PlatformFileSelectorStyle::SelectSaveFile) args.push_back ("--getsavefilename"); - else if (style == Style::kSelectDirectory) + else if (style == PlatformFileSelectorStyle::SelectDirectory) args.push_back ("--getexistingdirectory"); - if (allowMultiFileSelection) + if (hasBit (config.flags, PlatformFileSelectorFlags::MultiFileSelection)) args.push_back ("--multiple"); - if (!title.empty ()) { + if (!config.title.empty ()) + { args.push_back ("--title"); - args.push_back (title.getString ()); + args.push_back (config.title.getString ()); } - if (!initialPath.empty ()) - args.push_back (initialPath.getString ()); + if (!config.initialPath.empty ()) + args.push_back (config.initialPath.getString ()); if (startProcess (convertToArgv (args).data ())) { return true; @@ -124,23 +127,23 @@ struct FileSelector : CNewFileSelector return false; } - bool runZenity () + bool runZenity (const PlatformFileSelectorConfig& config) { std::vector args; args.reserve (16); args.push_back (zenitypath); args.push_back ("--file-selection"); - if (style == Style::kSelectDirectory) + if (style == PlatformFileSelectorStyle::SelectDirectory) args.push_back ("--directory"); - else if (style == Style::kSelectSaveFile) + else if (style == PlatformFileSelectorStyle::SelectSaveFile) { args.push_back ("--save"); args.push_back ("--confirm-overwrite"); } - if (!title.empty ()) - args.push_back ("--title=" + title.getString ()); - if (!initialPath.empty ()) - args.push_back ("--filename=" + initialPath.getString ()); + if (!config.title.empty ()) + args.push_back ("--title=" + config.title.getString ()); + if (!config.initialPath.empty ()) + args.push_back ("--filename=" + config.initialPath.getString ()); if (startProcess (convertToArgv (args).data ())) { return true; @@ -240,21 +243,18 @@ struct FileSelector : CNewFileSelector } } - Style style; - SharedPointer delegate; + PlatformFileSelectorStyle style; ExDialogType exDialogType{ExDialogType::none}; pid_t spawnPid = -1; int readerFd = -1; }; //------------------------------------------------------------------------ -} // X11 - -//------------------------------------------------------------------------ -CNewFileSelector* CNewFileSelector::create (CFrame* parent, Style style) +PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, Frame* frame) { - return new X11::FileSelector (parent, style); + return std::make_shared (style); } //------------------------------------------------------------------------ +} // X11 } // VSTGUI diff --git a/vstgui/lib/platform/linux/x11fileselector.h b/vstgui/lib/platform/linux/x11fileselector.h new file mode 100644 index 000000000..3ffc8b91e --- /dev/null +++ b/vstgui/lib/platform/linux/x11fileselector.h @@ -0,0 +1,19 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../iplatformfileselector.h" +#include "x11frame.h" + +//----------------------------------------------------------------------------- +namespace VSTGUI { +namespace X11 { + +//----------------------------------------------------------------------------- +PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, Frame* frame); + +//----------------------------------------------------------------------------- +} // X11 +} // VSTGUI diff --git a/vstgui/lib/platform/linux/x11frame.cpp b/vstgui/lib/platform/linux/x11frame.cpp index 785756ef3..99393735c 100644 --- a/vstgui/lib/platform/linux/x11frame.cpp +++ b/vstgui/lib/platform/linux/x11frame.cpp @@ -34,11 +34,52 @@ #undef None #endif +#include "../../events.h" + //------------------------------------------------------------------------ namespace VSTGUI { namespace X11 { namespace { +//------------------------------------------------------------------------ +inline void setupMouseEventButtons (MouseEvent& event, xcb_button_t value) +{ + switch (value) + { + case 1: + event.buttonState.add (MouseButton::Left); + break; + case 2: + event.buttonState.add (MouseButton::Middle); + break; + case 3: + event.buttonState.add (MouseButton::Right); + break; + } +} + +//------------------------------------------------------------------------ +inline void setupMouseEventButtons (MouseEvent& event, int state) +{ + if (state & XCB_BUTTON_MASK_1) + event.buttonState.add (MouseButton::Left); + if (state & XCB_BUTTON_MASK_2) + event.buttonState.add (MouseButton::Right); + if (state & XCB_BUTTON_MASK_3) + event.buttonState.add (MouseButton::Middle); +} + +//------------------------------------------------------------------------ +inline void setupEventModifiers (Modifiers& modifiers, int state) +{ + if (state & XCB_MOD_MASK_CONTROL) + modifiers.add (ModifierKey::Control); + if (state & XCB_MOD_MASK_SHIFT) + modifiers.add (ModifierKey::Shift); + if (state & (XCB_MOD_MASK_1 | XCB_MOD_MASK_5)) + modifiers.add (ModifierKey::Alt); +} + //------------------------------------------------------------------------ inline CButtonState translateMouseButtons (xcb_button_t value) { @@ -80,6 +121,21 @@ inline uint32_t translateModifiers (int state) return buttons; } +//------------------------------------------------------------------------ +inline Modifiers toModifiers (int state) +{ + Modifiers mods; + if (state & XCB_MOD_MASK_CONTROL) + mods.add (ModifierKey::Control); + if (state & XCB_MOD_MASK_SHIFT) + mods.add (ModifierKey::Shift); + if (state & (XCB_MOD_MASK_1 | XCB_MOD_MASK_5)) + mods.add (ModifierKey::Alt); + if (state & XCB_MOD_MASK_4) + mods.add (ModifierKey::Super); + return mods; +} + //------------------------------------------------------------------------ } // anonymous @@ -176,7 +232,20 @@ struct DrawHandler //------------------------------------------------------------------------ struct DoubleClickDetector { - void onMouseDown (CPoint where, CButtonState& buttons, xcb_timestamp_t time) + void onEvent (MouseDownUpMoveEvent& event, xcb_timestamp_t time) + { + if (event.type == EventType::MouseDown) + onMouseDown (event.mousePosition, event.buttonState, time); + if (event.type == EventType::MouseMove) + onMouseMove (event.mousePosition, event.buttonState, time); + if (event.type == EventType::MouseUp) + onMouseUp (event.mousePosition, event.buttonState, time); + if (isDoubleClick) + event.clickCount = 2; + } + +private: + void onMouseDown (CPoint where, MouseEventButtonState buttonState, xcb_timestamp_t time) { switch (state) { @@ -184,8 +253,9 @@ struct DoubleClickDetector case State::Uninitialized: { state = State::MouseDown; - firstClickState = buttons; + firstClickState = buttonState; firstClickTime = time; + isDoubleClick = false; point = where; break; } @@ -193,7 +263,7 @@ struct DoubleClickDetector { if (timeInside (time) && pointInside (where)) { - buttons |= kDoubleClick; + isDoubleClick = true; } state = State::Uninitialized; break; @@ -201,7 +271,7 @@ struct DoubleClickDetector } } - void onMouseUp (CPoint where, CButtonState buttons, xcb_timestamp_t time) + void onMouseUp (CPoint where, MouseEventButtonState buttonState, xcb_timestamp_t time) { if (state == State::MouseDown && pointInside (where)) state = State::MouseUp; @@ -209,13 +279,12 @@ struct DoubleClickDetector state = State::Uninitialized; } - void onMouseMove (CPoint where, CButtonState buttons, xcb_timestamp_t time) + void onMouseMove (CPoint where, MouseEventButtonState buttonState, xcb_timestamp_t time) { if (!pointInside (where)) state = State::Uninitialized; } -private: bool timeInside (xcb_timestamp_t time) { constexpr xcb_timestamp_t threshold = 250; // in milliseconds @@ -239,8 +308,9 @@ struct DoubleClickDetector }; State state {State::Uninitialized}; + bool isDoubleClick {false}; CPoint point; - CButtonState firstClickState; + MouseEventButtonState firstClickState; xcb_timestamp_t firstClickTime {0}; }; @@ -362,15 +432,8 @@ struct Frame::Impl : IFrameEventHandler void onEvent (xcb_key_press_event_t& event) override { auto type = (event.response_type & ~0x80); - auto keyCode = RunLoop::instance ().getCurrentKeyEvent (); - if (type == XCB_KEY_PRESS) - { - frame->platformOnKeyDown (keyCode); - } - else - { - frame->platformOnKeyUp (keyCode); - } + auto keyEvent = RunLoop::instance ().getCurrentKeyEvent (); + frame->platformOnEvent (keyEvent); } //------------------------------------------------------------------------ @@ -381,39 +444,44 @@ struct Frame::Impl : IFrameEventHandler { if (event.detail >= 4 && event.detail <= 7) // mouse wheel { - auto buttons = translateModifiers (event.state); + MouseWheelEvent wheelEvent; + wheelEvent.mousePosition = where; + wheelEvent.modifiers = toModifiers (event.state); switch (event.detail) { case 4: // up { - frame->platformOnMouseWheel (where, kMouseWheelAxisY, 1, buttons); + wheelEvent.deltaY = 1; break; } case 5: // down { - frame->platformOnMouseWheel (where, kMouseWheelAxisY, -1, buttons); + wheelEvent.deltaY = -1; break; } case 6: // left { - frame->platformOnMouseWheel (where, kMouseWheelAxisX, -1, buttons); + wheelEvent.deltaX = -1; break; } case 7: // right { - frame->platformOnMouseWheel (where, kMouseWheelAxisX, 1, buttons); + wheelEvent.deltaX = 1; break; } } + frame->platformOnEvent (wheelEvent); } else // mouse down { - auto buttons = translateMouseButtons (event.detail); - buttons |= translateModifiers (event.state); - doubleClickDetector.onMouseDown (where, buttons, event.time); - auto result = frame->platformOnMouseDown (where, buttons); + MouseDownEvent downEvent; + downEvent.mousePosition = where; + setupMouseEventButtons (downEvent, event.detail); + setupEventModifiers (downEvent.modifiers, event.state); + doubleClickDetector.onEvent (downEvent, event.time); + frame->platformOnEvent (downEvent); grabPointer (); - if (result != kMouseEventNotHandled) + if (downEvent.consumed) { auto xcb = RunLoop::instance ().getXcbConnection (); xcb_set_input_focus (xcb, XCB_INPUT_FOCUS_PARENT, window.getID (), @@ -428,10 +496,12 @@ struct Frame::Impl : IFrameEventHandler } else { - auto buttons = translateMouseButtons (event.detail); - buttons |= translateModifiers (event.state); - doubleClickDetector.onMouseUp (where, buttons, event.time); - frame->platformOnMouseUp (where, buttons); + MouseUpEvent upEvent; + upEvent.mousePosition = where; + setupMouseEventButtons (upEvent, event.detail); + setupEventModifiers (upEvent.modifiers, event.state); + doubleClickDetector.onEvent (upEvent, event.time); + frame->platformOnEvent (upEvent); ungrabPointer (); } } @@ -440,11 +510,12 @@ struct Frame::Impl : IFrameEventHandler //------------------------------------------------------------------------ void onEvent (xcb_motion_notify_event_t& event) override { - CPoint where (event.event_x, event.event_y); - auto buttons = translateMouseButtons (event.state); - buttons |= translateModifiers (event.state); - doubleClickDetector.onMouseMove (where, buttons, event.time); - frame->platformOnMouseMoved (where, buttons); + MouseMoveEvent moveEvent; + moveEvent.mousePosition (event.event_x, event.event_y); + setupMouseEventButtons (moveEvent, event.state); + setupEventModifiers (moveEvent.modifiers, event.state); + doubleClickDetector.onEvent (moveEvent, event.time); + frame->platformOnEvent (moveEvent); // make sure we get more motion events auto xcb = RunLoop::instance ().getXcbConnection (); xcb_get_motion_events (xcb, window.getID (), event.time, event.time + 10000000); @@ -455,10 +526,11 @@ struct Frame::Impl : IFrameEventHandler { if ((event.response_type & ~0x80) == XCB_LEAVE_NOTIFY) { - CPoint where (event.event_x, event.event_y); - auto buttons = translateMouseButtons (event.state); - buttons |= translateModifiers (event.state); - frame->platformOnMouseExited (where, buttons); + MouseExitEvent exitEvent; + exitEvent.mousePosition (event.event_x, event.event_y); + setupMouseEventButtons (exitEvent, event.state); + setupEventModifiers (exitEvent.modifiers, event.state); + frame->platformOnEvent (exitEvent); setCursorInternal (kCursorDefault); } else @@ -644,6 +716,12 @@ bool Frame::getCurrentMouseButtons (CButtonState& buttons) const return false; } +//------------------------------------------------------------------------ +bool Frame::getCurrentModifiers (Modifiers& modifiers) const +{ + return false; +} + //------------------------------------------------------------------------ bool Frame::setMouseCursor (CCursorType type) { @@ -705,7 +783,8 @@ SharedPointer Frame::createPlatformOptionMenu () GenericOptionMenuTheme theme; if (impl->genericOptionMenuTheme) theme = *impl->genericOptionMenuTheme.get (); - auto optionMenu = makeOwned (cFrame, 0, theme); + auto optionMenu = + makeOwned (cFrame, MouseEventButtonState (MouseButton::Left), theme); optionMenu->setListener (this); return optionMenu; } diff --git a/vstgui/lib/platform/linux/x11frame.h b/vstgui/lib/platform/linux/x11frame.h index a78d8788a..0aa88477c 100644 --- a/vstgui/lib/platform/linux/x11frame.h +++ b/vstgui/lib/platform/linux/x11frame.h @@ -33,6 +33,7 @@ class Frame bool getSize (CRect& size) const override; bool getCurrentMousePosition (CPoint& mousePosition) const override; bool getCurrentMouseButtons (CButtonState& buttons) const override; + bool getCurrentModifiers (Modifiers& modifiers) const override; bool setMouseCursor (CCursorType type) override; bool invalidRect (const CRect& rect) override; bool scrollRect (const CRect& src, const CPoint& distance) override; diff --git a/vstgui/lib/platform/linux/x11platform.cpp b/vstgui/lib/platform/linux/x11platform.cpp index 3f97857b5..8f714505d 100644 --- a/vstgui/lib/platform/linux/x11platform.cpp +++ b/vstgui/lib/platform/linux/x11platform.cpp @@ -6,6 +6,7 @@ #include "../../cfileselector.h" #include "../../cframe.h" #include "../../cstring.h" +#include "../../events.h" #include "x11frame.h" #include "x11dragging.h" #include "cairobitmap.h" @@ -31,6 +32,7 @@ #define explicit _explicit #include #undef explicit +#undef None //------------------------------------------------------------------------ namespace VSTGUI { @@ -41,63 +43,64 @@ namespace X11 { //------------------------------------------------------------------------ namespace { -using VirtMap = std::unordered_map; -const VirtMap keyMap = {{XKB_KEY_BackSpace, VKEY_BACK}, - {XKB_KEY_Tab, VKEY_TAB}, - {XKB_KEY_Clear, VKEY_CLEAR}, - {XKB_KEY_Return, VKEY_RETURN}, - {XKB_KEY_Pause, VKEY_PAUSE}, - {XKB_KEY_Escape, VKEY_ESCAPE}, - {XKB_KEY_space, VKEY_SPACE}, - {XKB_KEY_KP_Next, VKEY_NEXT}, - {XKB_KEY_End, VKEY_END}, - {XKB_KEY_Home, VKEY_HOME}, - - {XKB_KEY_Left, VKEY_LEFT}, - {XKB_KEY_Up, VKEY_UP}, - {XKB_KEY_Right, VKEY_RIGHT}, - {XKB_KEY_Down, VKEY_DOWN}, - {XKB_KEY_Page_Up, VKEY_PAGEUP}, - {XKB_KEY_Page_Down, VKEY_PAGEDOWN}, - {XKB_KEY_KP_Page_Up, VKEY_PAGEUP}, - {XKB_KEY_KP_Page_Down, VKEY_PAGEDOWN}, - - {XKB_KEY_Select, VKEY_SELECT}, - {XKB_KEY_Print, VKEY_PRINT}, - {XKB_KEY_KP_Enter, VKEY_ENTER}, - {XKB_KEY_Insert, VKEY_INSERT}, - {XKB_KEY_Delete, VKEY_DELETE}, - {XKB_KEY_Help, VKEY_HELP}, +using VirtMap = std::unordered_map; +const VirtMap keyMap = {{XKB_KEY_BackSpace, VirtualKey::Back}, + {XKB_KEY_Tab, VirtualKey::Tab}, + {XKB_KEY_Clear, VirtualKey::Clear}, + {XKB_KEY_Return, VirtualKey::Return}, + {XKB_KEY_Pause, VirtualKey::Pause}, + {XKB_KEY_Escape, VirtualKey::Escape}, + {XKB_KEY_space, VirtualKey::Space}, + {XKB_KEY_End, VirtualKey::End}, + {XKB_KEY_Home, VirtualKey::Home}, + + {XKB_KEY_Left, VirtualKey::Left}, + {XKB_KEY_Up, VirtualKey::Up}, + {XKB_KEY_Right, VirtualKey::Right}, + {XKB_KEY_Down, VirtualKey::Down}, + {XKB_KEY_Page_Up, VirtualKey::PageUp}, + {XKB_KEY_Page_Down, VirtualKey::PageDown}, + + {XKB_KEY_Select, VirtualKey::Select}, + {XKB_KEY_Print, VirtualKey::Print}, + {XKB_KEY_KP_Enter, VirtualKey::Enter}, + {XKB_KEY_Insert, VirtualKey::Insert}, + {XKB_KEY_Delete, VirtualKey::Delete}, + {XKB_KEY_Help, VirtualKey::Help}, // Numpads ??? - {XKB_KEY_KP_Multiply, VKEY_MULTIPLY}, - {XKB_KEY_KP_Add, VKEY_ADD}, - {XKB_KEY_KP_Separator, VKEY_SEPARATOR}, - {XKB_KEY_KP_Subtract, VKEY_SUBTRACT}, - {XKB_KEY_KP_Decimal, VKEY_DECIMAL}, - {XKB_KEY_KP_Divide, VKEY_DIVIDE}, - {XKB_KEY_F1, VKEY_F1}, - {XKB_KEY_F2, VKEY_F2}, - {XKB_KEY_F3, VKEY_F3}, - {XKB_KEY_F4, VKEY_F4}, - {XKB_KEY_F5, VKEY_F5}, - {XKB_KEY_F6, VKEY_F6}, - {XKB_KEY_F7, VKEY_F7}, - {XKB_KEY_F8, VKEY_F8}, - {XKB_KEY_F9, VKEY_F9}, - {XKB_KEY_F10, VKEY_F10}, - {XKB_KEY_F11, VKEY_F11}, - {XKB_KEY_F12, VKEY_F12}, - {XKB_KEY_Num_Lock, VKEY_NUMLOCK}, - {XKB_KEY_Scroll_Lock, VKEY_SCROLL}, // correct ? + {XKB_KEY_KP_Multiply, VirtualKey::Multiply}, + {XKB_KEY_KP_Add, VirtualKey::Add}, + {XKB_KEY_KP_Separator, VirtualKey::Separator}, + {XKB_KEY_KP_Subtract, VirtualKey::Subtract}, + {XKB_KEY_KP_Decimal, VirtualKey::Decimal}, + {XKB_KEY_KP_Divide, VirtualKey::Divide}, + {XKB_KEY_F1, VirtualKey::F1}, + {XKB_KEY_F2, VirtualKey::F2}, + {XKB_KEY_F3, VirtualKey::F3}, + {XKB_KEY_F4, VirtualKey::F4}, + {XKB_KEY_F5, VirtualKey::F5}, + {XKB_KEY_F6, VirtualKey::F6}, + {XKB_KEY_F7, VirtualKey::F7}, + {XKB_KEY_F8, VirtualKey::F8}, + {XKB_KEY_F9, VirtualKey::F9}, + {XKB_KEY_F10, VirtualKey::F10}, + {XKB_KEY_F11, VirtualKey::F11}, + {XKB_KEY_F12, VirtualKey::F12}, + {XKB_KEY_Num_Lock, VirtualKey::NumLock}, + {XKB_KEY_Scroll_Lock, VirtualKey::Scroll}, // correct ? #if 0 - {XKB_KEY_Shift_L, VKEY_SHIFT}, - {XKB_KEY_Shift_R, VKEY_SHIFT}, - {XKB_KEY_Control_L, VKEY_CONTROL}, - {XKB_KEY_Control_R, VKEY_CONTROL}, - {XKB_KEY_Alt_L, VKEY_ALT}, - {XKB_KEY_Alt_R, VKEY_ALT}, + {XKB_KEY_Shift_L, VirtualKey::SHIFT}, + {XKB_KEY_Shift_R, VirtualKey::SHIFT}, + {XKB_KEY_Control_L, VirtualKey::CONTROL}, + {XKB_KEY_Control_R, VirtualKey::CONTROL}, + {XKB_KEY_Alt_L, VirtualKey::ALT}, + {XKB_KEY_Alt_R, VirtualKey::ALT}, #endif - {XKB_KEY_VoidSymbol, 0}}; + {XKB_KEY_VoidSymbol, VirtualKey::None}}; +const VirtMap shiftKeyMap = {{XKB_KEY_KP_Page_Up, VirtualKey::PageUp}, + {XKB_KEY_KP_Page_Down, VirtualKey::PageDown}, + {XKB_KEY_KP_Home, VirtualKey::Home}, + {XKB_KEY_KP_End, VirtualKey::End}}; //------------------------------------------------------------------------ } // anonymous @@ -117,7 +120,7 @@ struct RunLoop::Impl : IEventHandler xkb_keymap* xkbKeymap {nullptr}; WindowEventHandlerMap windowEventHandlerMap; std::array cursors {{XCB_CURSOR_NONE}}; - VstKeyCode lastUnprocessedKeyEvent; + KeyboardEvent lastUnprocessedKeyEvent; uint32_t lastUtf32KeyEventChar {0}; void init (const SharedPointer& inRunLoop) @@ -142,6 +145,20 @@ struct RunLoop::Impl : IEventHandler XKB_KEYMAP_COMPILE_NO_FLAGS); xkbState = xkb_state_new (xkbKeymap); xkbUnprocessedState = xkb_state_new (xkbKeymap); + + auto xkbStateCookie = xcb_xkb_get_state (xcbConnection, deviceId); + auto* xkbStateReply = xcb_xkb_get_state_reply (xcbConnection, xkbStateCookie, nullptr); + if (xkbStateReply) + { + xkb_state_update_mask (xkbState, + xkbStateReply->baseMods, + xkbStateReply->latchedMods, + xkbStateReply->lockedMods, + xkbStateReply->baseGroup, + xkbStateReply->latchedGroup, + xkbStateReply->lockedGroup); + free (xkbStateReply); + } } } @@ -199,7 +216,8 @@ struct RunLoop::Impl : IEventHandler xcb_window_t targetId = getXdndProxy (windowId); if (targetId != 0) it = windowEventHandlerMap.find (targetId); - if (it != windowEventHandlerMap.end ()) { + if (it != windowEventHandlerMap.end ()) + { it->second->onEvent (cmsg, windowId); return; } @@ -213,25 +231,44 @@ struct RunLoop::Impl : IEventHandler if (!xkbUnprocessedState) return; - VstKeyCode code {}; + KeyboardEvent keyEvent; + keyEvent.type = isKeyDown ? EventType::KeyDown : EventType::KeyUp; if (event.state & XCB_MOD_MASK_SHIFT) - code.modifier |= MODIFIER_SHIFT; + keyEvent.modifiers.add (ModifierKey::Shift); if (event.state & XCB_MOD_MASK_CONTROL) - code.modifier |= MODIFIER_CONTROL; + keyEvent.modifiers.add (ModifierKey::Control); if (event.state & (XCB_MOD_MASK_1 | XCB_MOD_MASK_5)) - code.modifier |= MODIFIER_ALTERNATE; + keyEvent.modifiers.add (ModifierKey::Alt); auto ksym = xkb_state_key_get_one_sym (xkbUnprocessedState, event.detail); - auto it = keyMap.find (ksym); - if (it != keyMap.end ()) - code.virt = it->second; + xkb_state_update_key (xkbState, event.detail, isKeyDown ? XKB_KEY_DOWN : XKB_KEY_UP); + + VirtMap::const_iterator it; + bool ksymMapped = false; + if (!ksymMapped && keyEvent.modifiers.has(ModifierKey::Shift)) + { + it = shiftKeyMap.find (ksym); + ksymMapped = it != shiftKeyMap.end (); + } + if (!ksymMapped) + { + it = keyMap.find (ksym); + ksymMapped = it != keyMap.end (); + } + + if (ksymMapped) + { + keyEvent.virt = it->second; + lastUtf32KeyEventChar = 0; + } else - code.character = xkb_keysym_to_utf32 (ksym); + { + keyEvent.character = xkb_state_key_get_utf32 (xkbState, event.detail); + lastUtf32KeyEventChar = keyEvent.character; + } - xkb_state_update_key (xkbState, event.detail, isKeyDown ? XKB_KEY_DOWN : XKB_KEY_UP); - lastUtf32KeyEventChar = xkb_state_key_get_utf32 (xkbState, event.detail); - lastUnprocessedKeyEvent = code; + lastUnprocessedKeyEvent = std::move (keyEvent); } void onEvent () override @@ -490,9 +527,9 @@ uint32_t RunLoop::getCursorID (CCursorType cursor) } //------------------------------------------------------------------------ -VstKeyCode RunLoop::getCurrentKeyEvent () const +KeyboardEvent&& RunLoop::getCurrentKeyEvent () const { - return impl->lastUnprocessedKeyEvent; + return std::move (impl->lastUnprocessedKeyEvent); } //------------------------------------------------------------------------ diff --git a/vstgui/lib/platform/linux/x11platform.h b/vstgui/lib/platform/linux/x11platform.h index d9769c790..690f69fc7 100644 --- a/vstgui/lib/platform/linux/x11platform.h +++ b/vstgui/lib/platform/linux/x11platform.h @@ -59,7 +59,7 @@ struct RunLoop void unregisterWindowEventHandler (uint32_t windowId); uint32_t getCursorID (CCursorType cursor); - VstKeyCode getCurrentKeyEvent () const; + KeyboardEvent&& getCurrentKeyEvent () const; Optional convertCurrentKeyEventToText () const; static RunLoop& instance (); diff --git a/vstgui/lib/platform/mac/carbon/hiviewframe.cpp b/vstgui/lib/platform/mac/carbon/hiviewframe.cpp deleted file mode 100644 index 895e60ec7..000000000 --- a/vstgui/lib/platform/mac/carbon/hiviewframe.cpp +++ /dev/null @@ -1,1239 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#include "hiviewframe.h" - -#if MAC_CARBON - -#include "../../iplatformtextedit.h" -#include "../../iplatformbitmap.h" -#include "../../iplatformopenglview.h" -#include "../../iplatformviewlayer.h" -#include "../../../cbitmap.h" -#include "../../../cdrawcontext.h" -#include "../../../cdropsource.h" -#include "hiviewtextedit.h" -#include "hiviewoptionmenu.h" -#include "../cgdrawcontext.h" -#include "../cgbitmap.h" -#include "../macglobals.h" -#include "../quartzgraphicspath.h" -#include "../macclipboard.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - -namespace VSTGUI { - -SInt32 pSystemVersion; - -//----------------------------------------------------------------------------- -bool isWindowComposited (WindowRef window) -{ - WindowAttributes attr; - GetWindowAttributes (window, &attr); - if (attr & kWindowCompositingAttribute) - return true; - return false; -} - -static SharedPointer gDragContainer; - -//----------------------------------------------------------------------------- -static CPoint GetMacDragMouse (HIViewFrame* frame, DragRef drag) -{ - HIViewRef view = frame->getPlatformControl (); - CPoint where; - Point r; - if (GetDragMouse (drag, NULL, &r) == noErr) - { - HIPoint location; - location = CGPointMake ((CGFloat)r.h, (CGFloat)r.v); - HIPointConvert (&location, kHICoordSpaceScreenPixel, NULL, kHICoordSpaceView, view); - where.x = (CCoord)location.x; - where.y = (CCoord)location.y; - } - return where; -} - -static bool addViewToContentView = true; -//----------------------------------------------------------------------------- -void HIViewFrame::setAddToContentView (bool addToContentView) -{ - addViewToContentView = addToContentView; -} - -//----------------------------------------------------------------------------- -HIViewFrame::HIViewFrame (IPlatformFrameCallback* frame, const CRect& size, WindowRef parent) -: IPlatformFrame (frame) -, window (parent) -, controlRef (0) -, hasFocus (false) -, isInMouseTracking (false) -, mouseEventHandler (0) -{ - Gestalt (gestaltSystemVersion, &pSystemVersion); - - Rect r = {(short)size.top, (short)size.left, (short)size.bottom, (short)size.right}; - UInt32 features = kControlSupportsDragAndDrop - | kControlSupportsFocus - | kControlHandlesTracking - | kControlSupportsEmbedding - | kHIViewIsOpaque - | kHIViewFeatureDoesNotUseSpecialParts; - OSStatus status = CreateUserPaneControl (0, &r, features, &controlRef); - if (status != noErr) - { - fprintf (stderr, "Could not create Control : %d\n", (int32_t)status); - return; - } - const EventTypeSpec controlEventTypes[] = { - { kEventClassControl, kEventControlDraw}, - { kEventClassControl, kEventControlHitTest}, - { kEventClassControl, kEventControlClick}, - //{kEventClassControl, kEventControlTrack}, - //{kEventClassControl, kEventControlContextualMenuClick}, - { kEventClassKeyboard, kEventRawKeyDown}, - { kEventClassKeyboard, kEventRawKeyRepeat}, - { kEventClassMouse, kEventMouseWheelMoved}, - { kEventClassControl, kEventControlDragEnter}, - { kEventClassControl, kEventControlDragWithin}, - { kEventClassControl, kEventControlDragLeave}, - { kEventClassControl, kEventControlDragReceive}, - { kEventClassControl, kEventControlInitialize}, - { kEventClassControl, kEventControlGetClickActivation}, - { kEventClassControl, kEventControlGetOptimalBounds}, - { kEventClassScrollable,kEventScrollableGetInfo}, - { kEventClassScrollable,kEventScrollableScrollTo}, - { kEventClassControl, kEventControlSetFocusPart}, - { kEventClassControl, kEventControlGetFocusPart}, - { kEventClassControl, kEventControlTrackingAreaExited}, - }; - InstallControlEventHandler (controlRef, carbonEventHandler, GetEventTypeCount (controlEventTypes), controlEventTypes, this, NULL); - - const EventTypeSpec keyWorkaroundEvents[] = { - { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, - { kEventClassWindow, kEventWindowFocusAcquired }, - { kEventClassWindow, kEventWindowFocusRelinquish } - }; - InstallWindowEventHandler (window, carbonEventHandler, GetEventTypeCount (keyWorkaroundEvents), keyWorkaroundEvents, this, &keyboardEventHandler); - const EventTypeSpec mouseEvents[] = { - { kEventClassMouse, kEventMouseDown }, - { kEventClassMouse, kEventMouseUp }, - { kEventClassMouse, kEventMouseMoved }, - { kEventClassMouse, kEventMouseDragged }, - }; - InstallWindowEventHandler (window, carbonMouseEventHandler, GetEventTypeCount (mouseEvents), mouseEvents, this, &mouseEventHandler); - - SetControlDragTrackingEnabled (controlRef, true); - SetAutomaticControlDragTrackingEnabledForWindow (window, true); - - if (addViewToContentView) - { - if (isWindowComposited (window)) - { - HIViewRef contentView; - HIViewRef rootView = HIViewGetRoot (window); - if (HIViewFindByID (rootView, kHIViewWindowContentID, &contentView) != noErr) - contentView = rootView; - HIViewAddSubview (contentView, controlRef); - } - else - { - ControlRef rootControl; - GetRootControl (window, &rootControl); - if (rootControl == NULL) - CreateRootControl (window, &rootControl); - EmbedControl(controlRef, rootControl); - } - } - - HIViewTrackingAreaRef trackingAreaRef; // will automatically removed if view is destroyed - HIViewNewTrackingArea (controlRef, 0, 0, &trackingAreaRef); - - #if 0 - // TODO: currently this is not supported, do we need to ? - size.offset (-size.left, -size.top); - mouseableArea.offset (-size.left, -size.top); - #endif -} - -//----------------------------------------------------------------------------- -HIViewFrame::~HIViewFrame () noexcept -{ - if (keyboardEventHandler) - RemoveEventHandler (keyboardEventHandler); - if (mouseEventHandler) - RemoveEventHandler (mouseEventHandler); - if (controlRef) - { - if (HIViewRemoveFromSuperview (controlRef) == noErr && isWindowComposited (window)) - CFRelease (controlRef); - } -} - -// IPlatformFrame -//----------------------------------------------------------------------------- -bool HIViewFrame::getGlobalPosition (CPoint& pos) const -{ - Rect bounds; - GetWindowBounds (window, kWindowContentRgn, &bounds); - - CCoord x = bounds.left; - CCoord y = bounds.top; - - if (isWindowComposited (window)) - { - HIPoint hip = { 0.f, 0.f }; - HIViewRef contentView; - HIViewFindByID (HIViewGetRoot (window), kHIViewWindowContentID, &contentView); - if (contentView) - HIViewConvertPoint (&hip, controlRef, contentView); - x += (CCoord)hip.x; - y += (CCoord)hip.y; - } - else - { - HIRect hirect; - HIViewGetFrame (controlRef, &hirect); - x += (CCoord)hirect.origin.x; - y += (CCoord)hirect.origin.y; - } - x -= hiScrollOffset.x; - y -= hiScrollOffset.y; - - pos.x = x; - pos.y = y; - return true; -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::setSize (const CRect& newSize) -{ - HIRect frameRect; - HIViewGetFrame (controlRef, &frameRect); - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6 - // keep old values - CCoord oldWidth = frameRect.size.width; - CCoord oldHeight = frameRect.size.height; - - if (window) - { - if (!isWindowComposited (window)) - { - Rect bounds; - GetPortBounds (GetWindowPort (window), &bounds); - SizeWindow (window, (short)((bounds.right - bounds.left) - oldWidth + newSize.getWidth ()), - (short)((bounds.bottom - bounds.top) - oldHeight + newSize.getHeight ()), true); - } - } -#endif - - if (controlRef) - { - HIRect frameRect; - HIViewGetFrame (controlRef, &frameRect); - frameRect.origin.x = static_cast (newSize.left); - frameRect.origin.y = static_cast (newSize.top); - frameRect.size.width = static_cast (newSize.getWidth ()); - frameRect.size.height = static_cast (newSize.getHeight ()); - HIViewSetFrame (controlRef, &frameRect); - } - return true; -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::getSize (CRect& size) const -{ - HIRect hiRect; - if (HIViewGetFrame (controlRef, &hiRect) == noErr) - { - size.left = (CCoord)hiRect.origin.x; - size.top = (CCoord)hiRect.origin.y; - size.setWidth ((CCoord)hiRect.size.width); - size.setHeight ((CCoord)hiRect.size.height); - return true; - } -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6 - Rect bounds; - GetPortBounds (GetWindowPort (window), &bounds); - - size.left = bounds.left; - size.top = bounds.top; - size.right = bounds.right; - size.bottom = bounds.bottom; - return true; -#else - return false; -#endif -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::getCurrentMousePosition (CPoint& mousePosition) const -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6 - // no up-to-date API call available for this, so use old QuickDraw - Point p; - CGrafPtr savedPort; - Boolean portChanged = QDSwapPort (GetWindowPort (window), &savedPort); - GetMouse (&p); - if (portChanged) - QDSwapPort (savedPort, NULL); - mousePosition (p.h, p.v); -#endif - - HIPoint location; - HIViewRef fromView = NULL; - HIViewFindByID (HIViewGetRoot (window), kHIViewWindowContentID, &fromView); - -#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6 - HIGetMousePosition (kHICoordSpaceView, fromView, &location); - location.x = static_cast (floor (location.x + 0.5)); - location.y = static_cast (floor (location.y + 0.5)); -#else - location = CGPointMake (mousePosition.x, mousePosition.y); -#endif - - HIPointConvert (&location, kHICoordSpaceView, fromView, kHICoordSpaceView, controlRef); - mousePosition.x = (CCoord)location.x; - mousePosition.y = (CCoord)location.y; - - return true; -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::getCurrentMouseButtons (CButtonState& buttons) const -{ - UInt32 state = GetCurrentButtonState (); - if (state == kEventMouseButtonPrimary) - buttons |= kLButton; - if (state == kEventMouseButtonSecondary) - buttons |= kRButton; - if (state == kEventMouseButtonTertiary) - buttons |= kMButton; - if (state == 4) - buttons |= kButton4; - if (state == 5) - buttons |= kButton5; - - state = GetCurrentKeyModifiers (); - if (state & cmdKey) - buttons |= kControl; - if (state & shiftKey) - buttons |= kShift; - if (state & optionKey) - buttons |= kAlt; - if (state & controlKey) - buttons |= kApple; - // for the one buttons - if (buttons & kApple && buttons & kLButton) - { - buttons &= ~(kApple | kLButton); - buttons |= kRButton; - } - - return true; -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::setMouseCursor (CCursorType type) -{ - switch (type) - { - case kCursorWait: - SetThemeCursor (kThemeWatchCursor); - break; - case kCursorHSize: - SetThemeCursor (kThemeResizeLeftRightCursor); - break; - case kCursorVSize: - SetThemeCursor (kThemeResizeUpDownCursor); - break; - case kCursorNESWSize: - SetThemeCursor (kThemeCrossCursor); - break; - case kCursorNWSESize: - SetThemeCursor (kThemeCrossCursor); - break; - case kCursorSizeAll: - SetThemeCursor (kThemeCrossCursor); - break; - case kCursorCopy: - SetThemeCursor (kThemeCopyArrowCursor); - break; - case kCursorNotAllowed: - SetThemeCursor (kThemeNotAllowedCursor); - break; - case kCursorHand: - SetThemeCursor (kThemeOpenHandCursor); - break; - default: - SetThemeCursor (kThemeArrowCursor); - break; - } - return true; -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::invalidRect (const CRect& rect) -{ - if (isWindowComposited (window)) - { - HIRect r = { {static_cast(rect.left), static_cast(rect.top)}, {static_cast(rect.getWidth ()), static_cast(rect.getHeight ())} }; - HIViewSetNeedsDisplayInRect (controlRef, &r, true); - } - else - { - HIRect hiRect; - HIViewGetFrame (controlRef, &hiRect); - CRect _rect (rect); -// _rect.offset (size.left, size.top); - _rect.offset ((CCoord)hiRect.origin.x, (CCoord)hiRect.origin.y); - Rect r = {(short)_rect.top, (short)_rect.left, (short)_rect.bottom, (short)_rect.right}; - InvalWindowRect (window, &r); - } - return true; -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::scrollRect (const CRect& _src, const CPoint& distance) -{ - if (isWindowComposited (window)) - { - CRect src (_src); - if (distance.x > 0) - src.right += distance.x; - else if (distance.x < 0) - src.left += distance.x; - if (distance.y > 0) - src.bottom += distance.y; - else if (distance.y < 0) - src.top += distance.y; - CGRect cgRect = CGRectMake ((CGFloat)src.left, (CGFloat)src.top, (CGFloat)src.getWidth (), (CGFloat)src.getHeight ()); - if (HIViewScrollRect (controlRef, &cgRect, (CGFloat)distance.x, (CGFloat)distance.y) == noErr) - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::showTooltip (const CRect& _rect, const char* utf8Text) -{ - CRect rect (_rect); - CPoint p; - getGlobalPosition (p); - rect.offset (p.x, p.y); - - HMHelpContentRec helpContent = {0}; - helpContent.version = 0; - helpContent.absHotRect.left = static_cast (rect.left); - helpContent.absHotRect.right = static_cast (rect.right); - helpContent.absHotRect.top = static_cast (rect.top); - helpContent.absHotRect.bottom = static_cast (rect.bottom); - helpContent.tagSide = kHMDefaultSide; - helpContent.content[0].contentType = kHMCFStringContent; - helpContent.content[0].u.tagCFString = CFStringCreateWithCString (0, utf8Text, kCFStringEncodingUTF8); - HMDisplayTag(&helpContent); - CFRelease (helpContent.content[0].u.tagCFString); - - return true; -} - -//----------------------------------------------------------------------------- -bool HIViewFrame::hideTooltip () -{ - HMHideTag (); - return true; -} - -//----------------------------------------------------------------------------- -SharedPointer HIViewFrame::createPlatformTextEdit (IPlatformTextEditCallback* textEdit) -{ - auto control = makeOwned (controlRef, textEdit); - if (control->getPlatformControl ()) - return control; - return nullptr; -} - -//----------------------------------------------------------------------------- -SharedPointer HIViewFrame::createPlatformOptionMenu () -{ - return makeOwned (); -} - -#if VSTGUI_ENABLE_DEPRECATED_METHODS -//------------------------------------------------------------------------------------ -DragResult HIViewFrame::doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) -{ - DragResult result = kDragError; - PasteboardRef pb; - if (PasteboardCreate (kPasteboardUniqueName, &pb) == noErr) - { - PasteboardClear (pb); - for (uint32_t i = 0; i < source->getCount (); i++) - { - const void* buffer = 0; - IDataPackage::Type type; - uint32_t bufferSize = source->getData (i, buffer, type); - if (bufferSize > 0) - { - switch (type) - { - case IDataPackage::kFilePath: - { - CFURLRef cfUrl = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*)buffer, static_cast (bufferSize), false); - if (cfUrl) - { - CFDataRef dataRef = CFURLCreateData (0, cfUrl, kCFStringEncodingUTF8, false); - if (dataRef) - { - PasteboardPutItemFlavor (pb, (void*)buffer, kUTTypeFileURL, dataRef, kPasteboardFlavorNoFlags); - CFRelease (dataRef); - } - CFRelease (cfUrl); - } - break; - } - case IDataPackage::kText: - { - CFStringRef stringRef = CFStringCreateWithCString (0, (const char*)buffer, kCFStringEncodingUTF8); - if (stringRef) - { - CFDataRef dataRef = CFStringCreateExternalRepresentation (0, stringRef, kCFStringEncodingUTF8, 0); - if (dataRef) - { - PasteboardPutItemFlavor (pb, (void*)buffer, kUTTypeUTF8PlainText, dataRef, kPasteboardFlavorNoFlags); - CFRelease (dataRef); - } - CFRelease (stringRef); - } - break; - } - case IDataPackage::kBinary: - { - CFDataRef dataRef = CFDataCreate (0, (const UInt8*)buffer, static_cast (bufferSize)); - if (dataRef) - { - PasteboardPutItemFlavor (pb, (void*)buffer, kUTTypeData, dataRef, kPasteboardFlavorSenderOnly); - CFRelease (dataRef); - } - break; - } - case IDataPackage::kError: - break; - } - } - } - DragRef drag; - if (NewDragWithPasteboard (pb, &drag) == noErr) - { - EventRecord eventRecord; - EventRef eventRef = GetCurrentEvent (); - if (eventRef && ConvertEventRefToEventRecord (eventRef, &eventRecord)) - { - - CGBitmap* cgBitmap = dragBitmap ? dragBitmap->getPlatformBitmap ().cast () : 0; - CGImageRef cgImage = cgBitmap ? cgBitmap->getCGImage () : 0; - if (cgImage) - { - HIPoint imageOffset = { static_cast(offset.x), static_cast(offset.y) }; - SetDragImageWithCGImage (drag, cgImage, &imageOffset, 0); - } - - if (TrackDrag (drag, &eventRecord, NULL) == noErr) - { - DragActions action; - if (GetDragDropAction (drag, &action) == noErr) - { - if (action == kDragActionNothing) - result = kDragRefused; - else if (action & kDragActionMove) - result = kDragMoved; - else - result = kDragCopied; - } - } - } - DisposeDrag (drag); - } - CFRelease (pb); - } - return result; -} -#endif - -//----------------------------------------------------------------------------- -bool HIViewFrame::doDrag (const DragDescription& dragDescription, const SharedPointer& callback) -{ - return false; -} - - -#define ENABLE_LOGGING 0 - -#if ENABLE_LOGGING -#define LOG_HIPOINT(text,point) fprintf (stdout, "%s%d, %d\n", text, (int32_t)point.x, (int32_t)point.y); -#define LOG(text) fprintf (stdout, "%s\n", text); -#else -#define LOG_HIPOINT(x,y) -#define LOG(x) -#endif - -//----------------------------------------------------------------------------- -pascal OSStatus HIViewFrame::carbonMouseEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) -{ - OSStatus result = eventNotHandledErr; - HIViewFrame* hiviewframe = (HIViewFrame*)inUserData; - IPlatformFrameCallback* frame = hiviewframe->frame; - UInt32 eventClass = GetEventClass (inEvent); - UInt32 eventKind = GetEventKind (inEvent); - WindowRef window = (WindowRef)hiviewframe->window; - HIViewRef hiView = hiviewframe->controlRef; - - HIViewRef view; - if (HIViewGetViewForMouseEvent (HIViewGetRoot (window), inEvent, &view) == noErr) - { - if (view != hiView && !((eventKind == kEventMouseDragged || eventKind == kEventMouseUp) && hiviewframe->isInMouseTracking)) // TODO: make it work as above - return result; - } - switch (eventClass) - { - case kEventClassMouse: - { - UInt32 modifiers = 0; - EventMouseButton buttonState = 0; - CButtonState buttons = 0; - HIPoint location = { 0.f, 0.f }; - if (GetEventParameter (inEvent, kEventParamWindowMouseLocation, typeHIPoint, NULL, sizeof (HIPoint), NULL, &location) == noErr) - { - //LOG_HIPOINT("window :",location) - HIPointConvert (&location, kHICoordSpaceWindow, window, kHICoordSpaceView, hiView); - //LOG_HIPOINT("view :",location) - } - if (!isWindowComposited ((WindowRef)window)) - { - HIRect viewRect; - HIViewGetFrame(hiView, &viewRect); - location.x -= viewRect.origin.x; - location.y -= viewRect.origin.y; - } - GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); - GetEventParameter (inEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof (EventMouseButton), NULL, &buttonState); - if (buttonState == kEventMouseButtonPrimary) - buttons |= kLButton; - if (buttonState == kEventMouseButtonSecondary) - buttons |= kRButton; - if (buttonState == kEventMouseButtonTertiary) - buttons |= kMButton; - if (buttonState == 4) - buttons |= kButton4; - if (buttonState == 5) - buttons |= kButton5; - if (modifiers & cmdKey) - buttons |= kControl; - if (modifiers & shiftKey) - buttons |= kShift; - if (modifiers & optionKey) - buttons |= kAlt; - if (modifiers & controlKey) - buttons |= kApple; - CPoint point ((CCoord)location.x, (CCoord)location.y); - switch (eventKind) - { - case kEventMouseDown: - { - LOG("Mouse Down") - UInt32 clickCount = 0; - GetEventParameter (inEvent, kEventParamClickCount, typeUInt32, NULL, sizeof (UInt32), NULL, &clickCount); - if (clickCount > 1) - buttons |= kDoubleClick; - result = CallNextEventHandler (inHandlerCallRef, inEvent); // calls default handler, which activates the window if not already active, or sets the process to front - CMouseEventResult mer = frame->platformOnMouseDown (point, buttons); - if (mer == kMouseEventHandled) - hiviewframe->isInMouseTracking = true; - if (mer != kMouseEventNotHandled) - result = noErr; - break; - } - case kEventMouseUp: - { - LOG("Mouse Up") - if (frame->platformOnMouseUp (point, buttons) != kMouseEventNotHandled) - result = noErr; - hiviewframe->isInMouseTracking = false; - break; - } - case kEventMouseDragged: - { - //LOG("Mouse Dragged") - if (frame->platformOnMouseMoved (point, buttons) != kMouseEventNotHandled) - result = noErr; - break; - } - case kEventMouseMoved: - { - //LOG("Mouse Moved") - if (IsWindowActive (window)) - { - if (frame->platformOnMouseMoved (point, buttons) != kMouseEventNotHandled) - result = noErr; - } - break; - } - } - break; - } - } - return result; -} - -//------------------------------------------------------------------------------ -static short keyTable[] = { - VKEY_BACK, 0x33, - VKEY_TAB, 0x30, - VKEY_RETURN, 0x24, - VKEY_PAUSE, 0x71, - VKEY_ESCAPE, 0x35, - VKEY_SPACE, 0x31, - - VKEY_END, 0x77, - VKEY_HOME, 0x73, - - VKEY_LEFT, 0x7B, - VKEY_UP, 0x7E, - VKEY_RIGHT, 0x7C, - VKEY_DOWN, 0x7D, - VKEY_PAGEUP, 0x74, - VKEY_PAGEDOWN, 0x79, - - VKEY_PRINT, 0x69, - VKEY_ENTER, 0x4C, - VKEY_HELP, 0x72, - VKEY_DELETE, 0x75, - VKEY_NUMPAD0, 0x52, - VKEY_NUMPAD1, 0x53, - VKEY_NUMPAD2, 0x54, - VKEY_NUMPAD3, 0x55, - VKEY_NUMPAD4, 0x56, - VKEY_NUMPAD5, 0x57, - VKEY_NUMPAD6, 0x58, - VKEY_NUMPAD7, 0x59, - VKEY_NUMPAD8, 0x5B, - VKEY_NUMPAD9, 0x5C, - VKEY_MULTIPLY, 0x43, - VKEY_ADD, 0x45, - VKEY_SUBTRACT, 0x4E, - VKEY_DECIMAL, 0x41, - VKEY_DIVIDE, 0x4B, - VKEY_F1, 0x7A, - VKEY_F2, 0x78, - VKEY_F3, 0x63, - VKEY_F4, 0x76, - VKEY_F5, 0x60, - VKEY_F6, 0x61, - VKEY_F7, 0x62, - VKEY_F8, 0x64, - VKEY_F9, 0x65, - VKEY_F10, 0x6D, - VKEY_F11, 0x67, - VKEY_F12, 0x6F, - VKEY_NUMLOCK, 0x47, - VKEY_EQUALS, 0x51 -}; - -/// @cond ignore -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6 -class VSTGUIDrawRectsHelper -{ -public: - VSTGUIDrawRectsHelper (CFrame* inFrame, CDrawContext* inContext, bool inIsComposited) : frame (inFrame), context (inContext), isComposited (inIsComposited) {} - - CFrame* frame; - CDrawContext* context; - bool isComposited; -}; - -//----------------------------------------------------------------------------- -static void Rect2CRect (Rect &rr, CRect &cr) -{ - cr.left = rr.left; - cr.right = rr.right; - cr.top = rr.top; - cr.bottom = rr.bottom; -} - -static OSStatus VSTGUIDrawRectsProc (UInt16 message, RgnHandle rgn, const Rect* rect, void* refCon) -{ - if (message == kQDRegionToRectsMsgParse) - { - VSTGUIDrawRectsHelper* h = (VSTGUIDrawRectsHelper*)refCon; - CRect r; - Rect2CRect ((Rect&)*rect, r); -// if (!h->isComposited) -// r.offset (-h->context->offsetScreen.x, -h->context->offsetScreen.y); - h->context->saveGlobalState (); - h->frame->drawRect (h->context, r); - h->context->restoreGlobalState (); - } - return noErr; -} -#else -struct CFrameAndCDrawContext { - CFrame* frame; - CDrawContext* context; - - CFrameAndCDrawContext (CFrame* f, CDrawContext* c) : frame (f), context (c) {} -}; - -static OSStatus HIShapeEnumerateProc (int inMessage, HIShapeRef inShape, const CGRect *inRect, void *inRefcon) -{ - if (inMessage == kHIShapeEnumerateRect) - { - CFrameAndCDrawContext* tmp = (CFrameAndCDrawContext*)inRefcon; - CRect r (inRect->origin.x, inRect->origin.y, 0, 0); - r.setWidth (inRect->size.width); - r.setHeight (inRect->size.height); - tmp->context->saveGlobalState (); - tmp->frame->drawRect (tmp->context, r); - tmp->context->restoreGlobalState (); - } - return noErr; -} - -#endif -/// @endcond - -#ifndef kHIViewFeatureGetsFocusOnClick -#define kHIViewFeatureGetsFocusOnClick (1 << 8) -#endif - -bool hiToolboxAllowFocusChange = true; - -//----------------------------------------------------------------------------- -pascal OSStatus HIViewFrame::carbonEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) -{ - OSStatus result = eventNotHandledErr; - HIViewFrame* hiviewframe = (HIViewFrame*)inUserData; - IPlatformFrameCallback* frame = hiviewframe->frame; - UInt32 eventClass = GetEventClass (inEvent); - UInt32 eventKind = GetEventKind (inEvent); - WindowRef window = hiviewframe->window; - - switch (eventClass) - { - #if 0 // TODO: make it work, or remove support for embedding the frame into a native scroll view - case kEventClassScrollable: - { - switch (eventKind) - { - case kEventScrollableGetInfo: - { - HISize cs = {frame->getWidth (), frame->getHeight ()}; - SetEventParameter (inEvent, kEventParamImageSize, typeHISize, sizeof (HISize), &cs); - HIPoint origin = {frame->hiScrollOffset.x, frame->hiScrollOffset.y}; - SetEventParameter (inEvent, kEventParamOrigin, typeHIPoint, sizeof (HIPoint), &origin); - HISize lineSize = {50.0, 20.0}; - SetEventParameter(inEvent, kEventParamLineSize, typeHISize, sizeof(lineSize), &lineSize); - HIRect bounds; - HIViewGetBounds (hiviewframe->controlRef, &bounds); - SetEventParameter(inEvent, kEventParamViewSize, typeHISize, sizeof(bounds.size), &bounds.size); - result = noErr; - break; - } - case kEventScrollableScrollTo: - { - HIPoint where; - GetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, NULL, sizeof(where), NULL, &where); - frame->hiScrollOffset.x = (CCoord)where.x; - frame->hiScrollOffset.y = (CCoord)where.y; - HIViewSetBoundsOrigin(hiviewframe->controlRef, where.x, where.y); - HIViewSetNeedsDisplay(hiviewframe->controlRef, true); - result = noErr; - break; - } - } - break; - } - #endif - case kEventClassControl: - { - switch (eventKind) - { - case kEventControlInitialize: - { - UInt32 controlFeatures = kControlSupportsDragAndDrop | kControlSupportsFocus | kControlHandlesTracking | kControlSupportsEmbedding | kHIViewFeatureGetsFocusOnClick | kHIViewIsOpaque | kHIViewGetsFocusOnClick; - SetEventParameter (inEvent, kEventParamControlFeatures, typeUInt32, sizeof (UInt32), &controlFeatures); - result = noErr; - break; - } - case kEventControlDraw: - { - CDrawContext* context = 0; - HIRect controlBounds; - HIViewGetBounds (hiviewframe->controlRef, &controlBounds); - CRect dirtyRect; - dirtyRect.left = controlBounds.origin.x; - dirtyRect.top = controlBounds.origin.y; - dirtyRect.setWidth (controlBounds.size.width); - dirtyRect.setHeight (controlBounds.size.height); - CGContextRef cgcontext = 0; - OSStatus res = GetEventParameter (inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof (cgcontext), NULL, &cgcontext); - if (res != noErr) - break; - context = new CGDrawContext (cgcontext, dirtyRect); - context->beginDraw (); - - #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6 - RgnHandle dirtyRegion; - if (GetEventParameter (inEvent, kEventParamRgnHandle, typeQDRgnHandle, NULL, sizeof (RgnHandle), NULL, &dirtyRegion) == noErr) - { - CFrame* cframe = dynamic_cast (frame); - VSTGUIDrawRectsHelper helper (cframe, context, isWindowComposited (window)); - RegionToRectsUPP upp = NewRegionToRectsUPP (VSTGUIDrawRectsProc); - QDRegionToRects (dirtyRegion, kQDParseRegionFromTopLeft, upp, &helper); - DisposeRegionToRectsUPP (upp); - } - else - #else - HIShapeRef shapeRef; - if (GetEventParameter (inEvent, kEventParamShape, typeHIShapeRef, NULL, sizeof (HIShapeRef), NULL, &shapeRef) == noErr) - { - CFrame* cframe = dynamic_cast (frame); - CFrameAndCDrawContext tmp (cframe, context); - HIShapeEnumerate (shapeRef, kHIShapeParseFromTopLeft, HIShapeEnumerateProc, &tmp); - } - #endif - { - frame->platformDrawRect (context, dirtyRect); - } - context->endDraw (); - context->forget (); - result = noErr; - break; - } - case kEventControlGetClickActivation: - { - ClickActivationResult activation = kActivateAndHandleClick; - SetEventParameter (inEvent, kEventParamClickActivation, typeClickActivationResult, sizeof (ClickActivationResult), &activation); - result = noErr; - break; - } - case kEventControlHitTest: - { - ControlPartCode code = kControlContentMetaPart; - SetEventParameter (inEvent, kEventParamControlPart, typeControlPartCode, sizeof (ControlPartCode), &code); - result = noErr; - break; - } - case kEventControlClick: - { - return noErr; - EventMouseButton buttonState; - GetEventParameter (inEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof (EventMouseButton), NULL, &buttonState); - if (buttonState == kEventMouseButtonPrimary) - { - result = CallNextEventHandler (inHandlerCallRef, inEvent); - break; - } - } - case kEventControlTrack: - case kEventControlContextualMenuClick: - { - break; - } - #if 0 // TODO: is this necessary - case kEventControlGetOptimalBounds: - { - HIRect optimalBounds = { {0, 0}, { frame->getWidth (), frame->getHeight ()}}; - SetEventParameter (inEvent, kEventParamControlOptimalBounds, typeHIRect, sizeof (HIRect), &optimalBounds); - result = noErr; - break; - } - #endif - case kEventControlGetFocusPart: - { - if (hiToolboxAllowFocusChange) - { - ControlPartCode code = hiviewframe->hasFocus ? 127 : kControlFocusNoPart; - SetEventParameter (inEvent, kEventParamControlPart, typeControlPartCode, sizeof (ControlPartCode), &code); - result = noErr; - } - break; - } - case kEventControlSetFocusPart: - { - if (hiToolboxAllowFocusChange) - { - CFrame* cframe = dynamic_cast (frame); - if (cframe) - { - ControlPartCode code; - GetEventParameter (inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof (ControlPartCode), NULL, &code); - if (code == kControlFocusNoPart) - { - hiviewframe->hasFocus = false; - } - else - { - bool anfResult = false; - if (code == kControlFocusNextPart) - anfResult = cframe->advanceNextFocusView (cframe->getFocusView ()); - else if (code == kControlFocusPrevPart) - anfResult = cframe->advanceNextFocusView (cframe->getFocusView (), true); - if (anfResult) - { - hiviewframe->hasFocus = true; - code = 127; - } - else - { - hiviewframe->hasFocus = false; - code = kControlFocusNoPart; - } - SetEventParameter (inEvent, kEventParamControlPart, typeControlPartCode, sizeof (code), &code); - } - result = noErr; - } - } - break; - } - case kEventControlDragEnter: - { - DragRef dragRef; - if (GetEventParameter (inEvent, kEventParamDragRef, typeDragRef, NULL, sizeof (DragRef), NULL, &dragRef) == noErr) - { - gDragContainer = MacClipboard::createCarbonDragDataPackage (dragRef); - - CPoint where = GetMacDragMouse (hiviewframe, dragRef); - hiviewframe->setMouseCursor (kCursorNotAllowed); - DragEventData data {gDragContainer, where, 0}; - frame->platformOnDragEnter (data); - - Boolean acceptDrop = true; - SetEventParameter (inEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof (Boolean), &acceptDrop); - } - result = noErr; - break; - } - case kEventControlDragWithin: - { - DragRef dragRef; - if (gDragContainer && GetEventParameter (inEvent, kEventParamDragRef, typeDragRef, NULL, sizeof (DragRef), NULL, &dragRef) == noErr) - { - CPoint where = GetMacDragMouse (hiviewframe, dragRef); - DragEventData data {gDragContainer, where, 0}; - frame->platformOnDragMove (data); - } - result = noErr; - break; - } - case kEventControlDragLeave: - { - DragRef dragRef; - if (gDragContainer && GetEventParameter (inEvent, kEventParamDragRef, typeDragRef, NULL, sizeof (DragRef), NULL, &dragRef) == noErr) - { - CPoint where = GetMacDragMouse (hiviewframe, dragRef); - DragEventData data {gDragContainer, where, 0}; - frame->platformOnDragLeave (data); - hiviewframe->setMouseCursor (kCursorDefault); - } - result = noErr; - break; - } - case kEventControlDragReceive: - { - DragRef dragRef; - if (gDragContainer && GetEventParameter (inEvent, kEventParamDragRef, typeDragRef, NULL, sizeof (DragRef), NULL, &dragRef) == noErr) - { - CPoint where = GetMacDragMouse (hiviewframe, dragRef); - DragEventData data {gDragContainer, where, 0}; - frame->platformOnDrop (data); - hiviewframe->setMouseCursor (kCursorDefault); - gDragContainer = nullptr; - } - result = noErr; - break; - } - case kEventControlTrackingAreaExited: - { - HIPoint location = { 0.f, 0.f }; - if (GetEventParameter (inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof (HIPoint), NULL, &location) == noErr) - { - if (!isWindowComposited (window)) - { - HIRect viewRect; - HIViewGetFrame (hiviewframe->controlRef, &viewRect); - location.x -= viewRect.origin.x; - location.y -= viewRect.origin.y; - } - CPoint point ((CCoord)location.x, (CCoord)location.y); - frame->platformOnMouseExited (point, 0); - } - break; - } - } - break; - } - case kEventClassMouse: - { - switch (eventKind) - { - case kEventMouseWheelMoved: - { - UInt32 modifiers; - HIPoint windowHIPoint; - SInt32 wheelDelta; - EventMouseWheelAxis wheelAxis; - WindowRef windowRef; - GetEventParameter (inEvent, kEventParamWindowRef, typeWindowRef, NULL, sizeof (WindowRef), NULL, &windowRef); - GetEventParameter (inEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof (EventMouseWheelAxis), NULL, &wheelAxis); - GetEventParameter (inEvent, kEventParamMouseWheelDelta, typeSInt32, NULL, sizeof (SInt32), NULL, &wheelDelta); - GetEventParameter (inEvent, kEventParamWindowMouseLocation, typeHIPoint, NULL, sizeof (HIPoint), NULL, &windowHIPoint); - GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); - CButtonState buttons = 0; - if (modifiers & cmdKey) - buttons |= kControl; - if (modifiers & shiftKey) - buttons |= kShift; - if (modifiers & optionKey) - buttons |= kAlt; - if (modifiers & controlKey) - buttons |= kApple; - - HIPointConvert (&windowHIPoint, kHICoordSpaceWindow, windowRef, kHICoordSpaceView, hiviewframe->controlRef); - - // non-compositing window controls need to handle offset themselves - if (!isWindowComposited (windowRef)) - { - HIRect viewRect; - HIViewGetFrame(hiviewframe->controlRef, &viewRect); - windowHIPoint.x -= viewRect.origin.x; - windowHIPoint.y -= viewRect.origin.y; - } - - CPoint p ((CCoord)windowHIPoint.x, (CCoord)windowHIPoint.y); - float distance = wheelDelta; - CMouseWheelAxis axis = kMouseWheelAxisY; - if (wheelAxis == kEventMouseWheelAxisX) - axis = kMouseWheelAxisX; - frame->platformOnMouseWheel (p, axis, distance, buttons); - result = noErr; - break; - } - } - break; - } - case kEventClassTextInput: - { - switch (eventKind) - { - case kEventTextInputUnicodeForKeyEvent: - { - // The "Standard Event Handler" of a window would return noErr even though no one has handled the key event. - // This prevents the "Standard Handler" to be called for this event, with the exception of the tab key as it is used for control focus changes. - EventRef rawKeyEvent; - GetEventParameter (inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof (EventRef), NULL, &rawKeyEvent); - if (rawKeyEvent) - { - UInt32 keyCode = 0; - GetEventParameter (rawKeyEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &keyCode); - if (keyCode == (UInt32)keyTable[VKEY_TAB+1]) - return result; - } - result = eventPassToNextTargetErr; - break; - } - } - break; - } - case kEventClassWindow: - { - switch (eventKind) - { - case kEventWindowFocusAcquired: - { - frame->platformOnActivate (true); - break; - } - case kEventWindowFocusRelinquish: - { - frame->platformOnActivate (false); - break; - } - } - break; - } - case kEventClassKeyboard: - { - if (hiviewframe->hasFocus) - { - switch (eventKind) - { - case kEventRawKeyDown: - case kEventRawKeyRepeat: - { - // todo: make this work - - char character = 0; - UInt32 keyCode = 0; - UInt32 modifiers = 0; - GetEventParameter (inEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof (char), NULL, &character); - GetEventParameter (inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &keyCode); - GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); - char scanCode = static_cast (keyCode); - VstKeyCode vstKeyCode; - memset (&vstKeyCode, 0, sizeof (VstKeyCode)); - KeyboardLayoutRef layout; - if (KLGetCurrentKeyboardLayout (&layout) == noErr) - { - const void* pKCHR = 0; - KLGetKeyboardLayoutProperty (layout, kKLKCHRData, &pKCHR); - if (pKCHR) - { - static UInt32 keyTranslateState = 0; - vstKeyCode.character = static_cast (KeyTranslate (pKCHR, static_cast (keyCode), &keyTranslateState)); - if (modifiers & shiftKey) - { - vstKeyCode.character = toupper (vstKeyCode.character); - } - } - } - short entries = sizeof (keyTable) / (sizeof (short)); - for (int32_t i = 0; i < entries; i += 2) - { - if (keyTable[i + 1] == scanCode) - { - vstKeyCode.virt = static_cast (keyTable[i]); - vstKeyCode.character = 0; - break; - } - } - if (modifiers & cmdKey) - vstKeyCode.modifier |= MODIFIER_CONTROL; - if (modifiers & shiftKey) - vstKeyCode.modifier |= MODIFIER_SHIFT; - if (modifiers & optionKey) - vstKeyCode.modifier |= MODIFIER_ALTERNATE; - if (modifiers & controlKey) - vstKeyCode.modifier |= MODIFIER_COMMAND; - if (frame->platformOnKeyDown (vstKeyCode)) - result = noErr; - - break; - } - } - } - break; - } - } - return result; -} - - -} // VSTGUI - -#pragma clang diagnostic pop - -#endif // MAC_CARBON diff --git a/vstgui/lib/platform/mac/carbon/hiviewframe.h b/vstgui/lib/platform/mac/carbon/hiviewframe.h deleted file mode 100644 index 1074fc01e..000000000 --- a/vstgui/lib/platform/mac/carbon/hiviewframe.h +++ /dev/null @@ -1,73 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "../../../cframe.h" - -#if MAC_CARBON - -#include "../../iplatformframe.h" -#include - -namespace VSTGUI { - -extern bool isWindowComposited (WindowRef window); - -//----------------------------------------------------------------------------- -class HIViewFrame : public IPlatformFrame -{ -public: - static void setAddToContentView (bool addToContentView); // defaults to true - - HIViewFrame (IPlatformFrameCallback* frame, const CRect& size, WindowRef parent); - ~HIViewFrame () noexcept; - - HIViewRef getPlatformControl () const { return controlRef; } - const CPoint& getScrollOffset () const { return hiScrollOffset; } - - // IPlatformFrame - bool getGlobalPosition (CPoint& pos) const override; - bool setSize (const CRect& newSize) override; - bool getSize (CRect& size) const override; - bool getCurrentMousePosition (CPoint& mousePosition) const override; - bool getCurrentMouseButtons (CButtonState& buttons) const override; - bool setMouseCursor (CCursorType type) override; - bool invalidRect (const CRect& rect) override; - bool scrollRect (const CRect& src, const CPoint& distance) override; - bool showTooltip (const CRect& rect, const char* utf8Text) override; - bool hideTooltip () override; - void* getPlatformRepresentation () const override { return controlRef; } - SharedPointer createPlatformTextEdit (IPlatformTextEditCallback* textEdit) override; - SharedPointer createPlatformOptionMenu () override; -#if VSTGUI_OPENGL_SUPPORT - SharedPointer createPlatformOpenGLView () override { return nullptr; } // not supported -#endif - SharedPointer createPlatformViewLayer (IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer = nullptr) override { return 0; } // not supported -#if VSTGUI_ENABLE_DEPRECATED_METHODS - DragResult doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) override; -#endif - bool doDrag (const DragDescription& dragDescription, const SharedPointer& callback) override; - PlatformType getPlatformType () const override { return PlatformType::kWindowRef; } - void onFrameClosed () override {} - Optional convertCurrentKeyEventToText () override { return {}; } - bool setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme = nullptr) override { return false; } - -//----------------------------------------------------------------------------- -protected: - static pascal OSStatus carbonMouseEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); - static pascal OSStatus carbonEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); - - WindowRef window; - HIViewRef controlRef; - bool hasFocus; - bool isInMouseTracking; - EventHandlerRef mouseEventHandler; - EventHandlerRef keyboardEventHandler; - CPoint hiScrollOffset; -}; - -} // VSTGUI - -#endif // MAC_CARBON diff --git a/vstgui/lib/platform/mac/carbon/hiviewoptionmenu.cpp b/vstgui/lib/platform/mac/carbon/hiviewoptionmenu.cpp deleted file mode 100644 index 32178ae8f..000000000 --- a/vstgui/lib/platform/mac/carbon/hiviewoptionmenu.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#include "hiviewoptionmenu.h" - -#if MAC_CARBON - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - -#include "../../../controls/coptionmenu.h" -#include "../../../cframe.h" -#include "../../../cbitmap.h" -#include "../cgbitmap.h" - -namespace VSTGUI { - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 -using URefCon = UInt32; -#endif - -//----------------------------------------------------------------------------- -void HIViewOptionMenu::popup (COptionMenu* optionMenu, const Callback& callback) -{ - vstgui_assert (optionMenu && callback, "arguments are required"); - - PlatformOptionMenuResult popupResult = {0}; - - //---Transform local coordinates to global coordinates - CRect rect (optionMenu->getViewSize ()); - CPoint p (0, 0); - optionMenu->localToFrame (p); - rect.offset (p.x, p.y); - - int32_t offset; - - if (optionMenu->isPopupStyle ()) - offset = 0; - else - offset = (int32_t)optionMenu->getViewSize ().getHeight (); - - CCoord gx = 0, gy = 0; - optionMenu->getFrame()->getPosition (gx, gy); - gy += rect.top + offset; - gx += rect.left; - - MenuRef menuRef = createMenu (optionMenu); - if (menuRef) - { - CalcMenuSize (menuRef); - SInt16 menuWidth = GetMenuWidth (menuRef); - if (menuWidth < optionMenu->getViewSize ().getWidth ()) - SetMenuWidth (menuRef, static_cast (optionMenu->getViewSize ().getWidth ())); - int32_t popUpItem = optionMenu->isPopupStyle () ? (static_cast (optionMenu->getValue ()) + 1) : 1; - int32_t PopUpMenuItem = PopUpMenuItem = PopUpMenuSelect (menuRef, static_cast (gy), static_cast (gx), static_cast (popUpItem)); - - short result = LoWord (PopUpMenuItem) - 1; - short menuIDResult = HiWord (PopUpMenuItem); - if (menuIDResult != 0) - { - MenuRef usedMenuRef = GetMenuHandle (menuIDResult); - if (usedMenuRef) - { - COptionMenu* resultMenu = 0; - if (GetMenuItemRefCon (usedMenuRef, 0, (URefCon*)&resultMenu) == noErr) - { - popupResult.menu = resultMenu; - popupResult.index = result; - } - } - } - CFRelease (menuRef); - } - - callback (optionMenu, popupResult); -} - -//----------------------------------------------------------------------------- -MenuRef HIViewOptionMenu::createMenu (COptionMenu* menu) -{ - MenuRef menuRef = 0; - ResID menuID = UniqueID ('MENU'); - CMenuItemList* menuItems = menu->getItems (); - if (menuItems && CreateNewMenu (menuID, kMenuAttrCondenseSeparators, &menuRef) == noErr) - { - bool multipleCheck = menu->isMultipleCheckStyle () && !menu->isCheckStyle (); - CConstMenuItemIterator it = menuItems->begin (); - MenuItemIndex i = 0; - while (it != menuItems->end ()) - { - i++; - CMenuItem* item = (*it); - if (item->isSeparator ()) - AppendMenuItemTextWithCFString (menuRef, CFSTR(""), kMenuItemAttrSeparator, 0, NULL); - else - { - CFStringRef itemString = CFStringCreateWithCString (NULL, item->getTitle (), kCFStringEncodingUTF8); - if (menu->getPrefixNumbers ()) - { - CFStringRef prefixString = 0; - switch (menu->getPrefixNumbers ()) - { - case 2: - prefixString = CFStringCreateWithFormat (NULL, 0, CFSTR("%1d "),i); break; - case 3: - prefixString = CFStringCreateWithFormat (NULL, 0, CFSTR("%02d "),i); break; - case 4: - prefixString = CFStringCreateWithFormat (NULL, 0, CFSTR("%03d "),i); break; - } - CFMutableStringRef newItemString = CFStringCreateMutable (0, 0); - CFStringAppend (newItemString, prefixString); - CFStringAppend (newItemString, itemString); - CFRelease (itemString); - CFRelease (prefixString); - itemString = newItemString; - } - if (itemString == 0) - continue; - MenuItemAttributes itemAttribs = kMenuItemAttrIgnoreMeta; - if (!item->isEnabled ()) - itemAttribs |= kMenuItemAttrDisabled; - if (item->isTitle ()) - itemAttribs |= kMenuItemAttrSectionHeader; - - InsertMenuItemTextWithCFString (menuRef, itemString, i, itemAttribs, 0); - - if (item->isChecked () && multipleCheck) - CheckMenuItem (menuRef, i, true); - if (item->getSubmenu ()) - { - MenuRef submenu = createMenu (item->getSubmenu ()); - if (submenu) - { - SetMenuItemHierarchicalMenu (menuRef, i, submenu); - CFRelease (submenu); - } - } - if (item->getIcon ()) - { - IPlatformBitmap* platformBitmap = item->getIcon ()->getPlatformBitmap (); - CGBitmap* cgBitmap = platformBitmap ? dynamic_cast (platformBitmap) : 0; - CGImageRef image = cgBitmap ? cgBitmap->getCGImage () : 0; - if (image) - { - SetMenuItemIconHandle (menuRef, i, kMenuCGImageRefType, (Handle)image); - } - } - if (item->getKeycode ().empty () == false) - { - SetItemCmd (menuRef, i, static_cast (toupper (item->getKeycode ()[0]))); - UInt8 keyModifiers = 0; - int32_t itemModifiers = item->getKeyModifiers (); - if (itemModifiers & kShift) - keyModifiers |= kMenuShiftModifier; - if (!(itemModifiers & kControl)) - keyModifiers |= kMenuNoCommandModifier; - if (itemModifiers & kAlt) - keyModifiers |= kMenuOptionModifier; - if (itemModifiers & kApple) - keyModifiers |= kMenuControlModifier; - - SetMenuItemModifiers (menuRef, i, keyModifiers); - } - CFRelease (itemString); - } - it++; - } - if (menu->isCheckStyle () && !multipleCheck) - CheckMenuItem (menuRef, static_cast (menu->getCurrentIndex (true) + 1), true); - SetMenuItemRefCon (menuRef, 0, (URefCon)menu); - InsertMenu (menuRef, kInsertHierarchicalMenu); - } - return menuRef; -} - -} // VSTGUI - -#pragma clang diagnostic pop - -#endif // MAC_CARBON diff --git a/vstgui/lib/platform/mac/carbon/hiviewtextedit.cpp b/vstgui/lib/platform/mac/carbon/hiviewtextedit.cpp deleted file mode 100644 index 23aca3431..000000000 --- a/vstgui/lib/platform/mac/carbon/hiviewtextedit.cpp +++ /dev/null @@ -1,305 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#include "hiviewtextedit.h" -#include "hiviewframe.h" -#include "../cfontmac.h" -#include "../macstring.h" - -#if MAC_CARBON - -#include "../../iplatformopenglview.h" -#include "../../iplatformviewlayer.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - -namespace VSTGUI { - -//----------------------------------------------------------------------------- -HIViewTextEdit::HIViewTextEdit (HIViewRef parent, IPlatformTextEditCallback* textEdit) -: IPlatformTextEdit (textEdit) -, eventHandler (0) -, text (0) -{ - extern bool hiToolboxAllowFocusChange; - bool oldState = hiToolboxAllowFocusChange; - hiToolboxAllowFocusChange = false; - - WindowRef window = HIViewGetWindow (parent); - CRect rect = textEdit->platformGetSize (); - CFontRef fontID = textEdit->platformGetFont (); - - if (!isWindowComposited (window)) - { - HIRect hiRect; - HIViewGetFrame (parent, &hiRect); - rect.offset ((CCoord)hiRect.origin.x, (CCoord)hiRect.origin.y); - } - Rect r; - r.left = (short)rect.left;// + 2; - r.right = (short)rect.right;// - 4; - r.top = (short)rect.top;// + 2; - r.bottom = (short)rect.bottom;// - 4; - if (rect.getHeight () > fontID->getSize ()) - { - r.top = (short)(rect.top + rect.getHeight () / 2 - fontID->getSize () / 2 + 1); - r.bottom = (short)(r.top + fontID->getSize ()); - } - HIViewRef textControl = 0; - UTF8StringPtr text = textEdit->platformGetText (); - if (CreateEditUnicodeTextControl (NULL, &r, NULL, false, NULL, &textControl) == noErr) - { - HIViewAddSubview (parent, textControl); - EventTypeSpec eventTypes[] = { - { kEventClassWindow, kEventWindowDeactivated }, - { kEventClassKeyboard, kEventRawKeyDown }, - { kEventClassKeyboard, kEventRawKeyRepeat }, - { kEventClassControl, kEventControlDraw }, - { kEventClassTextField, kEventTextDidChange } - }; - InstallControlEventHandler (textControl, CarbonEventsTextControlProc, GetEventTypeCount (eventTypes), eventTypes, this, &eventHandler); - platformControl = textControl; - - setText (text); - - ControlEditTextSelectionRec selection; - selection.selStart = 0; - selection.selEnd = static_cast (strlen (text)); - SetControlData (platformControl, kControlEditTextPart, kControlEditTextSelectionTag, sizeof (ControlEditTextSelectionRec), &selection); - - Boolean singleLineStyle = true; - SetControlData (textControl, kControlEditTextPart, kControlEditTextSingleLineTag, sizeof (Boolean), &singleLineStyle); - ControlFontStyleRec fontStyle; - memset (&fontStyle, 0, sizeof (fontStyle)); - fontStyle.flags = kControlUseJustMask | kControlUseSizeMask | kControlUseFontMask; - switch (textEdit->platformGetHoriTxtAlign ()) - { - case kLeftText: fontStyle.just = teFlushLeft; break; - case kRightText: fontStyle.just = teFlushRight; break; - default: fontStyle.just = teCenter; break; - } - fontStyle.size = static_cast (fontID->getSize ()); - #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6 - Str255 fontName; - CopyCStringToPascal (fontID->getName (), fontName); - GetFNum (fontName, &fontStyle.font); - #else - if (auto ctFont = fontID->getPlatformFont ().cast ()) - { - if (auto ctFontRef = ctFont->getFontRef ()) - HIViewSetTextFont (textControl, kControlEditTextPart, ctFontRef); - } - #endif - SetControlData (textControl, kControlEditTextPart, kControlFontStyleTag, sizeof (fontStyle), &fontStyle); - HIViewSetVisible (textControl, true); - HIViewAdvanceFocus (textControl, 0); - SetKeyboardFocus (window, textControl, kControlEditTextPart); - SetUserFocusWindow (window); - } - hiToolboxAllowFocusChange = oldState; -} - -//----------------------------------------------------------------------------- -HIViewTextEdit::~HIViewTextEdit () noexcept -{ - if (eventHandler) - RemoveEventHandler (eventHandler); - if (platformControl) - { - HIViewSetVisible (platformControl, false); - HIViewRemoveFromSuperview (platformControl); - SetUserFocusWindow (kUserFocusAuto); - CFRelease (platformControl); - SetThemeCursor (kThemeArrowCursor); - } - freeText (); -} - -//----------------------------------------------------------------------------- -void HIViewTextEdit::freeText () -{ - if (text) - std::free (text); - text = 0; -} - -//----------------------------------------------------------------------------- -bool HIViewTextEdit::setText (const UTF8String& text) -{ - if (platformControl) - { - CFStringRef textString = fromUTF8String (text); - SetControlData (platformControl, kControlEditTextPart, kControlEditTextCFStringTag, sizeof (CFStringRef), &textString); - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -UTF8String HIViewTextEdit::getText () -{ - if (platformControl) - { - CFStringRef cfstr; - if (GetControlData (platformControl, kControlEditTextPart, kControlEditTextCFStringTag, sizeof cfstr, (void*)&cfstr, NULL) == noErr) - { - freeText (); - CFIndex textSize = CFStringGetMaximumSizeForEncoding (CFStringGetLength (cfstr), kCFStringEncodingUTF8); - text = static_cast (std::malloc (static_cast (textSize))); - - CFStringGetCString (cfstr, text, textSize, kCFStringEncodingUTF8); - CFRelease (cfstr); - return text; - } - } - return ""; -} - -//----------------------------------------------------------------------------- -bool HIViewTextEdit::updateSize () -{ - if (platformControl) - { - CRect rect = textEdit->platformGetSize (); - if (!isWindowComposited (HIViewGetWindow (platformControl))) - { - HIRect hiRect; - HIViewGetFrame (HIViewGetSuperview (platformControl), &hiRect); - rect.offset ((CCoord)hiRect.origin.x, (CCoord)hiRect.origin.y); - } - HIRect r; - r.origin.x = static_cast (rect.left); - r.origin.y = static_cast (rect.top); - r.size.width = static_cast (rect.getWidth ()); - r.size.height = static_cast (rect.getHeight ()); - if (HIViewSetFrame (platformControl, &r) == noErr) - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -pascal OSStatus HIViewTextEdit::CarbonEventsTextControlProc (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) -{ - OSStatus result = eventNotHandledErr; - UInt32 eventClass = GetEventClass (inEvent); - UInt32 eventKind = GetEventKind (inEvent); - HIViewTextEdit* textEdit = (HIViewTextEdit*)inUserData; - - switch (eventClass) - { - case kEventClassKeyboard: - { - switch (eventKind) - { - case kEventRawKeyDown: - case kEventRawKeyRepeat: - { - char macCharCode; - UInt32 keyCode; - UInt32 modifiers; - GetEventParameter (inEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof (char), NULL, &macCharCode); - GetEventParameter (inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &keyCode); - GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); - if (macCharCode == 13 || macCharCode == 3 || macCharCode == 27) - { - if (macCharCode == 27) - { - textEdit->setText (textEdit->textEdit->platformGetText ()); - } - textEdit->textEdit->platformLooseFocus (macCharCode == 27 ? false : true); - - result = noErr; - } - break; - } - } - break; - } - case kEventClassTextField: - { - switch (eventKind) - { - case kEventTextDidChange: - { - textEdit->textEdit->platformTextDidChange (); - break; - } - } - break; - } - case kEventClassControl: - { - switch (eventKind) - { - #if 0 // TODO: make this work - #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) - case kEventControlDraw: - { - CGContextRef cgContext; - if (GetEventParameter (inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof (cgContext), NULL, &cgContext) == noErr) - { - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 - if (HIRectConvert) - #endif - { - CRect viewSize = textEdit->getViewSize (viewSize); - viewSize.extend (10, 10); - CViewContainer* container = (CViewContainer*)textEdit->getParentView (); - while (!container->isTypeOf ("CScrollContainer")) - { - CRect containerSize = container->getViewSize (containerSize); - viewSize.offset (containerSize.left, containerSize.top); - if (container == container->getParentView () || container->getParentView () == 0) - break; - container = ((CViewContainer*)container->getParentView ()); - } - viewSize = container->getVisibleSize (viewSize); - CPoint cp (viewSize.left, viewSize.top); - container->localToFrame (cp); - viewSize.offset (-viewSize.left, -viewSize.top); - viewSize.offset (cp.x, cp.y); - CGRect cgViewSize = CGRectMake (viewSize.left, viewSize.top, viewSize.getWidth (), viewSize.getHeight ()); - HIRectConvert (&cgViewSize, kHICoordSpaceView, (HIViewRef)textEdit->getFrame ()->getPlatformControl (), kHICoordSpaceView, textEdit->platformControl); - CGContextClipToRect (cgContext, cgViewSize); - CGAffineTransform ctm = CGContextGetCTM (cgContext); - } - result = CallNextEventHandler (inHandlerCallRef, inEvent); - } - break; - } - #endif - #endif - } - break; - } - case kEventClassWindow: - { - WindowRef window; - if (GetEventParameter (inEvent, kEventParamDirectObject, typeWindowRef, NULL, sizeof (WindowRef), NULL, &window) != noErr) - break; - switch (eventKind) - { - case kEventWindowDeactivated: - { -// result = CallNextEventHandler (inHandlerCallRef, inEvent); -// ClearKeyboardFocus (window); - - textEdit->textEdit->platformLooseFocus (false); - - break; - } - } - break; - } - } - return result; -} - -} // VSTGUI - -#pragma clang diagnostic pop - -#endif diff --git a/vstgui/lib/platform/mac/carbon/hiviewtextedit.h b/vstgui/lib/platform/mac/carbon/hiviewtextedit.h deleted file mode 100644 index 23770a587..000000000 --- a/vstgui/lib/platform/mac/carbon/hiviewtextedit.h +++ /dev/null @@ -1,41 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "../../iplatformtextedit.h" - -#if MAC_CARBON - -#include - -namespace VSTGUI { - -//----------------------------------------------------------------------------- -class HIViewTextEdit : public IPlatformTextEdit -{ -public: - HIViewTextEdit (HIViewRef parent, IPlatformTextEditCallback* textEdit); - ~HIViewTextEdit () noexcept; - - UTF8String getText () override; - bool setText (const UTF8String& text) override; - bool updateSize () override; - bool drawsPlaceholder () const override { return false; } - - HIViewRef getPlatformControl () const { return platformControl; } -//----------------------------------------------------------------------------- -protected: - void freeText (); - - HIViewRef platformControl; - EventHandlerRef eventHandler; - UTF8StringBuffer text; - - static pascal OSStatus CarbonEventsTextControlProc (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); -}; - -} // VSTGUI - -#endif // MAC_CARBON diff --git a/vstgui/lib/platform/mac/caviewlayer.mm b/vstgui/lib/platform/mac/caviewlayer.mm index 939695900..ecc07ddaa 100644 --- a/vstgui/lib/platform/mac/caviewlayer.mm +++ b/vstgui/lib/platform/mac/caviewlayer.mm @@ -68,110 +68,122 @@ - (void)drawInContext:(CGContextRef)ctx #else #import "cocoa/cocoahelpers.h" #import "cocoa/autoreleasepool.h" +#import "cocoa/objcclassbuilder.h" //----------------------------------------------------------------------------- @interface VSTGUI_CALayer : CALayer //----------------------------------------------------------------------------- - (void)setDrawDelegate:(VSTGUI::IPlatformViewLayerDelegate*)viewLayerDelegate; @end -static Class viewLayerClass = nullptr; - //----------------------------------------------------------------------------- -static id VSTGUI_CALayer_Init (id self, SEL _cmd) +struct VSTGUI_macOS_CALayer { - __OBJC_SUPER (self) - self = SuperInit (SUPER, @selector (init)); - if (self) + static id newInstance () { - [self setNeedsDisplayOnBoundsChange:YES]; + return [[instance ().viewLayerClass alloc] init]; } - return self; -} -//----------------------------------------------------------------------------- -static id VSTGUI_CALayer_ActionForKey (id self, SEL _cmd, NSString* event) -{ - return nil; -} +private: + static constexpr const auto viewLayerDelegateVarName = "_viewLayerDelegate"; -//----------------------------------------------------------------------------- -static void VSTGUI_CALayer_SetDrawDelegate (id self, SEL _cmd, VSTGUI::IPlatformViewLayerDelegate* delegate) -{ - OBJC_SET_VALUE (self, _viewLayerDelegate, delegate); - -} + Class viewLayerClass {nullptr}; + + //----------------------------------------------------------------------------- + VSTGUI_macOS_CALayer () + { + viewLayerClass = VSTGUI::ObjCClassBuilder () + .init ("VSTGUI_CALayer", [CALayer class]) + .addMethod (@selector (init), Init) + .addMethod (@selector (actionForKey:), ActionForKey) + .addMethod (@selector (setDrawDelegate:), SetDrawDelegate) + .addMethod (@selector (drawInContext:), DrawInContext) + .addIvar (viewLayerDelegateVarName) + .finalize (); + } -#define VISUALIZE_LAYER 0 + //----------------------------------------------------------------------------- + ~VSTGUI_macOS_CALayer () noexcept + { + objc_disposeClassPair (viewLayerClass); + } -//----------------------------------------------------------------------------- -static void VSTGUI_CALayer_DrawInContext (id self, SEL _cmd, CGContextRef ctx) -{ - VSTGUI::IPlatformViewLayerDelegate* _viewLayerDelegate = (VSTGUI::IPlatformViewLayerDelegate*)OBJC_GET_VALUE(self, _viewLayerDelegate); - if (_viewLayerDelegate) + //----------------------------------------------------------------------------- + static VSTGUI_macOS_CALayer& instance () { - #if VISUALIZE_LAYER - CGContextClearRect (ctx, [self bounds]); - #endif - CGRect dirtyRect = CGContextGetClipBoundingBox (ctx); - if ([self contentsAreFlipped] == [self isGeometryFlipped]) + static VSTGUI_macOS_CALayer gInstance; + return gInstance; + } + + //----------------------------------------------------------------------------- + static id Init (id self, SEL _cmd) + { + self = VSTGUI::ObjCInstance (self).callSuper (_cmd); + if (self) { - CGContextScaleCTM (ctx, 1, -1); - CGContextTranslateCTM (ctx, 0, -[self bounds].size.height); - dirtyRect.origin.y = (-dirtyRect.origin.y - dirtyRect.size.height) + [self bounds].size.height; + [self setNeedsDisplayOnBoundsChange:YES]; } - CGContextSaveGState (ctx); - VSTGUI::CGDrawContext drawContext (ctx, VSTGUI::CRectFromCGRect ([(CALayer*)self bounds])); - _viewLayerDelegate->drawViewLayer (&drawContext, VSTGUI::CRectFromCGRect (dirtyRect)); - CGContextRestoreGState (ctx); - - #if VISUALIZE_LAYER - CGContextSetRGBFillColor (ctx, 1., 0., 0., 0.3); - CGContextFillRect (ctx, [self bounds]); - CGContextSetRGBFillColor (ctx, 0., 1., 0., 0.3); - CGContextFillRect (ctx, dirtyRect); - #endif + return self; } -} -#endif + //----------------------------------------------------------------------------- + static id ActionForKey (id self, SEL _cmd, NSString* event) { return nil; } -namespace VSTGUI { + //----------------------------------------------------------------------------- + static void SetDrawDelegate (id self, SEL _cmd, VSTGUI::IPlatformViewLayerDelegate* delegate) + { + using namespace VSTGUI; + if (auto var = ObjCInstance (self).getVariable ( + viewLayerDelegateVarName)) + var->set (delegate); + } -#if !TARGET_OS_IPHONE //----------------------------------------------------------------------------- -__attribute__((__destructor__)) static void cleanup_VSTGUI_CALayer () -{ - if (viewLayerClass) - objc_disposeClassPair (viewLayerClass); -} + static void DrawInContext (id self, SEL _cmd, CGContextRef ctx) + { + using namespace VSTGUI; + + if (auto var = ObjCInstance (self).getVariable ( + viewLayerDelegateVarName); + var.has_value ()) + { + static bool visualizeLayer = false; + if (visualizeLayer) + CGContextClearRect (ctx, [self bounds]); + + CGRect dirtyRect = CGContextGetClipBoundingBox (ctx); + if ([self contentsAreFlipped] == [self isGeometryFlipped]) + { + CGContextScaleCTM (ctx, 1, -1); + CGContextTranslateCTM (ctx, 0, -[self bounds].size.height); + dirtyRect.origin.y = + (-dirtyRect.origin.y - dirtyRect.size.height) + [self bounds].size.height; + } + CGContextSaveGState (ctx); + CGDrawContext drawContext (ctx, CRectFromCGRect ([(CALayer*)self bounds])); + var->get ()->drawViewLayer (&drawContext, CRectFromCGRect (dirtyRect)); + CGContextRestoreGState (ctx); + + if (visualizeLayer) + { + CGContextSetRGBFillColor (ctx, 1., 0., 0., 0.3); + CGContextFillRect (ctx, [self bounds]); + CGContextSetRGBFillColor (ctx, 0., 1., 0., 0.3); + CGContextFillRect (ctx, dirtyRect); + } + } + } +}; -//----------------------------------------------------------------------------- -static void initCALayerClass () -{ - if (viewLayerClass) - return; - - AutoreleasePool ap; - NSMutableString* caLayerClassName = [[[NSMutableString alloc] initWithString:@"VSTGUI_CALayer"] autorelease]; - viewLayerClass = generateUniqueClass (caLayerClassName, [CALayer class]); - VSTGUI_CHECK_YES(class_addMethod (viewLayerClass, @selector(init), IMP (VSTGUI_CALayer_Init), "@@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewLayerClass, @selector(actionForKey:), IMP (VSTGUI_CALayer_ActionForKey), "@@:@:@@")) - VSTGUI_CHECK_YES(class_addMethod (viewLayerClass, @selector(setDrawDelegate:), IMP (VSTGUI_CALayer_SetDrawDelegate), "v@:@:^:")) - char funcSig[100]; - sprintf (funcSig, "v@:@:%s:", @encode (CGContextRef)); - VSTGUI_CHECK_YES(class_addMethod (viewLayerClass, @selector(drawInContext:), IMP (VSTGUI_CALayer_DrawInContext), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addIvar (viewLayerClass, "_viewLayerDelegate", sizeof (void*), (uint8_t)log2(sizeof(void*)), @encode(void*))) - objc_registerClassPair (viewLayerClass); -} #endif +namespace VSTGUI { + //----------------------------------------------------------------------------- CAViewLayer::CAViewLayer (CALayer* parent) : layer (nullptr) { #if !TARGET_OS_IPHONE - initCALayerClass (); - layer = [[viewLayerClass alloc] init]; + layer = VSTGUI_macOS_CALayer::newInstance (); #else layer = [VSTGUI_CALayer new]; #endif @@ -194,7 +206,7 @@ static void initCALayerClass () //----------------------------------------------------------------------------- bool CAViewLayer::init (IPlatformViewLayerDelegate* drawDelegate) { - [(VSTGUI_CALayer*)layer setDrawDelegate:drawDelegate]; + [static_cast (layer) setDrawDelegate:drawDelegate]; return true; } diff --git a/vstgui/lib/platform/mac/cgbitmap.cpp b/vstgui/lib/platform/mac/cgbitmap.cpp index b00f04552..bb470f4d2 100644 --- a/vstgui/lib/platform/mac/cgbitmap.cpp +++ b/vstgui/lib/platform/mac/cgbitmap.cpp @@ -78,7 +78,7 @@ PNGBitmapBuffer CGBitmap::createMemoryPNGRepresentation (const PlatformBitmapPtr CFMutableDataRef data = CFDataCreateMutable (nullptr, 0); if (data) { - CGImageDestinationRef dest = CGImageDestinationCreateWithData (data, kUTTypePNG, 1, nullptr); + CGImageDestinationRef dest = CGImageDestinationCreateWithData (data, CFSTR ("public.png"), 1, nullptr); if (dest) { auto scaleFactor = bitmap->getScaleFactor (); diff --git a/vstgui/lib/platform/mac/cgdrawcontext.cpp b/vstgui/lib/platform/mac/cgdrawcontext.cpp index 9e5853639..1183a8de1 100644 --- a/vstgui/lib/platform/mac/cgdrawcontext.cpp +++ b/vstgui/lib/platform/mac/cgdrawcontext.cpp @@ -11,6 +11,7 @@ #include "quartzgraphicspath.h" #include "cfontmac.h" #include "../../cbitmap.h" +#include "../../cgradient.h" #ifndef CGFLOAT_DEFINED #define CGFLOAT_DEFINED @@ -99,22 +100,30 @@ void CGDrawContext::endDraw () //----------------------------------------------------------------------------- CGraphicsPath* CGDrawContext::createGraphicsPath () { - return new QuartzGraphicsPath; + return new CGraphicsPath (CGGraphicsPathFactory::instance ()); } //----------------------------------------------------------------------------- CGraphicsPath* CGDrawContext::createTextPath (const CFontRef font, UTF8StringPtr text) { - const CoreTextFont* ctFont = font->getPlatformFont ().cast (); - return ctFont ? new QuartzGraphicsPath (ctFont, text) : nullptr; + if (auto path = + CGGraphicsPathFactory::instance ()->createTextPath (font->getPlatformFont (), text)) + { + return new CGraphicsPath (CGGraphicsPathFactory::instance (), std::move (path)); + } + return nullptr; } //----------------------------------------------------------------------------- -void CGDrawContext::drawGraphicsPath (CGraphicsPath* _path, PathDrawMode mode, - CGraphicsTransform* t) +void CGDrawContext::drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode, CGraphicsTransform* t) { - QuartzGraphicsPath* path = dynamic_cast (_path); - if (path == nullptr) + if (!path) + return; + const auto& graphicsPath = path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored); + if (!graphicsPath) + return; + auto cgPath = dynamic_cast (graphicsPath.get ()); + if (!cgPath) return; if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) @@ -143,19 +152,24 @@ void CGDrawContext::drawGraphicsPath (CGraphicsPath* _path, PathDrawMode mode, DoGraphicStateSave (context, [&] () { if (t) { - CGAffineTransform transform = QuartzGraphicsPath::createCGAffineTransform (*t); + CGAffineTransform transform = createCGAffineTransform (*t); CGContextConcatCTM (context, transform); } if (getDrawMode ().integralMode () && getDrawMode ().aliasing ()) { DoGraphicStateSave (context, [&] () { applyLineWidthCTM (context); - path->pixelAlign (this); + cgPath->pixelAlign ( + [] (const CGPoint& p, void* context) { + auto cgDrawContext = reinterpret_cast (context); + return cgDrawContext->pixelAlligned (p); + }, + this); }); - CGContextAddPath (context, path->getCGPathRef ()); + CGContextAddPath (context, cgPath->getCGPathRef ()); } else - CGContextAddPath (context, path->getCGPathRef ()); + CGContextAddPath (context, cgPath->getCGPathRef ()); }); CGContextDrawPath (context, cgMode); @@ -165,18 +179,24 @@ void CGDrawContext::drawGraphicsPath (CGraphicsPath* _path, PathDrawMode mode, } //----------------------------------------------------------------------------- -void CGDrawContext::fillLinearGradient (CGraphicsPath* _path, const CGradient& gradient, - const CPoint& startPoint, const CPoint& endPoint, - bool evenOdd, CGraphicsTransform* t) +void CGDrawContext::fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, + const CPoint& startPoint, const CPoint& endPoint, + bool evenOdd, CGraphicsTransform* t) { - QuartzGraphicsPath* path = dynamic_cast (_path); if (path == nullptr) return; - const QuartzGradient* cgGradient = dynamic_cast (&gradient); + auto cgGradient = dynamic_cast (gradient.getPlatformGradient ().get ()); if (cgGradient == nullptr) return; + const auto& graphicsPath = path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored); + if (!graphicsPath) + return; + auto cgPath = dynamic_cast (graphicsPath.get ()); + if (!cgPath) + return; + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) { CGPoint start = CGPointFromCPoint (startPoint); @@ -189,13 +209,20 @@ void CGDrawContext::fillLinearGradient (CGraphicsPath* _path, const CGradient& g } if (t) { - CGAffineTransform transform = QuartzGraphicsPath::createCGAffineTransform (*t); + CGAffineTransform transform = createCGAffineTransform (*t); CGContextConcatCTM (context, transform); } if (getDrawMode ().integralMode () && getDrawMode ().aliasing ()) - path->pixelAlign (this); + { + cgPath->pixelAlign ( + [] (const CGPoint& p, void* context) { + auto cgDrawContext = reinterpret_cast (context); + return cgDrawContext->pixelAlligned (p); + }, + this); + } - CGContextAddPath (context, path->getCGPathRef ()); + CGContextAddPath (context, cgPath->getCGPathRef ()); }); if (evenOdd) @@ -212,31 +239,44 @@ void CGDrawContext::fillLinearGradient (CGraphicsPath* _path, const CGradient& g } //----------------------------------------------------------------------------- -void CGDrawContext::fillRadialGradient (CGraphicsPath* _path, const CGradient& gradient, - const CPoint& center, CCoord radius, - const CPoint& originOffset, bool evenOdd, - CGraphicsTransform* t) +void CGDrawContext::fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, + const CPoint& center, CCoord radius, + const CPoint& originOffset, bool evenOdd, + CGraphicsTransform* t) { - QuartzGraphicsPath* path = dynamic_cast (_path); if (path == nullptr) return; - const QuartzGradient* cgGradient = dynamic_cast (&gradient); + auto cgGradient = dynamic_cast (gradient.getPlatformGradient ().get ()); if (cgGradient == nullptr) return; + const auto& graphicsPath = path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored); + if (!graphicsPath) + return; + auto cgPath = dynamic_cast (graphicsPath.get ()); + if (!cgPath) + return; + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) { DoGraphicStateSave (context, [&] () { if (t) { - CGAffineTransform transform = QuartzGraphicsPath::createCGAffineTransform (*t); + CGAffineTransform transform = createCGAffineTransform (*t); CGContextConcatCTM (context, transform); } if (getDrawMode ().integralMode () && getDrawMode ().aliasing ()) - path->pixelAlign (this); + { + cgPath->pixelAlign ( + [] (const CGPoint& p, void* context) { + auto cgDrawContext = reinterpret_cast (context); + return cgDrawContext->pixelAlligned (p); + }, + this); + } - CGContextAddPath (context, path->getCGPathRef ()); + CGContextAddPath (context, cgPath->getCGPathRef ()); }); if (evenOdd) @@ -354,7 +394,14 @@ void CGDrawContext::drawLines (const LineList& lines) if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) { applyLineStyle (context); - auto cgPoints = std::unique_ptr (new CGPoint[lines.size () * 2]); + + static constexpr auto numStackPoints = 32; + CGPoint stackPoints[numStackPoints]; + + auto cgPointsPtr = (lines.size () * 2) < numStackPoints + ? nullptr + : std::unique_ptr (new CGPoint[lines.size () * 2]); + auto cgPoints = cgPointsPtr ? cgPointsPtr.get () : stackPoints; uint32_t index = 0; if (getDrawMode ().integralMode ()) { @@ -379,7 +426,7 @@ void CGDrawContext::drawLines (const LineList& lines) applyLineWidthCTM (context); const size_t maxPointsPerIteration = 16; - const CGPoint* pointPtr = cgPoints.get (); + const CGPoint* pointPtr = cgPoints; size_t numPoints = lines.size () * 2; while (numPoints) { @@ -826,7 +873,7 @@ CGContextRef CGDrawContext::beginCGContext (bool swapYAxis, bool integralOffset) t.dx = p.x; t.dy = p.y; } - CGContextConcatCTM (cgContext, QuartzGraphicsPath::createCGAffineTransform (t)); + CGContextConcatCTM (cgContext, createCGAffineTransform (t)); } if (!swapYAxis) diff --git a/vstgui/lib/platform/mac/cgdrawcontext.h b/vstgui/lib/platform/mac/cgdrawcontext.h index 1c6f448a8..0296db671 100644 --- a/vstgui/lib/platform/mac/cgdrawcontext.h +++ b/vstgui/lib/platform/mac/cgdrawcontext.h @@ -15,10 +15,6 @@ #include #endif -#if MAC_CARBON -#include -#endif - #include namespace VSTGUI { diff --git a/vstgui/lib/platform/mac/cocoa/cocoahelpers.h b/vstgui/lib/platform/mac/cocoa/cocoahelpers.h index d2b76c743..48880ff7e 100644 --- a/vstgui/lib/platform/mac/cocoa/cocoahelpers.h +++ b/vstgui/lib/platform/mac/cocoa/cocoahelpers.h @@ -14,59 +14,12 @@ #import #import #import -struct VstKeyCode; #define HIDDEN __attribute__((__visibility__("hidden"))) -#if DEBUG -#define VSTGUI_CHECK_YES(x) { BOOL res = x; vstgui_assert (res == YES); } -#else -#define VSTGUI_CHECK_YES(x) x; -#endif - -//------------------------------------------------------------------------------------ -inline HIDDEN id get_Objc_Value (id obj, const char* name) -{ - Ivar ivar = class_getInstanceVariable ([obj class], name); - if (ivar) - { - id value = object_getIvar (obj, ivar); - return value; - } - return nil; -} - -//------------------------------------------------------------------------------------ -inline HIDDEN void set_Objc_Value (id obj, const char* name, id value) -{ - Ivar ivar = class_getInstanceVariable ([obj class], name); - if (ivar) - { - object_setIvar (obj, ivar, value); - } -} - -#define __OBJC_SUPER(x) objc_super __os; __os.receiver = x; __os.super_class = class_getSuperclass ([x class]); -#define SUPER static_cast (&__os) -#define OBJC_GET_VALUE(x,y) get_Objc_Value (x, #y) -#define OBJC_SET_VALUE(x,y,z) set_Objc_Value (x, #y, (id)z) - -//------------------------------------------------------------------------------------ -static id (*SuperInit) (id, SEL) = (id (*) (id, SEL))objc_msgSendSuper; -static id (*SuperInitWithFrame) (id, SEL, NSRect) = (id (*) (id, SEL, NSRect))objc_msgSendSuper; -static void (*SuperDealloc) (id, SEL) = (void (*) (id, SEL))objc_msgSendSuper; -static void (*SuperRemoveFromSuperview) (id, SEL) = SuperDealloc; -static void (*SuperEventMsg) (id, SEL, NSEvent*) = (void (*) (id, SEL, NSEvent*))objc_msgSendSuper; -static void (*SuperUpdateTrackingAreas) (id, SEL) = (void (*) (id, SEL))objc_msgSendSuper; -static void (*SuperTextDidChange) (id, SEL, NSNotification*) = (void (*) (id, SEL, NSNotification*))objc_msgSendSuper; -static void (*SuperSetNeedsDisplayInRect) (id, SEL, - NSRect) = (void (*) (id, SEL, NSRect))objc_msgSendSuper; -static void (*SuperViewWillRedraw) (id, SEL) = SuperDealloc; - //------------------------------------------------------------------------------------ -extern HIDDEN Class generateUniqueClass (NSMutableString* className, Class baseClass); -extern HIDDEN VstKeyCode CreateVstKeyCodeFromNSEvent (NSEvent* theEvent); -extern HIDDEN NSString* GetVirtualKeyCodeString (int32_t virtualKeyCode); +extern HIDDEN bool CreateKeyboardEventFromNSEvent (NSEvent* theEvent, VSTGUI::KeyboardEvent& event); +extern HIDDEN NSString* GetVirtualKeyCodeString (VSTGUI::VirtualKey virtualKey); extern HIDDEN int32_t eventButton (NSEvent* theEvent); extern HIDDEN void convertPointToGlobal (NSView* view, NSPoint& p); extern HIDDEN NSImage* bitmapToNSImage (VSTGUI::CBitmap* bitmap); diff --git a/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm b/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm index 33752cd61..8905a992a 100644 --- a/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm +++ b/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm @@ -7,104 +7,110 @@ #if MAC_COCOA #include "../../../vstkeycode.h" +#include "../../../events.h" #include "../../../cview.h" #include "../../../cbitmap.h" #include "../cgbitmap.h" -//------------------------------------------------------------------------------------ -HIDDEN Class generateUniqueClass (NSMutableString* className, Class baseClass) -{ - NSString* _className = [NSString stringWithString:className]; - int32_t iteration = 0; - while (objc_lookUpClass ([className UTF8String]) != nil) - { - iteration++; - [className setString:[NSString stringWithFormat:@"%@_%d", _className, iteration]]; - } - Class resClass = objc_allocateClassPair (baseClass, [className UTF8String], 0); - return resClass; -} - using namespace VSTGUI; //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ -HIDDEN VstKeyCode CreateVstKeyCodeFromNSEvent (NSEvent* theEvent) +HIDDEN bool CreateKeyboardEventFromNSEvent (NSEvent* theEvent, KeyboardEvent& event) { - VstKeyCode kc = {}; + if (theEvent.type == NSEventTypeKeyUp) + event.type = EventType::KeyUp; + else if (theEvent.type == NSEventTypeKeyDown || theEvent.type == NSEventTypeFlagsChanged) + { + event.type = EventType::KeyDown; + if (theEvent.ARepeat) + event.isRepeat = true; + } + else + return false; NSString *s = [theEvent charactersIgnoringModifiers]; if ([s length] == 1) { - unichar c = [s characterAtIndex:0]; - switch (c) + char32_t utf32Char = {}; + if (![s getBytes:&utf32Char + maxLength:sizeof (utf32Char) + usedLength:nullptr + encoding:NSUTF32StringEncoding + options:0 + range:NSMakeRange (0, 1) + remainingRange:nullptr]) + { + utf32Char = [s characterAtIndex:0]; + } + switch (utf32Char) { - case 8: case 0x7f: kc.virt = VKEY_BACK; break; - case 9: case 0x19: kc.virt = VKEY_TAB; break; - case NSClearLineFunctionKey: kc.virt = VKEY_CLEAR; break; - case 0xd: kc.virt = VKEY_RETURN; break; - case NSPauseFunctionKey: kc.virt = VKEY_PAUSE; break; - case 0x1b: kc.virt = VKEY_ESCAPE; break; - case ' ': kc.virt = VKEY_SPACE; break; - case NSNextFunctionKey: kc.virt = VKEY_NEXT; break; - case NSEndFunctionKey: kc.virt = VKEY_END; break; - case NSHomeFunctionKey: kc.virt = VKEY_HOME; break; - - case NSLeftArrowFunctionKey: kc.virt = VKEY_LEFT; break; - case NSUpArrowFunctionKey: kc.virt = VKEY_UP; break; - case NSRightArrowFunctionKey: kc.virt = VKEY_RIGHT; break; - case NSDownArrowFunctionKey: kc.virt = VKEY_DOWN; break; - case NSPageUpFunctionKey: kc.virt = VKEY_PAGEUP; break; - case NSPageDownFunctionKey: kc.virt = VKEY_PAGEDOWN; break; + case 8: case 0x7f: event.virt = VirtualKey::Back; break; + case 9: case 0x19: event.virt = VirtualKey::Tab; break; + case NSClearLineFunctionKey: event.virt = VirtualKey::Clear; break; + case 0xd: event.virt = VirtualKey::Return; break; + case NSPauseFunctionKey: event.virt = VirtualKey::Pause; break; + case 0x1b: event.virt = VirtualKey::Escape; break; + case ' ': event.virt = VirtualKey::Space; break; + case NSNextFunctionKey: event.virt = VirtualKey::Next; break; + case NSEndFunctionKey: event.virt = VirtualKey::End; break; + case NSHomeFunctionKey: event.virt = VirtualKey::Home; break; + + case NSLeftArrowFunctionKey: event.virt = VirtualKey::Left; break; + case NSUpArrowFunctionKey: event.virt = VirtualKey::Up; break; + case NSRightArrowFunctionKey: event.virt = VirtualKey::Right; break; + case NSDownArrowFunctionKey: event.virt = VirtualKey::Down; break; + case NSPageUpFunctionKey: event.virt = VirtualKey::PageUp; break; + case NSPageDownFunctionKey: event.virt = VirtualKey::PageDown; break; - case NSSelectFunctionKey: kc.virt = VKEY_SELECT; break; - case NSPrintFunctionKey: kc.virt = VKEY_PRINT; break; - // VKEY_ENTER - // VKEY_SNAPSHOT - case NSInsertFunctionKey: kc.virt = VKEY_INSERT; break; - case NSDeleteFunctionKey: kc.virt = VKEY_DELETE; break; - case NSHelpFunctionKey: kc.virt = VKEY_HELP; break; - - - case NSF1FunctionKey: kc.virt = VKEY_F1; break; - case NSF2FunctionKey: kc.virt = VKEY_F2; break; - case NSF3FunctionKey: kc.virt = VKEY_F3; break; - case NSF4FunctionKey: kc.virt = VKEY_F4; break; - case NSF5FunctionKey: kc.virt = VKEY_F5; break; - case NSF6FunctionKey: kc.virt = VKEY_F6; break; - case NSF7FunctionKey: kc.virt = VKEY_F7; break; - case NSF8FunctionKey: kc.virt = VKEY_F8; break; - case NSF9FunctionKey: kc.virt = VKEY_F9; break; - case NSF10FunctionKey: kc.virt = VKEY_F10; break; - case NSF11FunctionKey: kc.virt = VKEY_F11; break; - case NSF12FunctionKey: kc.virt = VKEY_F12; break; + case NSSelectFunctionKey: event.virt = VirtualKey::Select; break; + case NSPrintFunctionKey: event.virt = VirtualKey::Print; break; + // VirtualKey::ENTER + // VirtualKey::SNAPSHOT + case NSInsertFunctionKey: event.virt = VirtualKey::Insert; break; + case NSDeleteFunctionKey: event.virt = VirtualKey::Delete; break; + case NSHelpFunctionKey: event.virt = VirtualKey::Help; break; + + + case NSF1FunctionKey: event.virt = VirtualKey::F1; break; + case NSF2FunctionKey: event.virt = VirtualKey::F2; break; + case NSF3FunctionKey: event.virt = VirtualKey::F3; break; + case NSF4FunctionKey: event.virt = VirtualKey::F4; break; + case NSF5FunctionKey: event.virt = VirtualKey::F5; break; + case NSF6FunctionKey: event.virt = VirtualKey::F6; break; + case NSF7FunctionKey: event.virt = VirtualKey::F7; break; + case NSF8FunctionKey: event.virt = VirtualKey::F8; break; + case NSF9FunctionKey: event.virt = VirtualKey::F9; break; + case NSF10FunctionKey: event.virt = VirtualKey::F10; break; + case NSF11FunctionKey: event.virt = VirtualKey::F11; break; + case NSF12FunctionKey: event.virt = VirtualKey::F12; break; default: { switch ([theEvent keyCode]) { - case 82: kc.virt = VKEY_NUMPAD0; break; - case 83: kc.virt = VKEY_NUMPAD1; break; - case 84: kc.virt = VKEY_NUMPAD2; break; - case 85: kc.virt = VKEY_NUMPAD3; break; - case 86: kc.virt = VKEY_NUMPAD4; break; - case 87: kc.virt = VKEY_NUMPAD5; break; - case 88: kc.virt = VKEY_NUMPAD6; break; - case 89: kc.virt = VKEY_NUMPAD7; break; - case 91: kc.virt = VKEY_NUMPAD8; break; - case 92: kc.virt = VKEY_NUMPAD9; break; - case 67: kc.virt = VKEY_MULTIPLY; break; - case 69: kc.virt = VKEY_ADD; break; - case 78: kc.virt = VKEY_SUBTRACT; break; - case 65: kc.virt = VKEY_DECIMAL; break; - case 75: kc.virt = VKEY_DIVIDE; break; - case 76: kc.virt = VKEY_ENTER; break; + case 82: event.virt = VirtualKey::NumPad0; break; + case 83: event.virt = VirtualKey::NumPad1; break; + case 84: event.virt = VirtualKey::NumPad2; break; + case 85: event.virt = VirtualKey::NumPad3; break; + case 86: event.virt = VirtualKey::NumPad4; break; + case 87: event.virt = VirtualKey::NumPad5; break; + case 88: event.virt = VirtualKey::NumPad6; break; + case 89: event.virt = VirtualKey::NumPad7; break; + case 91: event.virt = VirtualKey::NumPad8; break; + case 92: event.virt = VirtualKey::NumPad9; break; + case 67: event.virt = VirtualKey::Multiply; break; + case 69: event.virt = VirtualKey::Add; break; + case 78: event.virt = VirtualKey::Subtract; break; + case 65: event.virt = VirtualKey::Decimal; break; + case 75: event.virt = VirtualKey::Divide; break; + case 76: event.virt = VirtualKey::Enter; break; default: { - if ((c >= 'A') && (c <= 'Z')) - c += ('a' - 'A'); + if ((utf32Char >= 'A') && (utf32Char <= 'Z')) + utf32Char += ('a' - 'A'); else - c = static_cast (tolower (c)); - kc.character = c; + utf32Char = static_cast (tolower (utf32Char)); + event.character = utf32Char; break; } } @@ -114,77 +120,81 @@ HIDDEN VstKeyCode CreateVstKeyCodeFromNSEvent (NSEvent* theEvent) NSUInteger modifiers = [theEvent modifierFlags]; if (modifiers & MacEventModifier::ShiftKeyMask) - kc.modifier |= MODIFIER_SHIFT; + event.modifiers.add (ModifierKey::Shift); if (modifiers & MacEventModifier::CommandKeyMask) - kc.modifier |= MODIFIER_CONTROL; + event.modifiers.add (ModifierKey::Control); if (modifiers & MacEventModifier::AlternateKeyMask) - kc.modifier |= MODIFIER_ALTERNATE; + event.modifiers.add (ModifierKey::Alt); if (modifiers & MacEventModifier::ControlKeyMask) - kc.modifier |= MODIFIER_COMMAND; + event.modifiers.add (ModifierKey::Super); - return kc; + return true; } //------------------------------------------------------------------------------------ -HIDDEN NSString* GetVirtualKeyCodeString (int32_t virtualKeyCode) +HIDDEN NSString* GetVirtualKeyCodeString (VirtualKey virtualKey) { unichar character = 0; - switch (virtualKeyCode) + switch (virtualKey) { - case VKEY_BACK: character = NSDeleteCharacter; break; - case VKEY_TAB: character = NSTabCharacter; break; - case VKEY_CLEAR: character = NSClearLineFunctionKey; break; - case VKEY_RETURN: character = NSCarriageReturnCharacter; break; - case VKEY_PAUSE: character = NSPauseFunctionKey; break; - case VKEY_ESCAPE: character = 0x1b; break; - case VKEY_SPACE: character = ' '; break; - case VKEY_NEXT: character = NSNextFunctionKey; break; - case VKEY_END: character = NSEndFunctionKey; break; - case VKEY_HOME: character = NSHomeFunctionKey; break; - case VKEY_LEFT: character = NSLeftArrowFunctionKey; break; - case VKEY_UP: character = NSUpArrowFunctionKey; break; - case VKEY_RIGHT: character = NSRightArrowFunctionKey; break; - case VKEY_DOWN: character = NSDownArrowFunctionKey; break; - case VKEY_PAGEUP: character = NSPageUpFunctionKey; break; - case VKEY_PAGEDOWN: character = NSPageDownFunctionKey; break; - case VKEY_SELECT: character = NSSelectFunctionKey; break; - case VKEY_PRINT: character = NSPrintFunctionKey; break; - case VKEY_ENTER: character = NSEnterCharacter; break; - case VKEY_SNAPSHOT: break; - case VKEY_INSERT: character = NSInsertFunctionKey; break; - case VKEY_DELETE: character = NSDeleteFunctionKey; break; - case VKEY_HELP: character = NSHelpFunctionKey; break; - case VKEY_NUMPAD0: break; - case VKEY_NUMPAD1: break; - case VKEY_NUMPAD2: break; - case VKEY_NUMPAD3: break; - case VKEY_NUMPAD4: break; - case VKEY_NUMPAD5: break; - case VKEY_NUMPAD6: break; - case VKEY_NUMPAD7: break; - case VKEY_NUMPAD8: break; - case VKEY_NUMPAD9: break; - case VKEY_MULTIPLY: break; - case VKEY_ADD: break; - case VKEY_SEPARATOR: break; - case VKEY_SUBTRACT: break; - case VKEY_DECIMAL: break; - case VKEY_DIVIDE: break; - case VKEY_F1: character = NSF1FunctionKey; break; - case VKEY_F2: character = NSF2FunctionKey; break; - case VKEY_F3: character = NSF3FunctionKey; break; - case VKEY_F4: character = NSF4FunctionKey; break; - case VKEY_F5: character = NSF5FunctionKey; break; - case VKEY_F6: character = NSF6FunctionKey; break; - case VKEY_F7: character = NSF7FunctionKey; break; - case VKEY_F8: character = NSF8FunctionKey; break; - case VKEY_F9: character = NSF9FunctionKey; break; - case VKEY_F10: character = NSF10FunctionKey; break; - case VKEY_F11: character = NSF11FunctionKey; break; - case VKEY_F12: character = NSF12FunctionKey; break; - case VKEY_NUMLOCK: break; - case VKEY_SCROLL: break; - case VKEY_EQUALS: break; + case VirtualKey::Back: character = NSDeleteCharacter; break; + case VirtualKey::Tab: character = NSTabCharacter; break; + case VirtualKey::Clear: character = NSClearLineFunctionKey; break; + case VirtualKey::Return: character = NSCarriageReturnCharacter; break; + case VirtualKey::Pause: character = NSPauseFunctionKey; break; + case VirtualKey::Escape: character = 0x1b; break; + case VirtualKey::Space: character = ' '; break; + case VirtualKey::Next: character = NSNextFunctionKey; break; + case VirtualKey::End: character = NSEndFunctionKey; break; + case VirtualKey::Home: character = NSHomeFunctionKey; break; + case VirtualKey::Left: character = NSLeftArrowFunctionKey; break; + case VirtualKey::Up: character = NSUpArrowFunctionKey; break; + case VirtualKey::Right: character = NSRightArrowFunctionKey; break; + case VirtualKey::Down: character = NSDownArrowFunctionKey; break; + case VirtualKey::PageUp: character = NSPageUpFunctionKey; break; + case VirtualKey::PageDown: character = NSPageDownFunctionKey; break; + case VirtualKey::Select: character = NSSelectFunctionKey; break; + case VirtualKey::Print: character = NSPrintFunctionKey; break; + case VirtualKey::Enter: character = NSEnterCharacter; break; + case VirtualKey::Snapshot: break; + case VirtualKey::Insert: character = NSInsertFunctionKey; break; + case VirtualKey::Delete: character = NSDeleteFunctionKey; break; + case VirtualKey::Help: character = NSHelpFunctionKey; break; + case VirtualKey::NumPad0: break; + case VirtualKey::NumPad1: break; + case VirtualKey::NumPad2: break; + case VirtualKey::NumPad3: break; + case VirtualKey::NumPad4: break; + case VirtualKey::NumPad5: break; + case VirtualKey::NumPad6: break; + case VirtualKey::NumPad7: break; + case VirtualKey::NumPad8: break; + case VirtualKey::NumPad9: break; + case VirtualKey::Multiply: break; + case VirtualKey::Add: break; + case VirtualKey::Separator: break; + case VirtualKey::Subtract: break; + case VirtualKey::Decimal: break; + case VirtualKey::Divide: break; + case VirtualKey::F1: character = NSF1FunctionKey; break; + case VirtualKey::F2: character = NSF2FunctionKey; break; + case VirtualKey::F3: character = NSF3FunctionKey; break; + case VirtualKey::F4: character = NSF4FunctionKey; break; + case VirtualKey::F5: character = NSF5FunctionKey; break; + case VirtualKey::F6: character = NSF6FunctionKey; break; + case VirtualKey::F7: character = NSF7FunctionKey; break; + case VirtualKey::F8: character = NSF8FunctionKey; break; + case VirtualKey::F9: character = NSF9FunctionKey; break; + case VirtualKey::F10: character = NSF10FunctionKey; break; + case VirtualKey::F11: character = NSF11FunctionKey; break; + case VirtualKey::F12: character = NSF12FunctionKey; break; + case VirtualKey::NumLock: break; + case VirtualKey::Scroll: break; + case VirtualKey::Equals: break; + case VirtualKey::ShiftModifier: break; + case VirtualKey::ControlModifier: break; + case VirtualKey::AltModifier: break; + case VirtualKey::None: break; } if (character != 0) return [NSString stringWithFormat:@"%C", character]; diff --git a/vstgui/lib/platform/mac/cocoa/cocoaopenglview.h b/vstgui/lib/platform/mac/cocoa/cocoaopenglview.h index c52d86dc6..c481fa4a3 100644 --- a/vstgui/lib/platform/mac/cocoa/cocoaopenglview.h +++ b/vstgui/lib/platform/mac/cocoa/cocoaopenglview.h @@ -41,7 +41,6 @@ class CocoaOpenGLView : public IPlatformOpenGLView void doDraw (const CRect& r); void reshape (); protected: - static void initClass (); NSView* parent; #pragma clang diagnostic push diff --git a/vstgui/lib/platform/mac/cocoa/cocoaopenglview.mm b/vstgui/lib/platform/mac/cocoa/cocoaopenglview.mm index d8051b300..67417bb3a 100644 --- a/vstgui/lib/platform/mac/cocoa/cocoaopenglview.mm +++ b/vstgui/lib/platform/mac/cocoa/cocoaopenglview.mm @@ -11,20 +11,123 @@ #import "nsviewframe.h" #import "cocoahelpers.h" +#import "objcclassbuilder.h" #import "autoreleasepool.h" #import #import -static Class openGLViewClass = nullptr; - //----------------------------------------------------------------------------- @interface NSObject (VSTGUI_NSOpenGLView) -- (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat *)format callback:(VSTGUI::CocoaOpenGLView*)callback; +- (id)initWithFrame:(NSRect)frameRect + pixelFormat:(NSOpenGLPixelFormat*)format + callback:(VSTGUI::CocoaOpenGLView*)callback; @end +//----------------------------------------------------------------------------- namespace VSTGUI { +//----------------------------------------------------------------------------- +struct VSTGUI_NSOpenGLView +{ + static id alloc () { return [instance ().openGLViewClass alloc]; } + +private: + static constexpr const auto cocoaOpenGLViewVarName = "cocoaOpenGLView"; + + Class openGLViewClass = nullptr; + + //----------------------------------------------------------------------------- + VSTGUI_NSOpenGLView () + { + openGLViewClass = ObjCClassBuilder () + .init ("VSTGUI_NSOpenGLView", [NSOpenGLView class]) + .addMethod (@selector (initWithFrame:pixelFormat:callback:), Init) + .addMethod (@selector (dealloc), Dealloc) + .addMethod (@selector (update), Update_Reshape) + .addMethod (@selector (reshape), Update_Reshape) + .addMethod (@selector (isFlipped), IsFlipped) + .addMethod (@selector (drawRect:), DrawRect) + .addMethod (@selector (mouseMoved:), MouseXXX) + .addMethod (@selector (rightMouseDown:), MouseXXX) + .addMethod (@selector (rightMouseUp:), MouseXXX) + .addIvar (cocoaOpenGLViewVarName) + .finalize (); + } + + //----------------------------------------------------------------------------- + ~VSTGUI_NSOpenGLView () noexcept { objc_disposeClassPair (openGLViewClass); } + + //----------------------------------------------------------------------------- + static VSTGUI_NSOpenGLView& instance () + { + static VSTGUI_NSOpenGLView gInstance; + return gInstance; + } + + //----------------------------------------------------------------------------- + static id Init (id self, SEL _cmd, NSRect frameRect, NSOpenGLPixelFormat* format, + CocoaOpenGLView* callback) + { + ObjCInstance obj (self); + self = obj.callSuper ( + @selector (initWithFrame:pixelFormat:), frameRect, format); + if (self) + { + if (auto var = obj.getVariable (cocoaOpenGLViewVarName)) + { + var->set (callback); + callback->remember (); + } + } + return self; + } + + //----------------------------------------------------------------------------- + static void Dealloc (id self, SEL _cmd) + { + ObjCInstance obj (self); + if (auto var = obj.getVariable (cocoaOpenGLViewVarName)) + { + if (auto callback = var->get ()) + callback->forget (); + } + obj.callSuper (_cmd); + } + + //----------------------------------------------------------------------------- + static void Update_Reshape (id self, SEL _cmd) + { + ObjCInstance obj (self); + if (auto var = obj.getVariable (cocoaOpenGLViewVarName)) + { + if (auto callback = var->get ()) + callback->reshape (); + } + } + + //------------------------------------------------------------------------------------ + static BOOL IsFlipped (id self, SEL _cmd) { return YES; } + + //------------------------------------------------------------------------------------ + static void DrawRect (id self, SEL _cmd, NSRect rect) + { + ObjCInstance obj (self); + if (auto var = obj.getVariable (cocoaOpenGLViewVarName)) + { + if (auto callback = var->get ()) + callback->doDraw (rectFromNSRect (rect)); + } + } + + //------------------------------------------------------------------------------------ + static void MouseXXX (id self, SEL _cmd, NSEvent* theEvent) + { + if ([self nextResponder]) + [[self nextResponder] performSelector:_cmd withObject:theEvent]; + } +}; + #ifdef MAC_OS_X_VERSION_10_12 static constexpr auto CocoaOpenGLContextParameterSwapInterval = NSOpenGLContextParameterSwapInterval; static constexpr auto CocoaOpenGLContextParameterSurfaceOpacity = NSOpenGLContextParameterSurfaceOpacity; @@ -39,13 +142,12 @@ - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat *)format , platformView (nullptr) , view (nullptr) { - initClass (); } //----------------------------------------------------------------------------- bool CocoaOpenGLView::init (IOpenGLView* inView, PixelFormat* _pixelFormat) { - if (platformView || openGLViewClass == nullptr) + if (platformView) return false; if (parent) { @@ -98,8 +200,11 @@ - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat *)format formatAttributes.emplace_back (NSOpenGLProfileVersionLegacy); } formatAttributes.emplace_back (0); - NSOpenGLPixelFormat* nsPixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:&formatAttributes.front ()] autorelease]; - platformView = [[openGLViewClass alloc] initWithFrame:r pixelFormat:nsPixelFormat callback:this]; + NSOpenGLPixelFormat* nsPixelFormat = [[[NSOpenGLPixelFormat alloc] + initWithAttributes:&formatAttributes.front ()] autorelease]; + platformView = [VSTGUI_NSOpenGLView::alloc () initWithFrame:r + pixelFormat:nsPixelFormat + callback:this]; if (platformView) { NSOpenGLContext* context = [platformView openGLContext]; @@ -108,10 +213,13 @@ - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat *)format value = 0; [context setValues:&value forParameter:CocoaOpenGLContextParameterSurfaceOpacity]; - #if DEBUG +#if DEBUG if (pixelFormat.flags & PixelFormat::kModernOpenGL) - CGLEnable (static_cast ([context CGLContextObj]), kCGLCECrashOnRemovedFunctions); - #endif + { + CGLEnable (static_cast ([context CGLContextObj]), + kCGLCECrashOnRemovedFunctions); + } +#endif view = inView; platformView.wantsBestResolutionOpenGLSurface = YES; return true; @@ -240,100 +348,6 @@ - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat *)format [platformView setNeedsDisplay:YES]; } -//----------------------------------------------------------------------------- -__attribute__((__destructor__)) static void cleanup_VSTGUI_NSOpenGLView () -{ - if (openGLViewClass) - objc_disposeClassPair (openGLViewClass); -} - -static id (*SuperInitWithFramePixelFormat) (id, SEL, NSRect, NSOpenGLPixelFormat*) = (id (*) (id, SEL, NSRect, NSOpenGLPixelFormat*))objc_msgSendSuper; -//----------------------------------------------------------------------------- -static id VSTGUI_NSOpenGLView_Init (id self, SEL _cmd, NSRect frameRect, NSOpenGLPixelFormat* format, CocoaOpenGLView* callback) -{ - __OBJC_SUPER(self) - self = SuperInitWithFramePixelFormat (SUPER, @selector(initWithFrame:pixelFormat:), frameRect, format); - if (self) - { - OBJC_SET_VALUE(self, cocoaOpenGLView, callback); - callback->remember (); - } - return self; -} - -//----------------------------------------------------------------------------- -static void VSTGUI_NSOpenGLView_Dealloc (id self, SEL _cmd) -{ - CocoaOpenGLView* callback = (CocoaOpenGLView*)OBJC_GET_VALUE(self, cocoaOpenGLView); - if (callback) - callback->forget (); - __OBJC_SUPER(self) - SuperDealloc (SUPER, @selector(dealloc)); -} - -//----------------------------------------------------------------------------- -static void VSTGUI_NSOpenGLView_Update_Reshape (id self, SEL _cmd) -{ - CocoaOpenGLView* callback = (CocoaOpenGLView*)OBJC_GET_VALUE(self, cocoaOpenGLView); - if (callback) - callback->reshape (); -} - -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSOpenGLView_isFlipped (id self, SEL _cmd) -{ - return YES; -} - -//------------------------------------------------------------------------------------ -static void VSTGUI_NSOpenGLView_drawRect (id self, SEL _cmd, NSRect rect) -{ - CocoaOpenGLView* callback = (CocoaOpenGLView*)OBJC_GET_VALUE(self, cocoaOpenGLView); - if (callback) - callback->doDraw (rectFromNSRect(rect)); -} - -//------------------------------------------------------------------------------------ -static void VSTGUI_NSOpenGLView_mouseXXX (id self, SEL _cmd, NSEvent* theEvent) -{ - if ([self nextResponder]) - [[self nextResponder] performSelector:_cmd withObject:theEvent]; -} - - -//----------------------------------------------------------------------------- -void CocoaOpenGLView::initClass () -{ - if (openGLViewClass) - return; - - AutoreleasePool ap; - - const char* nsRectEncoded = @encode(NSRect); - char funcSig[100]; - - NSMutableString* viewClassName = [[[NSMutableString alloc] initWithString:@"VSTGUI_NSOpenGLView"] autorelease]; - openGLViewClass = generateUniqueClass (viewClassName, [NSOpenGLView class]); - if (openGLViewClass) - { - sprintf (funcSig, "@@:@:%s:@:^:", nsRectEncoded); - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(initWithFrame:pixelFormat:callback:), IMP (VSTGUI_NSOpenGLView_Init), funcSig)) - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(dealloc), IMP (VSTGUI_NSOpenGLView_Dealloc), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(update), IMP (VSTGUI_NSOpenGLView_Update_Reshape), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(reshape), IMP (VSTGUI_NSOpenGLView_Update_Reshape), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(isFlipped), IMP (VSTGUI_NSOpenGLView_isFlipped), "B@:@:")) - sprintf (funcSig, "v@:@:%s:", nsRectEncoded); - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(drawRect:), IMP (VSTGUI_NSOpenGLView_drawRect), funcSig)) - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(mouseMoved:), IMP (VSTGUI_NSOpenGLView_mouseXXX), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(rightMouseDown:), IMP (VSTGUI_NSOpenGLView_mouseXXX), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (openGLViewClass, @selector(rightMouseUp:), IMP (VSTGUI_NSOpenGLView_mouseXXX), "v@:@:^:")) - - VSTGUI_CHECK_YES(class_addIvar (openGLViewClass, "cocoaOpenGLView", sizeof (void*), (uint8_t)log2(sizeof(void*)), @encode(void*))) - - objc_registerClassPair (openGLViewClass); - } -} - } // VSTGUI #endif // VSTGUI_OPENGL_SUPPORT diff --git a/vstgui/lib/platform/mac/cocoa/cocoatextedit.h b/vstgui/lib/platform/mac/cocoa/cocoatextedit.h index 7ff42b322..895b0d44c 100644 --- a/vstgui/lib/platform/mac/cocoa/cocoatextedit.h +++ b/vstgui/lib/platform/mac/cocoa/cocoatextedit.h @@ -36,8 +36,6 @@ class CocoaTextEdit : public IPlatformTextEdit //----------------------------------------------------------------------------- protected: - static void initClass (); - NSTextField* platformControl; NSView* parent; }; diff --git a/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm b/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm index b4ddc346f..82459d677 100644 --- a/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm +++ b/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm @@ -7,20 +7,20 @@ #if MAC_COCOA #import "cocoahelpers.h" +#import "objcclassbuilder.h" #import "autoreleasepool.h" #import "../cfontmac.h" #import "../macstring.h" -#import "../../../vstkeycode.h" - -using namespace VSTGUI; - -static Class textFieldClass = nullptr; -static Class secureTextFieldClass = nullptr; +#import "../../../events.h" +//------------------------------------------------------------------------------------ @interface NSObject (VSTGUI_NSTextField_Private) -(id)initWithTextEdit:(id)textEit; @end +//------------------------------------------------------------------------------------ +namespace VSTGUI { + //------------------------------------------------------------------------------------ namespace MacTextAlignment { @@ -38,262 +38,280 @@ -(id)initWithTextEdit:(id)textEit; //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ -static id VSTGUI_NSTextField_Init (id self, SEL _cmd, void* textEdit) +struct VSTGUI_NSTextField { - __OBJC_SUPER(self) - if (self) + static id alloc () { return [instance ().textFieldClass alloc]; } + static id allocSecure () { return [instance ().secureTextFieldClass alloc]; } + +private: + static constexpr const auto textEditVarName = "_textEdit"; + + Class textFieldClass {nullptr}; + Class secureTextFieldClass {nullptr}; + + VSTGUI_NSTextField () { - CocoaTextEdit* te = (CocoaTextEdit*)textEdit; - IPlatformTextEditCallback* tec = te->getTextEdit (); - NSView* frameView = te->getParent (); - NSRect editFrameRect = nsRectFromCRect (tec->platformGetSize ()); - NSView* containerView = [[NSView alloc] initWithFrame:editFrameRect]; - [containerView setAutoresizesSubviews:YES]; - - if ([frameView wantsLayer]) + textFieldClass = + ObjCClassBuilder () + .init ("VSTGUI_NSTextField", [NSTextField class]) + .addMethod (@selector (initWithTextEdit:), Init) + .addMethod (@selector (syncSize), SyncSize) + .addMethod (@selector (removeFromSuperview), RemoveFromSuperview) + .addMethod (@selector (control:textView:doCommandBySelector:), DoCommandBySelector) + .addMethod (@selector (textDidChange:), TextDidChange) + .addIvar (textEditVarName) + .finalize (); + + secureTextFieldClass = + ObjCClassBuilder () + .init ("VSTGUI_NSSecureTextField", [NSSecureTextField class]) + .addMethod (@selector (initWithTextEdit:), Init) + .addMethod (@selector (syncSize), SyncSize) + .addMethod (@selector (removeFromSuperview), RemoveFromSuperview) + .addMethod (@selector (control:textView:doCommandBySelector:), DoCommandBySelector) + .addMethod (@selector (textDidChange:), TextDidChange) + .addIvar (textEditVarName) + .finalize (); + } + + ~VSTGUI_NSTextField () noexcept + { + if (textFieldClass) + objc_disposeClassPair (textFieldClass); + if (secureTextFieldClass) + objc_disposeClassPair (secureTextFieldClass); + } + + static VSTGUI_NSTextField& instance () + { + static VSTGUI_NSTextField gInstance; + return gInstance; + } + + static id Init (id self, SEL _cmd, void* textEdit) + { + ObjCInstance obj (self); + if (self) { - containerView.wantsLayer = YES; - double maxZPosition = -1.; - for (CALayer* layer in frameView.layer.sublayers) + CocoaTextEdit* te = (CocoaTextEdit*)textEdit; + IPlatformTextEditCallback* tec = te->getTextEdit (); + NSView* frameView = te->getParent (); + NSRect editFrameRect = nsRectFromCRect (tec->platformGetSize ()); + NSView* containerView = [[NSView alloc] initWithFrame:editFrameRect]; + [containerView setAutoresizesSubviews:YES]; + + if ([frameView wantsLayer]) { - double zPosition = layer.zPosition; - if (zPosition > maxZPosition) - maxZPosition = zPosition; + containerView.wantsLayer = YES; + double maxZPosition = -1.; + for (CALayer* layer in frameView.layer.sublayers) + { + double zPosition = layer.zPosition; + if (zPosition > maxZPosition) + maxZPosition = zPosition; + } + [containerView layer].zPosition = static_cast (maxZPosition + 1); } - [containerView layer].zPosition = static_cast (maxZPosition + 1); - } - - CPoint textInset = tec->platformGetTextInset (); - - editFrameRect.origin.x = static_cast (textInset.x/2. - 1.); - editFrameRect.origin.y = static_cast (textInset.y/2.); - editFrameRect.size.width -= textInset.x/2.; - editFrameRect.size.height -= textInset.y/2. - 1.; - self = SuperInitWithFrame (SUPER, @selector(initWithFrame:), editFrameRect); - if (!self) - { - [containerView release]; - return nil; - } - OBJC_SET_VALUE (self, _textEdit, textEdit); - CoreTextFont* ctf = tec->platformGetFont ()->getPlatformFont ().cast (); - if (ctf) - { - CTFontRef fontRef = ctf->getFontRef (); - if (fontRef) + CPoint textInset = tec->platformGetTextInset (); + + editFrameRect.origin.x = static_cast (textInset.x / 2. - 1.); + editFrameRect.origin.y = static_cast (textInset.y / 2.); + editFrameRect.size.width -= textInset.x / 2.; + editFrameRect.size.height -= textInset.y / 2. - 1.; + self = + obj.callSuper (@selector (initWithFrame:), editFrameRect); + if (!self) { - CTFontDescriptorRef fontDesc = CTFontCopyFontDescriptor (fontRef); - - [self setFont:[NSFont fontWithDescriptor:(NSFontDescriptor *)fontDesc size:ctf->getSize ()]]; - CFRelease (fontDesc); + [containerView release]; + return nil; } - } - - NSString* text = fromUTF8String (tec->platformGetText ()); - NSString* placeholder = fromUTF8String (tec->platformGetPlaceholderText ()); - - [self setTextColor:nsColorFromCColor (tec->platformGetFontColor ())]; - [self setBordered:NO]; - [self setAllowsEditingTextAttributes:NO]; - [self setImportsGraphics:NO]; - [self setStringValue:text]; - [self setFocusRingType:NSFocusRingTypeNone]; - [self sizeToFit]; - [(NSView*)self setAutoresizingMask:NSViewMinYMargin]; - if ([self frame].size.height < editFrameRect.size.height) - { - CGFloat offset = editFrameRect.size.height - [self frame].size.height; - editFrameRect.origin.y = static_cast (offset / 2.); - editFrameRect.size.height = [self frame].size.height; - } - else - editFrameRect.size.height = [self frame].size.height; - [self setFrame:editFrameRect]; - - [containerView addSubview:self]; - [self performSelector:@selector(syncSize)]; - [frameView addSubview:containerView]; - - NSTextFieldCell* cell = [self cell]; - [cell setDrawsBackground:NO]; - [cell setLineBreakMode: NSLineBreakByClipping]; - [cell setScrollable:YES]; - if (tec->platformGetHoriTxtAlign () == kCenterText) - [cell setAlignment:MacTextAlignment::Center]; - else if (tec->platformGetHoriTxtAlign () == kRightText) - [cell setAlignment:MacTextAlignment::Right]; - if (placeholder.length > 0) - { - CColor color = tec->platformGetFontColor (); - color.alpha /= 2; - NSMutableParagraphStyle* paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease]; - if (tec->platformGetHoriTxtAlign () == kCenterText) - paragraphStyle.alignment = MacTextAlignment::Center; - else if (tec->platformGetHoriTxtAlign () == kRightText) - paragraphStyle.alignment = MacTextAlignment::Right; - NSDictionary* attrDict = - [NSDictionary dictionaryWithObjectsAndKeys:[self font], NSFontAttributeName, - nsColorFromCColor (color), - NSForegroundColorAttributeName, - paragraphStyle, - NSParagraphStyleAttributeName, - nil]; - NSAttributedString* as = - [[[NSAttributedString alloc] initWithString:placeholder attributes:attrDict] - autorelease]; - [cell setPlaceholderAttributedString:as]; - } + if (auto var = obj.getVariable (textEditVarName)) + var->set (tec); - [self setDelegate:self]; - [self setNextKeyView:frameView]; + CoreTextFont* ctf = tec->platformGetFont ()->getPlatformFont ().cast (); + if (ctf) + { + CTFontRef fontRef = ctf->getFontRef (); + if (fontRef) + { + CTFontDescriptorRef fontDesc = CTFontCopyFontDescriptor (fontRef); + + [self setFont:[NSFont fontWithDescriptor:(NSFontDescriptor*)fontDesc + size:ctf->getSize ()]]; + CFRelease (fontDesc); + } + } + NSString* text = fromUTF8String (tec->platformGetText ()); + NSString* placeholder = fromUTF8String (tec->platformGetPlaceholderText ()); + + [self setTextColor:nsColorFromCColor (tec->platformGetFontColor ())]; + [self setBordered:NO]; + [self setAllowsEditingTextAttributes:NO]; + [self setImportsGraphics:NO]; + [self setStringValue:text]; + [self setFocusRingType:NSFocusRingTypeNone]; + [self sizeToFit]; + [(NSView*)self setAutoresizingMask:NSViewMinYMargin]; + if ([self frame].size.height < editFrameRect.size.height) + { + CGFloat offset = editFrameRect.size.height - [self frame].size.height; + editFrameRect.origin.y = static_cast (offset / 2.); + editFrameRect.size.height = [self frame].size.height; + } + else + editFrameRect.size.height = [self frame].size.height; + [self setFrame:editFrameRect]; + + [containerView addSubview:self]; + [self performSelector:@selector (syncSize)]; + [frameView addSubview:containerView]; + + NSTextFieldCell* cell = [self cell]; + [cell setDrawsBackground:NO]; + [cell setLineBreakMode:NSLineBreakByClipping]; + [cell setScrollable:YES]; + if (tec->platformGetHoriTxtAlign () == kCenterText) + [cell setAlignment:MacTextAlignment::Center]; + else if (tec->platformGetHoriTxtAlign () == kRightText) + [cell setAlignment:MacTextAlignment::Right]; + if (placeholder.length > 0) + { + CColor color = tec->platformGetFontColor (); + color.alpha /= 2; + NSMutableParagraphStyle* paragraphStyle = + [[[NSMutableParagraphStyle alloc] init] autorelease]; + if (tec->platformGetHoriTxtAlign () == kCenterText) + paragraphStyle.alignment = MacTextAlignment::Center; + else if (tec->platformGetHoriTxtAlign () == kRightText) + paragraphStyle.alignment = MacTextAlignment::Right; + NSDictionary* attrDict = [NSDictionary + dictionaryWithObjectsAndKeys:[self font], NSFontAttributeName, + nsColorFromCColor (color), + NSForegroundColorAttributeName, paragraphStyle, + NSParagraphStyleAttributeName, nil]; + NSAttributedString* as = + [[[NSAttributedString alloc] initWithString:placeholder + attributes:attrDict] autorelease]; + [cell setPlaceholderAttributedString:as]; + } - if (auto tv = static_cast ([[self window] fieldEditor:YES forObject:self])) - tv.insertionPointColor = nsColorFromCColor (tec->platformGetFontColor ()); + [self setDelegate:self]; + [self setNextKeyView:frameView]; - if ([frameView respondsToSelector:@selector (makeSubViewFirstResponder:)]) - [frameView performSelector:@selector (makeSubViewFirstResponder:) - withObject:self]; + if (auto tv = static_cast ([[self window] fieldEditor:YES forObject:self])) + tv.insertionPointColor = nsColorFromCColor (tec->platformGetFontColor ()); - dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t) (1 * NSEC_PER_MSEC)), - dispatch_get_main_queue (), ^{ - [[self window] makeFirstResponder:self]; - }); + if ([frameView respondsToSelector:@selector (makeSubViewFirstResponder:)]) + [frameView performSelector:@selector (makeSubViewFirstResponder:) withObject:self]; + dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t) (1 * NSEC_PER_MSEC)), + dispatch_get_main_queue (), ^{ + [[self window] makeFirstResponder:self]; + }); + } + return self; } - return self; -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSTextField_SyncSize (id self, SEL _cmd) -{ - CocoaTextEdit* te = (CocoaTextEdit*)OBJC_GET_VALUE(self, _textEdit); - if (!te) - return; - IPlatformTextEditCallback* tec = te->getTextEdit (); - NSView* containerView = [self superview]; - CRect rect (tec->platformGetVisibleSize ()); - rect.makeIntegral (); - - [containerView setFrame:nsRectFromCRect (rect)]; - - rect.extend (15,-15); - [[containerView superview] setNeedsDisplayInRect:nsRectFromCRect (rect)]; -} - -//------------------------------------------------------------------------------------ -static void VSTGUI_NSTextField_RemoveFromSuperview (id self, SEL _cmd) -{ - OBJC_SET_VALUE (self, _textEdit, nullptr); - NSView* containerView = [self superview]; - if (containerView) + //------------------------------------------------------------------------------------ + static void SyncSize (id self, SEL _cmd) { - [[containerView window] makeFirstResponder:[containerView superview]]; - [containerView removeFromSuperview]; - __OBJC_SUPER(self) - SuperRemoveFromSuperview (SUPER, @selector(removeFromSuperview)); // [super removeFromSuperview]; - [containerView release]; - } -} + if (auto te = ObjCInstance (self).getVariable (textEditVarName)) + { + auto textEdit = te->get (); + if (!textEdit) + return; + NSView* containerView = [self superview]; + CRect rect (textEdit->platformGetVisibleSize ()); + rect.makeIntegral (); -//------------------------------------------------------------------------------------ -static void VSTGUI_NSTextField_TextDidChange (id self, SEL _cmd, NSNotification* notification) -{ - CocoaTextEdit* te = (CocoaTextEdit*)OBJC_GET_VALUE(self, _textEdit); - if (te && te->getTextEdit ()) - { - te->getTextEdit ()->platformTextDidChange (); + [containerView setFrame:nsRectFromCRect (rect)]; + + rect.extend (15, -15); + [[containerView superview] setNeedsDisplayInRect:nsRectFromCRect (rect)]; + } } - __OBJC_SUPER(self) - SuperTextDidChange (SUPER, @selector(textDidChange:), notification); -} -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSTextField_DoCommandBySelector (id self, SEL _cmd, NSControl* control, NSTextView* textView, SEL commandSelector) -{ - CocoaTextEdit* te = (CocoaTextEdit*)OBJC_GET_VALUE(self, _textEdit); - if (!te) - return NO; - IPlatformTextEditCallback* tec = te->getTextEdit (); - if (commandSelector == @selector (insertNewline:)) + //------------------------------------------------------------------------------------ + static void RemoveFromSuperview (id self, SEL _cmd) { - VstKeyCode keyCode = {}; - keyCode.virt = VKEY_RETURN; - if (tec->platformOnKeyDown (keyCode)) + ObjCInstance obj (self); + if (auto var = obj.getVariable (textEditVarName)) + var->set (nullptr); + NSView* containerView = [self superview]; + if (containerView) { - return YES; + [[containerView window] makeFirstResponder:[containerView superview]]; + [containerView removeFromSuperview]; + // [super removeFromSuperview]; + obj.callSuper (_cmd); + [containerView release]; } } - else if (commandSelector == @selector (insertTab:)) + + //------------------------------------------------------------------------------------ + static void TextDidChange (id self, SEL _cmd, NSNotification* notification) { - VstKeyCode keyCode = {}; - keyCode.virt = VKEY_TAB; - if (tec->platformOnKeyDown (keyCode)) + ObjCInstance obj (self); + if (auto var = obj.getVariable (textEditVarName)) { - return YES; + if (auto te = var->get ()) + te->platformTextDidChange (); } + obj.callSuper (_cmd, notification); } - else if (commandSelector == @selector (insertBacktab:)) + + //------------------------------------------------------------------------------------ + static BOOL DoCommandBySelector (id self, SEL _cmd, NSControl* control, NSTextView* textView, + SEL commandSelector) { - VstKeyCode keyCode = {}; - keyCode.virt = VKEY_TAB; - keyCode.modifier = MODIFIER_SHIFT; - if (tec->platformOnKeyDown (keyCode)) + auto var = ObjCInstance (self).getVariable (textEditVarName); + if (!var) + return NO; + IPlatformTextEditCallback* tec = var->get (); + if (!tec) + return NO; + if (commandSelector == @selector (insertNewline:)) { - return YES; + KeyboardEvent event; + event.type = EventType::KeyDown; + event.virt = VirtualKey::Return; + tec->platformOnKeyboardEvent (event); + if (event.consumed) + return YES; } - } - else if (commandSelector == @selector (cancelOperation:)) - { - VstKeyCode keyCode = {}; - keyCode.virt = VKEY_ESCAPE; - if (tec->platformOnKeyDown (keyCode)) + else if (commandSelector == @selector (insertTab:)) { - return YES; + KeyboardEvent event; + event.type = EventType::KeyDown; + event.virt = VirtualKey::Tab; + tec->platformOnKeyboardEvent (event); + if (event.consumed) + return YES; } + else if (commandSelector == @selector (insertBacktab:)) + { + KeyboardEvent event; + event.type = EventType::KeyDown; + event.virt = VirtualKey::Tab; + event.modifiers.add (ModifierKey::Shift); + tec->platformOnKeyboardEvent (event); + if (event.consumed) + return YES; + } + else if (commandSelector == @selector (cancelOperation:)) + { + KeyboardEvent event; + event.type = EventType::KeyDown; + event.virt = VirtualKey::Escape; + tec->platformOnKeyboardEvent (event); + if (event.consumed) + return YES; + } + return NO; } - return NO; -} - -namespace VSTGUI { - -//----------------------------------------------------------------------------- -__attribute__((__destructor__)) static void cleanup_VSTGUI_NSTextField () -{ - if (textFieldClass) - objc_disposeClassPair (textFieldClass); - if (secureTextFieldClass) - objc_disposeClassPair (secureTextFieldClass); -} - -//----------------------------------------------------------------------------- -void CocoaTextEdit::initClass () -{ - if (textFieldClass == nullptr) - { - AutoreleasePool ap; - NSMutableString* textFieldClassName = [[[NSMutableString alloc] initWithString:@"VSTGUI_NSTextField"] autorelease]; - textFieldClass = generateUniqueClass (textFieldClassName, [NSTextField class]); - VSTGUI_CHECK_YES(class_addMethod (textFieldClass, @selector(initWithTextEdit:), IMP (VSTGUI_NSTextField_Init), "@@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (textFieldClass, @selector(syncSize), IMP (VSTGUI_NSTextField_SyncSize), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (textFieldClass, @selector(removeFromSuperview), IMP (VSTGUI_NSTextField_RemoveFromSuperview), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (textFieldClass, @selector(control:textView:doCommandBySelector:), IMP (VSTGUI_NSTextField_DoCommandBySelector), "B@:@:@:@::")) - VSTGUI_CHECK_YES(class_addMethod (textFieldClass, @selector(textDidChange:), IMP (VSTGUI_NSTextField_TextDidChange), "v@:@:@@:")) - VSTGUI_CHECK_YES(class_addIvar (textFieldClass, "_textEdit", sizeof (void*), (uint8_t)log2(sizeof(void*)), @encode(void*))) - objc_registerClassPair (textFieldClass); - - NSMutableString* secureTextFieldClassName = [[[NSMutableString alloc] initWithString:@"VSTGUI_NSSecureTextField"] autorelease]; - secureTextFieldClass = generateUniqueClass (secureTextFieldClassName, [NSSecureTextField class]); - VSTGUI_CHECK_YES(class_addMethod (secureTextFieldClass, @selector(initWithTextEdit:), IMP (VSTGUI_NSTextField_Init), "@@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (secureTextFieldClass, @selector(syncSize), IMP (VSTGUI_NSTextField_SyncSize), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (secureTextFieldClass, @selector(removeFromSuperview), IMP (VSTGUI_NSTextField_RemoveFromSuperview), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (secureTextFieldClass, @selector(control:textView:doCommandBySelector:), IMP (VSTGUI_NSTextField_DoCommandBySelector), "B@:@:@:@::")) - VSTGUI_CHECK_YES(class_addMethod (secureTextFieldClass, @selector(textDidChange:), IMP (VSTGUI_NSTextField_TextDidChange), "v@:@:@@:")) - VSTGUI_CHECK_YES(class_addIvar (secureTextFieldClass, "_textEdit", sizeof (void*), (uint8_t)log2(sizeof(void*)), @encode(void*))) - objc_registerClassPair (secureTextFieldClass); - } -} +}; //----------------------------------------------------------------------------- CocoaTextEdit::CocoaTextEdit (NSView* parent, IPlatformTextEditCallback* textEdit) @@ -301,11 +319,10 @@ static BOOL VSTGUI_NSTextField_DoCommandBySelector (id self, SEL _cmd, NSControl , platformControl (nullptr) , parent (parent) { - initClass (); if (textEdit->platformIsSecureTextEdit ()) - platformControl = [[secureTextFieldClass alloc] initWithTextEdit:(id)this]; + platformControl = [VSTGUI_NSTextField::allocSecure () initWithTextEdit:(id)this]; else - platformControl = [[textFieldClass alloc] initWithTextEdit:(id)this]; + platformControl = [VSTGUI_NSTextField::alloc () initWithTextEdit:(id)this]; } //----------------------------------------------------------------------------- @@ -335,6 +352,7 @@ static BOOL VSTGUI_NSTextField_DoCommandBySelector (id self, SEL _cmd, NSControl return true; } +//----------------------------------------------------------------------------- } // VSTGUI #endif // MAC_COCOA diff --git a/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm b/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm index b349598a1..0501614fd 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm +++ b/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm @@ -9,6 +9,7 @@ #import "../cgbitmap.h" #import "../macclipboard.h" #import "cocoahelpers.h" +#import "objcclassbuilder.h" //------------------------------------------------------------------------ @interface NSObject (VSTGUI_BinaryDataType_Private) @@ -37,6 +38,8 @@ - (id)initWithData:(const void*)data andSize:(size_t)size; } private: + static constexpr const auto dataVarName = "_data"; + static NSString* getCocoaPasteboardTypeString () { return [NSString stringWithCString:VSTGUI::MacClipboard::getPasteboardBinaryType () @@ -45,16 +48,28 @@ - (id)initWithData:(const void*)data andSize:(size_t)size; static id Init (id self, SEL, const void* buffer, size_t bufferSize) { - __OBJC_SUPER (self) - self = SuperInit (SUPER, @selector (init)); + ObjCInstance obj (self); + self = obj.callSuper (@selector (init)); if (self) { - auto data = [[[NSData alloc] initWithBytes:buffer length:bufferSize] autorelease]; - OBJC_SET_VALUE (self, _data, data); + auto data = [[NSData alloc] initWithBytes:buffer length:bufferSize]; + if (auto var = obj.getVariable (dataVarName)) + var->set (data); } return self; } + static void Dealloc (id self, SEL _cmd) + { + ObjCInstance obj (self); + if (auto var = obj.getVariable (dataVarName); var.has_value () && var->get ()) + { + [var->get () release]; + var->set (nullptr); + } + obj.callSuper (_cmd); + } + static NSArray* WritableTypesForPasteboard (id, SEL, NSPasteboard*) { return @[getCocoaPasteboardTypeString ()]; @@ -62,38 +77,30 @@ static id Init (id self, SEL, const void* buffer, size_t bufferSize) static id PasteboardPropertyListForType (id self, SEL, NSPasteboardType) { - return OBJC_GET_VALUE (self, _data); + ObjCInstance obj (self); + if (auto var = obj.getVariable (dataVarName); var.has_value () && var->get ()) + return var->get (); + return nullptr; } Class cl {nullptr}; BinaryDataType () { initClass (); } - ~BinaryDataType () - { - if (cl) - objc_disposeClassPair (cl); - } + ~BinaryDataType () { objc_disposeClassPair (cl); } void initClass () { - auto className = - [[[NSMutableString alloc] initWithString:@"VSTGUI_BinaryDataType"] autorelease]; - cl = generateUniqueClass (className, [NSObject class]); - VSTGUI_CHECK_YES (class_addProtocol (cl, objc_getProtocol ("NSPasteboardWriting"))) - char funcSig[100]; - sprintf (funcSig, "@@:@:%s:%s", @encode (const void*), @encode (size_t)); - VSTGUI_CHECK_YES ( - class_addMethod (cl, @selector (initWithData:andSize:), IMP (Init), funcSig)) - sprintf (funcSig, "%s@:@:%s", @encode (NSArray*), - @encode (NSPasteboard*)); - VSTGUI_CHECK_YES (class_addMethod (cl, @selector (writableTypesForPasteboard:), - IMP (WritableTypesForPasteboard), funcSig)) - sprintf (funcSig, "%s@:@:%s", @encode (id), @encode (NSPasteboardType)); - VSTGUI_CHECK_YES (class_addMethod (cl, @selector (pasteboardPropertyListForType:), - IMP (PasteboardPropertyListForType), funcSig)) - VSTGUI_CHECK_YES (class_addIvar (cl, "_data", sizeof (NSData*), - (uint8_t)log2 (sizeof (NSData*)), @encode (NSData*))) + cl = ObjCClassBuilder () + .init ("VSTGUI_BinaryDataType", [NSObject class]) + .addProtocol ("NSPasteboardWriting") + .addMethod (@selector (initWithData:andSize:), Init) + .addMethod (@selector (dealloc), Dealloc) + .addMethod (@selector (writableTypesForPasteboard:), WritableTypesForPasteboard) + .addMethod (@selector (pasteboardPropertyListForType:), + PasteboardPropertyListForType) + .addIvar (dataVarName) + .finalize (); } }; diff --git a/vstgui/lib/platform/mac/cocoa/nsviewframe.h b/vstgui/lib/platform/mac/cocoa/nsviewframe.h index 888eef408..cc62a7c5b 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewframe.h +++ b/vstgui/lib/platform/mac/cocoa/nsviewframe.h @@ -66,6 +66,7 @@ class NSViewFrame : public IPlatformFrame, public ICocoaPlatformFrame, public IP bool getSize (CRect& size) const override; bool getCurrentMousePosition (CPoint& mousePosition) const override; bool getCurrentMouseButtons (CButtonState& buttons) const override; + bool getCurrentModifiers (Modifiers& modifiers) const override; bool setMouseCursor (CCursorType type) override; bool invalidRect (const CRect& rect) override; bool scrollRect (const CRect& src, const CPoint& distance) override; @@ -96,8 +97,6 @@ class NSViewFrame : public IPlatformFrame, public ICocoaPlatformFrame, public IP protected: void addDebugRedrawRect (CRect r, bool isClipBoundingBox = false); - static void initClass (); - NSView* nsView; CocoaTooltipWindow* tooltipWindow; SharedPointer dragDataPackage; diff --git a/vstgui/lib/platform/mac/cocoa/nsviewframe.mm b/vstgui/lib/platform/mac/cocoa/nsviewframe.mm index f4c303f30..079d93d98 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewframe.mm +++ b/vstgui/lib/platform/mac/cocoa/nsviewframe.mm @@ -7,6 +7,7 @@ #if MAC_COCOA #import "cocoahelpers.h" +#import "objcclassbuilder.h" #import "cocoatextedit.h" #import "nsviewoptionmenu.h" #import "cocoaopenglview.h" @@ -20,14 +21,12 @@ #import "../../../cvstguitimer.h" #import "../../common/genericoptionmenu.h" #import "../../../cframe.h" - -#if MAC_CARBON - #import "../carbon/hiviewframe.h" - #import -#endif +#import "../../../events.h" #include +#import + #ifndef MAC_OS_X_VERSION_10_14 #define MAC_OS_X_VERSION_10_14 101400 #endif @@ -35,59 +34,83 @@ using namespace VSTGUI; #if DEBUG - //------------------------------------------------------------------------ -@interface DebugRedrawAnimDelegate : NSObject -@property (retain, readwrite) CALayer* layer; +struct VSTGUI_DebugRedrawAnimDelegate +{ + static id alloc () { return [instance ().objcClass alloc]; } -@end +private: + static constexpr const auto layerVarName = "layer"; -@implementation DebugRedrawAnimDelegate + Class objcClass {nullptr}; -//------------------------------------------------------------------------ -- (void)dealloc -{ - self.layer = nil; - [super dealloc]; -} + VSTGUI_DebugRedrawAnimDelegate () + { + objcClass = ObjCClassBuilder () + .init ("VSTGUI_DebugRedrawAnimDelegate", [NSObject class]) + .addProtocol ("CAAnimationDelegate") + .addMethod (@selector (dealloc), dealloc) + .addMethod (@selector (animationDidStop:finished:), animationDidStop) + .addMethod (@selector (setLayer:), setLayer) + .addIvar (layerVarName) + .finalize (); + } + ~VSTGUI_DebugRedrawAnimDelegate () { objc_disposeClassPair (objcClass); } -//------------------------------------------------------------------------ -- (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag -{ - if (flag) + static VSTGUI_DebugRedrawAnimDelegate& instance () { - [self.layer removeFromSuperlayer]; - self.layer = nil; + static VSTGUI_DebugRedrawAnimDelegate gInstance; + return gInstance; } -} -@end + static Ivar getVar (id self, const char* name) + { + return class_getInstanceVariable ([self class], name); + } -#endif // DEBUG + static CALayer* getLayer (id self) + { + if (auto ivar = getVar (self, layerVarName)) + if (id oldValue = object_getIvar (self, ivar)) + return oldValue; + return nullptr; + } -//------------------------------------------------------------------------------------ -HIDDEN inline IPlatformFrameCallback* getFrame (id obj) -{ - NSViewFrame* nsViewFrame = (NSViewFrame*)OBJC_GET_VALUE(obj, _nsViewFrame); - if (nsViewFrame) - return nsViewFrame->getFrame (); - return nullptr; -} + static void dealloc (id self, SEL _cmd) { setLayer (self, @selector (setLayer:), nullptr); } -//------------------------------------------------------------------------------------ -HIDDEN inline NSViewFrame* getNSViewFrame (id obj) -{ - return (NSViewFrame*)OBJC_GET_VALUE(obj, _nsViewFrame); -} + static void animationDidStop (id self, SEL _cmd, CAAnimation* anim, BOOL flag) + { + if (flag) + { + if (auto layer = getLayer (self)) + { + [layer removeFromSuperlayer]; + setLayer (self, @selector (setLayer:), nullptr); + } + } + } + + static void setLayer (id self, SEL _cmd, CALayer* layer) + { + if (auto ivar = getVar (self, layerVarName)) + { + if (id oldValue = object_getIvar (self, ivar)) + [oldValue release]; + if (layer) + [layer retain]; + object_setIvar (self, ivar, layer); + } + } +}; -static Class viewClass = nullptr; +#endif // DEBUG //------------------------------------------------------------------------------------ @interface NSObject (VSTGUI_NSView) -- (id) initWithNSViewFrame: (NSViewFrame*) frame parent: (NSView*) parent andSize: (const CRect*) size; -- (BOOL) onMouseDown: (NSEvent*) event; -- (BOOL) onMouseUp: (NSEvent*) event; -- (BOOL) onMouseMoved: (NSEvent*) event; +- (id)initWithNSViewFrame:(NSViewFrame*)frame parent:(NSView*)parent andSize:(const CRect*)size; +- (BOOL)onMouseDown:(NSEvent*)event; +- (BOOL)onMouseUp:(NSEvent*)event; +- (BOOL)onMouseMoved:(NSEvent*)event; @end //------------------------------------------------------------------------------------ @@ -103,6 +126,21 @@ static void mapModifiers (NSUInteger nsEventModifiers, CButtonState& buttonState buttonState |= kApple; } +//------------------------------------------------------------------------------------ +static Modifiers modifiersFromModifierFlags (NSUInteger nsEventModifiers) +{ + Modifiers mods; + if (nsEventModifiers & MacEventModifier::ShiftKeyMask) + mods.add (ModifierKey::Shift); + if (nsEventModifiers & MacEventModifier::CommandKeyMask) + mods.add (ModifierKey::Control); + if (nsEventModifiers & MacEventModifier::AlternateKeyMask) + mods.add (ModifierKey::Alt); + if (nsEventModifiers & MacEventModifier::ControlKeyMask) + mods.add (ModifierKey::Super); + return mods; +} + //------------------------------------------------------------------------------------ static bool nsViewGetCurrentMouseLocation (void* nsView, CPoint& where) { @@ -114,585 +152,768 @@ static bool nsViewGetCurrentMouseLocation (void* nsView, CPoint& where) } //------------------------------------------------------------------------------------ -static id VSTGUI_NSView_Init (id self, SEL _cmd, void* _frame, NSView* parentView, const void* _size) +static NSPoint getGlobalMouseLocation (NSView* view) +{ + NSRect r = {}; + r.origin = [NSEvent mouseLocation]; + r = [[view window] convertRectFromScreen:r]; + return [view convertPoint:r.origin fromView:nil]; +} + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14 +static auto VSTGUI_NSPasteboardTypeFileURL = NSPasteboardTypeFileURL; +#else +static auto VSTGUI_NSPasteboardTypeFileURL = NSFilenamesPboardType; +#endif + +//------------------------------------------------------------------------------------ +struct VSTGUI_NSView { - const CRect* size = (const CRect*)_size; - NSViewFrame* frame = (NSViewFrame*)_frame; - NSRect nsSize = nsRectFromCRect (*size); + static id alloc () { return [instance ().viewClass alloc]; } + +private: + static constexpr const auto nsViewFrameVarName = "_nsViewFrame"; - __OBJC_SUPER(self) - self = SuperInitWithFrame (SUPER, @selector(initWithFrame:), nsSize); // self = [super initWithFrame: nsSize]; - if (self) + Class viewClass {nullptr}; + + //------------------------------------------------------------------------------------ + VSTGUI_NSView () { - OBJC_SET_VALUE(self, _nsViewFrame, frame); // _vstguiframe = frame; + ObjCClassBuilder builder; + builder.init ("VSTGUI_NSView", [NSView class]) + .addMethod (@selector (initWithNSViewFrame:parent:andSize:), init) + // .addMethod (@selector(dealloc), IMP (Dealloc))) + .addMethod (@selector (updateTrackingAreas), updateTrackingAreas) + .addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow) + .addMethod (@selector (windowKeyStateChanged:), windowKeyStateChanged) + .addMethod (@selector (windowDidChangeBackingProperties:), + windowDidChangeBackingProperties) + .addMethod (@selector (isFlipped), isFlipped) + .addMethod (@selector (acceptsFirstResponder), acceptsFirstResponder) + .addMethod (@selector (becomeFirstResponder), becomeFirstResponder) + .addMethod (@selector (resignFirstResponder), resignFirstResponder) + .addMethod (@selector (canBecomeKeyView), canBecomeKeyView) + .addMethod (@selector (wantsDefaultClipping), wantsDefaultClipping) + .addMethod (@selector (isOpaque), isOpaque) + .addMethod (@selector (drawRect:), drawRect) + .addMethod (@selector (setNeedsDisplayInRect:), setNeedsDisplayInRect) + .addMethod (@selector (viewWillDraw), viewWillDraw) + .addMethod (@selector (shouldBeTreatedAsInkEvent:), shouldBeTreatedAsInkEvent) + .addMethod (@selector (onMouseDown:), onMouseDown) + .addMethod (@selector (onMouseUp:), onMouseUp) + .addMethod (@selector (onMouseMoved:), onMouseMoved) + .addMethod (@selector (mouseDown:), mouseDown) + .addMethod (@selector (rightMouseDown:), mouseDown) + .addMethod (@selector (otherMouseDown:), mouseDown) + .addMethod (@selector (mouseUp:), mouseUp) + .addMethod (@selector (rightMouseUp:), mouseUp) + .addMethod (@selector (otherMouseUp:), mouseUp) + .addMethod (@selector (mouseMoved:), mouseMoved) + .addMethod (@selector (mouseDragged:), mouseMoved) + .addMethod (@selector (rightMouseDragged:), mouseMoved) + .addMethod (@selector (otherMouseDragged:), mouseMoved) + .addMethod (@selector (scrollWheel:), scrollWheel) + .addMethod (@selector (mouseEntered:), mouseEntered) + .addMethod (@selector (mouseExited:), mouseExited) + .addMethod (@selector (cursorUpdate:), cursorUpdate) + .addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse) + .addMethod (@selector (performKeyEquivalent:), performKeyEquivalent) + .addMethod (@selector (keyDown:), keyDown) + .addMethod (@selector (keyUp:), keyUp) + .addMethod (@selector (magnifyWithEvent:), magnifyWithEvent) + .addMethod (@selector (focusRingType), focusRingType) + .addMethod (@selector (makeSubViewFirstResponder:), makeSubViewFirstResponder) + .addMethod (@selector (draggingEntered:), draggingEntered) + .addMethod (@selector (draggingUpdated:), draggingUpdated) + .addMethod (@selector (draggingExited:), draggingExited) + .addMethod (@selector (performDragOperation:), performDragOperation); - [parentView addSubview: self]; + if (auto nsDraggingSourceProtocol = objc_getProtocol ("NSDraggingSource")) + { + builder.addProtocol (nsDraggingSourceProtocol) + .addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), + draggingSessionSourceOperationMaskForDraggingContext) + .addMethod (@selector (draggingSession:willBeginAtPoint:), + draggingSessionWillBeginAtPoint) + .addMethod (@selector (draggingSession:movedToPoint:), draggingSessionMovedToPoint) + .addMethod (@selector (draggingSession:endedAtPoint:operation:), + draggingSessionEndedAtPoint); + } - [self - registerForDraggedTypes: - [NSArray - arrayWithObjects:NSPasteboardTypeString, -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14 - NSPasteboardTypeFileURL, -#else - NSFilenamesPboardType, +#if VSTGUI_ENABLE_DEPRECATED_METHODS + builder.addMethod (@selector (draggedImage:endedAt:operation:), + draggedImageEndedAtOperation); #endif - NSPasteboardTypeColor, - [NSString - stringWithCString:MacClipboard::getPasteboardBinaryType () - encoding:NSASCIIStringEncoding], - nil]]; - [self setFocusRingType:NSFocusRingTypeNone]; + // optional touchbar support + if (auto protocol = objc_getProtocol ("NSTouchBarProvider")) + { + builder.addProtocol (protocol).addMethod (@selector (makeTouchBar), makeTouchbar); + } + + builder.addIvar ("_nsViewFrame"); + + viewClass = builder.finalize (); } - return self; -} -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_isFlipped (id self, SEL _cmd) { return YES; } -static BOOL VSTGUI_NSView_acceptsFirstResponder (id self, SEL _cmd) { return YES; } -static BOOL VSTGUI_NSView_canBecomeKeyView (id self, SEL _cmd) { return YES; } -static BOOL VSTGUI_NSView_wantsDefaultClipping (id self, SEL _cmd) { return NO; } -static NSFocusRingType VSTGUI_NSView_focusRingType (id self) { return NSFocusRingTypeNone; } -static BOOL VSTGUI_NSView_shouldBeTreatedAsInkEvent (id self, SEL _cmd, NSEvent *event) { return NO; } + //------------------------------------------------------------------------------------ + ~VSTGUI_NSView () { objc_disposeClassPair (viewClass); } -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_makeSubViewFirstResponder (id self, SEL _cmd, NSResponder* newFirstResponder) -{ - NSViewFrame* nsFrame = getNSViewFrame (self); - if (nsFrame) + //------------------------------------------------------------------------------------ + static VSTGUI_NSView& instance () { - nsFrame->setIgnoreNextResignFirstResponder (true); - [[self window] makeFirstResponder:newFirstResponder]; - nsFrame->setIgnoreNextResignFirstResponder (false); + static VSTGUI_NSView gInstance; + return gInstance; } -} -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_becomeFirstResponder (id self, SEL _cmd) -{ - if ([[self window] isKeyWindow]) + //------------------------------------------------------------------------------------ + static NSViewFrame* getNSViewFrame (id obj) { - IPlatformFrameCallback* frame = getFrame (self); - if (frame) - frame->platformOnActivate (true); + if (auto var = ObjCInstance (obj).getVariable (nsViewFrameVarName)) + return var->get (); + return nullptr; } - return YES; -} -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_resignFirstResponder (id self, SEL _cmd) -{ - NSView* firstResponder = (NSView*)[[self window] firstResponder]; - if (![firstResponder isKindOfClass:[NSView class]]) - firstResponder = nil; - if (firstResponder) + //------------------------------------------------------------------------------------ + static IPlatformFrameCallback* getFrame (id obj) + { + if (auto nsViewFrame = getNSViewFrame (obj)) + return nsViewFrame->getFrame (); + return nullptr; + } + + //------------------------------------------------------------------------------------ + static id init (id self, SEL _cmd, void* _frame, NSView* parentView, const void* _size) + { + ObjCInstance obj (self); + + const CRect* size = (const CRect*)_size; + NSViewFrame* frame = (NSViewFrame*)_frame; + NSRect nsSize = nsRectFromCRect (*size); + + self = obj.callSuper (@selector (initWithFrame:), nsSize); + if (self) + { + if (auto var = obj.getVariable (nsViewFrameVarName)) + var->set (frame); + + [parentView addSubview:self]; + + [self + registerForDraggedTypes: + [NSArray arrayWithObjects:NSPasteboardTypeString, + VSTGUI_NSPasteboardTypeFileURL, NSPasteboardTypeColor, + [NSString stringWithCString: + MacClipboard::getPasteboardBinaryType () + encoding:NSASCIIStringEncoding], + nil]]; + + [self setFocusRingType:NSFocusRingTypeNone]; + } + return self; + } + + //------------------------------------------------------------------------------------ + static BOOL isFlipped (id self, SEL _cmd) { return YES; } + static BOOL acceptsFirstResponder (id self, SEL _cmd) { return YES; } + static BOOL canBecomeKeyView (id self, SEL _cmd) { return YES; } + static BOOL wantsDefaultClipping (id self, SEL _cmd) { return NO; } + static NSFocusRingType focusRingType (id self) { return NSFocusRingTypeNone; } + static BOOL shouldBeTreatedAsInkEvent (id self, SEL _cmd, NSEvent* event) { return NO; } + + //------------------------------------------------------------------------------------ + static void makeSubViewFirstResponder (id self, SEL _cmd, NSResponder* newFirstResponder) { NSViewFrame* nsFrame = getNSViewFrame (self); - if (nsFrame && nsFrame->getIgnoreNextResignFirstResponder ()) + if (nsFrame) { - while (firstResponder != self && firstResponder != nil) - firstResponder = [firstResponder superview]; - if (firstResponder == self && [[self window] isKeyWindow]) + nsFrame->setIgnoreNextResignFirstResponder (true); + [[self window] makeFirstResponder:newFirstResponder]; + nsFrame->setIgnoreNextResignFirstResponder (false); + } + } + + //------------------------------------------------------------------------------------ + static BOOL becomeFirstResponder (id self, SEL _cmd) + { + if ([[self window] isKeyWindow]) + { + IPlatformFrameCallback* frame = getFrame (self); + if (frame) + frame->platformOnActivate (true); + } + return YES; + } + + //------------------------------------------------------------------------------------ + static BOOL resignFirstResponder (id self, SEL _cmd) + { + NSView* firstResponder = (NSView*)[[self window] firstResponder]; + if (![firstResponder isKindOfClass:[NSView class]]) + firstResponder = nil; + if (firstResponder) + { + NSViewFrame* nsFrame = getNSViewFrame (self); + if (nsFrame && nsFrame->getIgnoreNextResignFirstResponder ()) { - return YES; + while (firstResponder != self && firstResponder != nil) + firstResponder = [firstResponder superview]; + if (firstResponder == self && [[self window] isKeyWindow]) + { + return YES; + } } + IPlatformFrameCallback* frame = getFrame (self); + if (frame) + frame->platformOnActivate (false); } - IPlatformFrameCallback* frame = getFrame (self); - if (frame) - frame->platformOnActivate (false); + return YES; } - return YES; -} - -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_updateTrackingAreas (id self, SEL _cmd) -{ - __OBJC_SUPER(self) - NSViewFrame* viewFrame = getNSViewFrame (self); - if (viewFrame) - viewFrame->initTrackingArea (); - - SuperUpdateTrackingAreas (SUPER, @selector(updateTrackingAreas)); -} + //------------------------------------------------------------------------------------ + static void updateTrackingAreas (id self, SEL _cmd) + { + NSViewFrame* viewFrame = getNSViewFrame (self); + if (viewFrame) + viewFrame->initTrackingArea (); -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_windowDidChangeBackingProperties (id self, SEL _cmd, NSNotification* notification) -{ - double scaleFactor = 1.; - if (auto window = [self window]) - scaleFactor = [window backingScaleFactor]; - - NSViewFrame* viewFrame = getNSViewFrame (self); - if (viewFrame) - viewFrame->scaleFactorChanged (scaleFactor); -} + ObjCInstance (self).callSuper (_cmd); + } -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_viewDidMoveToWindow (id self, SEL _cmd) -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - NSWindow* window = [self window]; - if (window) + //------------------------------------------------------------------------------------ + static void windowDidChangeBackingProperties (id self, SEL _cmd, NSNotification* notification) { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowKeyStateChanged:) name:NSWindowDidBecomeKeyNotification object:window]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowKeyStateChanged:) name:NSWindowDidResignKeyNotification object:window]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window]; - IPlatformFrameCallback* frame = getFrame (self); - if (frame) - frame->platformOnActivate ([window isKeyWindow] ? true : false); - VSTGUI_NSView_windowDidChangeBackingProperties (self, _cmd, nil); + double scaleFactor = 1.; + if (auto window = [self window]) + scaleFactor = [window backingScaleFactor]; + + NSViewFrame* viewFrame = getNSViewFrame (self); + if (viewFrame) + viewFrame->scaleFactorChanged (scaleFactor); } -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_windowKeyStateChanged (id self, SEL _cmd, NSNotification* notification) -{ - IPlatformFrameCallback* frame = getFrame (self); - auto active = [[notification name] isEqualToString:NSWindowDidBecomeKeyNotification] ? true : false; - NSView* firstResponder = (NSView*)[[self window] firstResponder]; - if (![firstResponder isKindOfClass:[NSView class]]) - firstResponder = nil; - if (firstResponder) - { - while (firstResponder != self && firstResponder != nil) - firstResponder = [firstResponder superview]; - if (firstResponder == self) + //------------------------------------------------------------------------------------ + static void viewDidMoveToWindow (id self, SEL _cmd) + { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + NSWindow* window = [self window]; + if (window) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector (windowKeyStateChanged:) + name:NSWindowDidBecomeKeyNotification + object:window]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector (windowKeyStateChanged:) + name:NSWindowDidResignKeyNotification + object:window]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector (windowDidChangeBackingProperties:) + name:NSWindowDidChangeBackingPropertiesNotification + object:window]; + IPlatformFrameCallback* frame = getFrame (self); if (frame) - frame->platformOnActivate (active); + frame->platformOnActivate ([window isKeyWindow] ? true : false); + windowDidChangeBackingProperties (self, _cmd, nil); } } - frame->platformOnWindowActivate (active); -} - -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_isOpaque (id self, SEL _cmd) -{ - return NO; -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_drawRect (id self, SEL _cmd, NSRect rect) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (frame) - frame->drawRect (&rect); -} - -//------------------------------------------------------------------------ -static void VSTGUI_NSView_viewWillDraw (id self, SEL _cmd) -{ - if (@available (macOS 10.12, *)) + //------------------------------------------------------------------------------------ + static void windowKeyStateChanged (id self, SEL _cmd, NSNotification* notification) { - if (auto layer = [self layer]) + IPlatformFrameCallback* frame = getFrame (self); + auto active = + [[notification name] isEqualToString:NSWindowDidBecomeKeyNotification] ? true : false; + NSView* firstResponder = (NSView*)[[self window] firstResponder]; + if (![firstResponder isKindOfClass:[NSView class]]) + firstResponder = nil; + if (firstResponder) { - layer.contentsFormat = kCAContentsFormatRGBA8Uint; + while (firstResponder != self && firstResponder != nil) + firstResponder = [firstResponder superview]; + if (firstResponder == self) + { + if (frame) + frame->platformOnActivate (active); + } } + frame->platformOnWindowActivate (active); } - __OBJC_SUPER (self) - SuperViewWillRedraw (SUPER, _cmd); -} -//------------------------------------------------------------------------ -static void VSTGUI_NSView_setNeedsDisplayInRect (id self, SEL _cmd, NSRect rect) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (frame) - frame->setNeedsDisplayInRect (rect); - __OBJC_SUPER (self) - SuperSetNeedsDisplayInRect (SUPER, _cmd, rect); -} + //------------------------------------------------------------------------------------ + static BOOL isOpaque (id self, SEL _cmd) { return NO; } -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_onMouseDown (id self, SEL _cmd, NSEvent* theEvent) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (frame) - return frame->onMouseDown (theEvent) ? YES : NO; - return NO; -} + //------------------------------------------------------------------------------------ + static void drawRect (id self, SEL _cmd, NSRect rect) + { + NSViewFrame* frame = getNSViewFrame (self); + if (frame) + frame->drawRect (&rect); + } -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_onMouseUp (id self, SEL _cmd, NSEvent* theEvent) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (frame) - return frame->onMouseUp (theEvent) ? YES : NO; - return NO; -} + //------------------------------------------------------------------------ + static void viewWillDraw (id self, SEL _cmd) + { + if (@available (macOS 10.12, *)) + { + if (auto layer = [self layer]) + { + layer.contentsFormat = kCAContentsFormatRGBA8Uint; + } + } + ObjCInstance (self).callSuper (_cmd); + } -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_onMouseMoved (id self, SEL _cmd, NSEvent* theEvent) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (frame) - return frame->onMouseMoved (theEvent) ? YES : NO; - return NO; -} + //------------------------------------------------------------------------ + static void setNeedsDisplayInRect (id self, SEL _cmd, NSRect rect) + { + NSViewFrame* frame = getNSViewFrame (self); + if (frame) + frame->setNeedsDisplayInRect (rect); + ObjCInstance (self).callSuper (_cmd, rect); + } -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_mouseDown (id self, SEL _cmd, NSEvent* theEvent) -{ - if (![self onMouseDown: theEvent]) + //------------------------------------------------------------------------------------ + static BOOL onMouseDown (id self, SEL _cmd, NSEvent* theEvent) { - __OBJC_SUPER(self) - SuperEventMsg (SUPER, _cmd, theEvent); + NSViewFrame* frame = getNSViewFrame (self); + if (frame) + return frame->onMouseDown (theEvent) ? YES : NO; + return NO; } -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_mouseUp (id self, SEL _cmd, NSEvent* theEvent) -{ - if (![self onMouseUp: theEvent]) + //------------------------------------------------------------------------------------ + static BOOL onMouseUp (id self, SEL _cmd, NSEvent* theEvent) { - __OBJC_SUPER(self) - SuperEventMsg (SUPER, _cmd, theEvent); + NSViewFrame* frame = getNSViewFrame (self); + if (frame) + return frame->onMouseUp (theEvent) ? YES : NO; + return NO; } -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_mouseMoved (id self, SEL _cmd, NSEvent* theEvent) -{ - if (![self onMouseMoved: theEvent]) + //------------------------------------------------------------------------------------ + static BOOL onMouseMoved (id self, SEL _cmd, NSEvent* theEvent) { - __OBJC_SUPER(self) - SuperEventMsg (SUPER, _cmd, theEvent); + NSViewFrame* frame = getNSViewFrame (self); + if (frame) + return frame->onMouseMoved (theEvent) ? YES : NO; + return NO; } -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_scrollWheel (id self, SEL _cmd, NSEvent* theEvent) -{ - IPlatformFrameCallback* _vstguiframe = getFrame (self); - if (!_vstguiframe) - return; + //------------------------------------------------------------------------------------ + static void mouseDown (id self, SEL _cmd, NSEvent* theEvent) + { + if (![self onMouseDown:theEvent]) + { + ObjCInstance (self).callSuper (_cmd, theEvent); + } + } - CButtonState buttons = 0; - NSUInteger modifiers = [theEvent modifierFlags]; - NSPoint nsPoint = [theEvent locationInWindow]; - nsPoint = [self convertPoint:nsPoint fromView:nil]; - mapModifiers (modifiers, buttons); - auto distanceX = [theEvent scrollingDeltaX]; - auto distanceY = [theEvent scrollingDeltaY]; - if ([theEvent hasPreciseScrollingDeltas]) + //------------------------------------------------------------------------------------ + static void mouseUp (id self, SEL _cmd, NSEvent* theEvent) { - distanceX *= 0.1; - distanceY *= 0.1; + if (![self onMouseUp:theEvent]) + { + ObjCInstance (self).callSuper (_cmd, theEvent); + } } - if ([theEvent isDirectionInvertedFromDevice]) + + //------------------------------------------------------------------------------------ + static void mouseMoved (id self, SEL _cmd, NSEvent* theEvent) { - distanceX *= -1; - distanceY *= -1; - buttons |= kMouseWheelInverted; + if (![self onMouseMoved:theEvent]) + { + ObjCInstance (self).callSuper (_cmd, theEvent); + } } - CPoint p = pointFromNSPoint (nsPoint); - if (distanceX != 0.) - _vstguiframe->platformOnMouseWheel (p, kMouseWheelAxisX, static_cast (distanceX), buttons); - if (distanceY != 0.) - _vstguiframe->platformOnMouseWheel (p, kMouseWheelAxisY, static_cast (distanceY), buttons); -} -//------------------------------------------------------------------------------------ -static NSPoint getGlobalMouseLocation (NSView* view) -{ - NSRect r = {}; - r.origin = [NSEvent mouseLocation]; - r = [[view window] convertRectFromScreen:r]; - return [view convertPoint:r.origin fromView:nil]; -} + //------------------------------------------------------------------------------------ + static void scrollWheel (id self, SEL _cmd, NSEvent* theEvent) + { + IPlatformFrameCallback* _vstguiframe = getFrame (self); + if (!_vstguiframe) + return; -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_mouseEntered (id self, SEL _cmd, NSEvent* theEvent) -{ - IPlatformFrameCallback* _vstguiframe = getFrame (self); - if (!_vstguiframe) - return; - CButtonState buttons = 0; //eventButton (theEvent); - NSUInteger modifiers = [theEvent modifierFlags]; - CPoint p = pointFromNSPoint (getGlobalMouseLocation (self)); + auto distanceX = [theEvent scrollingDeltaX]; + auto distanceY = [theEvent scrollingDeltaY]; + if (std::abs (distanceX) == 0. && std::abs (distanceY) == 0.) + return; - mapModifiers (modifiers, buttons); - _vstguiframe->platformOnMouseMoved (p, buttons); -} + MouseWheelEvent event; + event.timestamp = static_cast (theEvent.timestamp * 1000.); -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_mouseExited (id self, SEL _cmd, NSEvent* theEvent) -{ - IPlatformFrameCallback* _vstguiframe = getFrame (self); - if (!_vstguiframe) - return; - CButtonState buttons = 0; //eventButton (theEvent); - NSUInteger modifiers = [theEvent modifierFlags]; - mapModifiers (modifiers, buttons); - CPoint p = pointFromNSPoint (getGlobalMouseLocation (self)); - _vstguiframe->platformOnMouseExited (p, buttons); -} + NSPoint nsPoint = [theEvent locationInWindow]; + nsPoint = [self convertPoint:nsPoint fromView:nil]; -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_cursorUpdate (id self, SEL _cmd, NSEvent* theEvent) -{ - NSViewFrame* frame = getNSViewFrame(self); - if (frame) + if ([theEvent hasPreciseScrollingDeltas]) + { + distanceX *= 0.1; + distanceY *= 0.1; + event.flags |= MouseWheelEvent::PreciseDeltas; + } + if ([theEvent isDirectionInvertedFromDevice]) + { + distanceX *= -1; + distanceY *= -1; + event.flags |= MouseWheelEvent::DirectionInvertedFromDevice; + } + + event.mousePosition = pointFromNSPoint (nsPoint); + event.modifiers = modifiersFromModifierFlags ([theEvent modifierFlags]); + event.deltaX = distanceX; + event.deltaY = distanceY; + + _vstguiframe->platformOnEvent (event); + } + + //------------------------------------------------------------------------------------ + static void mouseEntered (id self, SEL _cmd, NSEvent* theEvent) { - frame->cursorUpdate (); + IPlatformFrameCallback* _vstguiframe = getFrame (self); + if (!_vstguiframe) + return; + + MouseMoveEvent event; + event.timestamp = static_cast (theEvent.timestamp * 1000.); + event.modifiers = modifiersFromModifierFlags (theEvent.modifierFlags); + event.mousePosition = pointFromNSPoint (getGlobalMouseLocation (self)); + + _vstguiframe->platformOnEvent (event); } -} -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_acceptsFirstMouse (id self, SEL _cmd, NSEvent* event) -{ - return YES; // click through -} + //------------------------------------------------------------------------------------ + static void mouseExited (id self, SEL _cmd, NSEvent* theEvent) + { + IPlatformFrameCallback* _vstguiframe = getFrame (self); + if (!_vstguiframe) + return; + MouseExitEvent event; + event.timestamp = static_cast (theEvent.timestamp * 1000.); + event.modifiers = modifiersFromModifierFlags (theEvent.modifierFlags); + event.mousePosition = pointFromNSPoint (getGlobalMouseLocation (self)); + _vstguiframe->platformOnEvent (event); + } -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_performKeyEquivalent (id self, SEL _cmd, NSEvent* theEvent) -{ - NSView* firstResponder = (NSView*)[[self window] firstResponder]; - if (![firstResponder isKindOfClass:[NSView class]]) - firstResponder = nil; - if (firstResponder) - { - while (firstResponder != self && firstResponder != nil) - firstResponder = [firstResponder superview]; - if (firstResponder == self) + //------------------------------------------------------------------------------------ + static void cursorUpdate (id self, SEL _cmd, NSEvent* theEvent) + { + NSViewFrame* frame = getNSViewFrame (self); + if (frame) { - IPlatformFrameCallback* frame = getFrame (self); - if (frame) + frame->cursorUpdate (); + } + } + + //------------------------------------------------------------------------------------ + static BOOL acceptsFirstMouse (id self, SEL _cmd, NSEvent* event) + { + return YES; // click through + } + + //------------------------------------------------------------------------------------ + static BOOL performKeyEquivalent (id self, SEL _cmd, NSEvent* theEvent) + { + NSView* firstResponder = (NSView*)[[self window] firstResponder]; + if (![firstResponder isKindOfClass:[NSView class]]) + firstResponder = nil; + if (firstResponder) + { + while (firstResponder != self && firstResponder != nil) + firstResponder = [firstResponder superview]; + if (firstResponder == self) { - VstKeyCode keyCode = CreateVstKeyCodeFromNSEvent (theEvent); - if (frame->platformOnKeyDown (keyCode)) - return YES; + if (auto _vstguiframe = getFrame (self)) + { + KeyboardEvent keyEvent; + keyEvent.timestamp = static_cast (theEvent.timestamp * 1000.); + if (CreateKeyboardEventFromNSEvent (theEvent, keyEvent)) + { + _vstguiframe->platformOnEvent (keyEvent); + return keyEvent.consumed ? YES : NO; + } + } } } + return NO; } - return NO; -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_keyDown (id self, SEL _cmd, NSEvent* theEvent) -{ - IPlatformFrameCallback* _vstguiframe = getFrame (self); - if (!_vstguiframe) - return; + //------------------------------------------------------------------------------------ + static void keyDown (id self, SEL _cmd, NSEvent* theEvent) + { + IPlatformFrameCallback* _vstguiframe = getFrame (self); + if (!_vstguiframe) + return; - VstKeyCode keyCode = CreateVstKeyCodeFromNSEvent (theEvent); - - bool res = _vstguiframe->platformOnKeyDown (keyCode); - if (!res&& keyCode.virt == VKEY_TAB) + KeyboardEvent keyEvent; + keyEvent.timestamp = static_cast (theEvent.timestamp * 1000.); + if (CreateKeyboardEventFromNSEvent (theEvent, keyEvent)) + { + _vstguiframe->platformOnEvent (keyEvent); + if (keyEvent.consumed) + return; + if (keyEvent.virt == VirtualKey::Tab) + { + if (keyEvent.modifiers.has (ModifierKey::Shift)) + [[self window] selectKeyViewPrecedingView:self]; + else + [[self window] selectKeyViewFollowingView:self]; + return; + } + } + [[self nextResponder] keyDown:theEvent]; + } + + //------------------------------------------------------------------------------------ + static void keyUp (id self, SEL _cmd, NSEvent* theEvent) { - if (keyCode.modifier & kShift) - [[self window] selectKeyViewPrecedingView:self]; - else - [[self window] selectKeyViewFollowingView:self]; + IPlatformFrameCallback* _vstguiframe = getFrame (self); + if (!_vstguiframe) + return; + + KeyboardEvent keyEvent; + keyEvent.timestamp = static_cast (theEvent.timestamp * 1000.); + if (CreateKeyboardEventFromNSEvent (theEvent, keyEvent)) + { + _vstguiframe->platformOnEvent (keyEvent); + if (keyEvent.consumed) + return; + } + [[self nextResponder] keyUp:theEvent]; } - else if (!res) - [[self nextResponder] keyDown:theEvent]; -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_keyUp (id self, SEL _cmd, NSEvent* theEvent) -{ - IPlatformFrameCallback* _vstguiframe = getFrame (self); - if (!_vstguiframe) - return; + //------------------------------------------------------------------------------------ + static void magnifyWithEvent (id self, SEL _cmd, NSEvent* theEvent) + { + IPlatformFrameCallback* _vstguiframe = getFrame (self); + if (!_vstguiframe) + return; - VstKeyCode keyCode = CreateVstKeyCodeFromNSEvent (theEvent); + NSPoint nsPoint = [theEvent locationInWindow]; + nsPoint = [self convertPoint:nsPoint fromView:nil]; - bool res = _vstguiframe->platformOnKeyUp (keyCode); - if (!res) - [[self nextResponder] keyUp:theEvent]; -} + ZoomGestureEvent event; + event.timestamp = static_cast (theEvent.timestamp * 1000.); + switch (theEvent.phase) + { + case NSEventPhaseBegan: + event.phase = ZoomGestureEvent::Phase::Begin; + break; + case NSEventPhaseChanged: + event.phase = ZoomGestureEvent::Phase::Changed; + break; + case NSEventPhaseCancelled: + [[fallthrough]]; + case NSEventPhaseEnded: + event.phase = ZoomGestureEvent::Phase::End; + break; + default: + return; + } -//------------------------------------------------------------------------------------ -static NSDragOperation VSTGUI_NSView_draggingEntered (id self, SEL _cmd, id sender) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (!frame) - return NSDragOperationNone; + event.mousePosition = pointFromNSPoint (nsPoint); + event.modifiers = modifiersFromModifierFlags (theEvent.modifierFlags); + event.zoom = theEvent.magnification; - NSPasteboard *pboard = [sender draggingPasteboard]; + _vstguiframe->platformOnEvent (event); + } - frame->setDragDataPackage (MacClipboard::createDragDataPackage (pboard)); + //------------------------------------------------------------------------------------ + static NSDragOperation draggingEntered (id self, SEL _cmd, id sender) + { + NSViewFrame* frame = getNSViewFrame (self); + if (!frame) + return NSDragOperationNone; - CPoint where; - nsViewGetCurrentMouseLocation (self, where); - NSUInteger modifiers = [NSEvent modifierFlags]; - CButtonState buttons = 0; - mapModifiers (modifiers, buttons); + NSPasteboard* pboard = [sender draggingPasteboard]; - DragEventData data {frame->getDragDataPackage (), where, buttons}; - auto result = frame->getFrame ()->platformOnDragEnter (data); - if (result == DragOperation::Copy) - return NSDragOperationCopy; - if (result == DragOperation::Move) - return NSDragOperationMove; + frame->setDragDataPackage (MacClipboard::createDragDataPackage (pboard)); - return NSDragOperationGeneric; -} + CPoint where; + nsViewGetCurrentMouseLocation (self, where); + auto modifiers = modifiersFromModifierFlags (NSEvent.modifierFlags); -//------------------------------------------------------------------------------------ -static NSDragOperation VSTGUI_NSView_draggingUpdated (id self, SEL _cmd, id sender) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (!frame) - return NSDragOperationNone; + DragEventData data {frame->getDragDataPackage (), where, modifiers}; + auto result = frame->getFrame ()->platformOnDragEnter (data); + if (result == DragOperation::Copy) + return NSDragOperationCopy; + if (result == DragOperation::Move) + return NSDragOperationMove; - CPoint where; - nsViewGetCurrentMouseLocation (self, where); - NSUInteger modifiers = [NSEvent modifierFlags]; - CButtonState buttons = 0; - mapModifiers (modifiers, buttons); + return NSDragOperationGeneric; + } - DragEventData data {frame->getDragDataPackage (), where, buttons}; - auto result = frame->getFrame ()->platformOnDragMove (data); - if (result == DragOperation::Copy) - return NSDragOperationCopy; - if (result == DragOperation::Move) - return NSDragOperationMove; + //------------------------------------------------------------------------------------ + static NSDragOperation draggingUpdated (id self, SEL _cmd, id sender) + { + NSViewFrame* frame = getNSViewFrame (self); + if (!frame) + return NSDragOperationNone; - return NSDragOperationNone; -} + CPoint where; + nsViewGetCurrentMouseLocation (self, where); + auto modifiers = modifiersFromModifierFlags (NSEvent.modifierFlags); -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_draggingExited (id self, SEL _cmd, id sender) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (!frame || !frame->getDragDataPackage ()) - return; + DragEventData data {frame->getDragDataPackage (), where, modifiers}; + auto result = frame->getFrame ()->platformOnDragMove (data); + if (result == DragOperation::Copy) + return NSDragOperationCopy; + if (result == DragOperation::Move) + return NSDragOperationMove; - CPoint where; - nsViewGetCurrentMouseLocation (self, where); - NSUInteger modifiers = [NSEvent modifierFlags]; - CButtonState buttons = 0; - mapModifiers (modifiers, buttons); + return NSDragOperationNone; + } - DragEventData data {frame->getDragDataPackage (), where, buttons}; - frame->getFrame ()->platformOnDragLeave (data); - frame->setDragDataPackage (nullptr); + //------------------------------------------------------------------------------------ + static void draggingExited (id self, SEL _cmd, id sender) + { + NSViewFrame* frame = getNSViewFrame (self); + if (!frame || !frame->getDragDataPackage ()) + return; - [[NSCursor arrowCursor] set]; // we may should remember the cursor via [NSCursor currentCursor] -} + CPoint where; + nsViewGetCurrentMouseLocation (self, where); + auto modifiers = modifiersFromModifierFlags (NSEvent.modifierFlags); -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSView_performDragOperation (id self, SEL _cmd, id sender) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (!frame || !frame->getDragDataPackage ()) - return NO; + DragEventData data {frame->getDragDataPackage (), where, modifiers}; + frame->getFrame ()->platformOnDragLeave (data); + frame->setDragDataPackage (nullptr); - CPoint where; - nsViewGetCurrentMouseLocation (self, where); - NSUInteger modifiers = [NSEvent modifierFlags]; - CButtonState buttons = 0; - mapModifiers (modifiers, buttons); + // we may should remember the cursor via [NSCursor currentCursor] + [[NSCursor arrowCursor] set]; + } - DragEventData data {frame->getDragDataPackage (), where, buttons}; - bool result = frame->getFrame ()->platformOnDrop (data); - frame->setMouseCursor (kCursorDefault); - frame->setDragDataPackage (nullptr); - return result; -} + //------------------------------------------------------------------------------------ + static BOOL performDragOperation (id self, SEL _cmd, id sender) + { + NSViewFrame* frame = getNSViewFrame (self); + if (!frame || !frame->getDragDataPackage ()) + return NO; + + CPoint where; + nsViewGetCurrentMouseLocation (self, where); + auto modifiers = modifiersFromModifierFlags (NSEvent.modifierFlags); + + DragEventData data {frame->getDragDataPackage (), where, modifiers}; + bool result = frame->getFrame ()->platformOnDrop (data); + frame->setMouseCursor (kCursorDefault); + frame->setDragDataPackage (nullptr); + return result; + } -//------------------------------------------------------------------------------------ -static NSDragOperation VSTGUI_NSView_draggingSessionSourceOperationMaskForDraggingContext (id self, SEL _cmd, NSDraggingSession* session, NSDraggingContext context) -{ - if (context == NSDraggingContextOutsideApplication) + //------------------------------------------------------------------------------------ + static NSDragOperation draggingSessionSourceOperationMaskForDraggingContext ( + id self, SEL _cmd, NSDraggingSession* session, NSDraggingContext context) { - if (auto dataPackage = MacClipboard::createDragDataPackage (session.draggingPasteboard)) + if (context == NSDraggingContextOutsideApplication) { - for (auto index = 0u, count = dataPackage->getCount (); index < count; ++index) + if (auto dataPackage = MacClipboard::createDragDataPackage (session.draggingPasteboard)) { - if (dataPackage->getDataType (index) == IDataPackage::kBinary) - return NSDragOperationPrivate; + for (auto index = 0u, count = dataPackage->getCount (); index < count; ++index) + { + if (dataPackage->getDataType (index) == IDataPackage::kBinary) + return NSDragOperationPrivate; + } } + return NSDragOperationCopy; } - return NSDragOperationCopy; + return NSDragOperationGeneric; } - return NSDragOperationGeneric; -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_draggingSessionWillBeginAtPoint (id self, SEL _cmd, NSDraggingSession* session, NSPoint position) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (!frame) - return; - if (auto dragSession = frame->getDraggingSession ()) + //------------------------------------------------------------------------------------ + static void draggingSessionWillBeginAtPoint (id self, SEL _cmd, NSDraggingSession* session, + NSPoint position) { - auto r = [[self window] convertRectFromScreen:{position, NSMakeSize (0, 0)}]; - auto pos = pointFromNSPoint ([self convertPoint:r.origin fromView:nil]); - dragSession->dragWillBegin (pos); + NSViewFrame* frame = getNSViewFrame (self); + if (!frame) + return; + if (auto dragSession = frame->getDraggingSession ()) + { + auto r = [[self window] convertRectFromScreen: {position, NSMakeSize (0, 0)}]; + auto pos = pointFromNSPoint ([self convertPoint:r.origin fromView:nil]); + dragSession->dragWillBegin (pos); + } } -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_draggingSessionMovedToPoint (id self, SEL _cmd, NSDraggingSession* session, NSPoint position) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (!frame) - return; - if (auto dragSession = frame->getDraggingSession ()) + //------------------------------------------------------------------------------------ + static void draggingSessionMovedToPoint (id self, SEL _cmd, NSDraggingSession* session, + NSPoint position) { - auto r = [[self window] convertRectFromScreen:{position, NSMakeSize (0, 0)}]; - auto pos = pointFromNSPoint ([self convertPoint:r.origin fromView:nil]); - dragSession->dragMoved (pos); + NSViewFrame* frame = getNSViewFrame (self); + if (!frame) + return; + if (auto dragSession = frame->getDraggingSession ()) + { + auto r = [[self window] convertRectFromScreen: {position, NSMakeSize (0, 0)}]; + auto pos = pointFromNSPoint ([self convertPoint:r.origin fromView:nil]); + dragSession->dragMoved (pos); + } } -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_draggingSessionEndedAtPoint (id self, SEL _cmd, NSDraggingSession* session, NSPoint position, NSDragOperation operation) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (!frame) - return; - if (auto dragSession = frame->getDraggingSession ()) + //------------------------------------------------------------------------------------ + static void draggingSessionEndedAtPoint (id self, SEL _cmd, NSDraggingSession* session, + NSPoint position, NSDragOperation operation) { - DragOperation result; - switch (operation) + NSViewFrame* frame = getNSViewFrame (self); + if (!frame) + return; + if (auto dragSession = frame->getDraggingSession ()) { - case NSDragOperationNone: result = DragOperation::None; break; - case NSDragOperationMove: result = DragOperation::Move; break; - default: result = DragOperation::Copy; break; + DragOperation result; + switch (operation) + { + case NSDragOperationNone: + result = DragOperation::None; + break; + case NSDragOperationMove: + result = DragOperation::Move; + break; + default: + result = DragOperation::Copy; + break; + } + auto r = [[self window] convertRectFromScreen: {position, NSMakeSize (0, 0)}]; + auto pos = pointFromNSPoint ([self convertPoint:r.origin fromView:nil]); + dragSession->dragEnded (pos, result); + frame->clearDraggingSession (); } - auto r = [[self window] convertRectFromScreen:{position, NSMakeSize (0, 0)}]; - auto pos = pointFromNSPoint ([self convertPoint:r.origin fromView:nil]); - dragSession->dragEnded (pos, result); - frame->clearDraggingSession (); } -} + + //------------------------------------------------------------------------------------ + static id makeTouchbar (id self) + { + NSViewFrame* frame = getNSViewFrame (self); + if (frame) + return reinterpret_cast (frame->makeTouchBar ()); + return nil; + } #if VSTGUI_ENABLE_DEPRECATED_METHODS -//------------------------------------------------------------------------------------ -static void VSTGUI_NSView_draggedImageEndedAtOperation (id self, SEL _cmd, NSImage* image, NSPoint aPoint, NSDragOperation operation) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (frame) + //------------------------------------------------------------------------------------ + static void draggedImageEndedAtOperation (id self, SEL _cmd, NSImage* image, NSPoint aPoint, + NSDragOperation operation) { - if (operation == NSDragOperationNone) - { - frame->setLastDragOperationResult (kDragRefused); - } - else if (operation == NSDragOperationMove) + NSViewFrame* frame = getNSViewFrame (self); + if (frame) { - frame->setLastDragOperationResult (kDragMoved); + if (operation == NSDragOperationNone) + { + frame->setLastDragOperationResult (kDragRefused); + } + else if (operation == NSDragOperationMove) + { + frame->setLastDragOperationResult (kDragMoved); + } + else + frame->setLastDragOperationResult (kDragCopied); } - else - frame->setLastDragOperationResult (kDragCopied); } -} #endif //------------------------------------------------------------------------------------ -static id VSTGUI_NSView_makeTouchbar (id self) -{ - NSViewFrame* frame = getNSViewFrame (self); - if (frame) - return reinterpret_cast (frame->makeTouchBar ()); - return nil; -} +}; // VSTGUI_NSView +//------------------------------------------------------------------------------------ namespace VSTGUI { //------------------------------------------------------------------------------------ @@ -713,109 +934,6 @@ static id VSTGUI_NSView_makeTouchbar (id self) NSTextField* textfield {nullptr}; }; -//----------------------------------------------------------------------------- -__attribute__((__destructor__)) static void cleanup_VSTGUI_NSView () -{ - if (viewClass) - objc_disposeClassPair (viewClass); -} - -//----------------------------------------------------------------------------- -void NSViewFrame::initClass () -{ - if (viewClass == nullptr) - { - AutoreleasePool ap; - - const char* nsUIntegerEncoded = @encode(NSUInteger); - const char* nsRectEncoded = @encode(NSRect); - char funcSig[100]; - - NSMutableString* viewClassName = [[[NSMutableString alloc] initWithString:@"VSTGUI_NSView"] autorelease]; - viewClass = generateUniqueClass (viewClassName, [NSView class]); - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(initWithNSViewFrame:parent:andSize:), IMP (VSTGUI_NSView_Init), "@@:@:^:^:^:")) - // VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(dealloc), IMP (VSTGUI_NSView_Dealloc), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(updateTrackingAreas), IMP (VSTGUI_NSView_updateTrackingAreas), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(viewDidMoveToWindow), IMP (VSTGUI_NSView_viewDidMoveToWindow), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(windowKeyStateChanged:), IMP (VSTGUI_NSView_windowKeyStateChanged), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(windowDidChangeBackingProperties:), IMP (VSTGUI_NSView_windowDidChangeBackingProperties), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(isFlipped), IMP (VSTGUI_NSView_isFlipped), "B@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(acceptsFirstResponder), IMP (VSTGUI_NSView_acceptsFirstResponder), "B@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(becomeFirstResponder), IMP (VSTGUI_NSView_becomeFirstResponder), "B@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(resignFirstResponder), IMP (VSTGUI_NSView_resignFirstResponder), "B@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(canBecomeKeyView), IMP (VSTGUI_NSView_canBecomeKeyView), "B@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(wantsDefaultClipping), IMP (VSTGUI_NSView_wantsDefaultClipping), "B@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(isOpaque), IMP (VSTGUI_NSView_isOpaque), "B@:@:")) - sprintf (funcSig, "v@:@:%s:", nsRectEncoded); - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(drawRect:), IMP (VSTGUI_NSView_drawRect), funcSig)) - VSTGUI_CHECK_YES (class_addMethod (viewClass, @selector (setNeedsDisplayInRect:), - IMP (VSTGUI_NSView_setNeedsDisplayInRect), funcSig)) - VSTGUI_CHECK_YES (class_addMethod (viewClass, @selector (viewWillDraw), - IMP (VSTGUI_NSView_viewWillDraw), "v@:@:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(shouldBeTreatedAsInkEvent:), IMP(VSTGUI_NSView_shouldBeTreatedAsInkEvent), "B@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(onMouseDown:), IMP (VSTGUI_NSView_onMouseDown), "B@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(onMouseUp:), IMP (VSTGUI_NSView_onMouseUp), "B@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(onMouseMoved:), IMP (VSTGUI_NSView_onMouseMoved), "B@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(mouseDown:), IMP (VSTGUI_NSView_mouseDown), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(rightMouseDown:), IMP (VSTGUI_NSView_mouseDown), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(otherMouseDown:), IMP (VSTGUI_NSView_mouseDown), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(mouseUp:), IMP (VSTGUI_NSView_mouseUp), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(rightMouseUp:), IMP (VSTGUI_NSView_mouseUp), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(otherMouseUp:), IMP (VSTGUI_NSView_mouseUp), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(mouseMoved:), IMP (VSTGUI_NSView_mouseMoved), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(mouseDragged:), IMP (VSTGUI_NSView_mouseMoved), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(rightMouseDragged:), IMP (VSTGUI_NSView_mouseMoved), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(otherMouseDragged:), IMP (VSTGUI_NSView_mouseMoved), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(scrollWheel:), IMP (VSTGUI_NSView_scrollWheel), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(mouseEntered:), IMP (VSTGUI_NSView_mouseEntered), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(mouseExited:), IMP (VSTGUI_NSView_mouseExited), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(cursorUpdate:), IMP (VSTGUI_NSView_cursorUpdate), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(acceptsFirstMouse:), IMP (VSTGUI_NSView_acceptsFirstMouse), "B@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(performKeyEquivalent:), IMP (VSTGUI_NSView_performKeyEquivalent), "B@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(keyDown:), IMP (VSTGUI_NSView_keyDown), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(keyUp:), IMP (VSTGUI_NSView_keyUp), "v@:@:^:")) - - sprintf (funcSig, "%s@:@:", @encode(NSFocusRingType)); - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(focusRingType), IMP (VSTGUI_NSView_focusRingType), funcSig)) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(makeSubViewFirstResponder:), IMP (VSTGUI_NSView_makeSubViewFirstResponder), "v@:@:^:")) - - sprintf (funcSig, "%s@:@:^:", nsUIntegerEncoded); - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(draggingEntered:), IMP (VSTGUI_NSView_draggingEntered), funcSig)) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(draggingUpdated:), IMP (VSTGUI_NSView_draggingUpdated), funcSig)) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(draggingExited:), IMP (VSTGUI_NSView_draggingExited), "v@:@:^:")) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(performDragOperation:), IMP (VSTGUI_NSView_performDragOperation), "B@:@:^:")) - - if (auto nsDraggingSourceProtocol = objc_getProtocol ("NSDraggingSource")) - { - sprintf (funcSig, "%s@:@:^:%s", @encode(NSDragOperation), @encode(NSDraggingSession)); - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(draggingSession:sourceOperationMaskForDraggingContext:), IMP (VSTGUI_NSView_draggingSessionSourceOperationMaskForDraggingContext), funcSig)) - sprintf (funcSig, "%s@:@:^:%s:%s", @encode(void), @encode(NSDraggingSession), @encode(NSPoint)); - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(draggingSession:willBeginAtPoint:), IMP (VSTGUI_NSView_draggingSessionWillBeginAtPoint), funcSig)) - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(draggingSession:movedToPoint:), IMP (VSTGUI_NSView_draggingSessionMovedToPoint), funcSig)) - sprintf (funcSig, "%s@:@:^:%s:%s:%s", @encode(void), @encode(NSDraggingSession), @encode(NSPoint), @encode(NSDragOperation)); - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(draggingSession:endedAtPoint:operation:), IMP (VSTGUI_NSView_draggingSessionEndedAtPoint), funcSig)) - - class_addProtocol (viewClass, nsDraggingSourceProtocol); - } - -#if VSTGUI_ENABLE_DEPRECATED_METHODS - sprintf (funcSig, "v@:@:^:%s:%s", @encode(NSPoint), nsUIntegerEncoded); - VSTGUI_CHECK_YES(class_addMethod (viewClass, @selector(draggedImage:endedAt:operation:), IMP (VSTGUI_NSView_draggedImageEndedAtOperation), funcSig)) -#endif - -// optional touchbar support - if (auto protocol = objc_getProtocol ("NSTouchBarProvider")) - { - class_addProtocol (viewClass, protocol); - sprintf (funcSig, "%s@:@:", @encode(NSObject*)); - class_addMethod (viewClass, @selector(makeTouchBar), IMP (VSTGUI_NSView_makeTouchbar), funcSig); - } - - VSTGUI_CHECK_YES(class_addIvar (viewClass, "_nsViewFrame", sizeof (void*), (uint8_t)log2(sizeof(void*)), @encode(void*))) - objc_registerClassPair (viewClass); - } -} - //----------------------------------------------------------------------------- NSViewFrame::NSViewFrame (IPlatformFrameCallback* frame, const CRect& size, NSView* parent, IPlatformFrameConfig* config) : IPlatformFrame (frame) @@ -827,9 +945,8 @@ static id VSTGUI_NSView_makeTouchbar (id self) , cursor (kCursorDefault) { auto cocoaConfig = dynamic_cast (config); - initClass (); - - nsView = [[viewClass alloc] initWithNSViewFrame: this parent: parent andSize: &size]; + + nsView = [VSTGUI_NSView::alloc () initWithNSViewFrame:this parent:parent andSize:&size]; if (cocoaConfig && cocoaConfig->flags & CocoaFrameConfig::kNoCALayer) return; @@ -889,8 +1006,10 @@ static id VSTGUI_NSView_makeTouchbar (id self) if ([nsView hitTest:p]) { options |= NSTrackingAssumeInside; - CPoint cp = pointFromNSPoint (p); - frame->platformOnMouseMoved (cp, 0); + + MouseMoveEvent event; + event.mousePosition = pointFromNSPoint (p); + frame->platformOnEvent (event); } NSTrackingArea* trackingArea = [[[NSTrackingArea alloc] initWithRect:[nsView frame] options:options owner:nsView userInfo:nil] autorelease]; [nsView addTrackingArea: trackingArea]; @@ -917,7 +1036,7 @@ static id VSTGUI_NSView_makeTouchbar (id self) #if DEBUG if (getPlatformFactory ().asMacFactory ()->enableVisualizeRedrawAreas () && nsView.layer) { - auto delegate = [[DebugRedrawAnimDelegate new] autorelease]; + id delegate = [[VSTGUI_DebugRedrawAnimDelegate::alloc () init] autorelease]; auto anim = [CABasicAnimation animation]; anim.fromValue = [NSNumber numberWithDouble:isClipBoundingBox ? 0.2 : 0.8]; anim.toValue = [NSNumber numberWithDouble:0.]; @@ -945,7 +1064,7 @@ static id VSTGUI_NSView_makeTouchbar (id self) layer.frame = nsRectFromCRect (r); [nsView.layer addSublayer:layer]; - delegate.layer = layer; + [delegate setLayer:layer]; [layer addAnimation:anim forKey:@"opacity"]; } #endif @@ -994,47 +1113,81 @@ static id VSTGUI_NSView_makeTouchbar (id self) inDraw = false; } +//------------------------------------------------------------------------ +static MouseEventButtonState buttonStateFromNSEvent (NSEvent* theEvent) +{ + MouseEventButtonState state; + if (theEvent.type == MacEventType::MouseMoved) + return state; + switch (theEvent.buttonNumber) + { + case 0: + { + if (theEvent.modifierFlags & MacEventModifier::ControlKeyMask) + state.add (MouseButton::Right); + else + state.add (MouseButton::Left); + break; + } + case 1: + state.add (MouseButton::Right); + break; + case 2: + state.add (MouseButton::Middle); + break; + case 3: + state.add (MouseButton::Fourth); + break; + case 4: + state.add (MouseButton::Fifth); + break; + } + return state; +} + //----------------------------------------------------------------------------- bool NSViewFrame::onMouseDown (NSEvent* theEvent) { - CButtonState buttons = eventButton (theEvent); - mouseDownButtonState = buttons.getButtonState (); - [nsView.window makeFirstResponder:nsView]; - NSUInteger modifiers = [theEvent modifierFlags]; + MouseDownEvent event; + event.timestamp = static_cast (theEvent.timestamp * 1000.); + event.buttonState = buttonStateFromNSEvent (theEvent); + event.modifiers = modifiersFromModifierFlags (theEvent.modifierFlags); + event.clickCount = theEvent.clickCount; NSPoint nsPoint = [theEvent locationInWindow]; nsPoint = [nsView convertPoint:nsPoint fromView:nil]; - mapModifiers (modifiers, buttons); - if ([theEvent clickCount] == 2) - buttons |= kDoubleClick; - CPoint p = pointFromNSPoint (nsPoint); - CMouseEventResult result = frame->platformOnMouseDown (p, buttons); - return (result != kMouseEventNotHandled) ? true : false; + event.mousePosition = pointFromNSPoint (nsPoint); + frame->platformOnEvent (event); + return event.consumed; } //----------------------------------------------------------------------------- bool NSViewFrame::onMouseUp (NSEvent* theEvent) { - CButtonState buttons = eventButton (theEvent); - NSUInteger modifiers = [theEvent modifierFlags]; - mapModifiers (modifiers, buttons); + MouseUpEvent event; + event.timestamp = static_cast (theEvent.timestamp * 1000.); + event.buttonState = buttonStateFromNSEvent (theEvent); + event.modifiers = modifiersFromModifierFlags (theEvent.modifierFlags); + event.clickCount = theEvent.clickCount; NSPoint nsPoint = [theEvent locationInWindow]; nsPoint = [nsView convertPoint:nsPoint fromView:nil]; - CPoint p = pointFromNSPoint (nsPoint); - CMouseEventResult result = frame->platformOnMouseUp (p, buttons); - return (result != kMouseEventNotHandled) ? true : false; + event.mousePosition = pointFromNSPoint (nsPoint); + frame->platformOnEvent (event); + return event.consumed; } //----------------------------------------------------------------------------- bool NSViewFrame::onMouseMoved (NSEvent* theEvent) { - NSUInteger modifiers = [theEvent modifierFlags]; - CButtonState buttons = theEvent.type == MacEventType::MouseMoved ? 0 : mouseDownButtonState; - mapModifiers (modifiers, buttons); + MouseMoveEvent event; + event.timestamp = static_cast (theEvent.timestamp * 1000.); + event.buttonState = buttonStateFromNSEvent (theEvent); + event.modifiers = modifiersFromModifierFlags (theEvent.modifierFlags); + event.clickCount = theEvent.clickCount; NSPoint nsPoint = [theEvent locationInWindow]; nsPoint = [nsView convertPoint:nsPoint fromView:nil]; - CPoint p = pointFromNSPoint (nsPoint); - CMouseEventResult result = frame->platformOnMouseMoved (p, buttons); - return (result != kMouseEventNotHandled) ? true : false; + event.mousePosition = pointFromNSPoint (nsPoint); + frame->platformOnEvent (event); + return event.consumed; } // IPlatformFrame @@ -1117,6 +1270,13 @@ static id VSTGUI_NSView_makeTouchbar (id self) return true; } +//----------------------------------------------------------------------------- +bool NSViewFrame::getCurrentModifiers (Modifiers& modifiers) const +{ + modifiers = modifiersFromModifierFlags (NSEvent.modifierFlags); + return true; +} + //----------------------------------------------------------------------------- bool NSViewFrame::setMouseCursor (CCursorType type) { @@ -1274,9 +1434,10 @@ static id VSTGUI_NSView_makeTouchbar (id self) { if (genericOptionMenuTheme) { - CButtonState buttons; - getCurrentMouseButtons (buttons); - return makeOwned (dynamic_cast (frame), buttons, + MouseEventButtonState buttonState; + if (auto event = [NSApp currentEvent]) + buttonState = buttonStateFromNSEvent (event); + return makeOwned (dynamic_cast (frame), buttonState, *genericOptionMenuTheme.get ()); } return makeOwned (); diff --git a/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.h b/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.h index 8c3e1561d..303fda2cb 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.h +++ b/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.h @@ -15,10 +15,6 @@ class NSViewOptionMenu : public IPlatformOptionMenu { public: void popup (COptionMenu* optionMenu, const Callback& callback) override; - -//----------------------------------------------------------------------------- -protected: - static bool initClass (); }; } // VSTGUI diff --git a/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.mm b/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.mm index cae35d8b9..5d3185a05 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.mm +++ b/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.mm @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -6,26 +6,21 @@ #if MAC_COCOA -#import "cocoahelpers.h" -#import "nsviewframe.h" #import "../../../cbitmap.h" #import "../../../cframe.h" #import "../../../controls/coptionmenu.h" #import "../cgbitmap.h" #import "../macstring.h" +#import "cocoahelpers.h" +#import "nsviewframe.h" +#import "objcclassbuilder.h" @interface NSObject (VSTGUI_NSMenu_Private) --(id)initWithOptionMenu:(id)menu; +- (id)initWithOptionMenu:(id)menu; @end namespace VSTGUI { -#if DEBUG -static int32_t menuClassCount = 0; -#endif - -static Class menuClass = nullptr; - #ifndef MAC_OS_X_VERSION_10_14 #define MAC_OS_X_VERSION_10_14 101400 #endif @@ -38,218 +33,256 @@ -(id)initWithOptionMenu:(id)menu; //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ -struct VSTGUI_NSMenu_Var +struct VSTGUI_NSMenu { - COptionMenu* _optionMenu; - COptionMenu* _selectedMenu; - int32_t _selectedItem; -}; + //------------------------------------------------------------------------------------ + static id alloc () { return [instance ().menuClass alloc]; } -//------------------------------------------------------------------------------------ -static id VSTGUI_NSMenu_Init (id self, SEL _cmd, void* _menu) -{ -#if DEBUG - menuClassCount++; -#endif - __OBJC_SUPER(self) - self = SuperInit (SUPER, @selector(init)); - if (self) +private: + static constexpr const auto privateVarName = "_private"; + + Class menuClass {nullptr}; + int32_t menuClassCount = 0; + + //------------------------------------------------------------------------------------ + VSTGUI_NSMenu () + { + menuClass = ObjCClassBuilder () + .init ("VSTGUI_NSMenu", [NSMenu class]) + .addMethod (@selector (initWithOptionMenu:), Init) + .addMethod (@selector (dealloc), Dealloc) + .addMethod (@selector (validateMenuItem:), ValidateMenuItem) + .addMethod (@selector (menuItemSelected:), MenuItemSelected) + .addMethod (@selector (optionMenu), OptionMenu) + .addMethod (@selector (selectedMenu), SelectedMenu) + .addMethod (@selector (selectedItem), SelectedItem) + .addMethod (@selector (setSelectedMenu:), SetSelectedMenu) + .addMethod (@selector (setSelectedItem:), SetSelectedItem) + .addIvar (privateVarName) + .finalize (); + } + + //------------------------------------------------------------------------------------ + ~VSTGUI_NSMenu () noexcept + { + objc_disposeClassPair (menuClass); + vstgui_assert (menuClassCount == 0); + } + + //------------------------------------------------------------------------------------ + struct Var + { + COptionMenu* _optionMenu {nullptr}; + COptionMenu* _selectedMenu {nullptr}; + int32_t _selectedItem {0}; + }; + + //------------------------------------------------------------------------------------ + static VSTGUI_NSMenu& instance () { - NSMenu* nsMenu = (NSMenu*)self; - COptionMenu* menu = (COptionMenu*)_menu; - VSTGUI_NSMenu_Var* var = new VSTGUI_NSMenu_Var; - var->_optionMenu = menu; - var->_selectedItem = 0; - var->_selectedMenu = nullptr; - OBJC_SET_VALUE(self, _private, var); - - int32_t index = -1; - bool multipleCheck = menu->isMultipleCheckStyle (); - CConstMenuItemIterator it = menu->getItems ()->begin (); - while (it != menu->getItems ()->end ()) + static VSTGUI_NSMenu gInstance; + return gInstance; + } + + //------------------------------------------------------------------------------------ + static id Init (id self, SEL _cmd, void* _menu) + { + instance ().menuClassCount++; + ObjCInstance obj (self); + self = obj.callSuper (@selector (init)); + if (self) { - CMenuItem* item = (*it); - it++; - index++; - NSMenuItem* nsItem = nullptr; - NSMutableString* itemTitle = [[[NSMutableString alloc] initWithString:fromUTF8String (item->getTitle ())] autorelease]; - if (menu->getPrefixNumbers ()) + NSMenu* nsMenu = (NSMenu*)self; + COptionMenu* menu = (COptionMenu*)_menu; + Var* var = new Var; + var->_optionMenu = menu; + setVar (self, var); + + int32_t index = -1; + bool multipleCheck = menu->isMultipleCheckStyle (); + CConstMenuItemIterator it = menu->getItems ()->begin (); + while (it != menu->getItems ()->end ()) { - NSString* prefixString = nullptr; - switch (menu->getPrefixNumbers ()) + CMenuItem* item = (*it); + it++; + index++; + NSMenuItem* nsItem = nullptr; + NSMutableString* itemTitle = [[[NSMutableString alloc] + initWithString:fromUTF8String (item->getTitle ())] autorelease]; + if (menu->getPrefixNumbers ()) { - case 2: prefixString = [NSString stringWithFormat:@"%1d ", index+1]; break; - case 3: prefixString = [NSString stringWithFormat:@"%02d ", index+1]; break; - case 4: prefixString = [NSString stringWithFormat:@"%03d ", index+1]; break; + NSString* prefixString = nullptr; + switch (menu->getPrefixNumbers ()) + { + case 2: + prefixString = [NSString stringWithFormat:@"%1d ", index + 1]; + break; + case 3: + prefixString = [NSString stringWithFormat:@"%02d ", index + 1]; + break; + case 4: + prefixString = [NSString stringWithFormat:@"%03d ", index + 1]; + break; + } + [itemTitle insertString:prefixString atIndex:0]; } - [itemTitle insertString:prefixString atIndex:0]; - } - if (item->getSubmenu ()) - { - nsItem = [nsMenu addItemWithTitle:itemTitle action:nil keyEquivalent:@""]; - NSMenu* subMenu = [[[menuClass alloc] initWithOptionMenu:(id)item->getSubmenu ()] autorelease]; - [nsMenu setSubmenu: subMenu forItem:nsItem]; - if (multipleCheck && item->isChecked ()) - [nsItem setState:NSControlStateValueOn]; - else - [nsItem setState:NSControlStateValueOff]; - } - else if (item->isSeparator ()) - { - [nsMenu addItem:[NSMenuItem separatorItem]]; - } - else - { - nsItem = [nsMenu addItemWithTitle:itemTitle action:@selector(menuItemSelected:) keyEquivalent:@""]; - if (item->isTitle ()) - [nsItem setIndentationLevel:1]; - [nsItem setTarget:nsMenu]; - [nsItem setTag: index]; - if (multipleCheck && item->isChecked ()) - [nsItem setState:NSControlStateValueOn]; - else - [nsItem setState:NSControlStateValueOff]; - NSString* keyEquivalent = nil; - if (!item->getKeycode ().empty ()) + if (item->getSubmenu ()) { - keyEquivalent = fromUTF8String (item->getKeycode ()); + nsItem = [nsMenu addItemWithTitle:itemTitle action:nil keyEquivalent:@""]; + NSMenu* subMenu = [[[[self class] alloc] + initWithOptionMenu:(id)item->getSubmenu ()] autorelease]; + [nsMenu setSubmenu:subMenu forItem:nsItem]; + if (multipleCheck && item->isChecked ()) + [nsItem setState:NSControlStateValueOn]; + else + [nsItem setState:NSControlStateValueOff]; } - else if (item->getVirtualKeyCode ()) + else if (item->isSeparator ()) { - keyEquivalent = GetVirtualKeyCodeString (item->getVirtualKeyCode ()); + [nsMenu addItem:[NSMenuItem separatorItem]]; } - if (keyEquivalent) + else { - [nsItem setKeyEquivalent:keyEquivalent]; - uint32_t keyModifiers = 0; - if (item->getKeyModifiers () & kControl) - keyModifiers |= MacEventModifier::CommandKeyMask; - if (item->getKeyModifiers () & kShift) - keyModifiers |= MacEventModifier::ShiftKeyMask; - if (item->getKeyModifiers () & kAlt) - keyModifiers |= MacEventModifier::AlternateKeyMask; - if (item->getKeyModifiers () & kApple) - keyModifiers |= MacEventModifier::ControlKeyMask; - [nsItem setKeyEquivalentModifierMask:keyModifiers]; + nsItem = [nsMenu addItemWithTitle:itemTitle + action:@selector (menuItemSelected:) + keyEquivalent:@""]; + if (item->isTitle ()) + [nsItem setIndentationLevel:1]; + [nsItem setTarget:nsMenu]; + [nsItem setTag:index]; + if (multipleCheck && item->isChecked ()) + [nsItem setState:NSControlStateValueOn]; + else + [nsItem setState:NSControlStateValueOff]; + NSString* keyEquivalent = nil; + if (!item->getKeycode ().empty ()) + { + keyEquivalent = fromUTF8String (item->getKeycode ()); + } + else if (item->getVirtualKey () != VirtualKey::None) + { + keyEquivalent = GetVirtualKeyCodeString (item->getVirtualKey ()); + } + if (keyEquivalent) + { + [nsItem setKeyEquivalent:keyEquivalent]; + uint32_t keyModifiers = 0; + if (item->getKeyModifiers () & kControl) + keyModifiers |= MacEventModifier::CommandKeyMask; + if (item->getKeyModifiers () & kShift) + keyModifiers |= MacEventModifier::ShiftKeyMask; + if (item->getKeyModifiers () & kAlt) + keyModifiers |= MacEventModifier::AlternateKeyMask; + if (item->getKeyModifiers () & kApple) + keyModifiers |= MacEventModifier::ControlKeyMask; + [nsItem setKeyEquivalentModifierMask:keyModifiers]; + } } - } - if (nsItem) - { - if (auto nsImage = bitmapToNSImage (item->getIcon ())) + if (nsItem) { - [nsItem setImage:nsImage]; - [nsImage release]; + if (auto nsImage = bitmapToNSImage (item->getIcon ())) + { + [nsItem setImage:nsImage]; + [nsImage release]; + } } } } + return self; } - return self; -} -//----------------------------------------------------------------------------- -static void VSTGUI_NSMenu_Dealloc (id self, SEL _cmd) -{ -#if DEBUG - menuClassCount--; -#endif - VSTGUI_NSMenu_Var* var = (VSTGUI_NSMenu_Var*)OBJC_GET_VALUE(self, _private); - if (var) - delete var; - __OBJC_SUPER(self) - SuperDealloc (SUPER, @selector(dealloc)); // [super dealloc]; -} + //----------------------------------------------------------------------------- + static void setVar (id self, Var* var) + { + if (auto v = ObjCInstance (self).getVariable (privateVarName)) + { + if (auto old = v->get ()) + delete old; + v->set (var); + } + } -//------------------------------------------------------------------------------------ -static BOOL VSTGUI_NSMenu_ValidateMenuItem (id self, SEL _cmd, id item) -{ - VSTGUI_NSMenu_Var* var = (VSTGUI_NSMenu_Var*)OBJC_GET_VALUE(self, _private); - if (var && var->_optionMenu) + //----------------------------------------------------------------------------- + static Var* getVar (id self) { - CMenuItem* menuItem = var->_optionMenu->getEntry ((int32_t)[item tag]); - if (!menuItem->isEnabled () || menuItem->isTitle ()) - return NO; + if (auto v = ObjCInstance (self).getVariable (privateVarName); v.has_value ()) + return v->get (); + return nullptr; } - return YES; -} -//------------------------------------------------------------------------------------ -static void VSTGUI_NSMenu_MenuItemSelected (id self, SEL _cmd, id item) -{ - VSTGUI_NSMenu_Var* var = (VSTGUI_NSMenu_Var*)OBJC_GET_VALUE(self, _private); - if (var) + //----------------------------------------------------------------------------- + static void Dealloc (id self, SEL _cmd) { - id menu = self; - while ([menu supermenu]) menu = [menu supermenu]; - [menu performSelector:@selector (setSelectedMenu:) withObject: (id)var->_optionMenu]; - [menu performSelector:@selector (setSelectedItem:) withObject: (id)[item tag]]; + instance ().menuClassCount--; + + setVar (self, nullptr); + ObjCInstance (self).callSuper (_cmd); } -} -//------------------------------------------------------------------------------------ -static void* VSTGUI_NSMenu_OptionMenu (id self, SEL _cmd) -{ - VSTGUI_NSMenu_Var* var = (VSTGUI_NSMenu_Var*)OBJC_GET_VALUE(self, _private); - return var ? var->_optionMenu : nullptr; -} + //------------------------------------------------------------------------------------ + static BOOL ValidateMenuItem (id self, SEL _cmd, id item) + { + Var* var = getVar (self); + if (var && var->_optionMenu) + { + CMenuItem* menuItem = var->_optionMenu->getEntry ((int32_t)[item tag]); + if (!menuItem->isEnabled () || menuItem->isTitle ()) + return NO; + } + return YES; + } -//------------------------------------------------------------------------------------ -static void* VSTGUI_NSMenu_SelectedMenu (id self, SEL _cmd) -{ - VSTGUI_NSMenu_Var* var = (VSTGUI_NSMenu_Var*)OBJC_GET_VALUE(self, _private); - return var ? var->_selectedMenu : nullptr; -} + //------------------------------------------------------------------------------------ + static void MenuItemSelected (id self, SEL _cmd, id item) + { + Var* var = getVar (self); + if (var) + { + id menu = self; + while ([menu supermenu]) + menu = [menu supermenu]; + [menu performSelector:@selector (setSelectedMenu:) withObject:(id)var->_optionMenu]; + [menu performSelector:@selector (setSelectedItem:) withObject:(id)[item tag]]; + } + } -//------------------------------------------------------------------------------------ -static int32_t VSTGUI_NSMenu_SelectedItem (id self, SEL _cmd) -{ - VSTGUI_NSMenu_Var* var = (VSTGUI_NSMenu_Var*)OBJC_GET_VALUE(self, _private); - return var ? var->_selectedItem : 0; -} + //------------------------------------------------------------------------------------ + static void* OptionMenu (id self, SEL _cmd) + { + Var* var = getVar (self); + return var ? var->_optionMenu : nullptr; + } -//------------------------------------------------------------------------------------ -static void VSTGUI_NSMenu_SetSelectedMenu (id self, SEL _cmd, void* menu) -{ - VSTGUI_NSMenu_Var* var = (VSTGUI_NSMenu_Var*)OBJC_GET_VALUE(self, _private); - if (var) - var->_selectedMenu = (COptionMenu*)menu; -} + //------------------------------------------------------------------------------------ + static void* SelectedMenu (id self, SEL _cmd) + { + Var* var = getVar (self); + return var ? var->_selectedMenu : nullptr; + } -//------------------------------------------------------------------------------------ -static void VSTGUI_NSMenu_SetSelectedItem (id self, SEL _cmd, int32_t item) -{ - VSTGUI_NSMenu_Var* var = (VSTGUI_NSMenu_Var*)OBJC_GET_VALUE(self, _private); - if (var) - var->_selectedItem = item; -} + //------------------------------------------------------------------------------------ + static int32_t SelectedItem (id self, SEL _cmd) + { + Var* var = getVar (self); + return var ? var->_selectedItem : 0; + } -//----------------------------------------------------------------------------- -__attribute__((__destructor__)) static void cleanup_VSTGUI_NSMenu () -{ - if (menuClass) - objc_disposeClassPair (menuClass); -} + //------------------------------------------------------------------------------------ + static void SetSelectedMenu (id self, SEL _cmd, void* menu) + { + Var* var = getVar (self); + if (var) + var->_selectedMenu = (COptionMenu*)menu; + } -//----------------------------------------------------------------------------- -bool NSViewOptionMenu::initClass () -{ - if (menuClass == nullptr) + //------------------------------------------------------------------------------------ + static void SetSelectedItem (id self, SEL _cmd, int32_t item) { - NSMutableString* menuClassName = [[[NSMutableString alloc] initWithString:@"VSTGUI_NSMenu"] autorelease]; - menuClass = generateUniqueClass (menuClassName, [NSMenu class]); - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(initWithOptionMenu:), IMP (VSTGUI_NSMenu_Init), "@@:@:^:")) - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(dealloc), IMP (VSTGUI_NSMenu_Dealloc), "v@:@:")) - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(validateMenuItem:), IMP (VSTGUI_NSMenu_ValidateMenuItem), "B@:@:@:")) - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(menuItemSelected:), IMP (VSTGUI_NSMenu_MenuItemSelected), "v@:@:@:")) - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(optionMenu), IMP (VSTGUI_NSMenu_OptionMenu), "^@:@:")) - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(selectedMenu), IMP (VSTGUI_NSMenu_SelectedMenu), "^@:@:")) - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(selectedItem), IMP (VSTGUI_NSMenu_SelectedItem), "i@:@:")) - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(setSelectedMenu:), IMP (VSTGUI_NSMenu_SetSelectedMenu), "^@:@:^:")) - VSTGUI_CHECK_YES (class_addMethod (menuClass, @selector(setSelectedItem:), IMP (VSTGUI_NSMenu_SetSelectedItem), "^@:@:i:")) - VSTGUI_CHECK_YES (class_addIvar (menuClass, "_private", sizeof (VSTGUI_NSMenu_Var*), (uint8_t)log2(sizeof(VSTGUI_NSMenu_Var*)), @encode(VSTGUI_NSMenu_Var*))) - objc_registerClassPair (menuClass); + Var* var = getVar (self); + if (var) + var->_selectedItem = item; } - return menuClass != nullptr; -} +}; //----------------------------------------------------------------------------- void NSViewOptionMenu::popup (COptionMenu* optionMenu, const Callback& callback) @@ -259,7 +292,7 @@ static void VSTGUI_NSMenu_SetSelectedItem (id self, SEL _cmd, int32_t item) PlatformOptionMenuResult result = {}; CFrame* frame = optionMenu->getFrame (); - if (!initClass () || !frame || !frame->getPlatformFrame ()) + if (!frame || !frame->getPlatformFrame ()) { callback (optionMenu, result); return; @@ -273,7 +306,7 @@ static void VSTGUI_NSMenu_SetSelectedItem (id self, SEL _cmd, int32_t item) bool multipleCheck = optionMenu->isMultipleCheckStyle (); NSView* view = nsViewFrame->getNSView (); - NSMenu* nsMenu = [[menuClass alloc] initWithOptionMenu:(id)optionMenu]; + NSMenu* nsMenu = [VSTGUI_NSMenu::alloc () initWithOptionMenu:(id)optionMenu]; CPoint p = globalSize.getTopLeft (); NSRect cellFrameRect = {}; cellFrameRect.origin = nsPointFromCPoint (p); @@ -287,7 +320,7 @@ static void VSTGUI_NSMenu_SetSelectedItem (id self, SEL _cmd, int32_t item) setState:NSControlStateValueOn]; } nsMenu.minimumWidth = cellFrameRect.size.width; - + NSView* menuContainer = [[NSView alloc] initWithFrame:cellFrameRect]; [view addSubview:menuContainer]; @@ -300,14 +333,13 @@ static void VSTGUI_NSMenu_SetSelectedItem (id self, SEL _cmd, int32_t item) [menuContainer removeFromSuperviewWithoutNeedingDisplay]; [menuContainer release]; - result.menu = (COptionMenu*)[nsMenu performSelector:@selector(selectedMenu)]; - result.index = (int32_t)(intptr_t)[nsMenu performSelector:@selector(selectedItem)]; + result.menu = (COptionMenu*)[nsMenu performSelector:@selector (selectedMenu)]; + result.index = (int32_t) (intptr_t)[nsMenu performSelector:@selector (selectedItem)]; [nsMenu release]; callback (optionMenu, result); } - } // VSTGUI #endif // MAC_COCOA diff --git a/vstgui/lib/platform/mac/cocoa/objcclassbuilder.h b/vstgui/lib/platform/mac/cocoa/objcclassbuilder.h new file mode 100644 index 000000000..f2b540d73 --- /dev/null +++ b/vstgui/lib/platform/mac/cocoa/objcclassbuilder.h @@ -0,0 +1,218 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#import +#import +#import +#import +#import + +//------------------------------------------------------------------------------------ +namespace VSTGUI { + +//------------------------------------------------------------------------------------ +template +struct ObjCVariable +{ + ObjCVariable (id obj, Ivar ivar) : obj (obj), ivar (ivar) {} + + T get () const { return static_cast (object_getIvar (obj, ivar)); } + + void set (const T& value) { object_setIvar (obj, ivar, static_cast (value)); } + +private: + id obj; + Ivar ivar {nullptr}; +}; + +//------------------------------------------------------------------------------------ +struct ObjCInstance +{ + ObjCInstance (id obj) : obj (obj) {} + + template + std::optional> getVariable (const char* name) const + { + if (auto ivar = class_getInstanceVariable ([obj class], name)) + { + return {ObjCVariable (obj, ivar)}; + } + return {}; + } + + template + void callSuper (SEL selector, T... args) const + { + void (*f) (id, SEL, T...) = (void (*) (id, SEL, T...))objc_msgSendSuper; + f (getSuper (), selector, args...); + } + + template + R callSuper (SEL selector, T... args) const + { + R (*f) (id, SEL, T...) = (R (*) (id, SEL, T...))objc_msgSendSuper; + return f (getSuper (), selector, args...); + } + +private: + id getSuper () const + { + if (os.receiver == nullptr) + { + os.receiver = obj; + os.super_class = class_getSuperclass ([obj class]); + } + return static_cast (&os); + } + + id obj; + mutable objc_super os {}; +}; + +//------------------------------------------------------------------------------------ +struct ObjCClassBuilder +{ + ObjCClassBuilder& init (const char* name, Class baseClass); + + template + ObjCClassBuilder& addMethod (SEL selector, Func imp); + template + ObjCClassBuilder& addIvar (const char* name); + + ObjCClassBuilder& addProtocol (const char* name); + ObjCClassBuilder& addProtocol (Protocol* proto); + + Class finalize (); + +private: + static Class generateUniqueClass (const std::string& inClassName, Class baseClass); + + template + ObjCClassBuilder& addMethod (SEL selector, Func imp, const char* types); + + ObjCClassBuilder& addIvar (const char* name, size_t size, uint8_t alignment, const char* types); + + template + static constexpr std::tuple functionArgs (R (*) (T...)) + { + return std::tuple (); + } + + template + static constexpr std::tuple functionArgs (void (*) (T...)) + { + return std::tuple (); + } + + template + static constexpr bool isVoidReturnType (R (*) (T...)) + { + return false; + } + + template + static constexpr bool isVoidReturnType (void (*) (T...)) + { + return true; + } + + template + static std::string encodeFunction (Proc proc) + { + std::string result; + if (isVoidReturnType (proc)) + result = "v"; + std::apply ([&] (auto&&... args) { ((result += @encode (decltype (args))), ...); }, + functionArgs (proc)); + return result; + } + + Class cl {nullptr}; + Class baseClass {nullptr}; +}; + +//------------------------------------------------------------------------ +inline ObjCClassBuilder& ObjCClassBuilder::init (const char* name, Class bc) +{ + baseClass = bc; + cl = generateUniqueClass (name, baseClass); + return *this; +} + +//------------------------------------------------------------------------------------ +inline Class ObjCClassBuilder::generateUniqueClass (const std::string& inClassName, Class baseClass) +{ + std::string className (inClassName); + int32_t iteration = 0; + while (objc_lookUpClass (className.data ()) != nil) + { + iteration++; + className = inClassName + "_" + std::to_string (iteration); + } + Class resClass = objc_allocateClassPair (baseClass, className.data (), 0); + return resClass; +} + +//----------------------------------------------------------------------------- +inline Class ObjCClassBuilder::finalize () +{ + objc_registerClassPair (cl); + + auto res = cl; + baseClass = cl = nullptr; + return res; +} + +//----------------------------------------------------------------------------- +template +inline ObjCClassBuilder& ObjCClassBuilder::addMethod (SEL selector, Func imp, const char* types) +{ + auto res = class_addMethod (cl, selector, IMP (imp), types); + vstgui_assert (res == true); + return *this; +} + +//----------------------------------------------------------------------------- +template +ObjCClassBuilder& ObjCClassBuilder::addMethod (SEL selector, Func imp) +{ + return addMethod (selector, imp, encodeFunction (imp).data ()); +} + +//------------------------------------------------------------------------ +template +inline ObjCClassBuilder& ObjCClassBuilder::addIvar (const char* name) +{ + return addIvar (name, sizeof (T), static_cast (log2 (sizeof (T))), @encode (T)); +} + +//----------------------------------------------------------------------------- +inline ObjCClassBuilder& ObjCClassBuilder::addIvar (const char* name, size_t size, + uint8_t alignment, const char* types) +{ + auto res = class_addIvar (cl, name, size, alignment, types); + vstgui_assert (res == true); + return *this; +} + +//----------------------------------------------------------------------------- +inline ObjCClassBuilder& ObjCClassBuilder::addProtocol (const char* name) +{ + if (auto protocol = objc_getProtocol (name)) + return addProtocol (protocol); + return *this; +} + +//----------------------------------------------------------------------------- +inline ObjCClassBuilder& ObjCClassBuilder::addProtocol (Protocol* proto) +{ + auto res = class_addProtocol (cl, proto); + vstgui_assert (res == true); + return *this; +} + +//------------------------------------------------------------------------------------ +} // VSTGUI diff --git a/vstgui/lib/platform/mac/ios/uitextedit.mm b/vstgui/lib/platform/mac/ios/uitextedit.mm index c20abba9a..26c85b610 100644 --- a/vstgui/lib/platform/mac/ios/uitextedit.mm +++ b/vstgui/lib/platform/mac/ios/uitextedit.mm @@ -141,10 +141,10 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField #endif platformControl = nil; } - delegate = nil; #if !ARC_ENABLED [delegate release]; #endif + delegate = nil; } //------------------------------------------------------------------------------------ diff --git a/vstgui/lib/platform/mac/ios/uiviewframe.h b/vstgui/lib/platform/mac/ios/uiviewframe.h index aeb2cfeed..d340c4b30 100644 --- a/vstgui/lib/platform/mac/ios/uiviewframe.h +++ b/vstgui/lib/platform/mac/ios/uiviewframe.h @@ -34,6 +34,7 @@ class UIViewFrame : public IPlatformFrame bool getSize (CRect& size) const override; bool getCurrentMousePosition (CPoint& mousePosition) const override { return false; }; bool getCurrentMouseButtons (CButtonState& buttons) const override { return false; }; + bool getCurrentModifiers (Modifiers& modifiers) const override { return false; } bool setMouseCursor (CCursorType type) override { return false; }; bool invalidRect (const CRect& rect) override; bool scrollRect (const CRect& src, const CPoint& distance) override; diff --git a/vstgui/lib/platform/mac/ios/uiviewframe.mm b/vstgui/lib/platform/mac/ios/uiviewframe.mm index 7b272a347..4b225f32b 100644 --- a/vstgui/lib/platform/mac/ios/uiviewframe.mm +++ b/vstgui/lib/platform/mac/ios/uiviewframe.mm @@ -178,17 +178,6 @@ - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event namespace VSTGUI { -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -CNewFileSelector* CNewFileSelector::create (CFrame* frame, Style style) -{ -#if DEBUG - DebugPrint ("No iOS implementation for CNewFileSelector\n"); -#endif - return 0; -} - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/vstgui/lib/platform/mac/macclipboard.h b/vstgui/lib/platform/mac/macclipboard.h index fe5015d41..5986fcd58 100644 --- a/vstgui/lib/platform/mac/macclipboard.h +++ b/vstgui/lib/platform/mac/macclipboard.h @@ -12,10 +12,6 @@ struct NSPasteboard; #endif -#if MAC_CARBON -using DragRef = struct OpaqueDragRef*; -#endif - namespace VSTGUI { class IDataPackage; @@ -26,8 +22,4 @@ extern SharedPointer createDragDataPackage (NSPasteboard* pasteboa extern void setClipboard (const SharedPointer& data); extern const char* getPasteboardBinaryType (); -#if MAC_CARBON -extern SharedPointer createCarbonDragDataPackage (DragRef drag); -#endif - }} // namespaces diff --git a/vstgui/lib/platform/mac/macclipboard.mm b/vstgui/lib/platform/mac/macclipboard.mm index 6629535a1..008c3ea34 100644 --- a/vstgui/lib/platform/mac/macclipboard.mm +++ b/vstgui/lib/platform/mac/macclipboard.mm @@ -9,10 +9,6 @@ #import #import -#if MAC_CARBON -#import -#endif - #ifndef MAC_OS_X_VERSION_10_14 #define MAC_OS_X_VERSION_10_14 101400 #endif @@ -288,24 +284,6 @@ static Entry makeEntry (NSPasteboardItem* item) return makeOwned (pasteboard); } -#if MAC_CARBON -//----------------------------------------------------------------------------- -SharedPointer createCarbonDragDataPackage (DragRef drag) -{ - PasteboardRef pr; - if (GetDragPasteboard (drag, &pr) == noErr) - { - CFStringRef pasteboardName; - if (PasteboardCopyName (pr, &pasteboardName) == noErr) - { - [(NSString*)pasteboardName autorelease]; - return makeOwned ([NSPasteboard pasteboardWithName:(NSString*)pasteboardName]); - } - } - return nullptr; -} -#endif - //----------------------------------------------------------------------------- void setClipboard (const SharedPointer& dataSource) { diff --git a/vstgui/lib/platform/mac/macfactory.h b/vstgui/lib/platform/mac/macfactory.h index 4c71b3ccb..50bd0ff4d 100644 --- a/vstgui/lib/platform/mac/macfactory.h +++ b/vstgui/lib/platform/mac/macfactory.h @@ -123,6 +123,19 @@ class MacFactory final : public IPlatformFactory COffscreenContextPtr createOffscreenContext (const CPoint& size, double scaleFactor = 1.) const noexcept final; + /** Create a platform gradient object + * @return platform gradient object or nullptr on failure + */ + PlatformGradientPtr createGradient () const noexcept final; + + /** Create a platform file selector + * @param style file selector style + * @param frame frame + * @return platform file selector or nullptr on failure + */ + PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, + IPlatformFrame* frame) const noexcept; + const LinuxFactory* asLinuxFactory () const noexcept final; const MacFactory* asMacFactory () const noexcept final; const Win32Factory* asWin32Factory () const noexcept final; diff --git a/vstgui/lib/platform/mac/macfactory.mm b/vstgui/lib/platform/mac/macfactory.mm index a929276b4..298194c58 100644 --- a/vstgui/lib/platform/mac/macfactory.mm +++ b/vstgui/lib/platform/mac/macfactory.mm @@ -9,14 +9,15 @@ #include "../iplatformresourceinputstream.h" #include "../iplatformstring.h" #include "../iplatformtimer.h" -#include "carbon/hiviewframe.h" #include "cfontmac.h" #include "cgbitmap.h" #include "cgdrawcontext.h" +#include "quartzgraphicspath.h" #include "cocoa/nsviewframe.h" #include "ios/uiviewframe.h" #include "macclipboard.h" #include "macfactory.h" +#include "macfileselector.h" #include "macglobals.h" #include "macstring.h" #include "mactimer.h" @@ -90,10 +91,6 @@ #if TARGET_OS_IPHONE return makeOwned (frame, size, (__bridge UIView*)parent); #else -#if MAC_CARBON - if (platformType == PlatformType::kWindowRef || platformType == PlatformType::kDefaultNative) - return makeOwned (frame, size, reinterpret_cast (parent)); -#endif // MAC_CARBON return makeOwned (frame, size, reinterpret_cast (parent), config); #endif } @@ -225,6 +222,23 @@ return nullptr; } +//----------------------------------------------------------------------------- +PlatformGradientPtr MacFactory::createGradient () const noexcept +{ + return std::make_unique (); +} + +//----------------------------------------------------------------------------- +PlatformFileSelectorPtr MacFactory::createFileSelector (PlatformFileSelectorStyle style, + IPlatformFrame* frame) const noexcept +{ +#if !TARGET_OS_IPHONE + auto nsViewFrame = dynamic_cast (frame); + return createCocoaFileSelector (style, nsViewFrame); +#endif + return nullptr; +} + //----------------------------------------------------------------------------- const LinuxFactory* MacFactory::asLinuxFactory () const noexcept { diff --git a/vstgui/lib/platform/mac/macfileselector.h b/vstgui/lib/platform/mac/macfileselector.h new file mode 100644 index 000000000..3a7656891 --- /dev/null +++ b/vstgui/lib/platform/mac/macfileselector.h @@ -0,0 +1,21 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#if !TARGET_OS_IPHONE + +#include "../iplatformfileselector.h" +#include "cocoa/nsviewframe.h" + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +PlatformFileSelectorPtr createCocoaFileSelector (PlatformFileSelectorStyle style, + NSViewFrame* frame); + +} // VSTGUI + +#endif + diff --git a/vstgui/lib/platform/mac/macfileselector.mm b/vstgui/lib/platform/mac/macfileselector.mm index 2d2bb17b3..64f33f680 100644 --- a/vstgui/lib/platform/mac/macfileselector.mm +++ b/vstgui/lib/platform/mac/macfileselector.mm @@ -1,20 +1,17 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE /// @cond ignore -#import "../../cfileselector.h" #import "../../cstring.h" -#import "../../cframe.h" - -// the cocoa fileselector is also used for carbon -#import #import "cocoa/cocoahelpers.h" +#import "macfileselector.h" #import "macstring.h" -#if MAC_COCOA -#import "cocoa/nsviewframe.h" +#if defined(MAC_OS_VERSION_11_0) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0) +#import +#define VSTGUI_USE_OBJC_UTTYPE #endif namespace VSTGUI { @@ -22,31 +19,35 @@ //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ //----------------------------------------------------------------------------- -class CocoaFileSelector : public CNewFileSelector +class CocoaFileSelector +: public IPlatformFileSelector +, public std::enable_shared_from_this { public: - CocoaFileSelector (CFrame* frame, Style style); + CocoaFileSelector (PlatformFileSelectorStyle style, NSViewFrame* frame); ~CocoaFileSelector () override = default; + bool run (const PlatformFileSelectorConfig& config) override; + bool cancel () override; + void openPanelDidEnd (NSSavePanel* panel, NSInteger resultCode); + protected: static void initClass (); - - bool runInternal (CBaseObject* delegate) override; - bool runModalInternal () override; - void cancelInternal () override; - void setupInitalDir (); + void setupInitalDir (const PlatformFileSelectorConfig& config); - Style style; - SharedPointer delegate; - NSSavePanel* savePanel; + PlatformFileSelectorStyle style; + NSViewFrame* frame {nullptr}; + NSSavePanel* savePanel {nullptr}; + PlatformFileSelectorConfig::CallbackFunc callback; }; //----------------------------------------------------------------------------- -CNewFileSelector* CNewFileSelector::create (CFrame* frame, Style style) +PlatformFileSelectorPtr createCocoaFileSelector (PlatformFileSelectorStyle style, + NSViewFrame* frame) { - return new CocoaFileSelector (frame, style); + return std::make_shared (style, frame); } //----------------------------------------------------------------------------- @@ -55,21 +56,19 @@ } //----------------------------------------------------------------------------- -CocoaFileSelector::CocoaFileSelector (CFrame* frame, Style style) -: CNewFileSelector (frame) -, style (style) -, delegate (nullptr) +CocoaFileSelector::CocoaFileSelector (PlatformFileSelectorStyle style, NSViewFrame* frame) +: style (style), frame (frame) { - savePanel = nil; initClass (); } //----------------------------------------------------------------------------- void CocoaFileSelector::openPanelDidEnd (NSSavePanel* panel, NSInteger res) { + std::vector result; if (res == NSModalResponseOK) { - if (style == kSelectSaveFile) + if (style == PlatformFileSelectorStyle::SelectSaveFile) { NSURL* url = [panel URL]; const char* utf8Path = url ? [[url path] UTF8String] : nullptr; @@ -95,76 +94,95 @@ } } } - if (delegate) - delegate->notify (this, CNewFileSelector::kSelectEndMessage); + if (callback) + callback (std::move (result)); } //----------------------------------------------------------------------------- -void CocoaFileSelector::cancelInternal () +bool CocoaFileSelector::cancel () { if (savePanel) + { [savePanel cancel:nil]; + return true; + } + return false; } //----------------------------------------------------------------------------- -bool CocoaFileSelector::runInternal (CBaseObject* _delegate) +bool CocoaFileSelector::run (const PlatformFileSelectorConfig& config) { - CBaseObjectGuard lifeGuard (this); - NSWindow* parentWindow = nil; - if (_delegate) - { - #if MAC_COCOA - if (frame && frame->getPlatformFrame () && - frame->getPlatformFrame ()->getPlatformType () == PlatformType::kNSView) - { - auto nsViewFrame = static_cast (frame->getPlatformFrame ()); - parentWindow = [(nsViewFrame->getNSView ())window]; - } - #endif - delegate = _delegate; - } + + if (!hasBit (config.flags, PlatformFileSelectorFlags::RunModal) && frame) + parentWindow = [(frame->getNSView ()) window]; + + callback = config.doneCallback; + NSOpenPanel* openPanel = nil; NSMutableArray* typesArray = nil; - if (extensions.empty () == false) + if (config.extensions.empty () == false) { typesArray = [[[NSMutableArray alloc] init] autorelease]; - for (auto& ext : extensions) + for (auto& ext : config.extensions) { +#ifdef VSTGUI_USE_OBJC_UTTYPE + UTType* uti = nullptr; + if (ext.uti.empty () == false) + uti = [UTType typeWithIdentifier:fromUTF8String (ext.uti)]; + if (uti == nullptr && ext.mimeType.empty() == false) + uti = [UTType typeWithMIMEType:fromUTF8String (ext.mimeType)]; + if (uti == nullptr && ext.extension.empty () == false) + uti = [UTType typeWithFilenameExtension:fromUTF8String (ext.extension)]; + if (uti) + [typesArray addObject:uti]; +#else NSString* uti = nullptr; - if (ext.getUTI ().empty () == false) - uti = [fromUTF8String (ext.getUTI ()) retain]; - if (uti == nullptr && ext.getMimeType ().empty () == false) - uti = (NSString*)UTTypeCreatePreferredIdentifierForTag (kUTTagClassMIMEType, fromUTF8String (ext.getMimeType ()), kUTTypeData); - if (uti == nullptr && ext.getMacType ()) + if (ext.uti.empty () == false) + uti = [fromUTF8String (ext.uti) retain]; + if (uti == nullptr && ext.mimeType.empty () == false) + uti = (NSString*)UTTypeCreatePreferredIdentifierForTag ( + kUTTagClassMIMEType, fromUTF8String (ext.mimeType), kUTTypeData); + if (uti == nullptr && ext.macType) { - NSString* osType = (NSString*)UTCreateStringForOSType (static_cast (ext.getMacType ())); + NSString* osType = + (NSString*)UTCreateStringForOSType (static_cast (ext.macType)); if (osType) { - uti = (NSString*)UTTypeCreatePreferredIdentifierForTag (kUTTagClassOSType, (CFStringRef)osType, kUTTypeData); + uti = (NSString*)UTTypeCreatePreferredIdentifierForTag ( + kUTTagClassOSType, (CFStringRef)osType, kUTTypeData); [osType release]; } } - if (uti == nullptr && ext.getExtension ().empty () == false) - uti = [fromUTF8String (ext.getExtension ()) retain]; + if (uti == nullptr && ext.extension.empty () == false) + uti = [fromUTF8String (ext.extension) retain]; if (uti) { [typesArray addObject:uti]; [uti release]; } +#endif } } - if (style == kSelectSaveFile) + if (style == PlatformFileSelectorStyle::SelectSaveFile) { savePanel = [NSSavePanel savePanel]; if (typesArray) + { +#ifdef VSTGUI_USE_OBJC_UTTYPE + [savePanel setAllowedContentTypes:typesArray]; +#else [savePanel setAllowedFileTypes:typesArray]; +#endif + } } else { savePanel = openPanel = [NSOpenPanel openPanel]; - if (style == kSelectFile) + if (style == PlatformFileSelectorStyle::SelectFile) { + bool allowMultiFileSelection = + hasBit (config.flags, PlatformFileSelectorFlags::MultiFileSelection); [openPanel setAllowsMultipleSelection:allowMultiFileSelection ? YES : NO]; } else @@ -172,42 +190,47 @@ [openPanel setCanChooseDirectories:YES]; } } - if (!title.empty () && savePanel) + if (!config.title.empty () && savePanel) { +#if 0 // Apple broke this again with macOS 12. Disable this now and always use the message to + // display the title. if (@available (macOS 11, *)) { if (parentWindow) - [savePanel setMessage:fromUTF8String (title)]; + [savePanel setMessage:fromUTF8String (config.title)]; else - [savePanel setTitle:fromUTF8String (title)]; + [savePanel setTitle:fromUTF8String (config.title)]; } - else if (@available (macOS 10.11, *)) + else +#endif + if (@available (macOS 10.11, *)) { - [savePanel setMessage:fromUTF8String (title)]; + [savePanel setMessage:fromUTF8String (config.title)]; } else { - [savePanel setTitle:fromUTF8String (title)]; + [savePanel setTitle:fromUTF8String (config.title)]; } } if (openPanel) { - #if MAC_COCOA +#ifdef VSTGUI_USE_OBJC_UTTYPE + openPanel.allowedContentTypes = typesArray; +#else + openPanel.allowedFileTypes = typesArray; +#endif if (parentWindow) { - setupInitalDir (); - openPanel.allowedFileTypes = typesArray; - remember (); - [openPanel beginSheetModalForWindow:parentWindow completionHandler:^(NSInteger result) { - openPanelDidEnd (openPanel, result); - forget (); - }]; + setupInitalDir (config); + auto This = shared_from_this (); + [openPanel beginSheetModalForWindow:parentWindow + completionHandler:^(NSInteger result) { + This->openPanelDidEnd (openPanel, result); + }]; } else - #endif { - setupInitalDir (); - openPanel.allowedFileTypes = typesArray; + setupInitalDir (config); NSInteger res = [openPanel runModal]; openPanelDidEnd (openPanel, res); return res == NSModalResponseOK; @@ -215,60 +238,56 @@ } else if (savePanel) { - #if MAC_COCOA +#ifdef VSTGUI_USE_OBJC_UTTYPE + savePanel.allowedContentTypes = typesArray; +#else + savePanel.allowedFileTypes = typesArray; +#endif if (parentWindow) { - setupInitalDir (); - savePanel.allowedFileTypes = typesArray; - remember (); - [savePanel beginSheetModalForWindow:parentWindow completionHandler:^(NSInteger result) { - openPanelDidEnd (savePanel, result); - forget (); - }]; + setupInitalDir (config); + auto This = shared_from_this (); + [savePanel beginSheetModalForWindow:parentWindow + completionHandler:^(NSInteger result) { + This->openPanelDidEnd (savePanel, result); + }]; } else - #endif { - setupInitalDir (); - savePanel.allowedFileTypes = typesArray; + setupInitalDir (config); NSInteger res = [savePanel runModal]; openPanelDidEnd (savePanel, res); return res == NSModalResponseOK; } } - + return true; } //----------------------------------------------------------------------------- -void CocoaFileSelector::setupInitalDir () +void CocoaFileSelector::setupInitalDir (const PlatformFileSelectorConfig& config) { - if (!initialPath.empty ()) + if (!config.initialPath.empty ()) { - NSURL* dirURL = [NSURL fileURLWithPath:fromUTF8String (initialPath)]; + NSURL* dirURL = [NSURL fileURLWithPath:fromUTF8String (config.initialPath)]; NSNumber* isDir; if ([dirURL getResourceValue:&isDir forKey:NSURLIsDirectoryKey error:nil]) { if ([isDir boolValue] == NO) { - savePanel.nameFieldStringValue = [[dirURL.path lastPathComponent] stringByDeletingPathExtension]; + savePanel.nameFieldStringValue = + [[dirURL.path lastPathComponent] stringByDeletingPathExtension]; dirURL = [NSURL fileURLWithPath:[dirURL.path stringByDeletingLastPathComponent]]; } savePanel.directoryURL = dirURL; } } - if (!defaultSaveName.empty ()) + if (!config.defaultSaveName.empty ()) { - savePanel.nameFieldStringValue = fromUTF8String (defaultSaveName); + savePanel.nameFieldStringValue = fromUTF8String (config.defaultSaveName); } } -//----------------------------------------------------------------------------- -bool CocoaFileSelector::runModalInternal () -{ - return runInternal (nullptr); -} - } // VSTGUI /// @endcond diff --git a/vstgui/lib/platform/mac/macglobals.cpp b/vstgui/lib/platform/mac/macglobals.cpp index e6d15043b..f3675d44b 100644 --- a/vstgui/lib/platform/mac/macglobals.cpp +++ b/vstgui/lib/platform/mac/macglobals.cpp @@ -12,8 +12,6 @@ #include "../common/fileresourceinputstream.h" #include "../std_unorderedmap.h" -#define USE_MAIN_DISPLAY_COLORSPACE 1 - namespace VSTGUI { //----------------------------------------------------------------------------- @@ -92,10 +90,24 @@ class GenericMacColorSpace return CGColorSpaceCreateDeviceRGB (); #else - ColorSyncProfileRef csProfileRef = ColorSyncProfileCreateWithDisplayID (0); + ColorSyncProfileRef csProfileRef = ColorSyncProfileCreateWithDisplayID (CGMainDisplayID ()); if (csProfileRef) { - CGColorSpaceRef colorSpace = CGColorSpaceCreateWithPlatformColorSpace (csProfileRef); + CGColorSpaceRef colorSpace = {}; + #if defined(MAC_OS_VERSION_12_0) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) + if (__builtin_available (macOS 12.0, *)) + { + colorSpace = CGColorSpaceCreateWithColorSyncProfile (csProfileRef, nullptr); + } + #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) + else + { + colorSpace = CGColorSpaceCreateWithPlatformColorSpace (csProfileRef); + } + #endif + #else + colorSpace = CGColorSpaceCreateWithPlatformColorSpace (csProfileRef); + #endif CFRelease (csProfileRef); return colorSpace; } diff --git a/vstgui/lib/platform/mac/quartzgraphicspath.cpp b/vstgui/lib/platform/mac/quartzgraphicspath.cpp index c476ee088..8b0638df8 100644 --- a/vstgui/lib/platform/mac/quartzgraphicspath.cpp +++ b/vstgui/lib/platform/mac/quartzgraphicspath.cpp @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -6,13 +6,15 @@ #if MAC -#include "cgdrawcontext.h" #include "cfontmac.h" +#include "cgdrawcontext.h" + +#include "../../cgradient.h" namespace VSTGUI { //----------------------------------------------------------------------------- -CGAffineTransform QuartzGraphicsPath::createCGAffineTransform (const CGraphicsTransform& t) +CGAffineTransform createCGAffineTransform (const CGraphicsTransform& t) { CGAffineTransform transform; transform.a = static_cast (t.m11); @@ -25,28 +27,21 @@ CGAffineTransform QuartzGraphicsPath::createCGAffineTransform (const CGraphicsTr } //----------------------------------------------------------------------------- -QuartzGraphicsPath::QuartzGraphicsPath () -: path (nullptr) -, originalTextPath (nullptr) -, isPixelAlligned (false) -{ -} - -//----------------------------------------------------------------------------- -QuartzGraphicsPath::QuartzGraphicsPath (const CoreTextFont* font, UTF8StringPtr text) -: isPixelAlligned (false) +static CGMutablePathRef createTextPath (const CoreTextFont* font, UTF8StringPtr text) { - path = CGPathCreateMutable (); - - CFStringRef str = CFStringCreateWithCString (kCFAllocatorDefault, text, kCFStringEncodingUTF8); - const void* keys [] = {kCTFontAttributeName}; - const void* values [] = {font->getFontRef ()}; - CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, keys, values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFAttributedStringRef attrString = CFAttributedStringCreate (kCFAllocatorDefault, str, dict); + auto textPath = CGPathCreateMutable (); + + CFStringRef str = CFStringCreateWithCString (kCFAllocatorDefault, text, kCFStringEncodingUTF8); + const void* keys[] = {kCTFontAttributeName}; + const void* values[] = {font->getFontRef ()}; + CFDictionaryRef dict = + CFDictionaryCreate (kCFAllocatorDefault, keys, values, 1, + &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFAttributedStringRef attrString = CFAttributedStringCreate (kCFAllocatorDefault, str, dict); CFRelease (dict); CFRelease (str); - CTLineRef line = CTLineCreateWithAttributedString (attrString); + CTLineRef line = CTLineCreateWithAttributedString (attrString); if (line != nullptr) { CCoord capHeight = font->getCapHeight (); @@ -54,194 +49,161 @@ QuartzGraphicsPath::QuartzGraphicsPath (const CoreTextFont* font, UTF8StringPtr for (CFIndex runIndex = 0; runIndex < CFArrayGetCount (runArray); runIndex++) { CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex (runArray, runIndex); - CTFontRef runFont = (CTFontRef)CFDictionaryGetValue (CTRunGetAttributes (run), kCTFontAttributeName); + CTFontRef runFont = + (CTFontRef)CFDictionaryGetValue (CTRunGetAttributes (run), kCTFontAttributeName); CFIndex glyphCount = CTRunGetGlyphCount (run); - for (CFRange glyphRange = CFRangeMake (0, 1); glyphRange.location < glyphCount; ++glyphRange.location) + for (CFRange glyphRange = CFRangeMake (0, 1); glyphRange.location < glyphCount; + ++glyphRange.location) { CGGlyph glyph; CGPoint position; CTRunGetGlyphs (run, glyphRange, &glyph); CTRunGetPositions (run, glyphRange, &position); - CGPathRef letter = CTFontCreatePathForGlyph (runFont, glyph, nullptr); CGAffineTransform t = CGAffineTransformMakeTranslation (position.x, position.y); t = CGAffineTransformScale (t, 1, -1); t = CGAffineTransformTranslate (t, 0, static_cast (-capHeight)); - CGPathAddPath (path, &t, letter); + CGPathAddPath (textPath, &t, letter); CGPathRelease (letter); } } CFRelease (line); } CFRelease (attrString); - originalTextPath = path; + return textPath; } //----------------------------------------------------------------------------- -QuartzGraphicsPath::~QuartzGraphicsPath () noexcept +PlatformGraphicsPathFactoryPtr CGGraphicsPathFactory::instance () { - dirty (); - if (originalTextPath) - CFRelease (originalTextPath); + static PlatformGraphicsPathFactoryPtr factory = std::make_shared (); + return factory; } //----------------------------------------------------------------------------- -CGradient* QuartzGraphicsPath::createGradient (double color1Start, double color2Start, const CColor& color1, const CColor& color2) +PlatformGraphicsPathPtr + CGGraphicsPathFactory::createPath ([[maybe_unused]] PlatformGraphicsPathFillMode fillMode) { - return new QuartzGradient (color1Start, color2Start, color1, color2); + return std::make_unique (CGPathCreateMutable ()); } //----------------------------------------------------------------------------- -CGPathRef QuartzGraphicsPath::getCGPathRef () +PlatformGraphicsPathPtr CGGraphicsPathFactory::createTextPath (const PlatformFontPtr& font, + UTF8StringPtr text) { - if (path == nullptr) + if (auto ctFont = font.cast ()) { - if (originalTextPath) - { - path = originalTextPath; - return path; - } - path = CGPathCreateMutable (); - for (const auto& e : elements) - { - switch (e.type) - { - case Element::kArc: - { - CCoord radiusX = (e.instruction.arc.rect.right - e.instruction.arc.rect.left) / 2.; - CCoord radiusY = (e.instruction.arc.rect.bottom - e.instruction.arc.rect.top) / 2.; - - CGFloat centerX = static_cast (e.instruction.arc.rect.left + radiusX); - CGFloat centerY = static_cast (e.instruction.arc.rect.top + radiusY); - - CGAffineTransform transform = CGAffineTransformMakeTranslation (centerX, centerY); - transform = CGAffineTransformScale (transform, static_cast (radiusX), static_cast (radiusY)); - - double startAngle = radians (e.instruction.arc.startAngle); - double endAngle = radians (e.instruction.arc.endAngle); - if (radiusX != radiusY) - { - startAngle = atan2 (sin (startAngle) * radiusX, cos (startAngle) * radiusY); - endAngle = atan2 (sin (endAngle) * radiusX, cos (endAngle) * radiusY); - } - if (CGPathIsEmpty (path)) - CGPathMoveToPoint (path, &transform, static_cast (std::cos (startAngle)), static_cast (std::sin (startAngle))); - - CGPathAddArc (path, &transform, 0, 0, 1, static_cast (startAngle), static_cast (endAngle), !e.instruction.arc.clockwise); - break; - } - case Element::kEllipse: - { - CCoord width = e.instruction.rect.right - e.instruction.rect.left; - CCoord height = e.instruction.rect.bottom - e.instruction.rect.top; - CGPathAddEllipseInRect (path, nullptr, CGRectMake (static_cast (e.instruction.rect.left), static_cast (e.instruction.rect.top), static_cast (width), static_cast (height))); - break; - } - case Element::kRect: - { - CCoord width = e.instruction.rect.right - e.instruction.rect.left; - CCoord height = e.instruction.rect.bottom - e.instruction.rect.top; - CGPathAddRect (path, nullptr, CGRectMake (static_cast (e.instruction.rect.left), static_cast (e.instruction.rect.top), static_cast (width), static_cast (height))); - break; - } - case Element::kLine: - { - CGPathAddLineToPoint (path, nullptr, static_cast (e.instruction.point.x), static_cast (e.instruction.point.y)); - break; - } - case Element::kBezierCurve: - { - CGPathAddCurveToPoint (path, nullptr, static_cast (e.instruction.curve.control1.x), static_cast (e.instruction.curve.control1.y), static_cast (e.instruction.curve.control2.x), static_cast (e.instruction.curve.control2.y), static_cast (e.instruction.curve.end.x), static_cast (e.instruction.curve.end.y)); - break; - } - case Element::kBeginSubpath: - { - CGPathMoveToPoint (path, nullptr, static_cast (e.instruction.point.x), static_cast (e.instruction.point.y)); - break; - } - case Element::kCloseSubpath: - { - CGPathCloseSubpath (path); - break; - } - } - } + return std::make_unique (VSTGUI::createTextPath (ctFont, text)); } - return path; + return nullptr; } //----------------------------------------------------------------------------- -void QuartzGraphicsPath::dirty () +CGGraphicsPath::CGGraphicsPath (CGMutablePathRef inPath) { - if (path) - { - if (originalTextPath != path) - CFRelease (path); - path = nullptr; - } + if (inPath) + path = inPath; + else + path = CGPathCreateMutable (); } //----------------------------------------------------------------------------- -bool QuartzGraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, CGraphicsTransform* transform) +CGGraphicsPath::~CGGraphicsPath () noexcept { - CGPathRef cgPath = getCGPathRef (); - if (cgPath) + CFRelease (path); +} + +//----------------------------------------------------------------------------- +void CGGraphicsPath::addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) +{ + CCoord radiusX = (rect.right - rect.left) / 2.; + CCoord radiusY = (rect.bottom - rect.top) / 2.; + CGFloat centerX = static_cast (rect.left + radiusX); + CGFloat centerY = static_cast (rect.top + radiusY); + CGAffineTransform transform = CGAffineTransformMakeTranslation (centerX, centerY); + transform = CGAffineTransformScale (transform, static_cast (radiusX), + static_cast (radiusY)); + startAngle = radians (startAngle); + endAngle = radians (endAngle); + if (radiusX != radiusY) + { + startAngle = std::atan2 (std::sin (startAngle) * radiusX, std::cos (startAngle) * radiusY); + endAngle = std::atan2 (std::sin (endAngle) * radiusX, std::cos (endAngle) * radiusY); + } + if (CGPathIsEmpty (path)) { - CGPoint cgPoint = CGPointFromCPoint (p); - CGAffineTransform cgTransform; - if (transform) - cgTransform = createCGAffineTransform (*transform); - return CGPathContainsPoint (cgPath, transform ? &cgTransform : nullptr, cgPoint, evenOddFilled); + CGPathMoveToPoint (path, &transform, static_cast (std::cos (startAngle)), + static_cast (std::sin (startAngle))); } - return false; + CGPathAddArc (path, &transform, 0, 0, 1, static_cast (startAngle), + static_cast (endAngle), !clockwise); } //----------------------------------------------------------------------------- -CPoint QuartzGraphicsPath::getCurrentPosition () +void CGGraphicsPath::addEllipse (const CRect& rect) { - CPoint p (0, 0); + CGPathAddEllipseInRect (path, nullptr, + CGRectMake (static_cast (rect.left), + static_cast (rect.top), + static_cast (rect.getWidth ()), + static_cast (rect.getHeight ()))); +} - CGPathRef cgPath = getCGPathRef (); - if (cgPath && !CGPathIsEmpty (cgPath)) - { - CGPoint cgPoint = CGPathGetCurrentPoint (cgPath); - p.x = cgPoint.x; - p.y = cgPoint.y; - } +//----------------------------------------------------------------------------- +void CGGraphicsPath::addRect (const CRect& rect) +{ + CGPathAddRect (path, nullptr, + CGRectMake (static_cast (rect.left), static_cast (rect.top), + static_cast (rect.getWidth ()), + static_cast (rect.getHeight ()))); +} - return p; +//----------------------------------------------------------------------------- +void CGGraphicsPath::addLine (const CPoint& to) +{ + CGPathAddLineToPoint (path, nullptr, static_cast (to.x), static_cast (to.y)); } //----------------------------------------------------------------------------- -CRect QuartzGraphicsPath::getBoundingBox () +void CGGraphicsPath::addBezierCurve (const CPoint& control1, const CPoint& control2, + const CPoint& end) { - CRect r; + CGPathAddCurveToPoint (path, nullptr, static_cast (control1.x), + static_cast (control1.y), static_cast (control2.x), + static_cast (control2.y), static_cast (end.x), + static_cast (end.y)); +} - CGPathRef cgPath = getCGPathRef (); - if (cgPath) - r = CRectFromCGRect (CGPathGetBoundingBox (cgPath)); - return r; +//----------------------------------------------------------------------------- +void CGGraphicsPath::beginSubpath (const CPoint& start) +{ + CGPathMoveToPoint (path, nullptr, static_cast (start.x), + static_cast (start.y)); } //----------------------------------------------------------------------------- -void QuartzGraphicsPath::pixelAlign (CDrawContext* context) +void CGGraphicsPath::closeSubpath () { - CGDrawContext* cgDrawContext = dynamic_cast(context); - if (cgDrawContext == nullptr) - return; + CGPathCloseSubpath (path); +} - if (isPixelAlligned) - dirty (); +//----------------------------------------------------------------------------- +void CGGraphicsPath::finishBuilding () +{ +} +//----------------------------------------------------------------------------- +void CGGraphicsPath::pixelAlign (const PixelAlignPointFunc& func, void* context) +{ struct PathIterator { + const PixelAlignPointFunc& pixelAlignFunc; + void* context; CGMutablePathRef path; - const CGDrawContext& context; - - explicit PathIterator (const CGDrawContext& context) - : context (context) + + explicit PathIterator (const PixelAlignPointFunc& inPixelAlignFunc, void* inContext) + : pixelAlignFunc (inPixelAlignFunc), context (inContext), path (CGPathCreateMutable ()) { - path = CGPathCreateMutable (); } void apply (const CGPathElement* element) { @@ -249,29 +211,35 @@ void QuartzGraphicsPath::pixelAlign (CDrawContext* context) { case kCGPathElementMoveToPoint: { - element->points[0] = context.pixelAlligned (element->points[0]); + element->points[0] = pixelAlignFunc (element->points[0], context); CGPathMoveToPoint (path, nullptr, element->points[0].x, element->points[0].y); break; } case kCGPathElementAddLineToPoint: { - element->points[0] = context.pixelAlligned (element->points[0]); - CGPathAddLineToPoint (path, nullptr, element->points[0].x, element->points[0].y); + element->points[0] = pixelAlignFunc (element->points[0], context); + CGPathAddLineToPoint (path, nullptr, element->points[0].x, + element->points[0].y); break; } case kCGPathElementAddQuadCurveToPoint: { - element->points[0] = context.pixelAlligned (element->points[0]); - element->points[1] = context.pixelAlligned (element->points[1]); - CGPathAddQuadCurveToPoint (path, nullptr, element->points[0].x, element->points[0].y, element->points[1].x, element->points[1].y); + element->points[0] = pixelAlignFunc (element->points[0], context); + element->points[1] = pixelAlignFunc (element->points[1], context); + CGPathAddQuadCurveToPoint (path, nullptr, element->points[0].x, + element->points[0].y, element->points[1].x, + element->points[1].y); break; } case kCGPathElementAddCurveToPoint: { - element->points[0] = context.pixelAlligned (element->points[0]); - element->points[1] = context.pixelAlligned (element->points[1]); - element->points[2] = context.pixelAlligned (element->points[2]); - CGPathAddCurveToPoint (path, nullptr, element->points[0].x, element->points[0].y, element->points[1].x, element->points[1].y, element->points[2].x, element->points[2].y); + element->points[0] = pixelAlignFunc (element->points[0], context); + element->points[1] = pixelAlignFunc (element->points[1], context); + element->points[2] = pixelAlignFunc (element->points[2], context); + CGPathAddCurveToPoint (path, nullptr, element->points[0].x, + element->points[0].y, element->points[1].x, + element->points[1].y, element->points[2].x, + element->points[2].y); break; } case kCGPathElementCloseSubpath: @@ -281,64 +249,62 @@ void QuartzGraphicsPath::pixelAlign (CDrawContext* context) } } } - + static void apply (void* info, const CGPathElement* element) { - PathIterator* This = static_cast(info); + PathIterator* This = static_cast (info); This->apply (element); } }; - PathIterator iterator (*cgDrawContext); - CGPathApply (getCGPathRef (), &iterator, PathIterator::apply); - dirty (); + PathIterator iterator (func, context); + CGPathApply (path, &iterator, PathIterator::apply); + CFRelease (path); path = iterator.path; - isPixelAlligned = true; } //----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -QuartzGradient::QuartzGradient (const ColorStopMap& map) -: CGradient (map) -, gradient (nullptr) +bool CGGraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, + CGraphicsTransform* transform) const { + auto cgPoint = CGPointFromCPoint (p); + CGAffineTransform cgTransform; + if (transform) + cgTransform = createCGAffineTransform (*transform); + return CGPathContainsPoint (path, transform ? &cgTransform : nullptr, cgPoint, evenOddFilled); } //----------------------------------------------------------------------------- -QuartzGradient::QuartzGradient (double _color1Start, double _color2Start, const CColor& _color1, const CColor& _color2) -: CGradient (_color1Start, _color2Start, _color1, _color2) -, gradient (nullptr) +CRect CGGraphicsPath::getBoundingBox () const { + auto cgRect = CGPathGetBoundingBox (path); + return CRectFromCGRect (cgRect); } //----------------------------------------------------------------------------- -QuartzGradient::~QuartzGradient () noexcept -{ - releaseCGGradient (); -} - //----------------------------------------------------------------------------- -void QuartzGradient::addColorStop (const std::pair& colorStop) +//----------------------------------------------------------------------------- +QuartzGradient::~QuartzGradient () noexcept { - CGradient::addColorStop (colorStop); releaseCGGradient (); } //----------------------------------------------------------------------------- -void QuartzGradient::addColorStop (std::pair&& colorStop) +void QuartzGradient::changed () { - CGradient::addColorStop (colorStop); releaseCGGradient (); } //----------------------------------------------------------------------------- void QuartzGradient::createCGGradient () const { - CGFloat* locations = new CGFloat [colorStops.size ()]; - CFMutableArrayRef colors = CFArrayCreateMutable (kCFAllocatorDefault, static_cast (colorStops.size ()), &kCFTypeArrayCallBacks); + assert (gradient == nullptr); + auto locations = new CGFloat[getColorStops ().size ()]; + auto colors = + CFArrayCreateMutable (kCFAllocatorDefault, static_cast (getColorStops ().size ()), + &kCFTypeArrayCallBacks); uint32_t index = 0; - for (const auto& it : colorStops) + for (const auto& it : getColorStops ()) { locations[index] = static_cast (it.first); CColor color = it.second; @@ -347,9 +313,9 @@ void QuartzGradient::createCGGradient () const } gradient = CGGradientCreateWithColors (GetCGColorSpace (), colors, locations); - + CFRelease (colors); - delete [] locations; + delete[] locations; } //----------------------------------------------------------------------------- @@ -372,13 +338,6 @@ QuartzGradient::operator CGGradientRef () const return gradient; } -//----------------------------------------------------------------------------- -CGradient* CGradient::create (const ColorStopMap& colorStopMap) -{ - return new QuartzGradient (colorStopMap); -} - } // VSTGUI - #endif diff --git a/vstgui/lib/platform/mac/quartzgraphicspath.h b/vstgui/lib/platform/mac/quartzgraphicspath.h index a6ebe8b0a..44df92db5 100644 --- a/vstgui/lib/platform/mac/quartzgraphicspath.h +++ b/vstgui/lib/platform/mac/quartzgraphicspath.h @@ -5,7 +5,8 @@ #pragma once #include "../../cgraphicspath.h" -#include "../../cgradient.h" +#include "../common/gradientbase.h" +#include "../iplatformgraphicspath.h" #if MAC @@ -18,54 +19,68 @@ #endif namespace VSTGUI { -class CoreTextFont; -class CDrawContext; -//------------------------------------------------------------------------------------ -class QuartzGraphicsPath : public CGraphicsPath +//----------------------------------------------------------------------------- +CGAffineTransform createCGAffineTransform (const CGraphicsTransform& t); + +//----------------------------------------------------------------------------- +class CGGraphicsPathFactory : public IPlatformGraphicsPathFactory { public: - QuartzGraphicsPath (); - QuartzGraphicsPath (const CoreTextFont* font, UTF8StringPtr text); - ~QuartzGraphicsPath () noexcept override; - - void pixelAlign (CDrawContext* context); - CGPathRef getCGPathRef (); - void dirty () override; + static PlatformGraphicsPathFactoryPtr instance (); - bool hitTest (const CPoint& p, bool evenOddFilled = false, CGraphicsTransform* transform = nullptr) override; - CPoint getCurrentPosition () override; - CRect getBoundingBox () override; - - CGradient* createGradient (double color1Start, double color2Start, const CColor& color1, const CColor& color2) override; - - static CGAffineTransform createCGAffineTransform (const CGraphicsTransform& t); + PlatformGraphicsPathPtr createPath (PlatformGraphicsPathFillMode fillMode) override; + PlatformGraphicsPathPtr createTextPath (const PlatformFontPtr& font, + UTF8StringPtr text) override; +}; -//------------------------------------------------------------------------------------ -protected: - CGMutablePathRef path; - CGMutablePathRef originalTextPath; - bool isPixelAlligned; +//----------------------------------------------------------------------------- +class CGGraphicsPath : public IPlatformGraphicsPath +{ +public: + CGGraphicsPath (CGMutablePathRef path = nullptr); // take ownership of path + ~CGGraphicsPath () noexcept; + + using PixelAlignPointFunc = CGPoint (*) (const CGPoint&, void* context); + void pixelAlign (const PixelAlignPointFunc& func, void* context); + + CGPathRef getCGPathRef () const { return path; } + + // IPlatformGraphicsPath + void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) override; + void addEllipse (const CRect& rect) override; + void addRect (const CRect& rect) override; + void addLine (const CPoint& to) override; + void addBezierCurve (const CPoint& control1, const CPoint& control2, + const CPoint& end) override; + void beginSubpath (const CPoint& start) override; + void closeSubpath () override; + void finishBuilding () override; + bool hitTest (const CPoint& p, bool evenOddFilled = false, + CGraphicsTransform* transform = nullptr) const override; + CRect getBoundingBox () const override; + PlatformGraphicsPathFillMode getFillMode () const override + { + return PlatformGraphicsPathFillMode::Ignored; + } + +private: + CGMutablePathRef path {nullptr}; }; //----------------------------------------------------------------------------- -class QuartzGradient : public CGradient +class QuartzGradient : public PlatformGradientBase { public: - explicit QuartzGradient (const ColorStopMap& map); - QuartzGradient (double _color1Start, double _color2Start, const CColor& _color1, const CColor& _color2); ~QuartzGradient () noexcept override; - operator CGGradientRef () const; - void addColorStop (const std::pair& colorStop) override; - void addColorStop (std::pair&& colorStop) override; - + void changed () override; protected: void createCGGradient () const; void releaseCGGradient (); - mutable CGGradientRef gradient; + mutable CGGradientRef gradient {nullptr}; }; } // VSTGUI diff --git a/vstgui/lib/platform/platformfactory.h b/vstgui/lib/platform/platformfactory.h index 0cd5ec6f5..45822cc08 100644 --- a/vstgui/lib/platform/platformfactory.h +++ b/vstgui/lib/platform/platformfactory.h @@ -138,6 +138,19 @@ class IPlatformFactory virtual COffscreenContextPtr createOffscreenContext (const CPoint& size, double scaleFactor = 1.) const noexcept = 0; + /** Create a platform gradient object + * @return platform gradient object or nullptr on failure + */ + virtual PlatformGradientPtr createGradient () const noexcept = 0; + + /** Create a platform file selector + * @param style file selector style + * @param frame frame + * @return platform file selector or nullptr on failure + */ + virtual PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, + IPlatformFrame* frame) const noexcept = 0; + virtual const LinuxFactory* asLinuxFactory () const noexcept = 0; virtual const MacFactory* asMacFactory () const noexcept = 0; virtual const Win32Factory* asWin32Factory () const noexcept = 0; diff --git a/vstgui/lib/platform/platformfwd.h b/vstgui/lib/platform/platformfwd.h index 5bf81076f..ad12a743a 100644 --- a/vstgui/lib/platform/platformfwd.h +++ b/vstgui/lib/platform/platformfwd.h @@ -4,6 +4,8 @@ #pragma once +#include "../vstguibase.h" + #include #include #include diff --git a/vstgui/lib/platform/win32/direct2d/d2dbitmap.cpp b/vstgui/lib/platform/win32/direct2d/d2dbitmap.cpp index be844ae07..ab4057aa9 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dbitmap.cpp +++ b/vstgui/lib/platform/win32/direct2d/d2dbitmap.cpp @@ -6,6 +6,7 @@ #if WINDOWS +#include "d2dbitmapcache.h" #include "../win32support.h" #include "../win32resourcestream.h" #include "../win32factory.h" @@ -51,9 +52,7 @@ D2DBitmap::~D2DBitmap () { if (source) { - D2DBitmapCache* gCache = D2DBitmapCache::instance (); - vstgui_assert (gCache, "D2D resources are already freed"); - gCache->removeBitmap (this); + D2DBitmapCache::removeBitmap (this); if (source) source->Release (); } @@ -254,7 +253,7 @@ void D2DBitmap::replaceBitmapSource (IWICBitmapSource* newSourceBitmap) { if (source) { - D2DBitmapCache::instance ()->removeBitmap (this); + D2DBitmapCache::removeBitmap (this); source->Release (); } source = newSourceBitmap; @@ -293,7 +292,7 @@ D2DBitmap::PixelAccess::~PixelAccess () } if (bitmap) { - D2DBitmapCache::instance ()->removeBitmap (bitmap); + D2DBitmapCache::removeBitmap (bitmap); bitmap->forget (); } } @@ -364,107 +363,6 @@ void D2DBitmap::PixelAccess::unpremultiplyAlpha (BYTE* ptr, UINT bytesPerRow, co //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -ID2D1Bitmap* D2DBitmapCache::getBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget) -{ - BitmapCache::iterator it = cache.find (bitmap); - if (it != cache.end ()) - { - RenderTargetBitmapMap::iterator it2 = it->second.find (renderTarget); - if (it2 != it->second.end ()) - { - return it2->second; - } - ID2D1Bitmap* b = createBitmap (bitmap, renderTarget); - if (b) - it->second.emplace (renderTarget, b); - return b; - } - auto insertSuccess = cache.emplace (bitmap, RenderTargetBitmapMap ()); - if (insertSuccess.second == true) - { - ID2D1Bitmap* b = createBitmap (bitmap, renderTarget); - if (b) - { - insertSuccess.first->second.emplace (renderTarget, b); - return b; - } - } - return nullptr; -} - -//----------------------------------------------------------------------------- -void D2DBitmapCache::removeBitmap (D2DBitmap* bitmap) -{ - BitmapCache::iterator it = cache.find (bitmap); - if (it != cache.end ()) - { - RenderTargetBitmapMap::iterator it2 = it->second.begin (); - while (it2 != it->second.end ()) - { - it2->second->Release (); - it2++; - } - cache.erase (it); - } -} - -//----------------------------------------------------------------------------- -void D2DBitmapCache::removeRenderTarget (ID2D1RenderTarget* renderTarget) -{ - BitmapCache::iterator it = cache.begin (); - while (it != cache.end ()) - { - RenderTargetBitmapMap::iterator it2 = it->second.begin (); - while (it2 != it->second.end ()) - { - if (it2->first == renderTarget) - { - it2->second->Release (); - it->second.erase (it2++); - } - else - it2++; - } - it++; - } -} - -//----------------------------------------------------------------------------- -ID2D1Bitmap* D2DBitmapCache::createBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget) -{ - if (bitmap->getSource () == nullptr) - return nullptr; - ID2D1Bitmap* d2d1Bitmap = nullptr; - renderTarget->CreateBitmapFromWicBitmap (bitmap->getSource (), &d2d1Bitmap); - return d2d1Bitmap; -} - -static D2DBitmapCache* gD2DBitmapCache = nullptr; -//----------------------------------------------------------------------------- -D2DBitmapCache::D2DBitmapCache () -{ - gD2DBitmapCache = this; -} - -//----------------------------------------------------------------------------- -D2DBitmapCache::~D2DBitmapCache () -{ -#if DEBUG - for (BitmapCache::const_iterator it = cache.begin (); it != cache.end (); it++) - { - vstgui_assert (it->second.empty ()); - } -#endif - gD2DBitmapCache = nullptr; -} - -//----------------------------------------------------------------------------- -D2DBitmapCache* D2DBitmapCache::instance () -{ - static D2DBitmapCache gInstance; - return gD2DBitmapCache; -} } // VSTGUI diff --git a/vstgui/lib/platform/win32/direct2d/d2dbitmap.h b/vstgui/lib/platform/win32/direct2d/d2dbitmap.h index 4b430c310..28f426c67 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dbitmap.h +++ b/vstgui/lib/platform/win32/direct2d/d2dbitmap.h @@ -72,26 +72,6 @@ class D2DBitmap final : public Win32BitmapBase IWICBitmapSource* source; }; -//----------------------------------------------------------------------------- -class D2DBitmapCache -{ -public: - ID2D1Bitmap* getBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget); - - void removeBitmap (D2DBitmap* bitmap); - void removeRenderTarget (ID2D1RenderTarget* renderTarget); - - static D2DBitmapCache* instance (); -//----------------------------------------------------------------------------- -protected: - D2DBitmapCache (); - ~D2DBitmapCache (); - ID2D1Bitmap* createBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget); - using RenderTargetBitmapMap = std::map; - using BitmapCache = std::map; - BitmapCache cache; -}; - } // VSTGUI #endif // WINDOWS diff --git a/vstgui/lib/platform/win32/direct2d/d2dbitmapcache.cpp b/vstgui/lib/platform/win32/direct2d/d2dbitmapcache.cpp new file mode 100644 index 000000000..5c1021ec7 --- /dev/null +++ b/vstgui/lib/platform/win32/direct2d/d2dbitmapcache.cpp @@ -0,0 +1,186 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "d2dbitmapcache.h" + +#if WINDOWS + +#include "../comptr.h" + +#include +#include +#include + +namespace VSTGUI { +namespace D2DBitmapCache { +namespace { + +//----------------------------------------------------------------------------- +struct Cache +{ + Cache () = default; + ~Cache () = default; + + ID2D1Bitmap* getBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget, + ID2D1Device* device); + void removeBitmap (D2DBitmap* bitmap); + void removeRenderTarget (ID2D1RenderTarget* renderTarget); + void removeDevice (ID2D1Device* device); + +private: + COM::Ptr createBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget) const; + + struct ResourceLocation + { + ID2D1Device* device {nullptr}; + ID2D1RenderTarget* renderTarget {nullptr}; + + bool operator== (const ResourceLocation& o) const + { + return o.device == device && o.renderTarget == renderTarget; + } + }; + + using ResourceLocationList = std::vector>>; + using BitmapMap = std::map; + BitmapMap bitmaps; +}; + +//----------------------------------------------------------------------------- +ID2D1Bitmap* Cache::getBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget, + ID2D1Device* device) +{ + ResourceLocation resLoc {device, device ? nullptr : renderTarget}; + + auto bitmapIt = bitmaps.find (bitmap); + if (bitmapIt != bitmaps.end ()) + { + + auto resLocIt = std::find_if (bitmapIt->second.begin (), bitmapIt->second.end (), + [&] (const auto& loc) { return loc.first == resLoc; }); + if (resLocIt != bitmapIt->second.end ()) + { + return resLocIt->second.get (); + } + auto b = createBitmap (bitmap, renderTarget); + if (b) + bitmapIt->second.emplace_back (std::make_pair (resLoc, b)); + return b.get (); + } + auto insertSuccess = bitmaps.emplace (bitmap, ResourceLocationList ()); + if (insertSuccess.second == true) + { + auto b = createBitmap (bitmap, renderTarget); + if (b) + { + insertSuccess.first->second.emplace_back (std::make_pair (resLoc, b)); + return b.get (); + } + } + return nullptr; +} + +//----------------------------------------------------------------------------- +void Cache::removeBitmap (D2DBitmap* bitmap) +{ + auto bitmapIt = bitmaps.find (bitmap); + if (bitmapIt != bitmaps.end ()) + bitmaps.erase (bitmapIt); +} + +//----------------------------------------------------------------------------- +void Cache::removeRenderTarget (ID2D1RenderTarget* renderTarget) +{ + auto bitmapIt = bitmaps.begin (); + while (bitmapIt != bitmaps.end ()) + { + auto end = + std::remove_if (bitmapIt->second.begin (), bitmapIt->second.end (), + [&] (const auto& el) { return el.first.renderTarget == renderTarget; }); + + bitmapIt->second.erase (end, bitmapIt->second.end ()); + + bitmapIt++; + } +} + +//----------------------------------------------------------------------------- +void Cache::removeDevice (ID2D1Device* device) +{ + auto bitmapIt = bitmaps.begin (); + while (bitmapIt != bitmaps.end ()) + { + auto end = std::remove_if (bitmapIt->second.begin (), bitmapIt->second.end (), + [&] (const auto& el) { return el.first.device == device; }); + + bitmapIt->second.erase (end, bitmapIt->second.end ()); + + bitmapIt++; + } +} + +//----------------------------------------------------------------------------- +COM::Ptr Cache::createBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget) const +{ + if (bitmap->getSource () == nullptr) + return {}; + COM::Ptr d2d1Bitmap; + renderTarget->CreateBitmapFromWicBitmap (bitmap->getSource (), d2d1Bitmap.adoptPtr ()); + return d2d1Bitmap; +} + +std::unique_ptr gCache; + +//----------------------------------------------------------------------------- +} // anonymous + +//----------------------------------------------------------------------------- +void init () +{ + gCache = std::make_unique (); +} + +//----------------------------------------------------------------------------- +void terminate () +{ + gCache.reset (); +} + +//----------------------------------------------------------------------------- +ID2D1Bitmap* getBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget, ID2D1Device* device) +{ + if (!gCache) + return nullptr; + return gCache->getBitmap (bitmap, renderTarget, device); +} + +//----------------------------------------------------------------------------- +void removeBitmap (D2DBitmap* bitmap) +{ + if (!gCache) + return; + gCache->removeBitmap (bitmap); +} + +//----------------------------------------------------------------------------- +void removeRenderTarget (ID2D1RenderTarget* renderTarget) +{ + if (!gCache) + return; + gCache->removeRenderTarget (renderTarget); +} + +//----------------------------------------------------------------------------- +void removeDevice (ID2D1Device* device) +{ + if (!gCache) + return; + gCache->removeDevice (device); +} + +//----------------------------------------------------------------------------- +} // D2DBitmapCache +} // VSTGUI + +#endif // WINDOWS diff --git a/vstgui/lib/platform/win32/direct2d/d2dbitmapcache.h b/vstgui/lib/platform/win32/direct2d/d2dbitmapcache.h new file mode 100644 index 000000000..a08450c23 --- /dev/null +++ b/vstgui/lib/platform/win32/direct2d/d2dbitmapcache.h @@ -0,0 +1,29 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "d2dbitmap.h" + +#if WINDOWS + +struct ID2D1Device; + +namespace VSTGUI { +namespace D2DBitmapCache { + +void init (); +void terminate (); + +ID2D1Bitmap* getBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget, + ID2D1Device* device = nullptr); + +void removeBitmap (D2DBitmap* bitmap); +void removeRenderTarget (ID2D1RenderTarget* renderTarget); +void removeDevice (ID2D1Device* device); + +} // D2DBitmapCache +} // namespace VSTGUI + +#endif // WINDOWS diff --git a/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp b/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp index a7c7021b8..bb3f593b6 100644 --- a/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp +++ b/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp @@ -10,19 +10,14 @@ #include "../win32factory.h" #include "../../../cgradient.h" #include "d2dbitmap.h" +#include "d2dbitmapcache.h" #include "d2dgraphicspath.h" #include "d2dfont.h" +#include "d2dgradient.h" #include namespace VSTGUI { -//----------------------------------------------------------------------------- -inline D2D1::ColorF toColorF (CColor c, float alpha) -{ - return D2D1::ColorF (c.normRed (), c.normGreen (), c.normBlue (), - c.normAlpha () * alpha); -} - //----------------------------------------------------------------------------- D2DDrawContext::D2DApplyClip::D2DApplyClip (D2DDrawContext* drawContext, bool halfPointOffset) : drawContext (drawContext) @@ -114,6 +109,23 @@ D2DDrawContext::D2DDrawContext (D2DBitmap* inBitmap) bitmap->forget (); } +//----------------------------------------------------------------------------- +D2DDrawContext::D2DDrawContext (ID2D1DeviceContext* deviceContext, const CRect& drawSurface, + ID2D1Device* device) +: COffscreenContext (drawSurface) +, window (nullptr) +, device (device) +, renderTarget (nullptr) +, fillBrush (nullptr) +, strokeBrush (nullptr) +, fontBrush (nullptr) +, strokeStyle (nullptr) +{ + renderTarget = deviceContext; + renderTarget->AddRef (); + init (); +} + //----------------------------------------------------------------------------- D2DDrawContext::~D2DDrawContext () { @@ -183,7 +195,8 @@ void D2DDrawContext::releaseRenderTarget () } if (renderTarget) { - D2DBitmapCache::instance ()->removeRenderTarget (renderTarget); + if (!device) + D2DBitmapCache::removeRenderTarget (renderTarget); renderTarget->Release (); renderTarget = nullptr; } @@ -226,7 +239,7 @@ void D2DDrawContext::endDraw () if (bitmap) { D2DBitmap* d2dBitmap = dynamic_cast (bitmap->getPlatformBitmap ().get ()); - D2DBitmapCache::instance ()->removeBitmap (d2dBitmap); + D2DBitmapCache::removeBitmap (d2dBitmap); } } } @@ -240,30 +253,46 @@ void D2DDrawContext::init () //----------------------------------------------------------------------------- CGraphicsPath* D2DDrawContext::createGraphicsPath () { - return new D2DGraphicsPath (); + return new CGraphicsPath (D2DGraphicsPathFactory::instance ()); } //----------------------------------------------------------------------------- CGraphicsPath* D2DDrawContext::createTextPath (const CFontRef font, UTF8StringPtr text) { - auto ctFont = font->getPlatformFont ().cast (); - return ctFont ? new D2DGraphicsPath (ctFont, text) : nullptr; + auto factory = D2DGraphicsPathFactory::instance (); + if (auto path = factory->createTextPath (font->getPlatformFont (), text)) + { + return new CGraphicsPath (D2DGraphicsPathFactory::instance (), std::move (path)); + } + return nullptr; } //----------------------------------------------------------------------------- -void D2DDrawContext::drawGraphicsPath (CGraphicsPath* _path, PathDrawMode mode, CGraphicsTransform* t) +void D2DDrawContext::drawGraphicsPath (CGraphicsPath* graphicsPath, PathDrawMode mode, + CGraphicsTransform* t) { - if (renderTarget == nullptr) + if (renderTarget == nullptr || graphicsPath == nullptr) return; D2DApplyClip ac (this); if (ac.isEmpty ()) return; - auto* d2dPath = dynamic_cast (_path); + auto d2dPath = dynamic_cast ( + graphicsPath + ->getPlatformPath (mode == kPathFilledEvenOdd ? PlatformGraphicsPathFillMode::Alternate + : PlatformGraphicsPathFillMode::Winding) + .get ()); if (d2dPath == nullptr) return; - ID2D1Geometry* path = d2dPath->createPath (mode == kPathFilledEvenOdd ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING, nullptr, t); + ID2D1Geometry* path = nullptr; + if (t) + path = d2dPath->createTransformedGeometry (getD2DFactory (), *t); + else + { + path = d2dPath->getPathGeometry (); + path->AddRef (); + } if (path) { if (mode == kPathFilled || mode == kPathFilledEvenOdd) @@ -275,39 +304,43 @@ void D2DDrawContext::drawGraphicsPath (CGraphicsPath* _path, PathDrawMode mode, } //----------------------------------------------------------------------------- -ID2D1GradientStopCollection* D2DDrawContext::createGradientStopCollection (const CGradient& d2dGradient) const +ID2D1GradientStopCollection* D2DDrawContext::createGradientStopCollection (const CGradient& gradient) const { - ID2D1GradientStopCollection* collection = nullptr; - auto* gradientStops = new D2D1_GRADIENT_STOP [d2dGradient.getColorStops ().size ()]; - uint32_t index = 0; - for (CGradient::ColorStopMap::const_iterator it = d2dGradient.getColorStops ().begin (); it != d2dGradient.getColorStops ().end (); ++it, ++index) - { - gradientStops[index].position = (FLOAT)it->first; - gradientStops[index].color = toColorF (it->second, getCurrentState ().globalAlpha); - } - getRenderTarget ()->CreateGradientStopCollection (gradientStops, static_cast (d2dGradient.getColorStops ().size ()), &collection); - delete [] gradientStops; - return collection; + if (auto d2dGradient = dynamic_cast (gradient.getPlatformGradient ().get ())) + return d2dGradient->create (getRenderTarget (), getCurrentState ().globalAlpha); + return nullptr; } //----------------------------------------------------------------------------- -void D2DDrawContext::fillLinearGradient (CGraphicsPath* _path, const CGradient& gradient, const CPoint& startPoint, const CPoint& endPoint, bool evenOdd, CGraphicsTransform* t) +void D2DDrawContext::fillLinearGradient (CGraphicsPath* graphicsPath, const CGradient& gradient, + const CPoint& startPoint, const CPoint& endPoint, + bool evenOdd, CGraphicsTransform* t) { - if (renderTarget == nullptr) + if (renderTarget == nullptr || graphicsPath == nullptr) return; D2DApplyClip ac (this, true); if (ac.isEmpty ()) return; - - auto* d2dPath = dynamic_cast (_path); + + auto d2dPath = dynamic_cast ( + graphicsPath + ->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate + : PlatformGraphicsPathFillMode::Winding) + .get ()); if (d2dPath == nullptr) return; + ID2D1Geometry* path = nullptr; + if (t) + path = d2dPath->createTransformedGeometry (getD2DFactory (), *t); + else + { + path = d2dPath->getPathGeometry (); + path->AddRef (); + } - ID2D1Geometry* path = d2dPath->createPath (evenOdd ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING, nullptr, t); if (path) { - ID2D1GradientStopCollection* collection = createGradientStopCollection (gradient); if (collection) { @@ -327,55 +360,54 @@ void D2DDrawContext::fillLinearGradient (CGraphicsPath* _path, const CGradient& } //----------------------------------------------------------------------------- -void D2DDrawContext::fillRadialGradient (CGraphicsPath* _path, const CGradient& gradient, const CPoint& center, CCoord radius, const CPoint& originOffset, bool evenOdd, CGraphicsTransform* t) +void D2DDrawContext::fillRadialGradient (CGraphicsPath* graphicsPath, const CGradient& gradient, + const CPoint& center, CCoord radius, + const CPoint& originOffset, bool evenOdd, + CGraphicsTransform* t) { - if (renderTarget == nullptr) + if (renderTarget == nullptr || graphicsPath == nullptr) return; D2DApplyClip ac (this, true); if (ac.isEmpty ()) return; - - auto* d2dPath = dynamic_cast (_path); + + auto d2dPath = dynamic_cast ( + graphicsPath + ->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate + : PlatformGraphicsPathFillMode::Winding) + .get ()); if (d2dPath == nullptr) return; - ID2D1Geometry* path = d2dPath->createPath (evenOdd ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING); + ID2D1Geometry* path = nullptr; + if (t) + path = d2dPath->createTransformedGeometry (getD2DFactory (), *t); + else + { + path = d2dPath->getPathGeometry (); + path->AddRef (); + } + if (path) { - ID2D1Geometry* geometry = nullptr; - if (t) - { - ID2D1TransformedGeometry* tg = nullptr; - getD2DFactory ()->CreateTransformedGeometry (path, convert (*t), &tg); - geometry = tg; - } - else + if (ID2D1GradientStopCollection* collection = createGradientStopCollection (gradient)) { - geometry = path; - geometry->AddRef (); - } - if (geometry) - { - if (ID2D1GradientStopCollection* collection = createGradientStopCollection (gradient)) + // brush properties + ID2D1RadialGradientBrush* brush = nullptr; + D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES properties; + properties.center = makeD2DPoint (center); + properties.gradientOriginOffset = makeD2DPoint (originOffset); + properties.radiusX = (FLOAT)radius; + properties.radiusY = (FLOAT)radius; + + if (SUCCEEDED (getRenderTarget ()->CreateRadialGradientBrush (properties, + collection, &brush))) { - // brush properties - ID2D1RadialGradientBrush* brush = nullptr; - D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES properties; - properties.center = makeD2DPoint (center); - properties.gradientOriginOffset = makeD2DPoint (originOffset); - properties.radiusX = (FLOAT)radius; - properties.radiusY = (FLOAT)radius; - - if (SUCCEEDED (getRenderTarget ()->CreateRadialGradientBrush (properties, - collection, &brush))) - { - getRenderTarget ()->FillGeometry (geometry, brush); - brush->Release (); - } - collection->Release (); + getRenderTarget ()->FillGeometry (path, brush); + brush->Release (); } - geometry->Release (); + collection->Release (); } path->Release (); } @@ -410,44 +442,40 @@ void D2DDrawContext::drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoin transformedScaleFactor *= t.m11; IPlatformBitmap* platformBitmap = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor); D2DBitmap* d2dBitmap = platformBitmap ? dynamic_cast (platformBitmap) : nullptr; - if (d2dBitmap) + if (d2dBitmap && d2dBitmap->getSource ()) { - if (d2dBitmap->getSource ()) + if (auto d2d1Bitmap = D2DBitmapCache::getBitmap (d2dBitmap, renderTarget, device)) { - ID2D1Bitmap* d2d1Bitmap = D2DBitmapCache::instance ()->getBitmap (d2dBitmap, renderTarget); - if (d2d1Bitmap) + double bitmapScaleFactor = platformBitmap->getScaleFactor (); + CGraphicsTransform bitmapTransform; + bitmapTransform.scale (1./bitmapScaleFactor, 1./bitmapScaleFactor); + Transform transform (*this, bitmapTransform); + + CRect d (dest); + d.setWidth (bitmap->getWidth ()); + d.setHeight (bitmap->getHeight ()); + d.offset (-offset.x, -offset.y); + d.makeIntegral (); + CRect source; + source.setWidth (d2d1Bitmap->GetSize ().width); + source.setHeight (d2d1Bitmap->GetSize ().height); + + D2D1_BITMAP_INTERPOLATION_MODE mode; + switch (getCurrentState ().bitmapQuality) { - double bitmapScaleFactor = platformBitmap->getScaleFactor (); - CGraphicsTransform bitmapTransform; - bitmapTransform.scale (1./bitmapScaleFactor, 1./bitmapScaleFactor); - Transform transform (*this, bitmapTransform); - - CRect d (dest); - d.setWidth (bitmap->getWidth ()); - d.setHeight (bitmap->getHeight ()); - d.offset (-offset.x, -offset.y); - d.makeIntegral (); - CRect source; - source.setWidth (d2d1Bitmap->GetSize ().width); - source.setHeight (d2d1Bitmap->GetSize ().height); - - D2D1_BITMAP_INTERPOLATION_MODE mode; - switch (getCurrentState ().bitmapQuality) - { - case BitmapInterpolationQuality::kLow: - mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; - break; - - case BitmapInterpolationQuality::kMedium: - case BitmapInterpolationQuality::kHigh: - default: - mode = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; - break; - } - - D2D1_RECT_F sourceRect = makeD2DRect (source); - renderTarget->DrawBitmap (d2d1Bitmap, makeD2DRect (d), alpha * getCurrentState ().globalAlpha, mode, &sourceRect); + case BitmapInterpolationQuality::kLow: + mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + break; + + case BitmapInterpolationQuality::kMedium: + case BitmapInterpolationQuality::kHigh: + default: + mode = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; + break; } + + D2D1_RECT_F sourceRect = makeD2DRect (source); + renderTarget->DrawBitmap (d2d1Bitmap, makeD2DRect (d), alpha * getCurrentState ().globalAlpha, mode, &sourceRect); } } } @@ -526,19 +554,19 @@ void D2DDrawContext::drawPolygon (const PointList& polygonPointList, const CDraw if (ac.isEmpty ()) return; - D2DGraphicsPath path; - path.beginSubpath (polygonPointList[0]); + auto path = owned (createGraphicsPath ()); + path->beginSubpath (polygonPointList[0]); for (uint32_t i = 1; i < polygonPointList.size (); ++i) { - path.addLine (polygonPointList[i]); + path->addLine (polygonPointList[i]); } if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked) { - drawGraphicsPath (&path, kPathFilled); + drawGraphicsPath (path, kPathFilled); } if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked) { - drawGraphicsPath (&path, kPathStroked); + drawGraphicsPath (path, kPathStroked); } } diff --git a/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.h b/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.h index c36ebe9c5..acbd5b0b4 100644 --- a/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.h +++ b/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.h @@ -8,10 +8,11 @@ #if WINDOWS struct IUnknown; +struct ID2D1DeviceContext; #include "d2dbitmap.h" #include -#include +#include #include namespace VSTGUI { @@ -22,6 +23,8 @@ class D2DDrawContext final : public COffscreenContext { public: D2DDrawContext (HWND window, const CRect& drawSurface); + D2DDrawContext (ID2D1DeviceContext* deviceContext, const CRect& drawSurface, + ID2D1Device* device = nullptr); D2DDrawContext (D2DBitmap* bitmap); ~D2DDrawContext (); @@ -97,6 +100,7 @@ class D2DDrawContext final : public COffscreenContext bool needsHalfPointOffset () const; HWND window; + ID2D1Device* device {nullptr}; ID2D1RenderTarget* renderTarget; ID2D1SolidColorBrush* fillBrush; ID2D1SolidColorBrush* strokeBrush; @@ -149,6 +153,14 @@ static inline D2D1_MATRIX_3X2_F convert (const CGraphicsTransform& t) return matrix; } +//----------------------------------------------------------------------------- +static inline D2D1::ColorF toColorF (CColor c, float alpha) +{ + return D2D1::ColorF (c.normRed (), c.normGreen (), c.normBlue (), + c.normAlpha () * alpha); +} + + } // VSTGUI #endif // WINDOWS diff --git a/vstgui/lib/platform/win32/direct2d/d2dfont.cpp b/vstgui/lib/platform/win32/direct2d/d2dfont.cpp index e8954ce43..cd815ae5d 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dfont.cpp +++ b/vstgui/lib/platform/win32/direct2d/d2dfont.cpp @@ -12,6 +12,7 @@ #include "../winstring.h" #include "../comptr.h" #include "d2ddrawcontext.h" +#include #include #include @@ -28,37 +29,25 @@ #endif namespace VSTGUI { - +namespace D2DFontPrivate { //----------------------------------------------------------------------------- struct CustomFonts { #if VSTGUI_WIN32_CUSTOMFONT_SUPPORT - static IDWriteFontCollection1* getFontCollection () - { - return instance ().fontCollection.get (); - } - static bool contains (const WCHAR* name, DWRITE_FONT_WEIGHT fontWeight, - DWRITE_FONT_STRETCH fontStretch, DWRITE_FONT_STYLE fontStyle) + IDWriteFontCollection1* getFontCollection () { return fontCollection.get (); } + bool contains (const WCHAR* name, DWRITE_FONT_WEIGHT fontWeight, + DWRITE_FONT_STRETCH fontStretch, DWRITE_FONT_STYLE fontStyle) { - if (auto fontSet = instance ().fontSet.get ()) + if (auto fs = fontSet.get ()) { COM::Ptr matchingFonts; - if (SUCCEEDED (fontSet->GetMatchingFonts (name, fontWeight, fontStretch, fontStyle, - matchingFonts.adoptPtr ()))) + if (SUCCEEDED (fs->GetMatchingFonts (name, fontWeight, fontStretch, fontStyle, + matchingFonts.adoptPtr ()))) return matchingFonts->GetFontCount () > 0; } return false; } -private: - COM::Ptr fontSet; - COM::Ptr fontCollection; - - static CustomFonts& instance () - { - static CustomFonts gInstance; - return gInstance; - } CustomFonts () { auto winFactory = getPlatformFactory ().asWin32Factory (); @@ -91,6 +80,10 @@ struct CustomFonts factory5->CreateFontCollectionFromFontSet (fontSet.get (), fontCollection.adoptPtr ()); } +private: + COM::Ptr fontSet; + COM::Ptr fontCollection; + std::vector getDirectoryContents (const UTF8String& path) const { std::vector result; @@ -112,51 +105,52 @@ struct CustomFonts return result; } #else - static IDWriteFontCollection* getFontCollection () { return nullptr; } - static bool contains (const WCHAR*, DWRITE_FONT_WEIGHT, DWRITE_FONT_STRETCH, DWRITE_FONT_STYLE) + IDWriteFontCollection* getFontCollection () { return nullptr; } + bool contains (const WCHAR*, DWRITE_FONT_WEIGHT, DWRITE_FONT_STRETCH, DWRITE_FONT_STYLE) { return false; } #endif }; +//----------------------------------------------------------------------------- +static std::unique_ptr customFonts; +static std::once_flag customFontsOnceFlag; + +//----------------------------------------------------------------------------- +static CustomFonts* getCustomFonts () +{ + std::call_once (customFontsOnceFlag, [] () { + customFonts = std::make_unique (); + }); + return customFonts.get (); +} + //----------------------------------------------------------------------------- static void gatherFonts (const FontFamilyCallback& callback, IDWriteFontCollection* collection) { UINT32 numFonts = collection->GetFontFamilyCount (); for (UINT32 i = 0; i < numFonts; ++i) { - IDWriteFontFamily* fontFamily = nullptr; - if (!SUCCEEDED (collection->GetFontFamily (i, &fontFamily))) + COM::Ptr fontFamily; + if (!SUCCEEDED (collection->GetFontFamily (i, fontFamily.adoptPtr ()))) continue; - IDWriteLocalizedStrings* names = nullptr; - if (!SUCCEEDED (fontFamily->GetFamilyNames (&names))) + COM::Ptr names; + if (!SUCCEEDED (fontFamily->GetFamilyNames (names.adoptPtr ()))) continue; UINT32 nameLength = 0; if (!SUCCEEDED (names->GetStringLength (0, &nameLength)) || nameLength < 1) continue; nameLength++; - WCHAR* name = new WCHAR[nameLength]; - if (SUCCEEDED (names->GetString (0, name, nameLength))) + auto name = std::unique_ptr (new WCHAR[nameLength]); + if (SUCCEEDED (names->GetString (0, name.get (), nameLength))) { - UTF8StringHelper str (name); + UTF8StringHelper str (name.get ()); callback (str.getUTF8String ()); } - delete [] name; } } -//----------------------------------------------------------------------------- -bool D2DFont::getAllFontFamilies (const FontFamilyCallback& callback) -{ - IDWriteFontCollection* collection = nullptr; - if (SUCCEEDED (getDWriteFactory ()->GetSystemFontCollection (&collection, true))) - gatherFonts (callback, collection); - if (auto customFontCollection = CustomFonts::getFontCollection ()) - gatherFonts (callback, customFontCollection); - return true; -} - //----------------------------------------------------------------------------- static COM::Ptr getFont (IDWriteTextFormat* format, int32_t style) { @@ -185,6 +179,29 @@ static COM::Ptr getFont (IDWriteTextFormat* format, int32_t style) return {}; } +//----------------------------------------------------------------------------- +} // D2DFontPrivate + +//----------------------------------------------------------------------------- +bool D2DFont::getAllFontFamilies (const FontFamilyCallback& callback) +{ + IDWriteFontCollection* collection = nullptr; + if (SUCCEEDED (getDWriteFactory ()->GetSystemFontCollection (&collection, true))) + D2DFontPrivate::gatherFonts (callback, collection); + if (D2DFontPrivate::customFonts) + { + if (auto customFontCollection = D2DFontPrivate::customFonts->getFontCollection ()) + D2DFontPrivate::gatherFonts (callback, customFontCollection); + } + return true; +} + +//----------------------------------------------------------------------------- +void D2DFont::terminate () +{ + D2DFontPrivate::customFonts = nullptr; +} + //----------------------------------------------------------------------------- D2DFont::D2DFont (const UTF8String& name, const CCoord& size, const int32_t& style) : textFormat (nullptr) @@ -199,16 +216,17 @@ D2DFont::D2DFont (const UTF8String& name, const CCoord& size, const int32_t& sty UTF8StringHelper nameStr (name.data ()); IDWriteFontCollection* fontCollection = nullptr; - if (CustomFonts::contains (nameStr.getWideString (), fontWeight, DWRITE_FONT_STRETCH_NORMAL, - fontStyle)) - fontCollection = CustomFonts::getFontCollection (); + auto customFonts = D2DFontPrivate::getCustomFonts (); + if (customFonts && customFonts->contains (nameStr.getWideString (), fontWeight, + DWRITE_FONT_STRETCH_NORMAL, fontStyle)) + fontCollection = customFonts->getFontCollection (); getDWriteFactory ()->CreateTextFormat (nameStr, fontCollection, fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL, (FLOAT)size, L"en-us", &textFormat); if (textFormat) { - if (auto font = getFont (textFormat, style)) + if (auto font = D2DFontPrivate::getFont (textFormat, style)) { DWRITE_FONT_METRICS fontMetrics; font->GetMetrics (&fontMetrics); @@ -235,7 +253,7 @@ bool D2DFont::asLogFont (LOGFONTW& logfont) const COM::Ptr interOp; if (FAILED (getDWriteFactory ()->GetGdiInterop (interOp.adoptPtr ()))) return false; - if (auto font = getFont (textFormat, style)) + if (auto font = D2DFontPrivate::getFont (textFormat, style)) { BOOL isSystemFont; if (SUCCEEDED (interOp->ConvertFontToLOGFONT (font.get (), &logfont, &isSystemFont))) @@ -283,12 +301,19 @@ void D2DFont::drawString (CDrawContext* context, IPlatformString* string, const textLayout->SetStrikethrough (true, range); } renderTarget->SetTextAntialiasMode (antialias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED); + CPoint pos (p); - pos.y -= textFormat->GetFontSize (); + DWRITE_LINE_METRICS lm; + UINT lineCount; + if (SUCCEEDED (textLayout->GetLineMetrics (&lm, 1, &lineCount))) + pos.y -= lm.baseline; + else + pos.y -= textFormat->GetFontSize (); + if (context->getDrawMode ().integralMode ()) pos.makeIntegral (); pos.y += 0.5; - + D2D1_POINT_2F origin = {(FLOAT)(p.x), (FLOAT)(pos.y)}; d2dContext->getRenderTarget ()->DrawTextLayout (origin, textLayout, d2dContext->getFontBrush ()); textLayout->Release (); diff --git a/vstgui/lib/platform/win32/direct2d/d2dfont.h b/vstgui/lib/platform/win32/direct2d/d2dfont.h index af9e8f091..7b641efbd 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dfont.h +++ b/vstgui/lib/platform/win32/direct2d/d2dfont.h @@ -27,6 +27,9 @@ class D2DFont final : public IPlatformFont, public IFontPainter bool asLogFont (LOGFONTW& logfont) const; static bool getAllFontFamilies (const FontFamilyCallback& callback); + + static void terminate (); + protected: ~D2DFont (); diff --git a/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp b/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp new file mode 100644 index 000000000..8a21afad7 --- /dev/null +++ b/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp @@ -0,0 +1,36 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "d2dgradient.h" + +#if WINDOWS + +#include "d2ddrawcontext.h" +#include + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +ID2D1GradientStopCollection* D2DGradient::create (ID2D1RenderTarget* renderTarget, + float globalAlpha) const +{ + std::vector gradientStops; + gradientStops.resize (getColorStops ().size ()); + uint32_t index = 0; + for (auto it = getColorStops ().begin (); it != getColorStops ().end (); ++it, ++index) + { + gradientStops[index].position = static_cast (it->first); + gradientStops[index].color = toColorF (it->second, globalAlpha); + } + ID2D1GradientStopCollection* collection = nullptr; + auto hr = renderTarget->CreateGradientStopCollection ( + gradientStops.data (), static_cast (getColorStops ().size ()), &collection); + if (SUCCEEDED (hr)) + return collection; + return nullptr; +} + +} // VSTGUI + +#endif // WINDOWS diff --git a/vstgui/lib/platform/mac/carbon/hiviewoptionmenu.h b/vstgui/lib/platform/win32/direct2d/d2dgradient.h similarity index 54% rename from vstgui/lib/platform/mac/carbon/hiviewoptionmenu.h rename to vstgui/lib/platform/win32/direct2d/d2dgradient.h index 54ec9a3dc..fc6939849 100644 --- a/vstgui/lib/platform/mac/carbon/hiviewoptionmenu.h +++ b/vstgui/lib/platform/win32/direct2d/d2dgradient.h @@ -1,27 +1,24 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #pragma once -#include "../../iplatformoptionmenu.h" +#include "../../common/gradientbase.h" -#if MAC_CARBON +#if WINDOWS -#include +#include namespace VSTGUI { //----------------------------------------------------------------------------- -class HIViewOptionMenu : public IPlatformOptionMenu +class D2DGradient : public PlatformGradientBase { public: - void popup (COptionMenu* optionMenu, const Callback& callback) override; - -protected: - MenuRef createMenu (COptionMenu* menu); + ID2D1GradientStopCollection* create (ID2D1RenderTarget* renderTarget, float globalAlpha) const; }; } // VSTGUI -#endif // MAC_CARBON +#endif // WINDOWS diff --git a/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp b/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp index e88bec6a1..5349668a6 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp +++ b/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -6,15 +6,16 @@ #if WINDOWS -#include "../win32support.h" -#include "../../../cstring.h" #include "../../../cgradient.h" +#include "../../../cstring.h" +#include "../win32support.h" #include "d2ddrawcontext.h" #include "d2dfont.h" #include #include +#include -#if defined (__GNUC__) && !defined (__clang__) +#if defined(__GNUC__) && !defined(__clang__) #define __maybenull #define __out #endif @@ -24,116 +25,79 @@ namespace VSTGUI { class D2DPathTextRenderer final : public IDWriteTextRenderer { public: - D2DPathTextRenderer (ID2D1GeometrySink* sink) : sink (sink) - { - } + D2DPathTextRenderer (ID2D1GeometrySink* sink) : sink (sink) {} - STDMETHOD (DrawGlyphRun) ( - void* clientDrawingContext, - FLOAT baselineOriginX, - FLOAT baselineOriginY, - DWRITE_MEASURING_MODE measuringMode, - DWRITE_GLYPH_RUN const* glyphRun, - DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, - IUnknown* clientDrawingEffect - ) override + STDMETHOD (DrawGlyphRun) + (void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const* glyphRun, + DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, + IUnknown* clientDrawingEffect) override { return glyphRun->fontFace->GetGlyphRunOutline ( - glyphRun->fontEmSize, - glyphRun->glyphIndices, - glyphRun->glyphAdvances, - glyphRun->glyphOffsets, - glyphRun->glyphCount, - glyphRun->isSideways, - glyphRun->bidiLevel%2, - sink - ); - } - - STDMETHOD (DrawUnderline) ( - void* clientDrawingContext, - FLOAT baselineOriginX, - FLOAT baselineOriginY, - DWRITE_UNDERLINE const* underline, - IUnknown* clientDrawingEffect - ) override + glyphRun->fontEmSize, glyphRun->glyphIndices, glyphRun->glyphAdvances, + glyphRun->glyphOffsets, glyphRun->glyphCount, glyphRun->isSideways, + glyphRun->bidiLevel % 2, sink); + } + + STDMETHOD (DrawUnderline) + (void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, + DWRITE_UNDERLINE const* underline, IUnknown* clientDrawingEffect) override { return S_FALSE; } - STDMETHOD (DrawStrikethrough) ( - void* clientDrawingContext, - FLOAT baselineOriginX, - FLOAT baselineOriginY, - DWRITE_STRIKETHROUGH const* strikethrough, - IUnknown* clientDrawingEffect - ) override + STDMETHOD (DrawStrikethrough) + (void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, + DWRITE_STRIKETHROUGH const* strikethrough, IUnknown* clientDrawingEffect) override { return S_FALSE; } - STDMETHOD (DrawInlineObject) ( - void* clientDrawingContext, - FLOAT originX, - FLOAT originY, - IDWriteInlineObject* inlineObject, - BOOL isSideways, - BOOL isRightToLeft, - IUnknown* clientDrawingEffect - ) override + STDMETHOD (DrawInlineObject) + (void* clientDrawingContext, FLOAT originX, FLOAT originY, IDWriteInlineObject* inlineObject, + BOOL isSideways, BOOL isRightToLeft, IUnknown* clientDrawingEffect) override { return S_FALSE; } - STDMETHOD (IsPixelSnappingDisabled) ( - __maybenull void* clientDrawingContext, - __out BOOL* isDisabled - ) override + STDMETHOD (IsPixelSnappingDisabled) + (__maybenull void* clientDrawingContext, __out BOOL* isDisabled) override { if (isDisabled) *isDisabled = FALSE; return S_FALSE; } - STDMETHOD (GetCurrentTransform) ( - __maybenull void* clientDrawingContext, - __out DWRITE_MATRIX* transform - ) override + STDMETHOD (GetCurrentTransform) + (__maybenull void* clientDrawingContext, __out DWRITE_MATRIX* transform) override { - const DWRITE_MATRIX identityTransform = - { - 1, 0, - 0, 1, - 0, 0 - }; + const DWRITE_MATRIX identityTransform = {1, 0, 0, 1, 0, 0}; if (transform) *transform = identityTransform; return S_OK; } - STDMETHOD (GetPixelsPerDip) ( - __maybenull void* clientDrawingContext, - __out FLOAT* pixelsPerDip - ) override + STDMETHOD (GetPixelsPerDip) + (__maybenull void* clientDrawingContext, __out FLOAT* pixelsPerDip) override { if (pixelsPerDip) *pixelsPerDip = 96; return S_OK; } - STDMETHOD (QueryInterface) (REFIID iid, void ** ppvObject) override + STDMETHOD (QueryInterface) (REFIID iid, void** ppvObject) override { - if (iid == __uuidof(IUnknown) - || iid == __uuidof(IDWriteTextRenderer)) + if (iid == __uuidof(IUnknown) || iid == __uuidof(IDWriteTextRenderer)) { - *ppvObject = static_cast(this); - AddRef(); + *ppvObject = static_cast (this); + AddRef (); return S_OK; - } else + } + else return E_NOINTERFACE; } - STDMETHOD_ (ULONG, AddRef) () override { return 1; } - STDMETHOD_ (ULONG, Release) () override { return 1; } + STDMETHOD_ (ULONG, AddRef) () override { return 1; } + STDMETHOD_ (ULONG, Release) () override { return 1; } private: ID2D1GeometrySink* sink; @@ -143,19 +107,19 @@ class D2DPathTextRenderer final : public IDWriteTextRenderer class AlignPixelSink final : public ID2D1SimplifiedGeometrySink { public: - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject) override + HRESULT STDMETHODCALLTYPE QueryInterface (REFIID iid, void** ppvObject) override { - if (iid == __uuidof(IUnknown) - || iid == __uuidof(ID2D1SimplifiedGeometrySink)) + if (iid == __uuidof(IUnknown) || iid == __uuidof(ID2D1SimplifiedGeometrySink)) { - *ppvObject = static_cast(this); - AddRef(); + *ppvObject = static_cast (this); + AddRef (); return S_OK; - } else + } + else return E_NOINTERFACE; } - ULONG STDMETHODCALLTYPE AddRef() override { return 1; } - ULONG STDMETHODCALLTYPE Release() override { return 1; } + ULONG STDMETHODCALLTYPE AddRef () override { return 1; } + ULONG STDMETHODCALLTYPE Release () override { return 1; } D2D1_POINT_2F alignPoint (const D2D1_POINT_2F& p) { @@ -166,8 +130,8 @@ class AlignPixelSink final : public ID2D1SimplifiedGeometrySink context->pixelAllign (point); return D2D1::Point2F (static_cast (point.x), static_cast (point.y)); } - - STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT * beziers, UINT beziersCount) override + + STDMETHOD_ (void, AddBeziers) (const D2D1_BEZIER_SEGMENT* beziers, UINT beziersCount) override { for (UINT i = 0; i < beziersCount; ++i) { @@ -179,7 +143,7 @@ class AlignPixelSink final : public ID2D1SimplifiedGeometrySink } } - STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointsCount) override + STDMETHOD_ (void, AddLines) (const D2D1_POINT_2F* points, UINT pointsCount) override { for (UINT i = 0; i < pointsCount; ++i) { @@ -188,39 +152,36 @@ class AlignPixelSink final : public ID2D1SimplifiedGeometrySink } } - STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, - D2D1_FIGURE_BEGIN figureBegin) override + STDMETHOD_ (void, BeginFigure) + (D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) override { startPoint = alignPoint (startPoint); sink->BeginFigure (startPoint, figureBegin); } - STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd) override + STDMETHOD_ (void, EndFigure) (D2D1_FIGURE_END figureEnd) override { sink->EndFigure (figureEnd); } - STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) override + STDMETHOD_ (void, SetFillMode) (D2D1_FILL_MODE fillMode) override { sink->SetFillMode (fillMode); } - STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) override + STDMETHOD_ (void, SetSegmentFlags) (D2D1_PATH_SEGMENT vertexFlags) override { sink->SetSegmentFlags (vertexFlags); } - STDMETHOD(Close)() override + STDMETHOD (Close) () override { isClosed = true; return sink->Close (); } AlignPixelSink (D2DDrawContext* context) - : path (nullptr) - , sink (nullptr) - , context (context) - , isClosed (true) + : path (nullptr), sink (nullptr), context (context), isClosed (true) { } @@ -231,7 +192,7 @@ class AlignPixelSink final : public ID2D1SimplifiedGeometrySink if (path) path->Release (); } - + bool init () { getD2DFactory ()->CreatePathGeometry (&path); @@ -260,6 +221,7 @@ class AlignPixelSink final : public ID2D1SimplifiedGeometrySink } return nullptr; } + private: ID2D1PathGeometry* path; ID2D1GeometrySink* sink; @@ -268,377 +230,382 @@ class AlignPixelSink final : public ID2D1SimplifiedGeometrySink }; //----------------------------------------------------------------------------- -D2DGraphicsPath::D2DGraphicsPath () -: path (nullptr) -, currentPathFillMode (-1) +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +D2DGraphicsPath::D2DGraphicsPath (ID2D1PathGeometry* path, PlatformGraphicsPathFillMode fillMode) +: path (path), fillMode (fillMode) { } //----------------------------------------------------------------------------- -D2DGraphicsPath::D2DGraphicsPath (const D2DFont* font, UTF8StringPtr text) -: path (nullptr) +D2DGraphicsPath::~D2DGraphicsPath () noexcept { - ID2D1PathGeometry* localPath = nullptr; - getD2DFactory ()->CreatePathGeometry (&localPath); - if (localPath == nullptr) - return; + if (sinkInternal) + sinkInternal->Release (); + path->Release (); +} - IDWriteTextLayout* layout = font->createTextLayout (UTF8String (text).getPlatformString ()); - if (layout == nullptr) - return; - - ID2D1PathGeometry* textPath = nullptr; - getD2DFactory ()->CreatePathGeometry (&textPath); - if (textPath == nullptr) - { - layout->Release (); - return; - } - - ID2D1GeometrySink* sink = nullptr; - if (!SUCCEEDED (textPath->Open (&sink))) +//----------------------------------------------------------------------------- +ID2D1Geometry* D2DGraphicsPath::createTransformedGeometry (ID2D1Factory* factory, + const CGraphicsTransform& tm) const +{ + ID2D1TransformedGeometry* tg = nullptr; + if (!SUCCEEDED (factory->CreateTransformedGeometry (path, convert (tm), &tg))) + return nullptr; + return tg; + ; +} + +//----------------------------------------------------------------------------- +ID2D1Geometry* D2DGraphicsPath::createPixelAlignedGeometry (ID2D1Factory* factory, + D2DDrawContext& context, + const CGraphicsTransform* tm) const +{ + ID2D1Geometry* workingPath = path; + workingPath->AddRef (); + if (tm) + workingPath = createTransformedGeometry (factory, *tm); + + AlignPixelSink alignSink (&context); + if (alignSink.init () == false) { - textPath->Release (); - layout->Release (); - return; + workingPath->Release (); + return nullptr; } - - D2DPathTextRenderer renderer (sink); - layout->Draw (nullptr, &renderer, 0, 0); - - sink->Close (); - sink->Release (); - layout->Release (); - - if (!SUCCEEDED (localPath->Open (&sink))) + if (!SUCCEEDED (workingPath->Simplify (D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, + nullptr, &alignSink))) { - textPath->Release (); - return; + workingPath->Release (); + return nullptr; } - - D2D1_RECT_F bounds = {}; - if (SUCCEEDED (textPath->GetBounds (nullptr, &bounds))) + workingPath->Release (); + + return alignSink.get (); +} + +//----------------------------------------------------------------------------- +ID2D1GeometrySink* D2DGraphicsPath::getSink () +{ + if (!sinkInternal) { - textPath->Simplify (D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, D2D1::Matrix3x2F::Translation (0, -bounds.top), sink); + if (FAILED (path->Open (&sinkInternal))) + { + sinkInternal = nullptr; + } + else + { + D2D1_FILL_MODE mode = fillMode == PlatformGraphicsPathFillMode::Alternate + ? D2D1_FILL_MODE_ALTERNATE + : D2D1_FILL_MODE_WINDING; + sinkInternal->SetFillMode (mode); + } } - - textPath->Release (); - sink->Close (); - sink->Release (); - path = localPath; + return sinkInternal; } //----------------------------------------------------------------------------- -D2DGraphicsPath::~D2DGraphicsPath () +void D2DGraphicsPath::addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) { - if (path) - { - path->Release (); - path = nullptr; + if (auto sink = getSink ()) + { + startAngle = radians (startAngle); + endAngle = radians (endAngle); + CRect o_r = rect; + CRect r (o_r); + o_r.originize (); + CPoint center = o_r.getCenter (); + if (center.x != center.y) + { + startAngle = atan2 (sin (startAngle) * center.x, cos (startAngle) * center.y); + endAngle = atan2 (sin (endAngle) * center.x, cos (endAngle) * center.y); + } + CPoint start; + start.x = r.left + center.x + center.x * cos (startAngle); + start.y = r.top + center.y + center.y * sin (startAngle); + if (!figureOpen) + { + sink->BeginFigure (makeD2DPoint (start), D2D1_FIGURE_BEGIN_FILLED); + figureOpen = true; + } + else if (lastPos != start) + { + sink->AddLine (makeD2DPoint (start)); + } + + double sweepangle = endAngle - startAngle; + if (clockwise) + { + // sweepangle positive + while (sweepangle < 0.0) + sweepangle += 2 * M_PI; + while (sweepangle > 2 * M_PI) + sweepangle -= 2 * M_PI; + } + else + { + // sweepangle negative + while (sweepangle > 0.0) + sweepangle -= 2 * M_PI; + while (sweepangle < -2 * M_PI) + sweepangle += 2 * M_PI; + } + + CPoint endPoint; + endPoint.x = r.left + center.x + center.x * cos (endAngle); + endPoint.y = r.top + center.y + center.y * sin (endAngle); + + D2D1_ARC_SEGMENT arc; + arc.size = makeD2DSize (r.getWidth () / 2., r.getHeight () / 2.); + arc.rotationAngle = 0; + arc.sweepDirection = + clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE; + arc.point = makeD2DPoint (endPoint); + arc.arcSize = fabs (sweepangle) <= M_PI ? D2D1_ARC_SIZE_SMALL : D2D1_ARC_SIZE_LARGE; + sink->AddArc (arc); + lastPos = endPoint; } } -// CGraphicsPath //----------------------------------------------------------------------------- -CGradient* D2DGraphicsPath::createGradient (double color1Start, double color2Start, const CColor& color1, const CColor& color2) +void D2DGraphicsPath::addEllipse (const CRect& rect) { - return CGradient::create (color1Start, color2Start, color1, color2); + if (auto sink = getSink ()) + { + CPoint top (rect.getTopLeft ()); + top.x += rect.getWidth () / 2.; + CPoint bottom (rect.getBottomLeft ()); + bottom.x += rect.getWidth () / 2.; + if (figureOpen && lastPos != rect.getTopLeft ()) + { + sink->EndFigure (D2D1_FIGURE_END_OPEN); + figureOpen = false; + } + if (!figureOpen) + { + sink->BeginFigure (makeD2DPoint (top), D2D1_FIGURE_BEGIN_FILLED); + figureOpen = true; + } + D2D1_ARC_SEGMENT arc = + D2D1::ArcSegment (makeD2DPoint (bottom), + D2D1::SizeF (static_cast (rect.getWidth () / 2.f), + static_cast (rect.getHeight () / 2.f)), + 180.f, D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_ARC_SIZE_SMALL); + sink->AddArc (arc); + arc.point = makeD2DPoint (top); + sink->AddArc (arc); + lastPos = top; + } } //----------------------------------------------------------------------------- -CPoint D2DGraphicsPath::getCurrentPosition () +void D2DGraphicsPath::addRect (const CRect& rect) { - // TODO: D2DGraphicsPath::getCurrentPosition - CPoint p; -#if DEBUG - DebugPrint ("D2DGraphicsPath::getCurrentPosition not implemented\n"); -#endif - return p; + if (auto sink = getSink ()) + { + D2D1_POINT_2F points[4] = {{(FLOAT)rect.right, (FLOAT)rect.top}, + {(FLOAT)rect.right, (FLOAT)rect.bottom}, + {(FLOAT)rect.left, (FLOAT)rect.bottom}, + {(FLOAT)rect.left, (FLOAT)rect.top}}; + if (figureOpen && lastPos != rect.getTopLeft ()) + { + sink->EndFigure (D2D1_FIGURE_END_OPEN); + figureOpen = false; + } + if (figureOpen == false) + { + sink->BeginFigure (points[3], D2D1_FIGURE_BEGIN_FILLED); + figureOpen = true; + } + sink->AddLine (points[0]); + sink->AddLine (points[1]); + sink->AddLine (points[2]); + sink->AddLine (points[3]); + lastPos = rect.getTopLeft (); + } } //----------------------------------------------------------------------------- -bool D2DGraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, CGraphicsTransform* transform) +void D2DGraphicsPath::addLine (const CPoint& to) { - ID2D1Geometry* _path = createPath (evenOddFilled ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING); - if (_path) + if (auto sink = getSink ()) { - D2D1::Matrix3x2F matrix = D2D1::Matrix3x2F::Identity (); - if (transform) + if (figureOpen) { - matrix._11 = (FLOAT)transform->m11; - matrix._12 = (FLOAT)transform->m12; - matrix._21 = (FLOAT)transform->m21; - matrix._22 = (FLOAT)transform->m22; - matrix._31 = (FLOAT)transform->dx; - matrix._32 = (FLOAT)transform->dy; - + D2D1_POINT_2F end = {(FLOAT)to.x, (FLOAT)to.y}; + sink->AddLine (end); + lastPos = to; } - BOOL result = false; - _path->FillContainsPoint (makeD2DPoint (p), matrix, &result); - _path->Release (); - return result ? true : false; } - return false; } //----------------------------------------------------------------------------- -CRect D2DGraphicsPath::getBoundingBox () +void D2DGraphicsPath::addBezierCurve (const CPoint& control1, const CPoint& control2, + const CPoint& end) { - CRect r; - ID2D1Geometry* _path = createPath (currentPathFillMode); - if (_path) + if (auto sink = getSink ()) { - D2D1_RECT_F bounds; - if (SUCCEEDED (_path->GetBounds (nullptr, &bounds))) + if (figureOpen) { - r.left = bounds.left; - r.top = bounds.top; - r.right = bounds.right; - r.bottom = bounds.bottom; + D2D1_POINT_2F d2dcontrol1 = {(FLOAT)control1.x, (FLOAT)control1.y}; + D2D1_POINT_2F d2dcontrol2 = {(FLOAT)control2.x, (FLOAT)control2.y}; + D2D1_POINT_2F d2dend = {(FLOAT)end.x, (FLOAT)end.y}; + D2D1_BEZIER_SEGMENT bezier = D2D1::BezierSegment (d2dcontrol1, d2dcontrol2, d2dend); + sink->AddBezier (bezier); + lastPos = end; } - _path->Release (); } - return r; } //----------------------------------------------------------------------------- -void D2DGraphicsPath::dirty () +void D2DGraphicsPath::beginSubpath (const CPoint& start) { - if (path) + if (auto sink = getSink ()) { - path->Release (); - path = nullptr; + if (figureOpen) + sink->EndFigure (D2D1_FIGURE_END_OPEN); + D2D1_POINT_2F d2dstart = {(FLOAT)start.x, (FLOAT)start.y}; + sink->BeginFigure (d2dstart, D2D1_FIGURE_BEGIN_FILLED); + figureOpen = true; + lastPos = start; } } //----------------------------------------------------------------------------- -ID2D1Geometry* D2DGraphicsPath::createPath (int32_t fillMode, D2DDrawContext* context, CGraphicsTransform* transform) +void D2DGraphicsPath::closeSubpath () { - if (!elements.empty () && (path == nullptr || fillMode != currentPathFillMode)) - { - dirty (); - ID2D1PathGeometry* localPath = nullptr; - if (!SUCCEEDED (getD2DFactory ()->CreatePathGeometry (&localPath))) - return nullptr; - if (fillMode == -1) - fillMode = 0; - currentPathFillMode = fillMode; - - ID2D1GeometrySink* sink = nullptr; - if (!SUCCEEDED (localPath->Open (&sink))) + if (auto sink = getSink ()) + { + if (figureOpen) { - localPath->Release (); - return nullptr; + sink->EndFigure (D2D1_FIGURE_END_CLOSED); + figureOpen = false; } + } +} - path = localPath; - - sink->SetFillMode ((D2D1_FILL_MODE)fillMode); - - bool figureOpen = false; - CPoint lastPos; - for (const CGraphicsPath::Element& e : elements) - { - switch (e.type) - { - case Element::kArc: - { - bool clockwise = e.instruction.arc.clockwise; - double startAngle = radians (e.instruction.arc.startAngle); - double endAngle = radians (e.instruction.arc.endAngle); - CRect o_r (e.instruction.arc.rect.left, e.instruction.arc.rect.top, e.instruction.arc.rect.right, e.instruction.arc.rect.bottom); - CRect r (o_r); - o_r.originize (); - CPoint center = o_r.getCenter (); - if (center.x != center.y) - { - startAngle = atan2 (sin (startAngle) * center.x, cos (startAngle) * center.y); - endAngle = atan2 (sin (endAngle) * center.x, cos (endAngle) * center.y); - } - CPoint start; - start.x = r.left + center.x + center.x * cos (startAngle); - start.y = r.top + center.y + center.y * sin (startAngle); - if (!figureOpen) - { - sink->BeginFigure (makeD2DPoint (start), D2D1_FIGURE_BEGIN_FILLED); - figureOpen = true; - } - else if (lastPos != start) - { - sink->AddLine (makeD2DPoint (start)); - } - - double sweepangle = endAngle - startAngle; - if (clockwise) { - // sweepangle positive - while (sweepangle < 0.0) - sweepangle += 2 * M_PI; - while (sweepangle > 2 * M_PI) - sweepangle -= 2 * M_PI; - } else { - // sweepangle negative - while (sweepangle > 0.0) - sweepangle -= 2 * M_PI; - while (sweepangle < -2 * M_PI) - sweepangle += 2 * M_PI; - } - - CPoint endPoint; - endPoint.x = r.left + center.x + center.x * cos (endAngle); - endPoint.y = r.top + center.y + center.y * sin (endAngle); - - D2D1_ARC_SEGMENT arc; - arc.size = makeD2DSize (r.getWidth ()/2., r.getHeight ()/2.); - arc.rotationAngle = 0; - arc.sweepDirection = clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE; - arc.point = makeD2DPoint (endPoint); - arc.arcSize = fabs(sweepangle) <= M_PI ? D2D1_ARC_SIZE_SMALL : D2D1_ARC_SIZE_LARGE; - sink->AddArc (arc); - lastPos = endPoint; - break; - } - case Element::kEllipse: - { - CRect r (e.instruction.rect.left, e.instruction.rect.top, e.instruction.rect.right, e.instruction.rect.bottom); - CPoint top (r.getTopLeft ()); - top.x += r.getWidth () / 2.; - CPoint bottom (r.getBottomLeft ()); - bottom.x += r.getWidth () / 2.; - if (figureOpen && lastPos != CPoint (e.instruction.rect.left, e.instruction.rect.top)) - { - sink->EndFigure (D2D1_FIGURE_END_OPEN); - figureOpen = false; - } - if (!figureOpen) - { - sink->BeginFigure (makeD2DPoint (top), D2D1_FIGURE_BEGIN_FILLED); - figureOpen = true; - } - D2D1_ARC_SEGMENT arc = D2D1::ArcSegment (makeD2DPoint (bottom), D2D1::SizeF ((FLOAT)r.getWidth ()/2.f, (FLOAT)r.getHeight ()/2.f), 180.f, D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_ARC_SIZE_SMALL); - sink->AddArc (arc); - arc.point = makeD2DPoint (top); - sink->AddArc (arc); - lastPos = top; - break; - } - case Element::kRect: - { - CRect r (e.instruction.rect.left, e.instruction.rect.top, e.instruction.rect.right, e.instruction.rect.bottom); - D2D1_POINT_2F points[4] = { - {(FLOAT)r.right, (FLOAT)r.top}, - {(FLOAT)r.right, (FLOAT)r.bottom}, - {(FLOAT)r.left, (FLOAT)r.bottom}, - {(FLOAT)r.left, (FLOAT)r.top} - }; - if (figureOpen && lastPos != CPoint (r.left, r.top)) - { - sink->EndFigure (D2D1_FIGURE_END_OPEN); - figureOpen = false; - } - if (figureOpen == false) - { - sink->BeginFigure (points[3], D2D1_FIGURE_BEGIN_FILLED); - figureOpen = true; - } - sink->AddLine (points[0]); - sink->AddLine (points[1]); - sink->AddLine (points[2]); - sink->AddLine (points[3]); - lastPos = CPoint (r.left, r.top); - break; - } - case Element::kLine: - { - if (figureOpen) - { - CPoint p (e.instruction.point.x, e.instruction.point.y); - D2D1_POINT_2F end = {(FLOAT)p.x, (FLOAT)p.y}; - sink->AddLine (end); - lastPos = p; - } - break; - } - case Element::kBezierCurve: - { - if (figureOpen) - { - D2D1_POINT_2F control1 = {(FLOAT)e.instruction.curve.control1.x, (FLOAT)e.instruction.curve.control1.y}; - D2D1_POINT_2F control2 = {(FLOAT)e.instruction.curve.control2.x, (FLOAT)e.instruction.curve.control2.y}; - D2D1_POINT_2F end = {(FLOAT)e.instruction.curve.end.x, (FLOAT)e.instruction.curve.end.y}; - D2D1_BEZIER_SEGMENT bezier = D2D1::BezierSegment (control1, control2, end); - sink->AddBezier (bezier); - lastPos = CPoint (e.instruction.curve.end.x, e.instruction.curve.end.y); - } - break; - } - case Element::kBeginSubpath: - { - if (figureOpen) - sink->EndFigure (D2D1_FIGURE_END_OPEN); - CPoint p (e.instruction.point.x, e.instruction.point.y); - D2D1_POINT_2F start = {(FLOAT)p.x, (FLOAT)p.y}; - sink->BeginFigure (start, D2D1_FIGURE_BEGIN_FILLED); - figureOpen = true; - lastPos = p; - break; - } - case Element::kCloseSubpath: - { - if (figureOpen) - { - sink->EndFigure (D2D1_FIGURE_END_CLOSED); - figureOpen = false; - } - break; - } - } - } +//----------------------------------------------------------------------------- +void D2DGraphicsPath::finishBuilding () +{ + if (auto sink = getSink ()) + { if (figureOpen) sink->EndFigure (D2D1_FIGURE_END_OPEN); HRESULT res = sink->Close (); - if (!SUCCEEDED (res)) - { - path->Release (); - path = nullptr; - } + assert (SUCCEEDED (res)); sink->Release (); + sinkInternal = nullptr; + figureOpen = false; } - if (path && (transform || context)) +} + +//----------------------------------------------------------------------------- +bool D2DGraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, + CGraphicsTransform* transform) const +{ + D2D1::Matrix3x2F matrix = D2D1::Matrix3x2F::Identity (); + if (transform) + { + matrix._11 = (FLOAT)transform->m11; + matrix._12 = (FLOAT)transform->m12; + matrix._21 = (FLOAT)transform->m21; + matrix._22 = (FLOAT)transform->m22; + matrix._31 = (FLOAT)transform->dx; + matrix._32 = (FLOAT)transform->dy; + } + BOOL result = false; + path->FillContainsPoint (makeD2DPoint (p), matrix, &result); + return result ? true : false; +} + +//----------------------------------------------------------------------------- +CRect D2DGraphicsPath::getBoundingBox () const +{ + CRect r; + D2D1_RECT_F bounds; + if (SUCCEEDED (path->GetBounds (nullptr, &bounds))) { - ID2D1Geometry* geometry = path; - if (transform) - { - ID2D1TransformedGeometry* tg = nullptr; - if (!SUCCEEDED (getD2DFactory ()->CreateTransformedGeometry (geometry, convert (*transform), &tg))) - return nullptr; - geometry = tg; - } - if (context) - { - AlignPixelSink sink (context); - if (sink.init () == false) - { - if (transform) - geometry->Release (); - return nullptr; - } - if (!SUCCEEDED (geometry->Simplify (D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, nullptr, &sink))) - { - if (transform) - geometry->Release (); - return nullptr; - } - - ID2D1PathGeometry* result = sink.get (); - if (transform) - geometry->Release (); - return result; - } - return geometry; + r.left = bounds.left; + r.top = bounds.top; + r.right = bounds.right; + r.bottom = bounds.bottom; + } + return r; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +PlatformGraphicsPathFactoryPtr D2DGraphicsPathFactory::instance () +{ + static PlatformGraphicsPathFactoryPtr factory = std::make_shared (); + return factory; +} + +//----------------------------------------------------------------------------- +PlatformGraphicsPathPtr D2DGraphicsPathFactory::createPath (PlatformGraphicsPathFillMode fillMode) +{ + ID2D1PathGeometry* path {nullptr}; + if (FAILED (getD2DFactory ()->CreatePathGeometry (&path))) + return nullptr; + return std::make_unique (path, fillMode); +} + +//----------------------------------------------------------------------------- +PlatformGraphicsPathPtr D2DGraphicsPathFactory::createTextPath (const PlatformFontPtr& font, + UTF8StringPtr text) +{ + auto d2dFont = font.cast (); + if (!d2dFont) + return nullptr; + ID2D1PathGeometry* localPath = nullptr; + getD2DFactory ()->CreatePathGeometry (&localPath); + if (localPath == nullptr) + return nullptr; + + IDWriteTextLayout* layout = d2dFont->createTextLayout (UTF8String (text).getPlatformString ()); + if (layout == nullptr) + return nullptr; + + ID2D1PathGeometry* textPath = nullptr; + getD2DFactory ()->CreatePathGeometry (&textPath); + if (textPath == nullptr) + { + layout->Release (); + return nullptr; } - if (path) - path->AddRef (); - return path; + + ID2D1GeometrySink* sink = nullptr; + if (!SUCCEEDED (textPath->Open (&sink))) + { + textPath->Release (); + layout->Release (); + return nullptr; + } + + D2DPathTextRenderer renderer (sink); + layout->Draw (nullptr, &renderer, 0, 0); + + sink->Close (); + sink->Release (); + layout->Release (); + + if (!SUCCEEDED (localPath->Open (&sink))) + { + textPath->Release (); + return nullptr; + } + + D2D1_RECT_F bounds = {}; + if (SUCCEEDED (textPath->GetBounds (nullptr, &bounds))) + { + textPath->Simplify (D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, + D2D1::Matrix3x2F::Translation (0, -bounds.top), sink); + } + + textPath->Release (); + sink->Close (); + sink->Release (); + return std::make_unique (localPath, PlatformGraphicsPathFillMode::Ignored); } } // VSTGUI diff --git a/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.h b/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.h index c77d6d5e3..fb67ccd52 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.h +++ b/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.h @@ -1,10 +1,11 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #pragma once #include "../../../cgraphicspath.h" +#include "../../iplatformgraphicspath.h" #if WINDOWS @@ -12,6 +13,8 @@ struct ID2D1PathGeometry; struct ID2D1Geometry; +struct ID2D1GeometrySink; +struct ID2D1Factory; struct D2D1_GRADIENT_STOP; namespace VSTGUI { @@ -19,24 +22,52 @@ class D2DFont; class D2DDrawContext; //----------------------------------------------------------------------------- -class D2DGraphicsPath final : public CGraphicsPath +class D2DGraphicsPathFactory : public IPlatformGraphicsPathFactory { public: - D2DGraphicsPath (); - D2DGraphicsPath (const D2DFont* font, UTF8StringPtr text); - ~D2DGraphicsPath (); - - ID2D1Geometry* createPath (int32_t fillMode, D2DDrawContext* context = nullptr, CGraphicsTransform* transform = nullptr); - - CGradient* createGradient (double color1Start, double color2Start, const CColor& color1, const CColor& color2) override; - - bool hitTest (const CPoint& p, bool evenOddFilled = false, CGraphicsTransform* transform = nullptr) override; - CPoint getCurrentPosition () override; - CRect getBoundingBox () override; - void dirty () override; -protected: - ID2D1Geometry* path; - int32_t currentPathFillMode; + static PlatformGraphicsPathFactoryPtr instance (); + + PlatformGraphicsPathPtr createPath (PlatformGraphicsPathFillMode fillMode) override; + PlatformGraphicsPathPtr createTextPath (const PlatformFontPtr& font, + UTF8StringPtr text) override; +}; + +//----------------------------------------------------------------------------- +class D2DGraphicsPath : public IPlatformGraphicsPath +{ +public: + D2DGraphicsPath (ID2D1PathGeometry* path, PlatformGraphicsPathFillMode fillMode); + ~D2DGraphicsPath () noexcept override; + + ID2D1PathGeometry* getPathGeometry () const { return path; } + ID2D1Geometry* createTransformedGeometry (ID2D1Factory* factory, + const CGraphicsTransform& tm) const; + ID2D1Geometry* createPixelAlignedGeometry (ID2D1Factory* factory, D2DDrawContext& context, + const CGraphicsTransform* tm = nullptr) const; + + // IPlatformGraphicsPath + void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) override; + void addEllipse (const CRect& rect) override; + void addRect (const CRect& rect) override; + void addLine (const CPoint& to) override; + void addBezierCurve (const CPoint& control1, const CPoint& control2, + const CPoint& end) override; + void beginSubpath (const CPoint& start) override; + void closeSubpath () override; + void finishBuilding () override; + bool hitTest (const CPoint& p, bool evenOddFilled = false, + CGraphicsTransform* transform = nullptr) const override; + CRect getBoundingBox () const override; + PlatformGraphicsPathFillMode getFillMode () const override { return fillMode; } + +private: + ID2D1GeometrySink* getSink (); + + ID2D1PathGeometry* path {nullptr}; + ID2D1GeometrySink* sinkInternal {nullptr}; + PlatformGraphicsPathFillMode fillMode {PlatformGraphicsPathFillMode::Winding}; + bool figureOpen {false}; + CPoint lastPos; }; } // VSTGUI diff --git a/vstgui/lib/platform/win32/win32directcomposition.cpp b/vstgui/lib/platform/win32/win32directcomposition.cpp new file mode 100644 index 000000000..75d47cb63 --- /dev/null +++ b/vstgui/lib/platform/win32/win32directcomposition.cpp @@ -0,0 +1,888 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "win32directcomposition.h" +#include "win32support.h" +#include "win32dll.h" +#include "wintimer.h" +#include "win32factory.h" +#include "direct2d/d2dbitmapcache.h" + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma comment(lib, "d3d11.lib") +#endif + +//----------------------------------------------------------------------------- +namespace VSTGUI { +namespace DirectComposition { +namespace { +struct RootVisual; +} + +//----------------------------------------------------------------------------- +struct Factory::Impl +{ + COM::Ptr d2dFactory; + COM::Ptr d3dDevice; + COM::Ptr d3dDeviceContext; + COM::Ptr dxgiDevice; + COM::Ptr d2dDevice; + COM::Ptr compositionDesktopDevice; + std::vector surfaces; + bool visualizeRedrawAreas {false}; + + bool init (IUnknown* _d2dFactory); + bool recreate (); + + bool commit (); + +private: + bool createObjects (); + bool createD3D11Device (); +}; + +//----------------------------------------------------------------------------- +namespace { + +//----------------------------------------------------------------------------- +struct DirectCompositionSupportDll : DllBase +{ + using DCompositionCreateDevice2Func = HRESULT (STDAPICALLTYPE*) (IUnknown*, REFIID, void**); + + static DirectCompositionSupportDll& instance () + { + static DirectCompositionSupportDll dll; + return dll; + } + + HRESULT createDevice2 (IUnknown* renderingDevice, REFIID iid, void** dcompositionDevice) const + { + if (!createDevice2Func) + return S_FALSE; + return createDevice2Func (renderingDevice, iid, dcompositionDevice); + } + +private: + DirectCompositionSupportDll () : DllBase ("dcomp.dll") + { + createDevice2Func = + getProcAddress ("DCompositionCreateDevice2"); + } + + DCompositionCreateDevice2Func createDevice2Func {nullptr}; +}; + +//----------------------------------------------------------------------------- +struct VisualSurfacePair +{ + COM::Ptr visual; + COM::Ptr surface; + UINT left {}; + UINT top {}; + UINT width {}; + UINT height {}; + + HRESULT update (RECT updateRect, const IVisual::DrawCallback& callback); + bool setOffset (uint32_t left, uint32_t top); + bool setSize (uint32_t width, uint32_t height); + bool setOpacity (float o); +}; +using VisualSurfacePairPtr = std::shared_ptr; + +//----------------------------------------------------------------------------- +struct SurfaceRedrawArea : IPlatformTimerCallback +{ + using DoneCallback = std::function; + + static constexpr uint32_t animationTime = 250; + static constexpr float animationTimeSeconds = animationTime / 1000.f; + static constexpr float startOpacity = 0.8f; + + static constexpr std::array colors = {{ + {1.f, 1.f, 0.f, 1.f}, + {1.f, 0.f, 0.f, 1.f}, + {1.f, 0.f, 1.f, 1.f}, + {0.f, 1.f, 1.f, 1.f}, + {0.f, 1.f, 0.f, 1.f}, + }}; + + SurfaceRedrawArea (IDCompositionDesktopDevice* compDevice, const VisualSurfacePairPtr& s, + CRect r, uint32_t depth, DoneCallback&& cb) + : surface (s), callback (std::move (cb)), rect (r) + { + surface->visual->SetOffsetX (static_cast (r.left)); + surface->visual->SetOffsetY (static_cast (r.top)); + auto tm = D2D1::Matrix3x2F::Scale (static_cast (r.getWidth ()), + static_cast (r.getHeight ())); + surface->visual->SetTransform (tm); + r.setSize ({1., 1.}); + surface->surface->Resize (static_cast (r.getWidth ()), + static_cast (r.getHeight ())); + r.originize (); + if (depth >= colors.size ()) + depth = static_cast (colors.size () - 1u); + + s->update (RECTfromRect (r), + [depth] (auto deviceContext, auto r, int32_t offsetX, int32_t offsetY) { + COM::Ptr brush; + D2D1_COLOR_F color = colors[depth]; //{1.f, 1.f, 0.f, 1.f}; + deviceContext->CreateSolidColorBrush (&color, nullptr, brush.adoptPtr ()); + D2D1_RECT_F rect; + rect.left = static_cast (r.left) + offsetX; + rect.top = static_cast (r.top) + offsetY; + rect.right = static_cast (r.right) + offsetX; + rect.bottom = static_cast (r.bottom) + offsetY; + deviceContext->FillRectangle (&rect, brush.get ()); + }); + compDevice->CreateAnimation (animation.adoptPtr ()); + + restart (); + } + + ~SurfaceRedrawArea () noexcept { fire (); } + + void restart () + { + COM::Ptr vis3; + auto hr = surface->visual->QueryInterface (__uuidof(IDCompositionVisual3), + reinterpret_cast (vis3.adoptPtr ())); + if (SUCCEEDED (hr) && vis3 && animation) + { + animation->Reset (); + hr = animation->AddCubic (0., startOpacity, -startOpacity / animationTimeSeconds, 0.f, + 0.f); + hr = animation->End (animationTimeSeconds, 0.0f); + vis3->SetOpacity (animation.get ()); + } + timer.stop (); + timer.start (animationTime); + } + + void fire () override + { + timer.stop (); + if (callback) + { + auto cb = std::move (callback); + callback = nullptr; + cb (this); + } + } + + IDCompositionVisual2* getVisual () const { return surface->visual.get (); } + const CRect& getRect () const { return rect; } + +private: + VisualSurfacePairPtr surface {}; + DoneCallback callback {}; + COM::Ptr animation {}; + WinTimer timer {this}; + CRect rect; +}; +using SurfaceRedrawAreaPtr = std::shared_ptr; + +struct RootVisual; +//----------------------------------------------------------------------------- +struct Visual : IVisual +{ + Visual () = default; + Visual (VisualSurfacePairPtr vsPair, const std::shared_ptr& parent); + ~Visual () noexcept; + + bool setPosition (uint32_t left, uint32_t top) override; + bool resize (uint32_t width, uint32_t height) override; + bool update (CRect updateRect, const DrawCallback& drawCallback) override; + bool setOpacity (float opacity) override; + bool commit () override; + bool setZIndex (uint32_t index) override; + + void addChild (Visual* child); + bool removeChild (Visual* child); + void onZIndexChanged (Visual* child); + + uint32_t getZIndex () const { return zIndex; } + Visual* getParent () const { return parent.get (); } + virtual bool removeFromParent (); + virtual RootVisual* getRootVisual (); + virtual void addRedrawArea (CRect r, uint32_t depth = 0); + virtual IDCompositionVisual* getTopVisual () const { return nullptr; } + + // protected: + using Children = std::vector; + + std::shared_ptr parent; + VisualSurfacePairPtr root; + Children children; + uint32_t zIndex {0}; +}; + +//----------------------------------------------------------------------------- +struct RootVisual : Visual +{ + using DestroyCallback = std::function; + + static std::shared_ptr create (HWND window, Factory::Impl* factory, + DestroyCallback&& destroy); + ~RootVisual () noexcept; + + bool setPosition (uint32_t left, uint32_t top) override; + bool update (CRect updateRect, const DrawCallback& drawCallback) override; + bool commit () override; + bool setZIndex (uint32_t zIndex) override { return false; } + + bool removeFromParent () final { return true; } + RootVisual* getRootVisual () final { return this; } + void onDriverFailure (); + + VisualSurfacePairPtr createVisualSurfacePair (uint32_t width, uint32_t height) const; + bool enableVisualizeRedrawAreas (bool state); + +private: + using RedrawAreaVector = std::vector; + + RootVisual (); + + bool create (); + + POINT getWindowSize () const; + void addRedrawArea (CRect r, uint32_t depth = 0) final; + IDCompositionVisual* getTopVisual () const final; + void recreateVisuals (Visual* visual); + + Factory::Impl* factory {nullptr}; + DestroyCallback destroyCallback; + HWND window {nullptr}; + COM::Ptr compositionTarget; + COM::Ptr compositionSurfaceFactory; + VisualSurfacePairPtr redrawAreaPlane; + RedrawAreaVector redrawAreas; + bool showUpdateRects {true}; +}; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +Visual::Visual (VisualSurfacePairPtr vsPair, const std::shared_ptr& inParent) +{ + root = vsPair; + parent = inParent; + parent->addChild (this); +} + +//----------------------------------------------------------------------------- +Visual::~Visual () noexcept +{ + vstgui_assert (children.empty ()); + vstgui_assert (parent == nullptr); +} + +//----------------------------------------------------------------------------- +void Visual::addChild (Visual* child) +{ + IDCompositionVisual* referenceVis = getTopVisual (); + if (!children.empty ()) + { + auto it = std::find_if ( + children.begin (), children.end (), + [zIndex = child->getZIndex ()] (const auto& c) { return c->getZIndex () >= zIndex; }); + if (it != children.end ()) + referenceVis = (*it)->root->visual.get (); + } + root->visual->AddVisual (child->root->visual.get (), FALSE, referenceVis); + children.push_back (child); +} + +//----------------------------------------------------------------------------- +bool Visual::removeChild (Visual* child) +{ + auto it = std::find (children.begin (), children.end (), child); + if (it == children.end ()) + return true; + root->visual->RemoveVisual (child->root->visual.get ()); + children.erase (it); + return true; +} + +//----------------------------------------------------------------------------- +void Visual::onZIndexChanged (Visual* child) +{ + if (children.size () == 1) + return; + if (!removeChild (child)) + return; + addChild (child); + commit (); +} + +//----------------------------------------------------------------------------- +bool Visual::resize (uint32_t width, uint32_t height) +{ + return root->setSize (width, height); +} + +//----------------------------------------------------------------------------- +bool Visual::setPosition (uint32_t left, uint32_t top) +{ + return root->setOffset (left, top); +} + +//----------------------------------------------------------------------------- +bool Visual::setOpacity (float opacity) +{ + return root->setOpacity (opacity); +} + +//----------------------------------------------------------------------------- +bool Visual::update (CRect inUpdateRect, const DrawCallback& drawCallback) +{ + if (!drawCallback || inUpdateRect.isEmpty ()) + return false; + + RECT updateRect = RECTfromRect (inUpdateRect); + auto hr = root->update (updateRect, drawCallback); + if (FAILED (hr)) + { + return false; + } + addRedrawArea (inUpdateRect); + return true; +} + +//----------------------------------------------------------------------------- +void Visual::addRedrawArea (CRect r, uint32_t depth) +{ + r.offset (root->left, root->top); + getParent ()->addRedrawArea (r, ++depth); +} + +//----------------------------------------------------------------------------- +bool Visual::commit () +{ + if (parent) + return parent->commit (); + return false; +} + +//----------------------------------------------------------------------------- +bool Visual::setZIndex (uint32_t index) +{ + if (zIndex == index) + return true; + zIndex = index; + vstgui_assert (getParent ()); + getParent ()->onZIndexChanged (this); + return true; +} + +//----------------------------------------------------------------------------- +RootVisual* Visual::getRootVisual () +{ + if (parent) + return parent->getRootVisual (); + return nullptr; +} + +//----------------------------------------------------------------------------- +bool Visual::removeFromParent () +{ + vstgui_assert (parent); + if (parent) + { + parent->removeChild (this); + parent = nullptr; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +RootVisual::RootVisual () = default; + +//----------------------------------------------------------------------------- +RootVisual::~RootVisual () noexcept +{ + enableVisualizeRedrawAreas (false); + vstgui_assert (children.empty ()); + if (compositionTarget) + compositionTarget->SetRoot (nullptr); + if (root->visual) + root->visual->SetContent (nullptr); + if (root->surface) + root->surface.reset (); + if (compositionSurfaceFactory) + compositionSurfaceFactory.reset (); + if (root->visual) + root->visual.reset (); + if (compositionTarget) + compositionTarget.reset (); + commit (); + + destroyCallback (this); +} + +//----------------------------------------------------------------------------- +std::shared_ptr RootVisual::create (HWND window, Factory::Impl* factory, + DestroyCallback&& destroy) +{ + auto surface = std::shared_ptr (new RootVisual); + if (!surface) + return {}; + surface->factory = factory; + surface->window = window; + if (!surface->create ()) + return {}; + surface->destroyCallback = std::move (destroy); + return surface; +} + +//----------------------------------------------------------------------------- +bool RootVisual::create () +{ + auto compDevice = factory->compositionDesktopDevice.get (); + auto hr = compDevice->CreateTargetForHwnd (window, false, compositionTarget.adoptPtr ()); + if (FAILED (hr)) + return false; + root = std::make_shared (); + hr = compDevice->CreateVisual (root->visual.adoptPtr ()); + if (FAILED (hr)) + return false; + hr = compDevice->CreateSurfaceFactory (factory->d2dDevice.get (), + compositionSurfaceFactory.adoptPtr ()); + if (FAILED (hr)) + return false; + auto windowSize = getWindowSize (); + root->width = windowSize.x; + root->height = windowSize.y; + + hr = compositionSurfaceFactory->CreateVirtualSurface ( + root->width, root->height, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_ALPHA_MODE_PREMULTIPLIED, + root->surface.adoptPtr ()); + if (FAILED (hr)) + return false; + hr = root->visual->SetContent (root->surface.get ()); + if (FAILED (hr)) + return false; + hr = compositionTarget->SetRoot (root->visual.get ()); + if (FAILED (hr)) + return false; + commit (); + return SUCCEEDED (hr); +} + +//----------------------------------------------------------------------------- +POINT RootVisual::getWindowSize () const +{ + RECT clientRect {}; + if (!GetClientRect (window, &clientRect)) + return {}; + auto width = clientRect.right - clientRect.left; + auto height = clientRect.bottom - clientRect.top; + return {width, height}; +} + +//----------------------------------------------------------------------------- +VisualSurfacePairPtr RootVisual::createVisualSurfacePair (uint32_t width, uint32_t height) const +{ + auto child = std::make_shared (); + auto hr = factory->compositionDesktopDevice->CreateVisual (child->visual.adoptPtr ()); + if (FAILED (hr)) + return {}; + hr = compositionSurfaceFactory->CreateVirtualSurface (width, height, DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_ALPHA_MODE_PREMULTIPLIED, + child->surface.adoptPtr ()); + if (FAILED (hr)) + return {}; + child->visual->SetContent (child->surface.get ()); + child->width = width; + child->height = height; + return child; +} + +//----------------------------------------------------------------------------- +bool RootVisual::setPosition (uint32_t left, uint32_t top) +{ + // the root visual is always at (0, 0) + return false; +} + +//----------------------------------------------------------------------------- +bool RootVisual::update (CRect inUpdateRect, const DrawCallback& drawCallback) +{ + if (!drawCallback) + return false; + + RECT updateRect {}; + if (inUpdateRect.isEmpty ()) + { + auto size = getWindowSize (); + updateRect.right = size.x; + updateRect.bottom = size.y; + } + else + { + updateRect = RECTfromRect (inUpdateRect); + } + + root->update (updateRect, drawCallback); + + addRedrawArea (rectFromRECT (updateRect)); + + return true; +} + +//----------------------------------------------------------------------------- +bool RootVisual::commit () +{ + return factory->commit (); +} + +//----------------------------------------------------------------------------- +void RootVisual::onDriverFailure () +{ + auto reEnableVisRedrawAreas = redrawAreaPlane != nullptr; + enableVisualizeRedrawAreas (false); + create (); + recreateVisuals (this); + enableVisualizeRedrawAreas (reEnableVisRedrawAreas); +} + +//----------------------------------------------------------------------------- +void RootVisual::recreateVisuals (Visual* visual) +{ + auto childrenCopy = visual->children; + visual->children.clear (); + for (auto& child : childrenCopy) + { + auto left = child->root->left; + auto top = child->root->top; + child->root = createVisualSurfacePair (child->root->width, child->root->height); + child->root->setOffset (left, top); + visual->addChild (child); + recreateVisuals (child); + } +} + +//----------------------------------------------------------------------------- +bool RootVisual::enableVisualizeRedrawAreas (bool state) +{ + if (state) + { + if (redrawAreaPlane) + return true; + + redrawAreaPlane = createVisualSurfacePair (root->width, root->height); + auto hr = root->visual->AddVisual (redrawAreaPlane->visual.get (), FALSE, nullptr); + return SUCCEEDED (hr); + } + if (!redrawAreaPlane) + return true; + + redrawAreas.clear (); + auto hr = root->visual->RemoveVisual (redrawAreaPlane->visual.get ()); + redrawAreaPlane = nullptr; + return SUCCEEDED (hr); +} + +//----------------------------------------------------------------------------- +void RootVisual::addRedrawArea (CRect r, uint32_t depth) +{ + if (!redrawAreaPlane) + return; + + for (auto& area : redrawAreas) + { + if (area->getRect () == r) + { + area->restart (); + return; + } + } + + auto vis = createVisualSurfacePair (static_cast (r.getWidth ()), + static_cast (r.getHeight ())); + auto compDevice = factory->compositionDesktopDevice.get (); + auto redrawArea = std::make_shared ( + compDevice, vis, r, depth, [this, compDevice] (auto area) { + auto it = std::find_if (redrawAreas.begin (), redrawAreas.end (), + [&] (const auto& el) { return el.get () == area; }); + if (it != redrawAreas.end ()) + { + redrawAreaPlane->visual->RemoveVisual (area->getVisual ()); + redrawAreas.erase (it); + commit (); + } + }); + redrawAreaPlane->visual->AddVisual (vis->visual.get (), TRUE, nullptr); + redrawAreas.emplace_back (std::move (redrawArea)); +} + +//------------------------------------------------------------------------ +IDCompositionVisual* RootVisual::getTopVisual () const +{ + if (redrawAreaPlane) + return redrawAreaPlane->visual.get (); + return nullptr; +} + +//------------------------------------------------------------------------ +} // anonymous + +//----------------------------------------------------------------------------- +Factory::Factory () +{ + impl = std::make_unique (); +} + +//----------------------------------------------------------------------------- +Factory::~Factory () noexcept +{ + vstgui_assert (impl->surfaces.empty ()); +} + +//----------------------------------------------------------------------------- +std::unique_ptr Factory::create (IUnknown* d2dFactory) +{ + auto obj = std::unique_ptr (new Factory); + if (obj->impl->init (d2dFactory)) + return std::move (obj); + return {}; +} + +//----------------------------------------------------------------------------- +bool Factory::Impl::init (IUnknown* _d2dFactory) +{ + if (DirectCompositionSupportDll::instance ().loaded () == false) + return false; + + auto hr = _d2dFactory->QueryInterface (__uuidof(ID2D1Factory1), + reinterpret_cast (d2dFactory.adoptPtr ())); + if (FAILED (hr)) + return false; + + return createObjects (); +} + +//----------------------------------------------------------------------------- +bool Factory::Impl::createD3D11Device () +{ + D3D_FEATURE_LEVEL featureLevels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; + UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; +#if DEBUG + creationFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + D3D_FEATURE_LEVEL featureLevel {}; + + COM::Ptr device; + COM::Ptr deviceContext; + + auto hr = D3D11CreateDevice (nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creationFlags, featureLevels, + ARRAYSIZE (featureLevels), D3D11_SDK_VERSION, device.adoptPtr (), + &featureLevel, deviceContext.adoptPtr ()); + if (FAILED (hr)) + return false; + hr = device->QueryInterface (__uuidof(ID3D11Device1), + reinterpret_cast (d3dDevice.adoptPtr ())); + if (FAILED (hr)) + return false; + hr = deviceContext->QueryInterface (__uuidof(ID3D11DeviceContext1), + reinterpret_cast (d3dDeviceContext.adoptPtr ())); + if (FAILED (hr)) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +bool Factory::Impl::createObjects () +{ + if (!createD3D11Device ()) + return false; + + auto hr = d3dDevice->QueryInterface (__uuidof(IDXGIDevice), + reinterpret_cast (dxgiDevice.adoptPtr ())); + if (FAILED (hr)) + return false; + + hr = d2dFactory->CreateDevice (dxgiDevice.get (), d2dDevice.adoptPtr ()); + + if (FAILED (hr)) + return false; + + hr = DirectCompositionSupportDll::instance ().createDevice2 ( + d2dDevice.get (), IID_PPV_ARGS (compositionDesktopDevice.adoptPtr ())); + if (FAILED (hr)) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +bool Factory::Impl::recreate () +{ + if (d3dDevice->GetDeviceRemovedReason () == S_OK) + return true; + + if (d2dDevice) + D2DBitmapCache::removeDevice (d2dDevice.get ()); + + d3dDeviceContext.reset (); + d3dDevice.reset (); + compositionDesktopDevice.reset (); + + return createObjects (); +} + +//----------------------------------------------------------------------------- +bool Factory::Impl::commit () +{ + auto hr = compositionDesktopDevice->Commit (); + if (FAILED (hr)) + { + recreate (); + for (auto& surface : surfaces) + { + surface->onDriverFailure (); + } + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +bool Factory::enableVisualizeRedrawAreas (bool state) +{ + if (impl->visualizeRedrawAreas == state) + return true; + + impl->visualizeRedrawAreas = state; + for (auto& s : impl->surfaces) + { + s->enableVisualizeRedrawAreas (state); + } + return true; +} + +//----------------------------------------------------------------------------- +bool Factory::isVisualRedrawAreasEnabled () const +{ + return impl->visualizeRedrawAreas; +} + +//----------------------------------------------------------------------------- +VisualPtr Factory::createVisualForHWND (HWND hwnd) +{ + if (auto surface = RootVisual::create (hwnd, impl.get (), [this] (auto surface) { + auto it = std::find (impl->surfaces.begin (), impl->surfaces.end (), surface); + vstgui_assert (it != impl->surfaces.end ()); + impl->surfaces.erase (it); + })) + { + surface->enableVisualizeRedrawAreas (impl->visualizeRedrawAreas); + impl->surfaces.push_back (surface.get ()); + return surface; + } + return nullptr; +} + +//------------------------------------------------------------------------ +VisualPtr Factory::createChildVisual (const VisualPtr& parent, uint32_t width, uint32_t height) +{ + if (auto visual = std::dynamic_pointer_cast (parent)) + { + if (auto root = visual->getRootVisual ()) + { + auto vsPair = root->createVisualSurfacePair (width, height); + auto childVisual = std::make_shared (vsPair, visual); + return childVisual; + } + } + + return nullptr; +} + +//------------------------------------------------------------------------ +bool Factory::removeVisual (const VisualPtr& visualPtr) +{ + if (auto visual = dynamic_cast (visualPtr.get ())) + { + return visual->removeFromParent (); + } + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HRESULT VisualSurfacePair::update (RECT updateRect, const IVisual::DrawCallback& callback) +{ + POINT offset {}; + COM::Ptr d2dDeviceContext; + auto hr = surface->BeginDraw (&updateRect, __uuidof(ID2D1DeviceContext), + reinterpret_cast (d2dDeviceContext.adoptPtr ()), &offset); + if (FAILED (hr)) + return hr; + + callback (d2dDeviceContext.get (), rectFromRECT (updateRect), offset.x, offset.y); + hr = surface->EndDraw (); + return hr; +} + +//----------------------------------------------------------------------------- +bool VisualSurfacePair::setOffset (uint32_t inLeft, uint32_t inTop) +{ + if (left == inLeft && top == inTop) + return true; + left = inLeft; + top = inTop; + auto hr = visual->SetOffsetX (static_cast (left)); + if (FAILED (hr)) + return false; + hr = visual->SetOffsetY (static_cast (top)); + return SUCCEEDED (hr); +} + +//----------------------------------------------------------------------------- +bool VisualSurfacePair::setSize (uint32_t w, uint32_t h) +{ + if (w != width || h != height) + { + if (SUCCEEDED (surface->Resize (w, h))) + { + width = w; + height = h; + } + else + { + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +bool VisualSurfacePair::setOpacity (float o) +{ + COM::Ptr vis3; + auto hr = visual->QueryInterface (__uuidof(IDCompositionVisual3), + reinterpret_cast (vis3.adoptPtr ())); + if (SUCCEEDED (hr) && vis3) + { + hr = vis3->SetOpacity (o); + } + return SUCCEEDED (hr); +} + +//------------------------------------------------------------------------ +} // DirectComposition +} // VSTGUI diff --git a/vstgui/lib/platform/win32/win32directcomposition.h b/vstgui/lib/platform/win32/win32directcomposition.h new file mode 100644 index 000000000..7ba9b6ac1 --- /dev/null +++ b/vstgui/lib/platform/win32/win32directcomposition.h @@ -0,0 +1,62 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "comptr.h" +#include "../../crect.h" +#include +#include +#include +#include + +interface ID2D1DeviceContext; + +//----------------------------------------------------------------------------- +namespace VSTGUI { +namespace DirectComposition { + +//----------------------------------------------------------------------------- +struct IVisual +{ + using DrawCallback = std::function; + + virtual bool setPosition (uint32_t left, uint32_t top) = 0; + virtual bool resize (uint32_t width, uint32_t height) = 0; + virtual bool update (CRect updateRect, const DrawCallback& drawCallback) = 0; + virtual bool setOpacity (float opacity) = 0; + virtual bool commit () = 0; + virtual bool setZIndex (uint32_t zIndex) = 0; + + virtual ~IVisual () noexcept = default; +}; + +using VisualPtr = std::shared_ptr; + +//----------------------------------------------------------------------------- +struct Factory +{ + static std::unique_ptr create (IUnknown* d2dFactory); + + bool enableVisualizeRedrawAreas (bool state); + bool isVisualRedrawAreasEnabled () const; + + VisualPtr createVisualForHWND (HWND hwnd); + VisualPtr createChildVisual (const VisualPtr& parent, uint32_t width, uint32_t height); + bool removeVisual (const VisualPtr& visual); + + ~Factory () noexcept; + + struct Impl; + +private: + Factory (); + + std::unique_ptr impl; +}; + +//------------------------------------------------------------------------ +} // DirectComposition +} // VSTGUI diff --git a/vstgui/lib/platform/win32/win32dll.h b/vstgui/lib/platform/win32/win32dll.h index ccb34d6bf..3824aa1d8 100644 --- a/vstgui/lib/platform/win32/win32dll.h +++ b/vstgui/lib/platform/win32/win32dll.h @@ -46,6 +46,8 @@ struct DllBase return reinterpret_cast (GetProcAddress (module, name)); } + bool loaded () const { return module != nullptr; } + private: HINSTANCE module {nullptr}; }; diff --git a/vstgui/lib/platform/win32/win32dragging.cpp b/vstgui/lib/platform/win32/win32dragging.cpp index 0990fb960..864a9a6e2 100644 --- a/vstgui/lib/platform/win32/win32dragging.cpp +++ b/vstgui/lib/platform/win32/win32dragging.cpp @@ -236,7 +236,7 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::DragEnter (IDataObject* dataObjec DragEventData data; data.drag = dragData; pFrame->getCurrentMousePosition (data.pos); - pFrame->getCurrentMouseButtons (data.modifiers); + pFrame->getCurrentModifiers (data.modifiers); auto result = pFrame->getFrame ()->platformOnDragEnter (data); if (result == DragOperation::Copy) *effect = DROPEFFECT_COPY; @@ -258,7 +258,7 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::DragOver (DWORD keyState, POINTL DragEventData data; data.drag = dragData; pFrame->getCurrentMousePosition (data.pos); - pFrame->getCurrentMouseButtons (data.modifiers); + pFrame->getCurrentModifiers (data.modifiers); auto result = pFrame->getFrame ()->platformOnDragMove (data); if (result == DragOperation::Copy) *effect = DROPEFFECT_COPY; @@ -278,7 +278,7 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::DragLeave () DragEventData data; data.drag = dragData; pFrame->getCurrentMousePosition (data.pos); - pFrame->getCurrentMouseButtons (data.modifiers); + pFrame->getCurrentModifiers (data.modifiers); pFrame->getFrame ()->platformOnDragLeave (data); dragData->forget (); dragData = nullptr; @@ -294,7 +294,7 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP CDropTarget::Drop (IDataObject* dataObject, DW DragEventData data; data.drag = dragData; pFrame->getCurrentMousePosition (data.pos); - pFrame->getCurrentMouseButtons (data.modifiers); + pFrame->getCurrentModifiers (data.modifiers); pFrame->getFrame ()->platformOnDrop (data); dragData->forget (); dragData = nullptr; diff --git a/vstgui/lib/platform/win32/win32factory.cpp b/vstgui/lib/platform/win32/win32factory.cpp index 323d84a9c..02e7153f3 100644 --- a/vstgui/lib/platform/win32/win32factory.cpp +++ b/vstgui/lib/platform/win32/win32factory.cpp @@ -3,6 +3,7 @@ // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "win32factory.h" +#include "win32directcomposition.h" #include "../iplatformbitmap.h" #include "../iplatformfont.h" #include "../iplatformframe.h" @@ -12,25 +13,46 @@ #include "../iplatformtimer.h" #include "../common/fileresourceinputstream.h" #include "direct2d/d2dbitmap.h" +#include "direct2d/d2dbitmapcache.h" #include "direct2d/d2ddrawcontext.h" #include "direct2d/d2dfont.h" +#include "direct2d/d2dgradient.h" #include "win32frame.h" #include "win32dragging.h" #include "win32resourcestream.h" +#include "winfileselector.h" #include "winstring.h" #include "wintimer.h" +#include "comptr.h" #include #include #include #include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma comment(lib, "windowscodecs.lib") +#pragma comment(lib, "d2d1.lib") +#pragma comment(lib, "dwrite.lib") +#endif //----------------------------------------------------------------------------- namespace VSTGUI { + //----------------------------------------------------------------------------- struct Win32Factory::Impl { HINSTANCE instance {nullptr}; + COM::Ptr d2dFactory; + COM::Ptr directWriteFactory; + COM::Ptr wicImagingFactory; + + std::unique_ptr directCompositionFactory; + UTF8String resourceBasePath; bool useD2DHardwareRenderer {false}; bool useGenericTextEdit {false}; @@ -68,6 +90,35 @@ Win32Factory::Win32Factory (HINSTANCE instance) { impl = std::unique_ptr (new Impl); impl->instance = instance; + + D2D1_FACTORY_OPTIONS* options = nullptr; +#if 0 // DEBUG + D2D1_FACTORY_OPTIONS debugOptions; + debugOptions.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; + options = &debugOptions; +#endif + D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED, __uuidof(ID2D1Factory), options, + (void**)impl->d2dFactory.adoptPtr ()); + DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), + (IUnknown**)impl->directWriteFactory.adoptPtr ()); +#if _WIN32_WINNT > 0x601 +// make sure when building with the Win 8.0 SDK we work on Win7 +#define VSTGUI_WICImagingFactory CLSID_WICImagingFactory1 +#else +#define VSTGUI_WICImagingFactory CLSID_WICImagingFactory +#endif + CoCreateInstance (VSTGUI_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, + IID_IWICImagingFactory, (void**)impl->wicImagingFactory.adoptPtr ()); + + impl->directCompositionFactory = DirectComposition::Factory::create (impl->d2dFactory.get ()); + D2DBitmapCache::init (); +} + +//----------------------------------------------------------------------------- +Win32Factory::~Win32Factory () noexcept +{ + D2DBitmapCache::terminate (); + D2DFont::terminate (); } //----------------------------------------------------------------------------- @@ -113,6 +164,30 @@ bool Win32Factory::useGenericTextEdit () const noexcept return impl->useGenericTextEdit; } +//----------------------------------------------------------------------------- +ID2D1Factory* Win32Factory::getD2DFactory () const noexcept +{ + return impl->d2dFactory.get (); +} + +//----------------------------------------------------------------------------- +IWICImagingFactory* Win32Factory::getWICImagingFactory () const noexcept +{ + return impl->wicImagingFactory.get (); +} + +//----------------------------------------------------------------------------- +IDWriteFactory* Win32Factory::getDirectWriteFactory () const noexcept +{ + return impl->directWriteFactory.get (); +} + +//----------------------------------------------------------------------------- +DirectComposition::Factory* Win32Factory::getDirectCompositionFactory () const noexcept +{ + return impl->directCompositionFactory.get (); +} + //----------------------------------------------------------------------------- uint64_t Win32Factory::getTicks () const noexcept { @@ -261,6 +336,20 @@ auto Win32Factory::createOffscreenContext (const CPoint& size, double scaleFacto return nullptr; } +//----------------------------------------------------------------------------- +PlatformGradientPtr Win32Factory::createGradient () const noexcept +{ + return std::make_unique (); +} + +//----------------------------------------------------------------------------- +PlatformFileSelectorPtr Win32Factory::createFileSelector (PlatformFileSelectorStyle style, + IPlatformFrame* frame) const noexcept +{ + auto win32Frame = dynamic_cast (frame); + return createWinFileSelector (style, win32Frame ? win32Frame->getHWND () : nullptr); +} + //----------------------------------------------------------------------------- const LinuxFactory* Win32Factory::asLinuxFactory () const noexcept { diff --git a/vstgui/lib/platform/win32/win32factory.h b/vstgui/lib/platform/win32/win32factory.h index d9af4a05a..03b09b508 100644 --- a/vstgui/lib/platform/win32/win32factory.h +++ b/vstgui/lib/platform/win32/win32factory.h @@ -8,14 +8,24 @@ #include "../../optional.h" #include "../../cstring.h" +struct ID2D1Factory; +struct IDWriteFactory; +struct IWICImagingFactory; + //----------------------------------------------------------------------------- namespace VSTGUI { +//------------------------------------------------------------------------ +namespace DirectComposition { +struct Factory; +} + //----------------------------------------------------------------------------- class Win32Factory final : public IPlatformFactory { public: Win32Factory (HINSTANCE instance); + ~Win32Factory () noexcept override; HINSTANCE getInstance () const noexcept; void setResourceBasePath (const UTF8String& path) const noexcept; @@ -27,6 +37,12 @@ class Win32Factory final : public IPlatformFactory void useGenericTextEdit (bool state) const noexcept; bool useGenericTextEdit () const noexcept; + ID2D1Factory* getD2DFactory () const noexcept; + IWICImagingFactory* getWICImagingFactory () const noexcept; + IDWriteFactory* getDirectWriteFactory () const noexcept; + + DirectComposition::Factory* getDirectCompositionFactory () const noexcept; + /** Return platform ticks (millisecond resolution) * @return ticks */ @@ -125,6 +141,19 @@ class Win32Factory final : public IPlatformFactory COffscreenContextPtr createOffscreenContext (const CPoint& size, double scaleFactor = 1.) const noexcept final; + /** Create a platform gradient object + * @return platform gradient object or nullptr on failure + */ + PlatformGradientPtr createGradient () const noexcept final; + + /** Create a platform file selector + * @param style file selector style + * @param frame frame + * @return platform file selector or nullptr on failure + */ + PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, + IPlatformFrame* frame) const noexcept; + const LinuxFactory* asLinuxFactory () const noexcept final; const MacFactory* asMacFactory () const noexcept final; const Win32Factory* asWin32Factory () const noexcept final; diff --git a/vstgui/lib/platform/win32/win32frame.cpp b/vstgui/lib/platform/win32/win32frame.cpp index aea65d28b..f17b2d13b 100644 --- a/vstgui/lib/platform/win32/win32frame.cpp +++ b/vstgui/lib/platform/win32/win32frame.cpp @@ -18,11 +18,17 @@ #include "win32support.h" #include "win32datapackage.h" #include "win32dragging.h" +#include "win32directcomposition.h" +#include "win32viewlayer.h" #include "../common/genericoptionmenu.h" #include "../common/generictextedit.h" #include "../../cdropsource.h" #include "../../cgradient.h" #include "../../cinvalidrectlist.h" +#include "../../events.h" +#include "../../finally.h" + +#include #if VSTGUI_OPENGL_SUPPORT #include "win32openglview.h" @@ -78,6 +84,7 @@ Win32Frame::Win32Frame (IPlatformFrameCallback* frame, const CRect& size, HWND p , updateRegionListSize (0) { useD2D (); + auto dcFactory = getPlatformFactory ().asWin32Factory ()->getDirectCompositionFactory (); if (parentType == PlatformType::kHWNDTopLevel) { windowHandle = parent; @@ -88,11 +95,14 @@ Win32Frame::Win32Frame (IPlatformFrameCallback* frame, const CRect& size, HWND p { initWindowClass (); - DWORD style = isParentLayered (parent) ? WS_EX_TRANSPARENT : 0; - windowHandle = CreateWindowEx (style, gClassName, TEXT("Window"), - WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, - 0, 0, (int)size.getWidth (), (int)size.getHeight (), - parentWindow, nullptr, GetInstance (), nullptr); + DWORD exStyle = isParentLayered (parent) ? WS_EX_TRANSPARENT : 0; + DWORD style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + if (dcFactory) + exStyle = WS_EX_NOREDIRECTIONBITMAP; + + windowHandle = CreateWindowEx (exStyle, gClassName, TEXT ("Window"), style, 0, 0, + (int)size.getWidth (), (int)size.getHeight (), parentWindow, + nullptr, GetInstance (), nullptr); if (windowHandle) { @@ -101,11 +111,22 @@ Win32Frame::Win32Frame (IPlatformFrameCallback* frame, const CRect& size, HWND p } } setMouseCursor (kCursorDefault); + if (dcFactory) + { + directCompositionVisual = dcFactory->createVisualForHWND (windowHandle); + } } //----------------------------------------------------------------------------- Win32Frame::~Win32Frame () noexcept { + if (directCompositionVisual) + { + if (auto dcFactory = getPlatformFactory ().asWin32Factory ()->getDirectCompositionFactory ()) + dcFactory->removeVisual (directCompositionVisual); + directCompositionVisual = nullptr; + } + if (updateRegionList) std::free (updateRegionList); if (deviceContext) @@ -312,6 +333,21 @@ bool Win32Frame::getCurrentMousePosition (CPoint& mousePosition) const return false; } +//----------------------------------------------------------------------------- +bool Win32Frame::getCurrentModifiers (Modifiers& modifiers) const +{ + modifiers.clear (); + if (GetAsyncKeyState (VK_SHIFT) < 0) + modifiers.add (ModifierKey::Shift); + if (GetAsyncKeyState (VK_CONTROL) < 0) + modifiers.add (ModifierKey::Control); + if (GetAsyncKeyState (VK_MENU) < 0) + modifiers.add (ModifierKey::Alt); + if (GetAsyncKeyState (VK_LWIN) < 0) + modifiers.add (ModifierKey::Super); + return true; +} + //----------------------------------------------------------------------------- bool Win32Frame::getCurrentMouseButtons (CButtonState& buttons) const { @@ -402,11 +438,7 @@ bool Win32Frame::showTooltip (const CRect& rect, const char* utf8Text) str.insert (pos, "\r\n"); } UTF8StringHelper tooltipText (str.data ()); - RECT rc; - rc.left = (LONG)rect.left; - rc.top = (LONG)rect.top; - rc.right = (LONG)rect.right; - rc.bottom = (LONG)rect.bottom; + RECT rc = RECTfromRect (rect); TOOLINFO ti = {}; ti.cbSize = sizeof(TOOLINFO); ti.hwnd = windowHandle; @@ -457,12 +489,45 @@ SharedPointer Win32Frame::createPlatformOptionMenu () { CButtonState buttons; getCurrentMouseButtons (buttons); - return makeOwned (dynamic_cast (frame), buttons, + MouseEventButtonState buttonState; + if (buttons.isLeftButton ()) + buttonState.set (MouseButton::Left); + else if (buttons.isRightButton ()) + buttonState.set (MouseButton::Right); + return makeOwned (dynamic_cast (frame), buttonState, *genericOptionMenuTheme); } return owned (new Win32OptionMenu (windowHandle)); } +//------------------------------------------------------------------------ +SharedPointer Win32Frame::createPlatformViewLayer ( + IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer) +{ + if (!directCompositionVisual) + return nullptr; // not supported when not using DirectComposition + auto parentWin32ViewLayer = dynamic_cast (parentLayer); + auto parent = + parentWin32ViewLayer ? parentWin32ViewLayer->getVisual () : directCompositionVisual; + if (parent) + { + auto visual = getPlatformFactory () + .asWin32Factory () + ->getDirectCompositionFactory () + ->createChildVisual (parent, 100, 100); + auto newLayer = + makeOwned (visual, drawDelegate, [this] (Win32ViewLayer* layer) { + auto it = std::find (viewLayers.begin (), viewLayers.end (), layer); + vstgui_assert (it != viewLayers.end ()); + if (it != viewLayers.end ()) + viewLayers.erase (it); + }); + viewLayers.push_back (newLayer); + return newLayer; + } + return nullptr; +} + #if VSTGUI_OPENGL_SUPPORT //----------------------------------------------------------------------------- SharedPointer Win32Frame::createPlatformOpenGLView () @@ -528,6 +593,45 @@ bool Win32Frame::setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme return true; } +//----------------------------------------------------------------------------- +template +void Win32Frame::iterateRegion (HRGN rgn, Proc func) +{ + DWORD len = GetRegionData (rgn, 0, nullptr); + if (len) + { + if (len > updateRegionListSize) + { + if (updateRegionList) + std::free (updateRegionList); + updateRegionListSize = len; + updateRegionList = (RGNDATA*)std::malloc (updateRegionListSize); + } + GetRegionData (rgn, len, updateRegionList); + if (updateRegionList->rdh.nCount > 0) + { + CInvalidRectList dirtyRects; + auto* rp = reinterpret_cast (updateRegionList->Buffer); + for (uint32_t i = 0; i < updateRegionList->rdh.nCount; ++i, ++rp) + { + CRect ur (rp->left, rp->top, rp->right, rp->bottom); + dirtyRects.add (ur); + } + for (auto& _updateRect : dirtyRects) + { + func (_updateRect); + } + } + else + { + RECT r; + GetRgnBox (rgn, &r); + auto updateRect = rectFromRECT (r); + func (updateRect); + } + } +} + //----------------------------------------------------------------------------- void Win32Frame::paint (HWND hwnd) { @@ -539,64 +643,71 @@ void Win32Frame::paint (HWND hwnd) } inPaint = true; - - PAINTSTRUCT ps; - HDC hdc = BeginPaint (hwnd, &ps); + bool needsInvalidation = false; + CRect frameSize; - if (hdc) + PAINTSTRUCT ps; + if (HDC hdc = BeginPaint (hwnd, &ps)) { - CRect updateRect ((CCoord)ps.rcPaint.left, (CCoord)ps.rcPaint.top, (CCoord)ps.rcPaint.right, (CCoord)ps.rcPaint.bottom); - CRect frameSize; - getSize (frameSize); - frameSize.offset (-frameSize.left, -frameSize.top); - if (deviceContext == nullptr) - deviceContext = createDrawContext (hwnd, hdc, frameSize); - if (deviceContext) + RECT clientRect; + GetClientRect (windowHandle, &clientRect); + frameSize = rectFromRECT (clientRect); + if (directCompositionVisual) { - deviceContext->setClipRect (updateRect); - - CDrawContext* drawContext = backBuffer ? backBuffer : deviceContext; - drawContext->beginDraw (); - DWORD len = GetRegionData (rgn, 0, nullptr); - if (len) + directCompositionVisual->resize (static_cast (frameSize.getWidth ()), + static_cast (frameSize.getHeight ())); + iterateRegion (rgn, [&] (const auto& rect) { + directCompositionVisual->update ( + rect, [&] (auto deviceContext, auto rect, auto offsetX, auto offsetY) { + COM::Ptr device; + deviceContext->GetDevice (device.adoptPtr ()); + D2DDrawContext drawContext (deviceContext, frameSize, device.get ()); + drawContext.setClipRect (rect); + CGraphicsTransform tm; + tm.translate (offsetX - rect.left, offsetY - rect.top); + CDrawContext::Transform transform (drawContext, tm); + { + drawContext.saveGlobalState (); + drawContext.clearRect (rect); + getFrame ()->platformDrawRect (&drawContext, rect); + drawContext.restoreGlobalState (); + } + }); + }); + for (auto& vl : viewLayers) + vl->drawInvalidRects (); + if (!directCompositionVisual->commit ()) + needsInvalidation = true; + } + else + { + if (deviceContext == nullptr) + deviceContext = createDrawContext (hwnd, hdc, frameSize); + if (deviceContext) { - if (len > updateRegionListSize) - { - if (updateRegionList) - std::free (updateRegionList); - updateRegionListSize = len; - updateRegionList = (RGNDATA*) std::malloc (updateRegionListSize); - } - GetRegionData (rgn, len, updateRegionList); - if (updateRegionList->rdh.nCount > 0) - { - CInvalidRectList dirtyRects; - auto* rp = reinterpret_cast (updateRegionList->Buffer); - for (uint32_t i = 0; i < updateRegionList->rdh.nCount; ++i, ++rp) - { - CRect ur (rp->left, rp->top, rp->right, rp->bottom); - dirtyRects.add (ur); - } - for (auto& _updateRect : dirtyRects) - { - drawContext->clearRect (_updateRect); - getFrame ()->platformDrawRect (drawContext, _updateRect); - } - } - else + GetRgnBox (rgn, &ps.rcPaint); + CRect updateRect ((CCoord)ps.rcPaint.left, (CCoord)ps.rcPaint.top, + (CCoord)ps.rcPaint.right, (CCoord)ps.rcPaint.bottom); + deviceContext->setClipRect (updateRect); + + CDrawContext* drawContext = backBuffer ? backBuffer : deviceContext; + drawContext->beginDraw (); + + iterateRegion (rgn, [&] (const auto& rect) { + drawContext->clearRect (rect); + getFrame ()->platformDrawRect (drawContext, rect); + }); + + drawContext->endDraw (); + if (backBuffer) { - drawContext->clearRect (updateRect); - getFrame ()->platformDrawRect (drawContext, updateRect); + deviceContext->beginDraw (); + deviceContext->clearRect (updateRect); + backBuffer->copyFrom (deviceContext, updateRect, + CPoint (updateRect.left, updateRect.top)); + deviceContext->endDraw (); } } - drawContext->endDraw (); - if (backBuffer) - { - deviceContext->beginDraw (); - deviceContext->clearRect (updateRect); - backBuffer->copyFrom (deviceContext, updateRect, CPoint (updateRect.left, updateRect.top)); - deviceContext->endDraw (); - } } } @@ -604,70 +715,37 @@ void Win32Frame::paint (HWND hwnd) DeleteObject (rgn); inPaint = false; + if (needsInvalidation && !frameSize.isEmpty ()) + { + invalidRect (frameSize); + for (auto& vl : viewLayers) + { + vl->invalidRect (vl->getViewSize ()); + } + } } -static unsigned char translateWinVirtualKey (WPARAM winVKey) +//----------------------------------------------------------------------------- +static void setupMouseEventFromWParam (MouseEvent& event, WPARAM wParam) { - switch (winVKey) - { - case VK_BACK: return VKEY_BACK; - case VK_TAB: return VKEY_TAB; - case VK_CLEAR: return VKEY_CLEAR; - case VK_RETURN: return VKEY_RETURN; - case VK_PAUSE: return VKEY_PAUSE; - case VK_ESCAPE: return VKEY_ESCAPE; - case VK_SPACE: return VKEY_SPACE; -// TODO: case VK_NEXT: return VKEY_NEXT; - case VK_END: return VKEY_END; - case VK_HOME: return VKEY_HOME; - case VK_LEFT: return VKEY_LEFT; - case VK_RIGHT: return VKEY_RIGHT; - case VK_UP: return VKEY_UP; - case VK_DOWN: return VKEY_DOWN; - case VK_PRIOR: return VKEY_PAGEUP; - case VK_NEXT: return VKEY_PAGEDOWN; - case VK_SELECT: return VKEY_SELECT; - case VK_PRINT: return VKEY_PRINT; - case VK_SNAPSHOT: return VKEY_SNAPSHOT; - case VK_INSERT: return VKEY_INSERT; - case VK_DELETE: return VKEY_DELETE; - case VK_HELP: return VKEY_HELP; - case VK_NUMPAD0: return VKEY_NUMPAD0; - case VK_NUMPAD1: return VKEY_NUMPAD1; - case VK_NUMPAD2: return VKEY_NUMPAD2; - case VK_NUMPAD3: return VKEY_NUMPAD3; - case VK_NUMPAD4: return VKEY_NUMPAD4; - case VK_NUMPAD5: return VKEY_NUMPAD5; - case VK_NUMPAD6: return VKEY_NUMPAD6; - case VK_NUMPAD7: return VKEY_NUMPAD7; - case VK_NUMPAD8: return VKEY_NUMPAD8; - case VK_NUMPAD9: return VKEY_NUMPAD9; - case VK_MULTIPLY: return VKEY_MULTIPLY; - case VK_ADD: return VKEY_ADD; - case VK_SEPARATOR: return VKEY_SEPARATOR; - case VK_SUBTRACT: return VKEY_SUBTRACT; - case VK_DECIMAL: return VKEY_DECIMAL; - case VK_DIVIDE: return VKEY_DIVIDE; - case VK_F1: return VKEY_F1; - case VK_F2: return VKEY_F2; - case VK_F3: return VKEY_F3; - case VK_F4: return VKEY_F4; - case VK_F5: return VKEY_F5; - case VK_F6: return VKEY_F6; - case VK_F7: return VKEY_F7; - case VK_F8: return VKEY_F8; - case VK_F9: return VKEY_F9; - case VK_F10: return VKEY_F10; - case VK_F11: return VKEY_F11; - case VK_F12: return VKEY_F12; - case VK_NUMLOCK: return VKEY_NUMLOCK; - case VK_SCROLL: return VKEY_SCROLL; - case VK_SHIFT: return VKEY_SHIFT; - case VK_CONTROL: return VKEY_CONTROL; - case VK_MENU: return VKEY_ALT; - case VKEY_EQUALS: return VKEY_EQUALS; - } - return 0; + if (wParam & MK_LBUTTON) + event.buttonState.add (MouseButton::Left); + if (wParam & MK_RBUTTON) + event.buttonState.add (MouseButton::Right); + if (wParam & MK_MBUTTON) + event.buttonState.add (MouseButton::Middle); + if (wParam & MK_XBUTTON1) + event.buttonState.add (MouseButton::Fourth); + if (wParam & MK_XBUTTON2) + event.buttonState.add (MouseButton::Fifth); + if (wParam & MK_CONTROL) + event.modifiers.add (ModifierKey::Control); + if (wParam & MK_SHIFT) + event.modifiers.add (ModifierKey::Shift); + if (GetAsyncKeyState (VK_MENU) < 0) + event.modifiers.add (ModifierKey::Alt); + if (GetAsyncKeyState (VK_LWIN) < 0) + event.modifiers.add (ModifierKey::Super); } //----------------------------------------------------------------------------- @@ -679,40 +757,31 @@ LONG_PTR WINAPI Win32Frame::proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM SharedPointer lifeGuard (this); IPlatformFrameCallback* pFrame = getFrame (); bool doubleClick = false; - + + auto oldEvent = std::move (currentEvent); + auto f = finally ([this, oldEvent = std::move (oldEvent)] () mutable { currentEvent = std::move (oldEvent); }); + currentEvent = Optional ({hwnd, message, wParam, lParam}); + switch (message) { case WM_MOUSEWHEEL: - { - CButtonState buttons = 0; - if (GetAsyncKeyState (VK_SHIFT) < 0) - buttons |= kShift; - if (GetAsyncKeyState (VK_CONTROL) < 0) - buttons |= kControl; - if (GetAsyncKeyState (VK_MENU) < 0) - buttons |= kAlt; - short zDelta = (short) GET_WHEEL_DELTA_WPARAM(wParam); - POINT p {GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)}; - ScreenToClient (windowHandle, &p); - CPoint where (p.x, p.y); - if (pFrame->platformOnMouseWheel (where, kMouseWheelAxisY, ((float)zDelta / WHEEL_DELTA), buttons)) - return 0; - break; - } case WM_MOUSEHWHEEL: // new since vista { - CButtonState buttons = 0; - if (GetAsyncKeyState (VK_SHIFT) < 0) - buttons |= kShift; - if (GetAsyncKeyState (VK_CONTROL) < 0) - buttons |= kControl; - if (GetAsyncKeyState (VK_MENU) < 0) - buttons |= kAlt; + MouseWheelEvent wheelEvent; + updateModifiers (wheelEvent.modifiers); + short zDelta = (short) GET_WHEEL_DELTA_WPARAM(wParam); POINT p {GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)}; ScreenToClient (windowHandle, &p); - CPoint where (p.x, p.y); - if (pFrame->platformOnMouseWheel (where, kMouseWheelAxisX, ((float)-zDelta / WHEEL_DELTA), buttons)) + wheelEvent.mousePosition = {static_cast (p.x), static_cast (p.y)}; + if (zDelta != WHEEL_DELTA) + wheelEvent.flags |= MouseWheelEvent::Flags::PreciseDeltas; + if (message == WM_MOUSEWHEEL) + wheelEvent.deltaY = static_cast (zDelta) / WHEEL_DELTA; + else + wheelEvent.deltaX = static_cast (zDelta) / WHEEL_DELTA; + pFrame->platformOnEvent (wheelEvent); + if (wheelEvent.consumed) return 0; break; } @@ -751,63 +820,33 @@ LONG_PTR WINAPI Win32Frame::proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM case WM_LBUTTONDOWN: case WM_XBUTTONDOWN: { - CButtonState buttons = 0; - if (wParam & MK_LBUTTON) - buttons |= kLButton; - if (wParam & MK_RBUTTON) - buttons |= kRButton; - if (wParam & MK_MBUTTON) - buttons |= kMButton; - if (wParam & MK_XBUTTON1) - buttons |= kButton4; - if (wParam & MK_XBUTTON2) - buttons |= kButton5; - if (wParam & MK_CONTROL) - buttons |= kControl; - if (wParam & MK_SHIFT) - buttons |= kShift; - if (GetAsyncKeyState (VK_MENU) < 0) - buttons |= kAlt; - if (doubleClick) - buttons |= kDoubleClick; - HWND oldFocus = SetFocus(getPlatformWindow()); + MouseDownEvent event; + setupMouseEventFromWParam (event, wParam); + event.clickCount = doubleClick ? 2 : 1; + + HWND oldFocus = SetFocus (getPlatformWindow ()); if(oldFocus != hwnd) oldFocusWindow = oldFocus; - CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)); - if (pFrame->platformOnMouseDown (where, buttons) == kMouseEventHandled && getPlatformWindow ()) + event.mousePosition (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)); + pFrame->platformOnEvent (event); + if (event.consumed && getPlatformWindow ()) SetCapture (getPlatformWindow ()); return 0; } case WM_MOUSELEAVE: { - CPoint where; - getCurrentMousePosition (where); - CButtonState buttons; - getCurrentMouseButtons (buttons); - pFrame->platformOnMouseExited (where, buttons); + MouseExitEvent event; + getCurrentMousePosition (event.mousePosition); + getCurrentModifiers (event.modifiers); + pFrame->platformOnEvent (event); mouseInside = false; return 0; } case WM_MOUSEMOVE: { - CButtonState buttons = 0; - if (wParam & MK_LBUTTON) - buttons |= kLButton; - if (wParam & MK_RBUTTON) - buttons |= kRButton; - if (wParam & MK_MBUTTON) - buttons |= kMButton; - if (wParam & MK_XBUTTON1) - buttons |= kButton4; - if (wParam & MK_XBUTTON2) - buttons |= kButton5; - if (wParam & MK_CONTROL) - buttons |= kControl; - if (wParam & MK_SHIFT) - buttons |= kShift; - if (GetAsyncKeyState (VK_MENU) < 0) - buttons |= kAlt; + MouseMoveEvent event; + setupMouseEventFromWParam (event, wParam); if (!mouseInside) { // this makes sure that WM_MOUSELEAVE will be generated by the system @@ -818,8 +857,8 @@ LONG_PTR WINAPI Win32Frame::proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM tme.hwndTrack = windowHandle; TrackMouseEvent (&tme); } - CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)); - pFrame->platformOnMouseMoved (where, buttons); + event.mousePosition (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)); + pFrame->platformOnEvent (event); return 0; } case WM_LBUTTONUP: @@ -827,74 +866,50 @@ LONG_PTR WINAPI Win32Frame::proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM case WM_MBUTTONUP: case WM_XBUTTONUP: { - CButtonState buttons = 0; - if (wParam & MK_LBUTTON || message == WM_LBUTTONUP) - buttons |= kLButton; - if (wParam & MK_RBUTTON || message == WM_RBUTTONUP) - buttons |= kRButton; - if (wParam & MK_MBUTTON || message == WM_MBUTTONUP) - buttons |= kMButton; - if (wParam & MK_XBUTTON1) - buttons |= kButton4; - if (wParam & MK_XBUTTON2) - buttons |= kButton5; - if (wParam & MK_CONTROL) - buttons |= kControl; - if (wParam & MK_SHIFT) - buttons |= kShift; - if (GetAsyncKeyState (VK_MENU) < 0) - buttons |= kAlt; - CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)); - pFrame->platformOnMouseUp (where, buttons); + MouseUpEvent event; + setupMouseEventFromWParam (event, wParam); + + if (message == WM_LBUTTONUP) + event.buttonState.add (MouseButton::Left); + else if (message == WM_RBUTTONUP) + event.buttonState.add (MouseButton::Right); + else if (message == WM_MBUTTONUP) + event.buttonState.add (MouseButton::Middle); + + event.mousePosition (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)); + pFrame->platformOnEvent (event); ReleaseCapture (); return 0; } + case WM_KEYUP: [[fallthrough]]; case WM_KEYDOWN: { - VstKeyCode key {}; - if (GetAsyncKeyState (VK_SHIFT) < 0) - key.modifier |= MODIFIER_SHIFT; - if (GetAsyncKeyState (VK_CONTROL) < 0) - key.modifier |= MODIFIER_CONTROL; - if (GetAsyncKeyState (VK_MENU) < 0) - key.modifier |= MODIFIER_ALTERNATE; - key.virt = translateWinVirtualKey (wParam); - key.character = MapVirtualKey (static_cast (wParam), MAPVK_VK_TO_CHAR); - if (key.virt || key.character) + KeyboardEvent keyEvent; + if (message == WM_KEYDOWN) { - key.character = std::tolower (key.character); - if (pFrame->platformOnKeyDown (key)) - return 0; + keyEvent.type = EventType::KeyDown; + auto repeatCount = (lParam & 0xFFFF); + if (repeatCount > 1) + keyEvent.isRepeat = true; } + else + keyEvent.type = EventType::KeyUp; + updateModifiers (keyEvent.modifiers); - if (IsWindow (oldFocusWindow)) - { - auto oldProc = reinterpret_cast (GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC)); - if (oldProc && oldProc != WindowProc) - return CallWindowProc (oldProc, oldFocusWindow, message, wParam, lParam); - } - break; - } - case WM_KEYUP: - { - VstKeyCode key {}; - if (GetAsyncKeyState (VK_SHIFT) < 0) - key.modifier |= MODIFIER_SHIFT; - if (GetAsyncKeyState (VK_CONTROL) < 0) - key.modifier |= MODIFIER_CONTROL; - if (GetAsyncKeyState (VK_MENU) < 0) - key.modifier |= MODIFIER_ALTERNATE; - key.virt = translateWinVirtualKey (wParam); - key.character = MapVirtualKey (static_cast (wParam), MAPVK_VK_TO_CHAR); - if (key.virt || key.character) + keyEvent.virt = translateWinVirtualKey (wParam); + keyEvent.character = MapVirtualKey (static_cast (wParam), MAPVK_VK_TO_CHAR); + if (keyEvent.virt != VirtualKey::None || keyEvent.character) { - if (pFrame->platformOnKeyUp (key)) + keyEvent.character = std::tolower (keyEvent.character); + pFrame->platformOnEvent (keyEvent); + if (keyEvent.consumed) return 0; } if (IsWindow (oldFocusWindow)) { - auto oldProc = reinterpret_cast (GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC)); + auto oldProc = + reinterpret_cast (GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC)); if (oldProc && oldProc != WindowProc) return CallWindowProc (oldProc, oldFocusWindow, message, wParam, lParam); } @@ -945,6 +960,22 @@ LONG_PTR WINAPI Win32Frame::proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM return DefWindowProc (hwnd, message, wParam, lParam); } +//----------------------------------------------------------------------------- +Optional Win32Frame::convertCurrentKeyEventToText () +{ + if (currentEvent && currentEvent->message == WM_KEYDOWN) + { + MSG msg; + if (PeekMessage (&msg, windowHandle, WM_CHAR, WM_CHAR, PM_REMOVE | PM_NOYIELD)) + { + std::wstring wideStr (1, static_cast (msg.wParam)); + UTF8StringHelper helper (wideStr.data ()); + return Optional (UTF8String (helper.getUTF8String ())); + } + } + return {}; +} + //----------------------------------------------------------------------------- LONG_PTR WINAPI Win32Frame::WindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -956,12 +987,6 @@ LONG_PTR WINAPI Win32Frame::WindowProc (HWND hwnd, UINT message, WPARAM wParam, return DefWindowProc (hwnd, message, wParam, lParam); } -//----------------------------------------------------------------------------- -CGradient* CGradient::create (const ColorStopMap& colorStopMap) -{ - return new CGradient (colorStopMap); -} - } // VSTGUI #endif // WINDOWS diff --git a/vstgui/lib/platform/win32/win32frame.h b/vstgui/lib/platform/win32/win32frame.h index 3a200aba6..80ba85413 100644 --- a/vstgui/lib/platform/win32/win32frame.h +++ b/vstgui/lib/platform/win32/win32frame.h @@ -9,8 +9,10 @@ #if WINDOWS #include "../../cframe.h" +#include "win32directcomposition.h" namespace VSTGUI { +class Win32ViewLayer; //----------------------------------------------------------------------------- class Win32Frame final : public IPlatformFrame, public IWin32PlatformFrame @@ -27,12 +29,14 @@ class Win32Frame final : public IPlatformFrame, public IWin32PlatformFrame CCursorType getLastSetCursor () const { return lastSetCursor; } + // IPlatformFrame bool getGlobalPosition (CPoint& pos) const override; bool setSize (const CRect& newSize) override; bool getSize (CRect& size) const override; bool getCurrentMousePosition (CPoint& mousePosition) const override; bool getCurrentMouseButtons (CButtonState& buttons) const override; + bool getCurrentModifiers (Modifiers& modifiers) const override; bool setMouseCursor (CCursorType type) override; bool invalidRect (const CRect& rect) override; bool scrollRect (const CRect& src, const CPoint& distance) override; @@ -44,7 +48,7 @@ class Win32Frame final : public IPlatformFrame, public IWin32PlatformFrame #if VSTGUI_OPENGL_SUPPORT SharedPointer createPlatformOpenGLView () override; #endif - SharedPointer createPlatformViewLayer (IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer = nullptr) override { return nullptr; } // not yet supported + SharedPointer createPlatformViewLayer (IPlatformViewLayerDelegate* drawDelegate, IPlatformViewLayer* parentLayer = nullptr) override; #if VSTGUI_ENABLE_DEPRECATED_METHODS DragResult doDrag (IDataPackage* source, const CPoint& offset, CBitmap* dragBitmap) override; #endif @@ -52,15 +56,20 @@ class Win32Frame final : public IPlatformFrame, public IWin32PlatformFrame PlatformType getPlatformType () const override { return PlatformType::kHWND; } void onFrameClosed () override; - Optional convertCurrentKeyEventToText () override { return {}; } + Optional convertCurrentKeyEventToText () override; bool setupGenericOptionMenu (bool use, GenericOptionMenuTheme* theme = nullptr) override; LONG_PTR WINAPI proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); //----------------------------------------------------------------------------- protected: + using ViewLayers = std::vector; + void initTooltip (); void paint (HWND hwnd); + template + void iterateRegion (HRGN rgn, Proc func); + static void initWindowClass (); static void destroyWindowClass (); static LONG_PTR WINAPI WindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); @@ -74,6 +83,9 @@ class Win32Frame final : public IPlatformFrame, public IWin32PlatformFrame SharedPointer backBuffer; CDrawContext* deviceContext; std::unique_ptr genericOptionMenuTheme; + DirectComposition::VisualPtr directCompositionVisual; + Optional currentEvent; + ViewLayers viewLayers; bool inPaint; bool mouseInside; diff --git a/vstgui/lib/platform/win32/win32support.cpp b/vstgui/lib/platform/win32/win32support.cpp index 47afe0687..1882ae28c 100644 --- a/vstgui/lib/platform/win32/win32support.cpp +++ b/vstgui/lib/platform/win32/win32support.cpp @@ -6,25 +6,13 @@ #if WINDOWS -#include "../../vstkeycode.h" +#include "../../events.h" #include "../common/fileresourceinputstream.h" #include "../platform_win32.h" #include "win32factory.h" -#include -#include -#include - #include #include "direct2d/d2ddrawcontext.h" -#include "direct2d/d2dbitmap.h" -#include "direct2d/d2dfont.h" - -#ifdef _MSC_VER -#pragma comment (lib,"windowscodecs.lib") -#pragma comment (lib,"d2d1.lib") -#pragma comment (lib,"dwrite.lib") -#endif namespace VSTGUI { @@ -36,124 +24,29 @@ HINSTANCE GetInstance () return nullptr; } -//----------------------------------------------------------------------------- -class D2DFactory -{ -public: - D2DFactory () - { - } - - ~D2DFactory () noexcept - { - CFontDesc::cleanup (); - releaseFactory (); - } - ID2D1Factory* getFactory () const - { - if (factory == nullptr) - { - D2D1_FACTORY_OPTIONS* options = nullptr; - #if 0 //DEBUG - D2D1_FACTORY_OPTIONS debugOptions; - debugOptions.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; - options = &debugOptions; - #endif - D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED, __uuidof(ID2D1Factory), options, (void**)&factory); - } - return factory; - } - - IDWriteFactory* getWriteFactory () - { - if (!writeFactory) - DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), (IUnknown**)&writeFactory); - return writeFactory; - } - - IWICImagingFactory* getImagingFactory () - { - if (imagingFactory == nullptr) - { -#if _WIN32_WINNT > 0x601 -// make sure when building with the Win 8.0 SDK we work on Win7 -#define VSTGUI_WICImagingFactory CLSID_WICImagingFactory1 -#else -#define VSTGUI_WICImagingFactory CLSID_WICImagingFactory -#endif - CoCreateInstance (VSTGUI_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (void**)&imagingFactory); - } - return imagingFactory; - } - - void use () - { - ++useCount; - } - - void unuse () - { - vstgui_assert (useCount > 0); - if (--useCount == 0) - releaseFactory (); - } - -private: - void releaseFactory () - { - if (writeFactory) - writeFactory->Release (); - writeFactory = nullptr; - if (imagingFactory) - imagingFactory->Release (); - imagingFactory = nullptr; - if (factory) - factory->Release (); - factory = nullptr; - } - - ID2D1Factory* factory {nullptr}; - IDWriteFactory* writeFactory {nullptr}; - IWICImagingFactory* imagingFactory {nullptr}; - int32_t useCount {0}; -}; - -//----------------------------------------------------------------------------- -D2DFactory& getD2DFactoryInstance () -{ - static D2DFactory d2dFactory; - return d2dFactory; -} - //----------------------------------------------------------------------------- ID2D1Factory* getD2DFactory () { - return getD2DFactoryInstance ().getFactory (); + return getPlatformFactory ().asWin32Factory ()->getD2DFactory (); } //----------------------------------------------------------------------------- IWICImagingFactory* getWICImageingFactory () { - return getD2DFactoryInstance ().getImagingFactory (); + return getPlatformFactory ().asWin32Factory ()->getWICImagingFactory (); } //----------------------------------------------------------------------------- -void useD2D () +IDWriteFactory* getDWriteFactory () { - getD2DFactoryInstance ().use (); + return getPlatformFactory ().asWin32Factory ()->getDirectWriteFactory (); } //----------------------------------------------------------------------------- -void unuseD2D () -{ - getD2DFactoryInstance ().unuse (); -} +void useD2D () {} //----------------------------------------------------------------------------- -IDWriteFactory* getDWriteFactory () -{ - return getD2DFactoryInstance ().getWriteFactory (); -} +void unuseD2D () {} //----------------------------------------------------------------------------- CDrawContext* createDrawContext (HWND window, HDC device, const CRect& surfaceRect) @@ -169,76 +62,91 @@ CDrawContext* createDrawContext (HWND window, HDC device, const CRect& surfaceRe //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -Optional keyMessageToKeyCode (WPARAM wParam, LPARAM lParam) +VirtualKey translateWinVirtualKey (WPARAM winVKey) { - static std::map vMap = { - {VK_BACK, VKEY_BACK}, - {VK_TAB, VKEY_TAB}, - {VK_CLEAR, VKEY_CLEAR}, - {VK_RETURN, VKEY_RETURN}, - {VK_PAUSE, VKEY_PAUSE}, - {VK_ESCAPE, VKEY_ESCAPE}, - {VK_SPACE, VKEY_SPACE}, - {VK_NEXT, VKEY_NEXT}, - {VK_END, VKEY_END}, - {VK_HOME, VKEY_HOME}, - {VK_LEFT, VKEY_LEFT}, - {VK_UP, VKEY_UP}, - {VK_RIGHT, VKEY_RIGHT}, - {VK_DOWN, VKEY_DOWN}, - // {VK_PAGEUP, VKEY_PAGEUP}, - // {VK_PAGEDOWN, VKEY_PAGEDOWN}, - {VK_SELECT, VKEY_SELECT}, - {VK_PRINT, VKEY_PRINT}, - // {VK_ENTER, VKEY_ENTER}, - {VK_SNAPSHOT, VKEY_SNAPSHOT}, - {VK_INSERT, VKEY_INSERT}, - {VK_DELETE, VKEY_DELETE}, - {VK_HELP, VKEY_HELP}, - {VK_NUMPAD0, VKEY_NUMPAD0}, - {VK_NUMPAD1, VKEY_NUMPAD1}, - {VK_NUMPAD2, VKEY_NUMPAD2}, - {VK_NUMPAD3, VKEY_NUMPAD3}, - {VK_NUMPAD4, VKEY_NUMPAD4}, - {VK_NUMPAD5, VKEY_NUMPAD5}, - {VK_NUMPAD6, VKEY_NUMPAD6}, - {VK_NUMPAD7, VKEY_NUMPAD7}, - {VK_NUMPAD8, VKEY_NUMPAD8}, - {VK_NUMPAD9, VKEY_NUMPAD9}, - {VK_MULTIPLY, VKEY_MULTIPLY}, - {VK_ADD, VKEY_ADD}, - {VK_SEPARATOR, VKEY_SEPARATOR}, - {VK_SUBTRACT, VKEY_SUBTRACT}, - {VK_DECIMAL, VKEY_DECIMAL}, - {VK_DIVIDE, VKEY_DIVIDE}, - {VK_F1, VKEY_F1}, - {VK_F2, VKEY_F2}, - {VK_F3, VKEY_F3}, - {VK_F4, VKEY_F4}, - {VK_F5, VKEY_F5}, - {VK_F6, VKEY_F6}, - {VK_F7, VKEY_F7}, - {VK_F8, VKEY_F8}, - {VK_F9, VKEY_F9}, - {VK_F10, VKEY_F10}, - {VK_F11, VKEY_F11}, - {VK_F12, VKEY_F12}, - {VK_NUMLOCK, VKEY_NUMLOCK}, - {VK_SCROLL, VKEY_SCROLL}, - {VK_SHIFT, VKEY_SHIFT}, - {VK_CONTROL, VKEY_CONTROL}, - // {VK_ALT, VKEY_ALT}, - {VK_OEM_NEC_EQUAL, VKEY_EQUALS} // TODO: verify - }; - auto it = vMap.find (wParam); - if (it != vMap.end ()) + switch (winVKey) { - VstKeyCode res {}; - res.virt = it->second; - return Optional (res); + case VK_BACK: return VirtualKey::Back; + case VK_TAB: return VirtualKey::Tab; + case VK_CLEAR: return VirtualKey::Clear; + case VK_RETURN: return VirtualKey::Return; + case VK_PAUSE: return VirtualKey::Pause; + case VK_ESCAPE: return VirtualKey::Escape; + case VK_SPACE: return VirtualKey::Space; +// TODO: case VK_NEXT: return VirtualKey::Next; + case VK_END: return VirtualKey::End; + case VK_HOME: return VirtualKey::Home; + case VK_LEFT: return VirtualKey::Left; + case VK_RIGHT: return VirtualKey::Right; + case VK_UP: return VirtualKey::Up; + case VK_DOWN: return VirtualKey::Down; + case VK_PRIOR: return VirtualKey::PageUp; + case VK_NEXT: return VirtualKey::PageDown; + case VK_SELECT: return VirtualKey::Select; + case VK_PRINT: return VirtualKey::Print; + case VK_SNAPSHOT: return VirtualKey::Snapshot; + case VK_INSERT: return VirtualKey::Insert; + case VK_DELETE: return VirtualKey::Delete; + case VK_HELP: return VirtualKey::Help; + case VK_NUMPAD0: return VirtualKey::NumPad0; + case VK_NUMPAD1: return VirtualKey::NumPad1; + case VK_NUMPAD2: return VirtualKey::NumPad2; + case VK_NUMPAD3: return VirtualKey::NumPad3; + case VK_NUMPAD4: return VirtualKey::NumPad4; + case VK_NUMPAD5: return VirtualKey::NumPad5; + case VK_NUMPAD6: return VirtualKey::NumPad6; + case VK_NUMPAD7: return VirtualKey::NumPad7; + case VK_NUMPAD8: return VirtualKey::NumPad8; + case VK_NUMPAD9: return VirtualKey::NumPad9; + case VK_MULTIPLY: return VirtualKey::Multiply; + case VK_ADD: return VirtualKey::Add; + case VK_SEPARATOR: return VirtualKey::Separator; + case VK_SUBTRACT: return VirtualKey::Subtract; + case VK_DECIMAL: return VirtualKey::Decimal; + case VK_DIVIDE: return VirtualKey::Divide; + case VK_F1: return VirtualKey::F1; + case VK_F2: return VirtualKey::F2; + case VK_F3: return VirtualKey::F3; + case VK_F4: return VirtualKey::F4; + case VK_F5: return VirtualKey::F5; + case VK_F6: return VirtualKey::F6; + case VK_F7: return VirtualKey::F7; + case VK_F8: return VirtualKey::F8; + case VK_F9: return VirtualKey::F9; + case VK_F10: return VirtualKey::F10; + case VK_F11: return VirtualKey::F11; + case VK_F12: return VirtualKey::F12; + case VK_NUMLOCK: return VirtualKey::NumLock; + case VK_SCROLL: return VirtualKey::Scroll; + case VK_SHIFT: return VirtualKey::ShiftModifier; + case VK_CONTROL: return VirtualKey::ControlModifier; + case VK_MENU: return VirtualKey::AltModifier; + case VK_OEM_PLUS: return VirtualKey::Equals; } + return VirtualKey::None; +} + +//----------------------------------------------------------------------------- +void updateModifiers (Modifiers& modifiers) +{ + if (GetAsyncKeyState (VK_SHIFT) < 0) + modifiers.add (ModifierKey::Shift); + if (GetAsyncKeyState (VK_CONTROL) < 0) + modifiers.add (ModifierKey::Control); + if (GetAsyncKeyState (VK_MENU) < 0) + modifiers.add (ModifierKey::Alt); + if (GetAsyncKeyState (VK_LWIN) < 0 || GetAsyncKeyState (VK_RWIN) < 0) + modifiers.add (ModifierKey::Super); +} + +//----------------------------------------------------------------------------- +Optional keyMessageToKeyboardEvent (WPARAM wParam, LPARAM lParam) +{ + KeyboardEvent event; + updateModifiers (event.modifiers); + event.virt = translateWinVirtualKey (wParam); + if (event.virt != VirtualKey::None) + return Optional (std::move (event)); return {}; } diff --git a/vstgui/lib/platform/win32/win32support.h b/vstgui/lib/platform/win32/win32support.h index 31666232a..ee4d5cf3a 100644 --- a/vstgui/lib/platform/win32/win32support.h +++ b/vstgui/lib/platform/win32/win32support.h @@ -12,6 +12,7 @@ struct IUnknown; #include "../../cbitmap.h" #include "../../optional.h" +#include "../../crect.h" #include "../iplatformresourceinputstream.h" #include #include @@ -21,8 +22,6 @@ interface ID2D1Factory; interface IDWriteFactory; interface IWICImagingFactory; -struct VstKeyCode; - namespace VSTGUI { #define VSTGUI_STRCMP wcscmp @@ -42,7 +41,9 @@ extern void useD2D (); extern void unuseD2D (); extern IDWriteFactory* getDWriteFactory (); extern CDrawContext* createDrawContext (HWND window, HDC device, const CRect& surfaceRect); -extern Optional keyMessageToKeyCode (WPARAM wParam, LPARAM lParam); +extern VirtualKey translateWinVirtualKey (WPARAM winVKey); +extern void updateModifiers (Modifiers& modifiers); +extern Optional keyMessageToKeyboardEvent (WPARAM wParam, LPARAM lParam); class UTF8StringHelper { @@ -116,6 +117,17 @@ class UTF8StringHelper int numCharacters {-1}; }; +inline CRect rectFromRECT (const RECT& r) +{ + return CRect (r.left, r.top, r.right, r.bottom); +} + +inline RECT RECTfromRect (const CRect& rect) +{ + return {static_cast (rect.left), static_cast (rect.top), + static_cast (rect.right), static_cast (rect.bottom)}; +} + /// @endcond } // VSTGUI diff --git a/vstgui/lib/platform/win32/win32textedit.cpp b/vstgui/lib/platform/win32/win32textedit.cpp index 992470d4e..41cabdb58 100644 --- a/vstgui/lib/platform/win32/win32textedit.cpp +++ b/vstgui/lib/platform/win32/win32textedit.cpp @@ -8,7 +8,7 @@ #include "win32support.h" #include "direct2d/d2dfont.h" -#include "../../vstkeycode.h" +#include "../../events.h" namespace VSTGUI { @@ -200,11 +200,16 @@ LONG_PTR WINAPI Win32TextEdit::procEdit (HWND hwnd, UINT message, WPARAM wParam, { if (win32TextEdit->textEdit) { - if (auto keyCode = keyMessageToKeyCode (wParam, lParam)) + if (auto keyEvent = keyMessageToKeyboardEvent (wParam, lParam)) { + keyEvent->type = EventType::KeyDown; // for now only dispatch virtual keys - if (keyCode->character == 0 && win32TextEdit->textEdit->platformOnKeyDown (*keyCode)) - return 0; + if (keyEvent->character == 0) + { + win32TextEdit->textEdit->platformOnKeyboardEvent (*keyEvent); + if (keyEvent->consumed) + return 0; + } } } break; diff --git a/vstgui/lib/platform/win32/win32viewlayer.cpp b/vstgui/lib/platform/win32/win32viewlayer.cpp new file mode 100644 index 000000000..ce30590d7 --- /dev/null +++ b/vstgui/lib/platform/win32/win32viewlayer.cpp @@ -0,0 +1,126 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "win32viewlayer.h" +#include "win32directcomposition.h" +#include "win32factory.h" +#include "direct2d/d2ddrawcontext.h" + +//------------------------------------------------------------------------ +namespace VSTGUI { + +//------------------------------------------------------------------------ +Win32ViewLayer::Win32ViewLayer (const DirectComposition::VisualPtr& visual, + IPlatformViewLayerDelegate* inDelegate, + DestroyCallback&& destroyCallback) +: visual (visual), delegate (inDelegate), destroyCallback (std::move (destroyCallback)) +{ +} + +//------------------------------------------------------------------------ +Win32ViewLayer::~Win32ViewLayer () noexcept +{ + getPlatformFactory ().asWin32Factory ()->getDirectCompositionFactory ()->removeVisual (visual); + destroyCallback (this); +} + +//------------------------------------------------------------------------ +void Win32ViewLayer::fire () +{ + if (drawInvalidRects ()) + { + if (!visual->commit ()) + invalidRect (viewSize); + } + timer = nullptr; +} + +//------------------------------------------------------------------------ +bool Win32ViewLayer::drawInvalidRects () +{ + if (invalidRectList.empty ()) + return false; + for (const auto& r : invalidRectList) + { + visual->update (r, [&] (auto deviceContext, auto updateRect, auto offsetX, auto offsetY) { + COM::Ptr device; + deviceContext->GetDevice (device.adoptPtr ()); + D2DDrawContext drawContext (deviceContext, viewSize, device.get ()); + drawContext.setClipRect (updateRect); + CGraphicsTransform tm; + tm.translate (offsetX - updateRect.left, offsetY - updateRect.top); + CDrawContext::Transform transform (drawContext, tm); + { + drawContext.saveGlobalState (); + drawContext.clearRect (updateRect); + delegate->drawViewLayer (&drawContext, updateRect); + drawContext.restoreGlobalState (); + } + }); + } + lastDrawTime = getPlatformFactory ().getTicks (); + invalidRectList.clear (); + return true; +} + +//------------------------------------------------------------------------ +void Win32ViewLayer::invalidRect (const CRect& size) +{ + auto r = size; + r.normalize (); + r.makeIntegral (); + r.bound (viewSize); + invalidRectList.add (r); + if (!timer) + { + auto ticks = getPlatformFactory ().getTicks () - lastDrawTime; + if (ticks > 15) + ticks = 0; + timer = makeOwned (this); + timer->start (static_cast (ticks)); + } +} + +//------------------------------------------------------------------------ +void Win32ViewLayer::setSize (const CRect& size) +{ + invalidRectList.clear (); + + auto r = size; + r.normalize (); + r.makeIntegral (); + visual->setPosition (static_cast (r.left), static_cast (size.top)); + visual->resize (static_cast (r.getWidth ()), static_cast (r.getHeight ())); + viewSize = r; + viewSize.originize (); + invalidRect (viewSize); +} + +//------------------------------------------------------------------------ +void Win32ViewLayer::setZIndex (uint32_t zIndex) +{ + visual->setZIndex (zIndex); +} + +//------------------------------------------------------------------------ +void Win32ViewLayer::setAlpha (float alpha) +{ + visual->setOpacity (alpha); + visual->commit (); +} + +//------------------------------------------------------------------------ +void Win32ViewLayer::draw (CDrawContext* context, const CRect& updateRect) {} + +//------------------------------------------------------------------------ +void Win32ViewLayer::onScaleFactorChanged (double newScaleFactor) {} + +//------------------------------------------------------------------------ +const DirectComposition::VisualPtr& Win32ViewLayer::getVisual () const +{ + return visual; +} + +//------------------------------------------------------------------------ +} // VSTGUI diff --git a/vstgui/lib/platform/win32/win32viewlayer.h b/vstgui/lib/platform/win32/win32viewlayer.h new file mode 100644 index 000000000..66a9d09c6 --- /dev/null +++ b/vstgui/lib/platform/win32/win32viewlayer.h @@ -0,0 +1,51 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../iplatformviewlayer.h" +#include "../../cinvalidrectlist.h" +#include "win32directcomposition.h" +#include "wintimer.h" +#include + +//------------------------------------------------------------------------ +namespace VSTGUI { + +//------------------------------------------------------------------------ +class Win32ViewLayer +: public IPlatformViewLayer +, public IPlatformTimerCallback +{ +public: + using DestroyCallback = std::function; + Win32ViewLayer (const DirectComposition::VisualPtr& visual, + IPlatformViewLayerDelegate* inDelegate, DestroyCallback&& destroyCallback); + ~Win32ViewLayer () noexcept; + + void invalidRect (const CRect& size) override; + void setSize (const CRect& size) override; + void setZIndex (uint32_t zIndex) override; + void setAlpha (float alpha) override; + void draw (CDrawContext* context, const CRect& updateRect) override; + void onScaleFactorChanged (double newScaleFactor) override; + + bool drawInvalidRects (); + const DirectComposition::VisualPtr& getVisual () const; + const CRect& getViewSize () const { return viewSize; } + +private: + void fire () override; + + DirectComposition::VisualPtr visual; + DestroyCallback destroyCallback; + IPlatformViewLayerDelegate* delegate {nullptr}; + CRect viewSize; + uint64_t lastDrawTime {0u}; + CInvalidRectList invalidRectList; + SharedPointer timer; +}; + +//------------------------------------------------------------------------ +} // VSTGUI diff --git a/vstgui/lib/platform/win32/winfileselector.cpp b/vstgui/lib/platform/win32/winfileselector.cpp index 80f1986e0..feff17f52 100644 --- a/vstgui/lib/platform/win32/winfileselector.cpp +++ b/vstgui/lib/platform/win32/winfileselector.cpp @@ -1,8 +1,8 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE -#include "../../cfileselector.h" +#include "winfileselector.h" #if WINDOWS @@ -17,25 +17,27 @@ #define IID_PPV_ARG(IType, ppType) IID_##IType, (void**)ppType -extern OSVERSIONINFOEX gSystemVersion; +extern OSVERSIONINFOEX gSystemVersion; namespace VSTGUI { -#define kAllSupportedFileTypesString L"All supported file types" +#define kAllSupportedFileTypesString L"All supported file types" //----------------------------------------------------------------------------- -static COMDLG_FILTERSPEC* buildExtensionFilter (std::list& extensions, const CFileExtension* defaultExtension, DWORD& numExtensions, DWORD& defaultFileTypeIndex) +static COMDLG_FILTERSPEC* + buildExtensionFilter (const std::vector& extensions, + const PlatformFileExtension& defaultExtension, DWORD& numExtensions, + DWORD& defaultFileTypeIndex) { if (extensions.empty () == false) { DWORD i = extensions.size () > 1 ? 1u : 0u; auto* filters = new COMDLG_FILTERSPEC[extensions.size () + 1 + i]; size_t allExtensionCharCount = 0; - std::list::iterator it = extensions.begin (); - while (it != extensions.end ()) + for (const auto& extension : extensions) { - UTF8StringHelper desc ((*it).getDescription ().data ()); - UTF8StringHelper ext ((*it).getExtension ().data ()); + UTF8StringHelper desc (extension.description.data ()); + UTF8StringHelper ext (extension.extension.data ()); WCHAR* wDesc = (WCHAR*)std::malloc ((wcslen (desc) + 1) * sizeof (WCHAR)); WCHAR* wSpec = (WCHAR*)std::malloc ((wcslen (ext) + 3) * sizeof (WCHAR)); if (wDesc && wSpec) @@ -47,7 +49,7 @@ static COMDLG_FILTERSPEC* buildExtensionFilter (std::list& exten filters[i].pszName = wDesc; filters[i].pszSpec = wSpec; - if (defaultExtension && *defaultExtension == (*it)) + if (extension == defaultExtension) defaultFileTypeIndex = i + 1; allExtensionCharCount += wcslen (filters[i].pszSpec) + 1; } @@ -58,13 +60,12 @@ static COMDLG_FILTERSPEC* buildExtensionFilter (std::list& exten break; } - it++; i++; } if (extensions.size () > 1) { WCHAR* wAllName = - (WCHAR*)std::malloc ((wcslen (kAllSupportedFileTypesString) + 1) * sizeof (WCHAR)); + (WCHAR*)std::malloc ((wcslen (kAllSupportedFileTypesString) + 1) * sizeof (WCHAR)); WCHAR* wAllSpec = (WCHAR*)std::malloc (allExtensionCharCount * sizeof (WCHAR)); if (wAllName && wAllSpec) { @@ -105,104 +106,91 @@ static void freeExtensionFilter (COMDLG_FILTERSPEC* filters) } //----------------------------------------------------------------------------- -class VistaFileSelector final : public CNewFileSelector +class VistaFileSelector final : public IPlatformFileSelector { public: - VistaFileSelector (CFrame* frame, Style style); + VistaFileSelector (PlatformFileSelectorStyle style, HWND parent); ~VistaFileSelector () noexcept; - bool runInternal (CBaseObject* delegate) override; - void cancelInternal () override; - bool runModalInternal () override; + bool run (const PlatformFileSelectorConfig& config) override; + bool cancel () override; protected: - Style style; - IFileDialog* fileDialog; + PlatformFileSelectorStyle style; + HWND parent {nullptr}; + IFileDialog* fileDialog {nullptr}; }; //----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -CNewFileSelector* CNewFileSelector::create (CFrame* parent, Style style) +PlatformFileSelectorPtr createWinFileSelector (PlatformFileSelectorStyle style, HWND parent) { - if (parent == nullptr) - { - #if DEBUG - DebugPrint ("Need frame for CNewFileSelector\n"); - #endif - return nullptr; - } - return new VistaFileSelector (parent, style); + return std::make_shared (style, parent); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- #ifndef __GNUC__ -using SHCreateItemFromParsingNameProc = HRESULT (STDAPICALLTYPE *) (__in PCWSTR pszPath, __in_opt IBindCtx *pbc, __in REFIID riid, __deref_out void **ppv); +using SHCreateItemFromParsingNameProc = HRESULT (STDAPICALLTYPE*) (__in PCWSTR pszPath, + __in_opt IBindCtx* pbc, + __in REFIID riid, + __deref_out void** ppv); #else -using SHCreateItemFromParsingNameProc = HRESULT (STDAPICALLTYPE *) (PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); +using SHCreateItemFromParsingNameProc = HRESULT (STDAPICALLTYPE*) (PCWSTR pszPath, IBindCtx* pbc, + REFIID riid, void** ppv); #endif SHCreateItemFromParsingNameProc _SHCreateItemFromParsingName = nullptr; //----------------------------------------------------------------------------- -VistaFileSelector::VistaFileSelector (CFrame* frame, Style style) -: CNewFileSelector (frame) -, style (style) -, fileDialog (nullptr) +VistaFileSelector::VistaFileSelector (PlatformFileSelectorStyle style, HWND parent) +: style (style), parent (parent) { if (_SHCreateItemFromParsingName == nullptr) { HINSTANCE shell32Instance = LoadLibraryA ("shell32.dll"); if (shell32Instance) { - _SHCreateItemFromParsingName = (SHCreateItemFromParsingNameProc)GetProcAddress (shell32Instance, "SHCreateItemFromParsingName"); + _SHCreateItemFromParsingName = (SHCreateItemFromParsingNameProc)GetProcAddress ( + shell32Instance, "SHCreateItemFromParsingName"); } } } //----------------------------------------------------------------------------- -VistaFileSelector::~VistaFileSelector () noexcept -{ -} - -//----------------------------------------------------------------------------- -bool VistaFileSelector::runInternal (CBaseObject* delegate) -{ - bool result = runModalInternal (); - if (delegate) - { - delegate->notify (this, kSelectEndMessage); - } - return result; -} +VistaFileSelector::~VistaFileSelector () noexcept {} //----------------------------------------------------------------------------- -void VistaFileSelector::cancelInternal () +bool VistaFileSelector::cancel () { if (fileDialog) + { fileDialog->Close (-1); + return true; + } + return false; } //----------------------------------------------------------------------------- -bool VistaFileSelector::runModalInternal () +bool VistaFileSelector::run (const PlatformFileSelectorConfig& config) { fileDialog = nullptr; HRESULT hr = E_UNEXPECTED; - if (style == kSelectSaveFile) + if (style == PlatformFileSelectorStyle::SelectSaveFile) { - hr = CoCreateInstance (CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IFileDialog, &fileDialog)); - if (!defaultSaveName.empty ()) + hr = CoCreateInstance (CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARG (IFileDialog, &fileDialog)); + if (!config.defaultSaveName.empty ()) { - fileDialog->SetFileName (UTF8StringHelper (defaultSaveName.data ())); + fileDialog->SetFileName (UTF8StringHelper (config.defaultSaveName.data ())); } } else { - hr = CoCreateInstance (CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IFileDialog, &fileDialog)); + hr = CoCreateInstance (CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARG (IFileDialog, &fileDialog)); if (SUCCEEDED (hr)) { - if (style == kSelectDirectory) + if (style == PlatformFileSelectorStyle::SelectDirectory) { DWORD dwOptions = 0; hr = fileDialog->GetOptions (&dwOptions); @@ -215,7 +203,7 @@ bool VistaFileSelector::runModalInternal () return false; } } - if (allowMultiFileSelection) + if (hasBit (config.flags, PlatformFileSelectorFlags::MultiFileSelection)) { DWORD dwOptions = 0; hr = fileDialog->GetOptions (&dwOptions); @@ -236,36 +224,38 @@ bool VistaFileSelector::runModalInternal () return false; } - if (!title.empty ()) - hr = fileDialog->SetTitle (UTF8StringHelper (title.data ())); + if (!config.title.empty ()) + hr = fileDialog->SetTitle (UTF8StringHelper (config.title.data ())); DWORD numExtensions = 0; DWORD defaultFileTypeIndex = 0; - COMDLG_FILTERSPEC* filters = buildExtensionFilter (extensions, defaultExtension, numExtensions, defaultFileTypeIndex); + COMDLG_FILTERSPEC* filters = buildExtensionFilter (config.extensions, config.defaultExtension, + numExtensions, defaultFileTypeIndex); if (filters) { fileDialog->SetFileTypes (numExtensions, filters); if (defaultFileTypeIndex) fileDialog->SetFileTypeIndex (defaultFileTypeIndex); } - if (!initialPath.empty () && _SHCreateItemFromParsingName) + if (!config.initialPath.empty () && _SHCreateItemFromParsingName) { IShellItem* shellItem; - hr = _SHCreateItemFromParsingName (UTF8StringHelper (initialPath.data ()), nullptr, IID_PPV_ARG (IShellItem, &shellItem)); + hr = _SHCreateItemFromParsingName (UTF8StringHelper (config.initialPath.data ()), nullptr, + IID_PPV_ARG (IShellItem, &shellItem)); if (SUCCEEDED (hr)) { fileDialog->SetFolder (shellItem); shellItem->Release (); } } - auto win32Frame = dynamic_cast (frame->getPlatformFrame ()); - hr = fileDialog->Show (win32Frame ? win32Frame->getHWND () : nullptr); + hr = fileDialog->Show (parent); if (SUCCEEDED (hr)) { - if (allowMultiFileSelection) + std::vector result; + if (hasBit (config.flags, PlatformFileSelectorFlags::MultiFileSelection)) { IFileOpenDialog* openFileDialog = nullptr; - hr = fileDialog->QueryInterface (IID_PPV_ARG(IFileOpenDialog, &openFileDialog)); + hr = fileDialog->QueryInterface (IID_PPV_ARG (IFileOpenDialog, &openFileDialog)); if (SUCCEEDED (hr)) { IShellItemArray* items; @@ -297,7 +287,7 @@ bool VistaFileSelector::runModalInternal () } else { - IShellItem *item; + IShellItem* item; hr = fileDialog->GetResult (&item); if (SUCCEEDED (hr)) { @@ -311,6 +301,8 @@ bool VistaFileSelector::runModalInternal () item->Release (); } } + if (config.doneCallback) + config.doneCallback (std::move (result)); } fileDialog->Release (); fileDialog = nullptr; diff --git a/vstgui/lib/platform/win32/winfileselector.h b/vstgui/lib/platform/win32/winfileselector.h new file mode 100644 index 000000000..4bf437361 --- /dev/null +++ b/vstgui/lib/platform/win32/winfileselector.h @@ -0,0 +1,20 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../iplatformfileselector.h" + +#if WINDOWS + +#include "win32frame.h" + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +PlatformFileSelectorPtr createWinFileSelector (PlatformFileSelectorStyle style, HWND parent); + +} // VSTGUI + +#endif diff --git a/vstgui/lib/private/disabledeprecatedmessage.h b/vstgui/lib/private/disabledeprecatedmessage.h new file mode 100644 index 000000000..1a85a124d --- /dev/null +++ b/vstgui/lib/private/disabledeprecatedmessage.h @@ -0,0 +1,15 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +#elif defined __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) // deprecated +#else +#endif diff --git a/vstgui/tests/uidescription vst3/source/prefix.h b/vstgui/lib/private/enabledeprecatedmessage.h similarity index 59% rename from vstgui/tests/uidescription vst3/source/prefix.h rename to vstgui/lib/private/enabledeprecatedmessage.h index f0e7ffabf..71bb3a151 100644 --- a/vstgui/tests/uidescription vst3/source/prefix.h +++ b/vstgui/lib/private/enabledeprecatedmessage.h @@ -1,11 +1,12 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE -#if DEVELOPMENT - #define VSTGUI_LIVE_EDITING 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined __GNUC__ +#pragma GCC diagnostic pop +#elif _MSC_VER +#pragma warning(pop) +#else #endif - -#define VSTGUI_DISABLE_GLOBAL_NAMESPACE_POLLUTION 0 - -#include "vstgui/lib/vstguifwd.h" diff --git a/vstgui/lib/vstguibase.h b/vstgui/lib/vstguibase.h index 29c0e3819..b0183187a 100644 --- a/vstgui/lib/vstguibase.h +++ b/vstgui/lib/vstguibase.h @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -12,8 +12,8 @@ // VSTGUI Version //----------------------------------------------------------------------------- #define VSTGUI_VERSION_MAJOR 4 -#define VSTGUI_VERSION_MINOR 10 -#define VSTGUI_VERSION_PATCHLEVEL 3 +#define VSTGUI_VERSION_MINOR 11 +#define VSTGUI_VERSION_PATCHLEVEL 0 //----------------------------------------------------------------------------- // Platform definitions @@ -44,15 +44,6 @@ #ifndef MAC #define MAC 1 #endif - #if !__LP64__ && !defined (MAC_CARBON) - #define MAC_CARBON 1 - #ifndef TARGET_API_MAC_CARBON - #define TARGET_API_MAC_CARBON 1 - #endif - #ifndef __CF_USE_FRAMEWORK_INCLUDES__ - #define __CF_USE_FRAMEWORK_INCLUDES__ 1 - #endif - #endif #endif #ifndef __has_feature @@ -66,8 +57,6 @@ #endif #include - #define VSTGUI_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) - #if defined (__clang__) && __clang_major__ > 4 #if defined (VSTGUI_WARN_EVERYTHING) && VSTGUI_WARN_EVERYTHING == 1 #pragma clang diagnostic warning "-Weverything" @@ -106,7 +95,10 @@ #define WINDOWS 1 #endif #if (defined(_M_ARM64) || defined(_M_ARM)) - #define VSTGUI_OPENGL_SUPPORT 0 + #if defined(VSTGUI_OPENGL_SUPPORT) + #undef VSTGUI_OPENGL_SUPPORT + #endif + #define VSTGUI_OPENGL_SUPPORT 0 #endif #ifdef _MSC_VER #pragma warning(3 : 4189) // local variable is initialized but not referenced @@ -119,15 +111,12 @@ #endif #if defined (__clang__) && __clang__ - #define VSTGUI_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) #if defined (VSTGUI_WARN_EVERYTHING) && VSTGUI_WARN_EVERYTHING == 1 #pragma clang diagnostic warning "-Wconversion" #pragma clang diagnostic ignored "-Wreorder" #else #pragma clang diagnostic warning "-Wunreachable-code" #endif - #else - #define VSTGUI_DEPRECATED_ATTRIBUTE __declspec(deprecated) #endif #include @@ -163,14 +152,12 @@ #define VSTGUI_ENABLE_DEPRECATED_METHODS 1 #endif -#ifndef VSTGUI_DEPRECATED_ATTRIBUTE - #define VSTGUI_DEPRECATED_ATTRIBUTE -#endif - #if VSTGUI_ENABLE_DEPRECATED_METHODS - #define VSTGUI_DEPRECATED(x) VSTGUI_DEPRECATED_ATTRIBUTE x + #define VSTGUI_DEPRECATED(x) [[deprecated]] x + #define VSTGUI_DEPRECATED_MSG(x, msg) [[deprecated(msg)]] x #else #define VSTGUI_DEPRECATED(x) + #define VSTGUI_DEPRECATED_MSG(x, msg) #endif //---------------------------------------------------- @@ -220,7 +207,7 @@ //---------------------------------------------------- namespace VSTGUI { - + /** coordinate type */ using CCoord = double; /** ID String pointer */ @@ -269,7 +256,7 @@ class ReferenceCounted : virtual public IReference public: ReferenceCounted () = default; virtual ~ReferenceCounted () noexcept = default; - + ReferenceCounted (const ReferenceCounted&) {}; ReferenceCounted& operator= (const ReferenceCounted&) { return *this; } @@ -281,9 +268,10 @@ class ReferenceCounted : virtual public IReference void remember () override { nbReference++; } /** get refcount */ virtual int32_t getNbReference () const { return nbReference; } - virtual void beforeDelete () {} //@} private: + virtual void beforeDelete () {} + T nbReference {1}; }; @@ -300,14 +288,18 @@ class CBaseObject : public NonAtomicReferenceCounted CBaseObject () = default; ~CBaseObject () noexcept override = default; - CBaseObject (const CBaseObject& o) {}; - CBaseObject& operator= (const CBaseObject& obj) { return *this; } - + CBaseObject (const CBaseObject&) {}; + CBaseObject& operator= (const CBaseObject&) { return *this; } + //----------------------------------------------------------------------------- /// @name Message Methods //----------------------------------------------------------------------------- //@{ - virtual CMessageResult notify (CBaseObject* sender, IdStringPtr message) { return kMessageUnknown; } + virtual CMessageResult notify ([[maybe_unused]] CBaseObject* sender, + [[maybe_unused]] IdStringPtr message) + { + return kMessageUnknown; + } //@} /// @cond ignore @@ -351,7 +343,7 @@ class SharedPointer *this = static_cast (op.get ()); return *this; } - + template inline SharedPointer (SharedPointer&& op) noexcept { @@ -367,7 +359,7 @@ class SharedPointer op.ptr = nullptr; return *this; } - + //------------------------------------------------------------------------ protected: template @@ -546,9 +538,10 @@ struct BitScopeToggleT //----------------------------------------------------------------------------- #define VSTGUI_NEWER_THAN(major, minor) \ - (VSTGUI_VERSION > major || VSTGUI_VERSION_MAJOR == major && VSTGUI_VERSION_MINOR > minor) + (VSTGUI_VERSION > major || (VSTGUI_VERSION_MAJOR == major && VSTGUI_VERSION_MINOR > minor)) #define VSTGUI_NEWER_THAN_4_10 VSTGUI_NEWER_THAN (4, 10) +#define VSTGUI_NEWER_THAN_4_11 VSTGUI_NEWER_THAN (4, 11) } // VSTGUI diff --git a/vstgui/lib/vstguidebug.cpp b/vstgui/lib/vstguidebug.cpp index eda35e6a6..892499588 100644 --- a/vstgui/lib/vstguidebug.cpp +++ b/vstgui/lib/vstguidebug.cpp @@ -6,6 +6,10 @@ #include #include +#if !defined(NDEBUG) && !defined(DEBUG) +#error VSTGUI expects either NDEBUG or DEBUG to be defined +#endif + #if DEBUG #include "cstring.h" @@ -96,7 +100,8 @@ bool hasAssertionHandler () } //------------------------------------------------------------------------ -void doAssert (const char* filename, const char* line, const char* desc) noexcept (false) +void doAssert (const char* filename, const char* line, const char* condition, + const char* desc) noexcept (false) { #if NDEBUG if (!hasAssertionHandler ()) @@ -105,7 +110,7 @@ void doAssert (const char* filename, const char* line, const char* desc) noexcep if (hasAssertionHandler ()) { try { - assertionHandler (filename, line, desc); + assertionHandler (filename, line, condition, desc); } catch (...) { std::rethrow_exception (std::current_exception ()); @@ -114,7 +119,8 @@ void doAssert (const char* filename, const char* line, const char* desc) noexcep #if DEBUG else { - DebugPrint ("\nassert at %s:%s: %s\n", filename, line, desc ? desc : "unknown"); + DebugPrint ("%s:%s: Assertion '%s' failed. %s\n", filename, line, condition, + desc ? desc : ""); assert (false); } #endif // DEBUG diff --git a/vstgui/lib/vstguidebug.h b/vstgui/lib/vstguidebug.h index da12d9ece..98071b774 100644 --- a/vstgui/lib/vstguidebug.h +++ b/vstgui/lib/vstguidebug.h @@ -9,12 +9,17 @@ //------------------------------------------------------------------------ namespace VSTGUI { -using AssertionHandler = void (*) (const char* filename, const char* line, const char* desc); +using AssertionHandler = void (*) (const char* filename, const char* line, const char* condition, + const char* desc); void setAssertionHandler (AssertionHandler handler); bool hasAssertionHandler (); -void doAssert (const char* filename, const char* line, const char* desc = nullptr) noexcept (false); +void doAssert (const char* filename, const char* line, const char* condition, + const char* desc = nullptr) noexcept (false); -#define vstgui_assert(x, ...) if (!(x)) VSTGUI::doAssert (__FILE__, VSTGUI_MAKE_STRING(__LINE__), ## __VA_ARGS__); +#define vstgui_assert(x, ...) \ + if (!(x)) \ + VSTGUI::doAssert (__FILE__, VSTGUI_MAKE_STRING (__LINE__), VSTGUI_MAKE_STRING (x), \ + ##__VA_ARGS__); } // VSTGUI diff --git a/vstgui/lib/vstguifwd.h b/vstgui/lib/vstguifwd.h index 9d5250879..4ad7d9c84 100644 --- a/vstgui/lib/vstguifwd.h +++ b/vstgui/lib/vstguifwd.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace VSTGUI { @@ -136,8 +137,12 @@ struct ModalViewSession; struct CListControlRowDesc; struct CNinePartTiledDescription; +using GradientColorStop = std::pair; +using GradientColorStopMap = std::multimap; + // interfaces class IViewListener; +class IViewEventListener; class IViewContainerListener; class IViewMouseListener; class IDataPackage; @@ -244,6 +249,29 @@ class CVuMeter; class CXYPad; class CListControl; +// events +struct Event; +struct ModifierEvent; +struct MousePositionEvent; +struct MouseEvent; +struct MouseDownUpMoveEvent; +struct MouseDownEvent; +struct MouseMoveEvent; +struct MouseUpEvent; +struct MouseCancelEvent; +struct MouseEnterEvent; +struct MouseExitEvent; +struct GestureEvent; +struct MouseWheelEvent; +struct ZoomGestureEvent; +struct KeyboardEvent; +struct Modifiers; +enum class EventType : uint32_t; +enum class VirtualKey : uint32_t; +enum class ModifierKey : uint32_t; + +const Event& noEvent (); + // animation namespace Animation { class IAnimationTarget; @@ -282,14 +310,24 @@ class IPlatformFactory; class IPlatformFrame; class IPlatformBitmap; class IPlatformFont; +class IPlatformGradient; +class IPlatformGraphicsPath; +class IPlatformGraphicsPathFactory; class IPlatformString; class IPlatformTimer; class IPlatformResourceInputStream; class IPlatformFrameConfig; class IPlatformFrameCallback; class IPlatformTimerCallback; +class IPlatformFileSelector; + +struct PlatformFileExtension; +struct PlatformFileSelectorConfig; enum class PlatformType : int32_t; +enum class PlatformGraphicsPathFillMode : int32_t; +enum class PlatformFileSelectorStyle : uint32_t; +enum class PlatformFileSelectorFlags : uint32_t; using PlatformFramePtr = SharedPointer; using PlatformBitmapPtr = SharedPointer; @@ -298,6 +336,9 @@ using PlatformStringPtr = SharedPointer; using PlatformTimerPtr = SharedPointer; using PlatformResourceInputStreamPtr = std::unique_ptr; using PlatformFactoryPtr = std::unique_ptr; - +using PlatformGradientPtr = std::unique_ptr; +using PlatformGraphicsPathPtr = std::unique_ptr; +using PlatformGraphicsPathFactoryPtr = std::shared_ptr; +using PlatformFileSelectorPtr = std::shared_ptr; } // VSTGUI diff --git a/vstgui/lib/vstguiinit.cpp b/vstgui/lib/vstguiinit.cpp index dccb296a5..bbd9cc613 100644 --- a/vstgui/lib/vstguiinit.cpp +++ b/vstgui/lib/vstguiinit.cpp @@ -2,6 +2,7 @@ // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "platform/platformfactory.h" +#include "cfont.h" //----------------------------------------------------------------------------- namespace VSTGUI { @@ -10,11 +11,13 @@ namespace VSTGUI { void init (PlatformInstanceHandle instance) { initPlatform (instance); + CFontDesc::init (); } //----------------------------------------------------------------------------- void exit () { + CFontDesc::cleanup (); exitPlatform (); } diff --git a/vstgui/plugin-bindings/aeffguieditor.cpp b/vstgui/plugin-bindings/aeffguieditor.cpp index cf1507c12..26d01d385 100644 --- a/vstgui/plugin-bindings/aeffguieditor.cpp +++ b/vstgui/plugin-bindings/aeffguieditor.cpp @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -12,92 +12,49 @@ #include "../lib/platform/iplatformframe.h" -#define kIdleRate 100 // host idle rate in ms -#define kIdleRate2 50 -#define kIdleRateMin 4 // minimum time between 2 idles in ms - #if WINDOWS #include #include "../lib/platform/win32/win32support.h" #endif -#if MAC -#include -#include "getpluginbundle.h" - -namespace VSTGUI { -static void InitMachOLibrary (); -static void ExitMachOLibrary (); -} // VSTGUI -#endif - namespace VSTGUI { //----------------------------------------------------------------------------- // AEffGUIEditor Implementation //----------------------------------------------------------------------------- -AEffGUIEditor::AEffGUIEditor (void* pEffect) +AEffGUIEditor::AEffGUIEditor (void* pEffect) : AEffEditor ((AudioEffect*)pEffect) -, inIdleStuff (false) { ((AudioEffect*)pEffect)->setEditor (this); systemWindow = 0; - lLastTicks = getTicks (); #if WINDOWS OleInitialize (nullptr); #endif - #if MAC - InitMachOLibrary (); - #endif } //----------------------------------------------------------------------------- -AEffGUIEditor::~AEffGUIEditor () +AEffGUIEditor::~AEffGUIEditor () { #if WINDOWS OleUninitialize (); #endif - #if MAC - ExitMachOLibrary (); - #endif } //----------------------------------------------------------------------------- #if VST_2_1_EXTENSIONS bool AEffGUIEditor::onKeyDown (VstKeyCode& keyCode) { - return frame ? frame->onKeyDown (keyCode) > 0 : false; + return false; } //----------------------------------------------------------------------------- bool AEffGUIEditor::onKeyUp (VstKeyCode& keyCode) { - return frame ? frame->onKeyUp (keyCode) > 0 : false; + return false; } #endif -//----------------------------------------------------------------------------- -void AEffGUIEditor::draw (ERect* ppErect) -{ -#if VSTGUI_ENABLE_DEPRECATED_METHODS - if (frame) - { - CRect r; - if (ppErect) - r (ppErect->left, ppErect->top, ppErect->right, ppErect->bottom); - else - r = frame->getViewSize (); - CDrawContext* context = frame->createDrawContext (); - if (context) - { - frame->drawRect (context, r); - context->forget(); - } - } -#endif -} - //----------------------------------------------------------------------------- bool AEffGUIEditor::open (void* ptr) { @@ -107,9 +64,6 @@ bool AEffGUIEditor::open (void* ptr) //----------------------------------------------------------------------------- void AEffGUIEditor::idle () { - if (inIdleStuff) - return; - AEffEditor::idle (); if (frame) frame->idle (); @@ -119,7 +73,7 @@ void AEffGUIEditor::idle () int32_t AEffGUIEditor::knobMode = kCircularMode; //----------------------------------------------------------------------------- -bool AEffGUIEditor::setKnobMode (int32_t val) +bool AEffGUIEditor::setKnobMode (int32_t val) { AEffGUIEditor::knobMode = val; return true; @@ -128,75 +82,13 @@ bool AEffGUIEditor::setKnobMode (int32_t val) //----------------------------------------------------------------------------- bool AEffGUIEditor::onWheel (float distance) { - #if VSTGUI_ENABLE_DEPRECATED_METHODS - if (frame) - { - CPoint where; - frame->getCurrentMouseLocation (where); - return frame->onWheel (where, distance, frame->getCurrentMouseButtons ()); - } - #endif return false; } -//----------------------------------------------------------------------------- -void AEffGUIEditor::wait (uint32_t ms) -{ - #if MAC - RunCurrentEventLoop (kEventDurationMillisecond * ms); - - #elif WINDOWS - Sleep (ms); - - #endif -} - -//----------------------------------------------------------------------------- -uint32_t AEffGUIEditor::getTicks () -{ - #if MAC - return (TickCount () * 1000) / 60; - - #elif WINDOWS - return (uint32_t)GetTickCount (); - - #endif - - return 0; -} - //----------------------------------------------------------------------------- void AEffGUIEditor::doIdleStuff () { - // get the current time - uint32_t currentTicks = getTicks (); - - if (currentTicks < lLastTicks) - { - wait (kIdleRateMin); - currentTicks += kIdleRateMin; - if (currentTicks < lLastTicks - kIdleRate2) - return; - } - - idle (); - - #if WINDOWS - struct tagMSG windowsMessage; - if (PeekMessage (&windowsMessage, nullptr, WM_PAINT, WM_PAINT, PM_REMOVE)) - DispatchMessage (&windowsMessage); - - #endif - - // save the next time - lLastTicks = currentTicks + kIdleRate; - - inIdleStuff = true; - - if (effect) - effect->masterIdle (); - - inIdleStuff = false; + vstgui_assert (false, "unexpected call"); } //----------------------------------------------------------------------------- @@ -227,12 +119,12 @@ bool AEffGUIEditor::beforeSizeChange (const CRect& newSize, const CRect& oldSize RECT rctTempWnd, rctParentWnd; HWND hTempWnd; long iFrame = (2 * GetSystemMetrics (SM_CYFIXEDFRAME)); - + long diffWidth = 0; long diffHeight = 0; - + hTempWnd = (HWND)getFrame ()->getPlatformFrame ()->getPlatformRepresentation (); - + while ((diffWidth != iFrame) && (hTempWnd != nullptr)) // look for FrameWindow { HWND hTempParentWnd = GetParent (hTempWnd); @@ -242,12 +134,12 @@ bool AEffGUIEditor::beforeSizeChange (const CRect& newSize, const CRect& oldSize break; GetWindowRect (hTempWnd, &rctTempWnd); GetWindowRect (hTempParentWnd, &rctParentWnd); - + SetWindowPos (hTempWnd, HWND_TOP, 0, 0, (int)newSize.getWidth () + diffWidth, (int)newSize.getHeight () + diffHeight, SWP_NOMOVE); - + diffWidth += (rctParentWnd.right - rctParentWnd.left) - (rctTempWnd.right - rctTempWnd.left); diffHeight += (rctParentWnd.bottom - rctParentWnd.top) - (rctTempWnd.bottom - rctTempWnd.top); - + if ((diffWidth > 80) || (diffHeight > 80)) // parent belongs to host return true; @@ -255,33 +147,14 @@ bool AEffGUIEditor::beforeSizeChange (const CRect& newSize, const CRect& oldSize diffWidth = 0; if (diffHeight < 0) diffHeight = 0; - + hTempWnd = hTempParentWnd; } - + if (hTempWnd) SetWindowPos (hTempWnd, HWND_TOP, 0, 0, (int)newSize.getWidth () + diffWidth, (int)newSize.getHeight () + diffHeight, SWP_NOMOVE); #endif return true; } -#if MAC -// ----------------------------------------------------------------------------- -void* gBundleRef = 0; - -// ----------------------------------------------------------------------------- -void InitMachOLibrary () -{ - gBundleRef = GetPluginBundle (); -} - -// ----------------------------------------------------------------------------- -void ExitMachOLibrary () -{ - if (gBundleRef) - CFRelease (gBundleRef); -} - -#endif - } // VSTGUI diff --git a/vstgui/plugin-bindings/aeffguieditor.h b/vstgui/plugin-bindings/aeffguieditor.h index 3d75852e5..14fd55f64 100644 --- a/vstgui/plugin-bindings/aeffguieditor.h +++ b/vstgui/plugin-bindings/aeffguieditor.h @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -28,23 +28,16 @@ public : virtual ~AEffGUIEditor (); - virtual void setParameter (VstInt32 index, float value) {} + virtual void setParameter (VstInt32 index, float value) {} virtual bool getRect (ERect** ppRect); virtual bool open (void* ptr); virtual void idle (); - virtual void draw (ERect* pRect); #if VST_2_1_EXTENSIONS virtual bool onKeyDown (VstKeyCode& keyCode); virtual bool onKeyUp (VstKeyCode& keyCode); #endif - // wait (in ms) - void wait (uint32_t ms); - - // get the current time (in ms) - uint32_t getTicks (); - // feedback to appli. virtual void doIdleStuff (); @@ -72,9 +65,6 @@ public : ERect rect; private: - uint32_t lLastTicks; - bool inIdleStuff; - static int32_t knobMode; }; diff --git a/vstgui/plugin-bindings/plugguieditor.cpp b/vstgui/plugin-bindings/plugguieditor.cpp index 3baed0da4..a8a067ab5 100644 --- a/vstgui/plugin-bindings/plugguieditor.cpp +++ b/vstgui/plugin-bindings/plugguieditor.cpp @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -6,25 +6,11 @@ #include "plugguieditor.h" #endif -#define kIdleRate 100 // host idle rate in ms -#define kIdleRate2 50 -#define kIdleRateMin 4 // minimum time between 2 idles in ms - #if WINDOWS #include #include #endif -#if MAC -#include -#include "getpluginbundle.h" - -namespace VSTGUI { -static void InitMachOLibrary (); -static void ExitMachOLibrary (); -} // VSTGUI -#endif - namespace VSTGUI { //----------------------------------------------------------------------------- @@ -34,34 +20,22 @@ namespace VSTGUI { This is the same as the AEffGUIEditor class except that this one allows the VSTGUI lib to build without VST dependencies. */ -PluginGUIEditor::PluginGUIEditor (void *pEffect) +PluginGUIEditor::PluginGUIEditor (void *pEffect) : effect (pEffect) { systemWindow = nullptr; - lLastTicks = getTicks (); #if WINDOWS OleInitialize (nullptr); #endif - #if MAC - InitMachOLibrary (); - #endif } //----------------------------------------------------------------------------- -PluginGUIEditor::~PluginGUIEditor () +PluginGUIEditor::~PluginGUIEditor () { #if WINDOWS OleUninitialize (); #endif - #if MAC - ExitMachOLibrary (); - #endif -} - -//----------------------------------------------------------------------------- -void PluginGUIEditor::draw (ERect *ppErect) -{ } //----------------------------------------------------------------------------- @@ -82,75 +56,16 @@ void PluginGUIEditor::idle () int32_t PluginGUIEditor::knobMode = kCircularMode; //----------------------------------------------------------------------------- -int32_t PluginGUIEditor::setKnobMode (int32_t val) +int32_t PluginGUIEditor::setKnobMode (int32_t val) { PluginGUIEditor::knobMode = val; return 1; } -//----------------------------------------------------------------------------- -void PluginGUIEditor::wait (uint32_t ms) -{ - #if MAC - RunCurrentEventLoop (kEventDurationMillisecond * ms); - - #elif WINDOWS - Sleep (ms); - - #endif -} - -//----------------------------------------------------------------------------- -uint32_t PluginGUIEditor::getTicks () -{ - #if MAC - return (TickCount () * 1000) / 60; - - #elif WINDOWS - return (uint32_t)GetTickCount (); - - #endif - - return 0; -} - //----------------------------------------------------------------------------- void PluginGUIEditor::doIdleStuff () { - // get the current time - uint32_t currentTicks = getTicks (); - - // YG TEST idle (); - if (currentTicks < lLastTicks) - { - #if (MAC && TARGET_API_MAC_CARBON) - RunCurrentEventLoop (kEventDurationMillisecond * kIdleRateMin); - #else - wait (kIdleRateMin); - #endif - currentTicks += kIdleRateMin; - if (currentTicks < lLastTicks - kIdleRate2) - return; - } - idle (); // TEST - - #if WINDOWS - struct tagMSG windowsMessage; - if (PeekMessage (&windowsMessage, nullptr, WM_PAINT, WM_PAINT, PM_REMOVE)) - DispatchMessage (&windowsMessage); - - #elif MAC && !__LP64__ - EventRef event; - EventTypeSpec eventTypes[] = { {kEventClassWindow, kEventWindowUpdate}, {kEventClassWindow, kEventWindowDrawContent} }; - if (ReceiveNextEvent (GetEventTypeCount (eventTypes), eventTypes, kEventDurationNoWait, true, &event) == noErr) - { - SendEventToEventTarget (event, GetEventDispatcherTarget ()); - ReleaseEvent (event); - } - #endif - - // save the next time - lLastTicks = currentTicks + kIdleRate; + vstgui_assert (false, "unexpected call"); } //----------------------------------------------------------------------------- @@ -160,25 +75,4 @@ bool PluginGUIEditor::getRect (ERect **ppErect) return true; } -#if MAC -// ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- - -void* gBundleRef = 0; - -// ----------------------------------------------------------------------------- -void InitMachOLibrary () -{ - VSTGUI::gBundleRef = GetPluginBundle (); -} - -// ----------------------------------------------------------------------------- -void ExitMachOLibrary () -{ - if (VSTGUI::gBundleRef) - CFRelease (VSTGUI::gBundleRef); -} - -#endif - } // VSTGUI diff --git a/vstgui/plugin-bindings/plugguieditor.h b/vstgui/plugin-bindings/plugguieditor.h index 7215057e5..b9d4bf25f 100644 --- a/vstgui/plugin-bindings/plugguieditor.h +++ b/vstgui/plugin-bindings/plugguieditor.h @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -28,18 +28,11 @@ public : PluginGUIEditor (void *pEffect); ~PluginGUIEditor () override; - virtual void setParameter (int32_t index, float value) {} + virtual void setParameter (int32_t index, float value) {} virtual bool getRect (ERect **ppRect); virtual bool open (void *ptr); virtual void close () { systemWindow = nullptr; } virtual void idle (); - virtual void draw (ERect *pRect); - - // wait (in ms) - void wait (uint32_t ms); - - // get the current time (in ms) - uint32_t getTicks (); // feedback to appli. void doIdleStuff () override; diff --git a/vstgui/plugin-bindings/vst3editor.cpp b/vstgui/plugin-bindings/vst3editor.cpp index 23a1d81b9..28899a2f9 100644 --- a/vstgui/plugin-bindings/vst3editor.cpp +++ b/vstgui/plugin-bindings/vst3editor.cpp @@ -6,6 +6,8 @@ #include "../vstgui.h" #include "../lib/cvstguitimer.h" #include "../lib/vstkeycode.h" +#include "../lib/animation/timingfunctions.h" +#include "../lib/animation/animations.h" #include "../uidescription/detail/uiviewcreatorattributes.h" #include "../uidescription/editing/uieditcontroller.h" #include "../uidescription/editing/uieditmenucontroller.h" @@ -302,25 +304,40 @@ class ParameterChangeListener : public Steinberg::FObject { c->setMin (minValue); c->setMax (maxValue); - auto* optMenu = dynamic_cast(c); - if (optMenu) + + auto getParamStringByIndex = [&] (int32_t i) { + Steinberg::Vst::String128 utf16Str; + editController->getParamStringByValue ( + getParameterID (), + (Steinberg::Vst::ParamValue)i / + (Steinberg::Vst::ParamValue)parameter->getInfo ().stepCount, + utf16Str); + Steinberg::String utf8Str (utf16Str); + utf8Str.toMultiByte (Steinberg::kCP_Utf8); + return utf8Str; + }; + + if (auto optMenu = dynamic_cast (c)) { optMenu->removeAllEntry (); + for (Steinberg::int32 i = 0; i <= parameter->getInfo ().stepCount; i++) + optMenu->addEntry (getParamStringByIndex (i).text8 ()); + c->setValue ((float)value - minValue); + } + else if (auto segmentButton = dynamic_cast (c)) + { + segmentButton->removeAllSegments (); for (Steinberg::int32 i = 0; i <= parameter->getInfo ().stepCount; i++) { - Steinberg::Vst::String128 utf16Str; - editController->getParamStringByValue (getParameterID (), (Steinberg::Vst::ParamValue)i / (Steinberg::Vst::ParamValue)parameter->getInfo ().stepCount, utf16Str); - Steinberg::String utf8Str (utf16Str); - utf8Str.toMultiByte (Steinberg::kCP_Utf8); - optMenu->addEntry (utf8Str.text8 ()); + CSegmentButton::Segment segment; + segment.name = getParamStringByIndex (i).text8 (); + segmentButton->addSegment (std::move (segment)); } c->setValue ((float)value - minValue); } else { c->setValue ((float)value); - if (c->isDirty ()) - c->valueChanged (); } } else @@ -387,9 +404,9 @@ Now you can define tags, colors, fonts, bitmaps and add views to your editor. See @ref page_uidescription_editor @n */ //----------------------------------------------------------------------------- -VST3Editor::VST3Editor (Steinberg::Vst::EditController* controller, UTF8StringPtr _viewName, UTF8StringPtr _xmlFile) -: VSTGUIEditor (controller) -, delegate (dynamic_cast (controller)) +VST3Editor::VST3Editor (Steinberg::Vst::EditController* controller, UTF8StringPtr _viewName, + UTF8StringPtr _xmlFile) +: VSTGUIEditor (controller), delegate (dynamic_cast (controller)) { description = new UIDescription (_xmlFile); viewName = _viewName; @@ -398,9 +415,9 @@ VST3Editor::VST3Editor (Steinberg::Vst::EditController* controller, UTF8StringPt } //----------------------------------------------------------------------------- -VST3Editor::VST3Editor (UIDescription* desc, Steinberg::Vst::EditController* controller, UTF8StringPtr _viewName, UTF8StringPtr _xmlFile) -: VSTGUIEditor (controller) -, delegate (dynamic_cast (controller)) +VST3Editor::VST3Editor (UIDescription* desc, Steinberg::Vst::EditController* controller, + UTF8StringPtr _viewName, UTF8StringPtr _xmlFile) +: VSTGUIEditor (controller), delegate (dynamic_cast (controller)) { description = desc; description->remember (); @@ -494,7 +511,7 @@ bool VST3Editor::exchangeView (UTF8StringPtr newViewName) if (attr) { viewName = newViewName; - doCreateView = true; + requestRecreateView (); return true; } return false; @@ -571,16 +588,19 @@ void VST3Editor::setZoomFactor (double factor) return; getFrame ()->setZoom (getAbsScaleFactor ()); + + if (delegate) + delegate->onZoomChanged (this, zoomFactor); } //----------------------------------------------------------------------------- bool VST3Editor::beforeSizeChange (const CRect& newSize, const CRect& oldSize) { - if (requestResizeGuard) + if (sizeRequest) return true; - requestResizeGuard = true; - bool result = requestResize (newSize.getSize ()); - requestResizeGuard = false; + sizeRequest = {newSize.getSize ()}; + bool result = requestResize (*sizeRequest); + sizeRequest = {}; return result; } @@ -589,18 +609,12 @@ bool VST3Editor::requestResize (const CPoint& newSize) { if (!plugFrame) return false; - CCoord width = newSize.x; - CCoord height = newSize.y; - double scaleFactor = getAbsScaleFactor (); - if (editingEnabled || (width >= std::round (minSize.x * scaleFactor) && width <= std::round (maxSize.x * scaleFactor) - && height >= std::round (minSize.y * scaleFactor) && height <= std::round (maxSize.y * scaleFactor))) - { - Steinberg::ViewRect vr; - vr.right = static_cast (width); - vr.bottom = static_cast (height); - return plugFrame->resizeView (this, &vr) == Steinberg::kResultTrue ? true : false; - } - return false; + + Steinberg::ViewRect vr; + vr.right = static_cast (std::floor (newSize.x)); + vr.bottom = static_cast (std::floor (newSize.y)); + return plugFrame->resizeView (this, &vr) == Steinberg::kResultTrue ? true : false; + } //----------------------------------------------------------------------------- @@ -822,12 +836,14 @@ static void addCOptionMenuEntriesToIContextMenu (VST3Editor* editor, COptionMenu #endif //----------------------------------------------------------------------------- -CMouseEventResult VST3Editor::onMouseDown (CFrame* frame, const CPoint& where, const CButtonState& buttons) +void VST3Editor::onMouseEvent (MouseEvent& event, CFrame* frame) { - CMouseEventResult result = kMouseEventNotHandled; - if (buttons.isRightButton ()) + if (event.type != EventType::MouseDown) + return; + + if (event.buttonState.isRight ()) { - COptionMenu* controllerMenu = (delegate && editingEnabled == false) ? delegate->createContextMenu (where, this) : nullptr; + COptionMenu* controllerMenu = (delegate && editingEnabled == false) ? delegate->createContextMenu (event.mousePosition, this) : nullptr; if (allowedZoomFactors.empty () == false && editingEnabled == false) { if (controllerMenu == nullptr) @@ -862,20 +878,31 @@ CMouseEventResult VST3Editor::onMouseDown (CFrame* frame, const CPoint& where, c } #endif CViewContainer::ViewList views; - if (editingEnabled == false && getFrame ()->getViewsAt (where, views, GetViewOptions (GetViewOptions::kDeep|GetViewOptions::kIncludeViewContainer))) + auto nonScaledPos = event.mousePosition; + frame->getTransform ().transform (nonScaledPos); + if (getFrame ()->getViewsAt (nonScaledPos, views, GetViewOptions ().deep ().includeViewContainer ())) { - for (const auto& view : views) - { - auto* contextMenuController = dynamic_cast (getViewController (view)); - if (contextMenuController == nullptr) - continue; + auto createOrPrepareMenu = [&] () { if (controllerMenu == nullptr) controllerMenu = new COptionMenu (); else controllerMenu->addSeparator (); - CPoint p (where); - view->frameToLocal (p); - contextMenuController->appendContextMenuItems (*controllerMenu, p); + }; + for (const auto& view : views) + { + auto viewController = getViewController (view); + if (!viewController) + continue; + if (auto ctrler = dynamic_cast (viewController)) + { + createOrPrepareMenu (); + ctrler->appendContextMenuItems (*controllerMenu, view, view->translateToLocal (nonScaledPos)); + } + else if (auto contextMenuController = dynamic_cast (viewController)) + { + createOrPrepareMenu (); + contextMenuController->appendContextMenuItems (*controllerMenu, view->translateToLocal (nonScaledPos)); + } } } #if VST3_SUPPORTS_CONTEXTMENU @@ -883,7 +910,7 @@ CMouseEventResult VST3Editor::onMouseDown (CFrame* frame, const CPoint& where, c Steinberg::Vst::ParamID paramID; if (handler) { - CPoint where2 (where); + CPoint where2 (event.mousePosition); getFrame ()->getTransform ().transform (where2); bool paramFound = findParameter ((Steinberg::int32)where2.x, (Steinberg::int32)where2.y, paramID) == Steinberg::kResultTrue; Steinberg::Vst::IContextMenu* contextMenu = handler->createContextMenu (this, paramFound ? ¶mID : nullptr); @@ -897,29 +924,28 @@ CMouseEventResult VST3Editor::onMouseDown (CFrame* frame, const CPoint& where, c static_cast (where2.y)); contextMenu->release (); }); - result = kMouseEventHandled; + event.consumed = true; } } - if (result == kMouseEventNotHandled) + if (!event.consumed) { #endif - if (controllerMenu) + if (controllerMenu && controllerMenu->getNbEntries () > 0) { controllerMenu->remember (); SharedPointer blockFrame = getFrame (); - getFrame ()->doAfterEventProcessing ([=] () { + getFrame ()->doAfterEventProcessing ([=, mousePosition = event.mousePosition] () { controllerMenu->setStyle (COptionMenu::kPopupStyle | COptionMenu::kMultipleCheckStyle); - controllerMenu->popup (blockFrame, where); + controllerMenu->popup (blockFrame, mousePosition); controllerMenu->forget (); }); - result = kMouseEventHandled; + event.consumed = true; } } if (controllerMenu) controllerMenu->forget (); } - return result; } //----------------------------------------------------------------------------- @@ -1018,6 +1044,26 @@ void VST3Editor::recreateView () enableEditing (editingEnabled); } +//------------------------------------------------------------------------ +void VST3Editor::requestRecreateView () +{ + if (doCreateView || !frame) + return; + doCreateView = true; + auto task = [Self = Steinberg::IPtr (this)] () { + if (Self->frame) + Self->recreateView (); + }; + if (frame->inEventProcessing ()) + { + frame->doAfterEventProcessing (std::move (task)); + } + else + { + task (); + } +} + #if LINUX // Map Steinberg Vst Interface to VSTGUI Interface class RunLoop : public X11::IRunLoop, public AtomicReferenceCounted @@ -1132,7 +1178,7 @@ class RunLoop : public X11::IRunLoop, public AtomicReferenceCounted struct VST3Editor::KeyboardHook : public IKeyboardHook { public: - using Func = std::function; + using Func = std::function; KeyboardHook (Func&& keyDown, Func&& keyUp) : onKeyDownFunc (std::move (keyDown)), onKeyUpFunc (std::move (keyUp)) @@ -1140,13 +1186,16 @@ struct VST3Editor::KeyboardHook : public IKeyboardHook } private: - int32_t onKeyDown (const VstKeyCode& code, CFrame* frame) override + void onKeyboardEvent (KeyboardEvent& event, CFrame* frame) override { - return onKeyDownFunc (code, frame); - } - int32_t onKeyUp (const VstKeyCode& code, CFrame* frame) override - { - return onKeyUpFunc (code, frame); + if (event.type == EventType::KeyDown) + { + onKeyDownFunc (event, frame); + } + else if (event.type == EventType::KeyUp) + { + onKeyUpFunc (event, frame); + } } Func onKeyDownFunc; @@ -1166,18 +1215,18 @@ bool PLUGIN_API VST3Editor::open (void* parent, const PlatformType& type) #if VSTGUI_LIVE_EDITING // will delete itself when the frame will be destroyed keyboardHook = new KeyboardHook ( - [this] (const VstKeyCode& code, CFrame* frame) { - if (code.modifier == MODIFIER_CONTROL && frame->getModalView () == nullptr) + [this] (KeyboardEvent& event, CFrame* frame) { + if (event.modifiers.is (ModifierKey::Control) && frame->getModalView () == nullptr) { - if (code.character == 'e') + if (event.character == 'e') { - enableEditing (!editingEnabled); - return 1; + editingEnabled = !editingEnabled; + requestRecreateView (); + event.consumed = true; } } - return -1; }, - [] (const VstKeyCode&, CFrame*) { return -1; }); + [] (KeyboardEvent&, CFrame*) { }); getFrame ()->registerKeyboardHook (keyboardHook); #endif getFrame ()->enableTooltips (tooltipsEnabled); @@ -1246,18 +1295,33 @@ void PLUGIN_API VST3Editor::close () //------------------------------------------------------------------------ Steinberg::tresult PLUGIN_API VST3Editor::onSize (Steinberg::ViewRect* newSize) { + if (sizeRequest) + { + auto width = static_cast (std::floor (sizeRequest->x)); + auto height = static_cast (std::floor (sizeRequest->y)); + if (width == newSize->getWidth () && height == newSize->getHeight ()) + { + VSTGUIEditor::onSize (newSize); + return Steinberg::kResultTrue; + } + return Steinberg::kResultFalse; + } if (getFrame ()) { - CRect r (newSize->left, newSize->top, newSize->right, newSize->bottom); - CRect currentSize; - getFrame ()->getSize (currentSize); - if (r == currentSize) + CRect frameSize; + getFrame ()->getSize (frameSize); + auto width = static_cast (std::floor (frameSize.getWidth ())); + auto height = static_cast (std::floor (frameSize.getHeight ())); + if (frameSize.left == newSize->left && frameSize.top == newSize->top && + width == newSize->getWidth () && height == newSize->getHeight ()) + { + VSTGUIEditor::onSize (newSize); return Steinberg::kResultTrue; + } } - auto oldState = requestResizeGuard; - requestResizeGuard = true; + sizeRequest = {CPoint (newSize->getWidth (), newSize->getHeight ())}; auto result = VSTGUIEditor::onSize (newSize); - requestResizeGuard = oldState; + sizeRequest = {}; return result; } @@ -1286,10 +1350,10 @@ Steinberg::tresult PLUGIN_API VST3Editor::checkSizeConstraint (Steinberg::ViewRe height = minSize.y * scaleFactor; else if (height > maxSize.y * scaleFactor) height = maxSize.y * scaleFactor; - if (width != rect->right - rect->left || height != rect->bottom - rect->top) + if (width != rect->getWidth () || height != rect->getHeight ()) { - rect->right = (Steinberg::int32)width + rect->left; - rect->bottom = (Steinberg::int32)height + rect->top; + rect->right = static_cast (std::floor (width + rect->left)); + rect->bottom = static_cast (std::floor (height + rect->top)); } return Steinberg::kResultTrue; } @@ -1339,13 +1403,13 @@ bool VST3Editor::onCommandMenuItemSelected (CCommandMenuItem* item) if (cmdName == "Open UIDescription Editor") { editingEnabled = true; - doCreateView = true; + requestRecreateView (); return true; } else if (cmdName == "Close UIDescription Editor") { editingEnabled = false; - doCreateView = true; + requestRecreateView (); return true; } else if (cmdName == "Save") @@ -1375,17 +1439,6 @@ bool VST3Editor::onCommandMenuItemSelected (CCommandMenuItem* item) return false; } -//------------------------------------------------------------------------ -CMessageResult VST3Editor::notify (CBaseObject* sender, IdStringPtr message) -{ - if (message == CVSTGUITimer::kMsgTimer) - { - if (doCreateView) - recreateView (); - } - return VSTGUIEditor::notify (sender, message); -} - namespace VST3EditorInternal { //------------------------------------------------------------------------ static int32_t getUIDescriptionSaveOptions (CFrame* frame) @@ -1537,6 +1590,97 @@ void VST3Editor::syncParameterTags () #endif } +#if VSTGUI_LIVE_EDITING +//------------------------------------------------------------------------ +class EnterEditModeController +: public ViewListenerAdapter +, public ViewEventListenerAdapter +, public IControlListener +{ +public: + using EnterEditModeFunc = std::function; + + static constexpr const auto strFull = "Open UI Editor"; + static constexpr const auto strMinimized = "e"; + + EnterEditModeController (CFrame* frame, EnterEditModeFunc&& func) + : enterEditMode (std::move (func)) + { + button = new CTextButton ({0, 0, 120, 20}); + button->setTitle (strFull); + button->setRoundRadius (2.); + button->setFrameWidth (-1); + button->registerViewListener (this); + button->registerViewEventListener (this); + button->registerControlListener (this); + frame->addView (button); + } + + void valueChanged (CControl* c) override + { + if (c->getValue () == 1.) + { + enterEditMode (); + } + } + void viewAttached (CView* view) override + { + view->addAnimation ("SizeAnim", new Animation::AlphaValueAnimation (1.f), + new Animation::LinearTimingFunction (1000), + [&] (auto, auto, auto) { close (); }); + } + + void viewWillDelete (CView* view) override + { + vstgui_assert (view == button); + button->unregisterViewEventListener (this); + button->unregisterViewListener (this); + button->unregisterControlListener (this); + delete this; + } + void viewOnEvent (CView* view, Event& event) override + { + if (event.type == EventType::MouseEnter) + { + open (); + } + else if (event.type == EventType::MouseExit) + { + close (); + } + } + + Animation::ITimingFunction* createDefAnimTimingFunc () const + { + using namespace Animation; + + static const constexpr auto AnimationTime = 150; + return new CubicBezierTimingFunction (CubicBezierTimingFunction::easyInOut (AnimationTime)); + } + + void open () + { + button->addAnimation ("SizeAnim", new Animation::ViewSizeAnimation ({0, 0, 120, 20}), + createDefAnimTimingFunc (), + [&] (auto view, auto, auto) { button->setTitle (strFull); }); + button->addAnimation ("AlphaValue", new Animation::AlphaValueAnimation (1.f), + createDefAnimTimingFunc ()); + } + void close () + { + button->addAnimation ("SizeAnim", new Animation::ViewSizeAnimation ({0, 0, 10, 20}), + createDefAnimTimingFunc (), + [&] (auto view, auto, auto) { button->setTitle (strMinimized); }); + button->addAnimation ("AlphaValue", new Animation::AlphaValueAnimation (0.3f), + createDefAnimTimingFunc ()); + } + + EnterEditModeFunc enterEditMode; + CTextButton* button {nullptr}; +}; + +#endif + //------------------------------------------------------------------------ bool VST3Editor::enableEditing (bool state) { @@ -1617,8 +1761,8 @@ bool VST3Editor::enableEditing (bool state) if (view) { double scaleFactor = getAbsScaleFactor (); - CCoord width = std::ceil (view->getWidth () * scaleFactor); - CCoord height = std::ceil (view->getHeight () * scaleFactor); + CCoord width = view->getWidth () * scaleFactor; + CCoord height = view->getHeight () * scaleFactor; if (canResize () == Steinberg::kResultTrue) { @@ -1647,9 +1791,9 @@ bool VST3Editor::enableEditing (bool state) } else { - checkSizeConstraint (&rect); - onSize (&rect); - requestResize (CPoint (rect.getWidth (), rect.getHeight ())); + rect.right = rect.left + width; + rect.bottom = rect.top + height; + requestResize ({width, height}); } getFrame ()->setFocusDrawingEnabled (false); @@ -1702,6 +1846,9 @@ bool VST3Editor::enableEditing (bool state) getFrame ()->setFocusWidth (focusWidth); } } +#if VSTGUI_LIVE_EDITING + new EnterEditModeController (getFrame (), [this] () { enableEditing (true); }); +#endif return true; } } @@ -1709,4 +1856,23 @@ bool VST3Editor::enableEditing (bool state) return false; } -} // namespace +//------------------------------------------------------------------------ +void VST3Editor::setDelegate (IVST3EditorDelegate* inDelegate) +{ + delegate = inDelegate; +} + +//------------------------------------------------------------------------ +IVST3EditorDelegate* VST3Editor::getDelegate () const +{ + return delegate; +} + +//------------------------------------------------------------------------ +UIDescription* VST3Editor::getUIDescription () const +{ + return description; +} + +//------------------------------------------------------------------------ +} // VSTGUI diff --git a/vstgui/plugin-bindings/vst3editor.h b/vstgui/plugin-bindings/vst3editor.h index ee6546ec1..11cc098b7 100644 --- a/vstgui/plugin-bindings/vst3editor.h +++ b/vstgui/plugin-bindings/vst3editor.h @@ -9,6 +9,7 @@ #include "../uidescription/uidescription.h" #include "../uidescription/icontroller.h" #include "../lib/controls/icommandmenuitemtarget.h" +#include "../lib/optional.h" #include #include #include @@ -23,31 +24,79 @@ class ParameterChangeListener; class VST3Editor; //----------------------------------------------------------------------------- -//! @brief delegate extension to Steinberg::Vst::EditController for a VST3 Editor +//! @brief delegate interface for a VST3Editor. +//! +//! You either extend Steinberg::Vst::EditController with this interface and pass the editor +//! controller to the constructor of the VST3Editor class, or you create a delegate without +//! extending Steinberg::Vst::EditController and explicitly set the delegate of the VST3Editor. +//! //! @ingroup new_in_4_0 //----------------------------------------------------------------------------- -class VST3EditorDelegate +class IVST3EditorDelegate { public: - virtual ~VST3EditorDelegate () {} - - virtual CView* createCustomView (UTF8StringPtr name, const UIAttributes& attributes, const IUIDescription* description, VST3Editor* editor) { return nullptr; } ///< create a custom view - virtual CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description, VST3Editor* editor) { return view; } ///< verify a view after it was created - virtual bool findParameter (const CPoint& pos, Steinberg::Vst::ParamID& paramID, VST3Editor* editor) { return false; } ///< find a parameter - virtual bool isPrivateParameter (const Steinberg::Vst::ParamID paramID) { return false; } ///< check if parameter ID is private and should not be exposed to the host - virtual void didOpen (VST3Editor* editor) {} ///< called after the editor was opened - virtual void willClose (VST3Editor* editor) {} ///< called before the editor will close - virtual COptionMenu* createContextMenu (const CPoint& pos, VST3Editor* editor) { return nullptr; } ///< create the context menu for the editor, will be added to the host menu - + virtual ~IVST3EditorDelegate () = default; + + /** create a custom view */ + virtual CView* createCustomView (UTF8StringPtr name, const UIAttributes& attributes, + const IUIDescription* description, VST3Editor* editor) = 0; + /** verify a view after it was created */ + virtual CView* verifyView (CView* view, const UIAttributes& attributes, + const IUIDescription* description, VST3Editor* editor) = 0; + /** find a parameter */ + virtual bool findParameter (const CPoint& pos, Steinberg::Vst::ParamID& paramID, + VST3Editor* editor) = 0; + /** check if parameter ID is private and should not be exposed to the host */ + virtual bool isPrivateParameter (const Steinberg::Vst::ParamID paramID) = 0; + /** called after the editor was opened */ + virtual void didOpen (VST3Editor* editor) = 0; + /** called before the editor will close */ + virtual void willClose (VST3Editor* editor) = 0; + /** create the context menu for the editor, will be added to the host menu */ + virtual COptionMenu* createContextMenu (const CPoint& pos, VST3Editor* editor) = 0; /** called when a sub controller should be created. The controller is now owned by the editor, which will call forget() if it is a CBaseObject, release() if it is a Steinberg::FObject or it will be simply deleted if the frame gets closed. */ virtual IController* createSubController (UTF8StringPtr name, const IUIDescription* description, - VST3Editor* editor) + VST3Editor* editor) = 0; + /** called when the user zoom factor of the editor was changed */ + virtual void onZoomChanged (VST3Editor* editor, double newZoom) = 0; +}; + +//------------------------------------------------------------------------ +/** Default adapter implementation for IVST3EditorDelegate */ +class VST3EditorDelegate : public IVST3EditorDelegate +{ +public: + CView* createCustomView (UTF8StringPtr name, const UIAttributes& attributes, + const IUIDescription* description, VST3Editor* editor) override + { + return nullptr; + } + CView* verifyView (CView* view, const UIAttributes& attributes, + const IUIDescription* description, VST3Editor* editor) override + { + return view; + } + bool findParameter (const CPoint& pos, Steinberg::Vst::ParamID& paramID, + VST3Editor* editor) override + { + return false; + } + bool isPrivateParameter (const Steinberg::Vst::ParamID paramID) override { return false; } + void didOpen (VST3Editor* editor) override {} + void willClose (VST3Editor* editor) override {} + COptionMenu* createContextMenu (const CPoint& pos, VST3Editor* editor) override + { + return nullptr; + } + IController* createSubController (UTF8StringPtr name, const IUIDescription* description, + VST3Editor* editor) override { return nullptr; - } ///< create a sub controller + } + void onZoomChanged (VST3Editor* editor, double newZoom) override {} }; //----------------------------------------------------------------------------- @@ -80,7 +129,12 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, void setAllowedZoomFactors (std::vector zoomFactors) { allowedZoomFactors = zoomFactors; } -//----------------------------------------------------------------------------- + /** set the delegate of the editor. no reference counting is happening here. */ + void setDelegate (IVST3EditorDelegate* delegate); + IVST3EditorDelegate* getDelegate () const; + UIDescription* getUIDescription () const; + + //----------------------------------------------------------------------------- DELEGATE_REFCOUNT(Steinberg::Vst::VSTGUIEditor) Steinberg::tresult PLUGIN_API queryInterface (const ::Steinberg::TUID iid, void** obj) override; protected: @@ -89,6 +143,7 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, double getAbsScaleFactor () const; ParameterChangeListener* getParameterChangeListener (int32_t tag) const; void recreateView (); + void requestRecreateView (); void syncParameterTags (); void save (bool saveAs = false); @@ -104,8 +159,6 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override; IController* createSubController (UTF8StringPtr name, const IUIDescription* description) override; - CMessageResult notify (CBaseObject* sender, IdStringPtr message) override; - bool beforeSizeChange (const CRect& newSize, const CRect& oldSize) override; Steinberg::tresult PLUGIN_API onSize (Steinberg::ViewRect* newSize) override; @@ -129,8 +182,7 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, // IMouseObserver void onMouseEntered (CView* view, CFrame* frame) override {} void onMouseExited (CView* view, CFrame* frame) override {} - CMouseEventResult onMouseMoved (CFrame* frame, const CPoint& where, const CButtonState& buttons) override { return kMouseEventNotHandled; } - CMouseEventResult onMouseDown (CFrame* frame, const CPoint& where, const CButtonState& buttons) override; + void onMouseEvent (MouseEvent& event, CFrame* frame) override; // CommandMenuItemTargetAdapter bool validateCommandMenuItem (CCommandMenuItem* item) override; @@ -143,7 +195,7 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, struct KeyboardHook; KeyboardHook* keyboardHook {nullptr}; UIDescription* description {nullptr}; - VST3EditorDelegate* delegate {nullptr}; + IVST3EditorDelegate* delegate {nullptr}; IController* originalController {nullptr}; using ParameterChangeListenerMap = std::map; ParameterChangeListenerMap paramChangeListeners; @@ -152,7 +204,6 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, bool tooltipsEnabled {true}; bool doCreateView {false}; bool editingEnabled {false}; - bool requestResizeGuard {false}; double contentScaleFactor {1.}; double zoomFactor {1.}; @@ -161,6 +212,9 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, CPoint minSize; CPoint maxSize; CRect nonEditRect; + + Optional sizeRequest; }; -} // namespace +//------------------------------------------------------------------------ +} // VSTGUI diff --git a/vstgui/standalone/CMakeLists.txt b/vstgui/standalone/CMakeLists.txt index 44da317b1..f7b00b642 100644 --- a/vstgui/standalone/CMakeLists.txt +++ b/vstgui/standalone/CMakeLists.txt @@ -122,7 +122,7 @@ endif() add_library(${target} STATIC ${${target}_sources}) target_compile_definitions(${target} ${VSTGUI_COMPILE_DEFINITIONS}) -vstgui_set_cxx_version(${target} 14) +vstgui_set_cxx_version(${target} 17) vstgui_source_group_by_folder(${target}) if(VSTGUI_STANDALONE_EXAMPLES) @@ -136,3 +136,7 @@ if(LINUX) target_include_directories(${target} PRIVATE ${SQLITE3_INCLUDE_DIRS}) target_link_libraries(${target} PRIVATE vstgui_uidescription ${LINUX_LIBRARIES} dl ${SQLITE3_LIBRARIES}) endif() + +if(CMAKE_HOST_APPLE) + target_compile_options(${target} PRIVATE -Wall -Werror) +endif() diff --git a/vstgui/standalone/cmake/modules/vstgui_add_executable.cmake b/vstgui/standalone/cmake/modules/vstgui_add_executable.cmake index 31bff2bb6..233a5ebdb 100644 --- a/vstgui/standalone/cmake/modules/vstgui_add_executable.cmake +++ b/vstgui/standalone/cmake/modules/vstgui_add_executable.cmake @@ -32,12 +32,6 @@ function(vstgui_add_executable target sources) MACOSX_PACKAGE_LOCATION "." ) add_executable(${target} ${sources} ${PkgInfoResource}) - set(PLATFORM_LIBRARIES - "-framework Cocoa" - "-framework OpenGL" - "-framework QuartzCore" - "-framework Accelerate" - ) set_target_properties(${target} PROPERTIES MACOSX_BUNDLE TRUE XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT $<$:dwarf>$<$>:dwarf-with-dsym> @@ -101,6 +95,15 @@ function(vstgui_add_resources target resources) endif() endfunction() +########################################################################################### +function(vstgui_set_target_bundle_id target identifier) + if(CMAKE_HOST_APPLE) + set_target_properties(${target} PROPERTIES + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER ${identifier} + ) + endif(CMAKE_HOST_APPLE) +endfunction() + ########################################################################################### function(vstgui_set_target_infoplist target infoplist) if(CMAKE_HOST_APPLE) diff --git a/vstgui/standalone/examples/mandelbrot/CMakeLists.txt b/vstgui/standalone/examples/mandelbrot/CMakeLists.txt index f99203cc3..1366d2ddf 100644 --- a/vstgui/standalone/examples/mandelbrot/CMakeLists.txt +++ b/vstgui/standalone/examples/mandelbrot/CMakeLists.txt @@ -28,8 +28,9 @@ endif() ########################################################################################## vstgui_add_executable(${target} "${${target}_sources}") vstgui_add_resources(${target} "${mandelbrot_resources}") +vstgui_set_target_bundle_id(${target} "vstgui.examples.mandelbrot") vstgui_set_target_infoplist(${target} "resource/Info.plist") vstgui_set_target_rcfile(${target} "resource/mandelbrot.rc") -vstgui_set_cxx_version(${target} 14) +vstgui_set_cxx_version(${target} 17) target_include_directories(${target} PRIVATE ../../../../) set_target_properties(${target} PROPERTIES ${APP_PROPERTIES} ${VSTGUI_STANDALONE_EXAMPLES_FOLDER}) diff --git a/vstgui/standalone/examples/mandelbrot/resource/Info.plist b/vstgui/standalone/examples/mandelbrot/resource/Info.plist index 722adf74c..c64e510a1 100644 --- a/vstgui/standalone/examples/mandelbrot/resource/Info.plist +++ b/vstgui/standalone/examples/mandelbrot/resource/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable mandelbrot CFBundleIdentifier - vstgui.examples.mandelbrot + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/vstgui/standalone/examples/mandelbrot/source/mandelbrotview.cpp b/vstgui/standalone/examples/mandelbrot/source/mandelbrotview.cpp index 10bb97cb3..17b72b6b5 100644 --- a/vstgui/standalone/examples/mandelbrot/source/mandelbrotview.cpp +++ b/vstgui/standalone/examples/mandelbrot/source/mandelbrotview.cpp @@ -6,6 +6,7 @@ #include "vstgui/lib/cbitmap.h" #include "vstgui/lib/cdrawcontext.h" #include "vstgui/lib/cframe.h" +#include "vstgui/lib/events.h" //------------------------------------------------------------------------ namespace Mandelbrot { @@ -70,14 +71,14 @@ CMouseEventResult View::onMouseCancel () } //------------------------------------------------------------------------ -int32_t View::onKeyDown (VstKeyCode& keyCode) +void View::onKeyboardEvent (KeyboardEvent& event) { - if (keyCode.virt == VKEY_ESCAPE) + if (event.type == EventType::KeyDown && event.virt == VirtualKey::Escape) { onMouseCancel (); - return 1; + event.consumed = true; + return; } - return -1; } //------------------------------------------------------------------------ diff --git a/vstgui/standalone/examples/mandelbrot/source/mandelbrotview.h b/vstgui/standalone/examples/mandelbrot/source/mandelbrotview.h index 6d5033b26..94f4290b6 100644 --- a/vstgui/standalone/examples/mandelbrot/source/mandelbrotview.h +++ b/vstgui/standalone/examples/mandelbrot/source/mandelbrotview.h @@ -20,11 +20,12 @@ struct View : public VSTGUI::CView, public VSTGUI::IFocusDrawing using CButtonState = VSTGUI::CButtonState; using CDrawContext = VSTGUI::CDrawContext; using CGraphicsPath = VSTGUI::CGraphicsPath; + using KeyboardEvent = VSTGUI::KeyboardEvent; using ChangedFunc = std::function; View (ChangedFunc&& func); - int32_t onKeyDown (VstKeyCode& keyCode) override; + void onKeyboardEvent (KeyboardEvent& event) override; CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override; CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override; diff --git a/vstgui/standalone/examples/minesweeper/CMakeLists.txt b/vstgui/standalone/examples/minesweeper/CMakeLists.txt index 1801c8bf0..0dd2d9254 100644 --- a/vstgui/standalone/examples/minesweeper/CMakeLists.txt +++ b/vstgui/standalone/examples/minesweeper/CMakeLists.txt @@ -35,9 +35,10 @@ set(Minesweeper_font vstgui_add_executable(${target} "${${target}_sources}" ) vstgui_add_resources(${target} "${Minesweeper_resources}") vstgui_add_resources(${target} "${Minesweeper_font}" "Fonts") +vstgui_set_target_bundle_id(${target} "vstgui.examples.minesweeper") vstgui_set_target_infoplist(${target} "resource/Info.plist") vstgui_set_target_rcfile(${target} "resource/Minesweeper.rc") -vstgui_set_cxx_version(${target} 14) +vstgui_set_cxx_version(${target} 17) target_include_directories(${target} PRIVATE ../../../../) set_target_properties(${target} PROPERTIES ${APP_PROPERTIES} ${VSTGUI_STANDALONE_EXAMPLES_FOLDER}) diff --git a/vstgui/standalone/examples/minesweeper/resource/Info.plist b/vstgui/standalone/examples/minesweeper/resource/Info.plist index c6c32b47f..0dba8fffa 100644 --- a/vstgui/standalone/examples/minesweeper/resource/Info.plist +++ b/vstgui/standalone/examples/minesweeper/resource/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - vstgui.examples.minesweeper + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/vstgui/standalone/examples/simple_standalone/CMakeLists.txt b/vstgui/standalone/examples/simple_standalone/CMakeLists.txt index 723ff1cbf..ef5b07043 100644 --- a/vstgui/standalone/examples/simple_standalone/CMakeLists.txt +++ b/vstgui/standalone/examples/simple_standalone/CMakeLists.txt @@ -14,9 +14,10 @@ set(simple_standalone_resources ########################################################################################## vstgui_add_executable(${target} "${${target}_sources}" ) vstgui_add_resources(${target} "${simple_standalone_resources}") +vstgui_set_target_bundle_id(${target} "vstgui.examples.simplestandalone") vstgui_set_target_infoplist(${target} "resource/Info.plist") vstgui_set_target_rcfile(${target} "resource/simple_standalone.rc") -vstgui_set_cxx_version(${target} 14) +vstgui_set_cxx_version(${target} 17) target_include_directories(${target} PRIVATE ../../../../) set_target_properties(${target} PROPERTIES ${APP_PROPERTIES} ${VSTGUI_STANDALONE_EXAMPLES_FOLDER}) diff --git a/vstgui/standalone/examples/simple_standalone/resource/Info.plist b/vstgui/standalone/examples/simple_standalone/resource/Info.plist index 3cb0baf00..d0152722e 100644 --- a/vstgui/standalone/examples/simple_standalone/resource/Info.plist +++ b/vstgui/standalone/examples/simple_standalone/resource/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable simple_standalone CFBundleIdentifier - vstgui.examples.simplestandalone + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/vstgui/standalone/examples/standalone/CMakeLists.txt b/vstgui/standalone/examples/standalone/CMakeLists.txt index 151c76032..c9a84974c 100644 --- a/vstgui/standalone/examples/standalone/CMakeLists.txt +++ b/vstgui/standalone/examples/standalone/CMakeLists.txt @@ -30,8 +30,9 @@ set(standalone_font vstgui_add_executable(${target} "${standalone_sources}") vstgui_add_resources(${target} "${standalone_resources}") vstgui_add_resources(${target} "${standalone_font}" "Fonts") +vstgui_set_target_bundle_id(${target} "vstgui.examples.standalone") vstgui_set_target_infoplist(${target} "resource/Info.plist") vstgui_set_target_rcfile(${target} "resource/standalone.rc") -vstgui_set_cxx_version(${target} 14) +vstgui_set_cxx_version(${target} 17) target_include_directories(${target} PRIVATE ../../../../) set_target_properties(${target} PROPERTIES ${APP_PROPERTIES} ${VSTGUI_STANDALONE_EXAMPLES_FOLDER}) diff --git a/vstgui/standalone/examples/standalone/resource/Info.plist b/vstgui/standalone/examples/standalone/resource/Info.plist index 1c26872a6..574b459ac 100644 --- a/vstgui/standalone/examples/standalone/resource/Info.plist +++ b/vstgui/standalone/examples/standalone/resource/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - vstgui.examples.standalone + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/vstgui/standalone/examples/standalone/resource/resources.uidesc b/vstgui/standalone/examples/standalone/resource/resources.uidesc index 86eee7359..d83a352e2 100644 --- a/vstgui/standalone/examples/standalone/resource/resources.uidesc +++ b/vstgui/standalone/examples/standalone/resource/resources.uidesc @@ -118,7 +118,7 @@ }, "custom": { "UIDescFilePath": { - "path": "/media/psf/Source/vstgui/vstgui/standalone/examples/standalone/resource/resources.uidesc" + "path": "/Volumes/vst3/vstgui/vstgui/standalone/examples/standalone/resource/resources.uidesc" } } } diff --git a/vstgui/standalone/examples/standalone/resource/test.uidesc b/vstgui/standalone/examples/standalone/resource/test.uidesc index 2944a5f20..fa481af08 100644 --- a/vstgui/standalone/examples/standalone/resource/test.uidesc +++ b/vstgui/standalone/examples/standalone/resource/test.uidesc @@ -24,22 +24,24 @@ }, "UIEditController": { "EditViewScale": "1.5", - "EditorSize": "0, 0, 1158, 1005", - "SplitViewSize_0_0": "0.7436159346271705716802102870133239775896", - "SplitViewSize_0_1": "0.2359550561797752743498080008066608570516", - "SplitViewSize_1_0": "0.5842696629213482983900007639022078365088", - "SplitViewSize_1_1": "0.411644535240040854162657524284441024065", + "EditorSize": "0, 0, 1158, 997", + "SplitViewSize_0_0": "0.7415036045314109536263913469156250357628", + "SplitViewSize_0_1": "0.2378990731204943254173400646322988905013", + "SplitViewSize_1_0": "0.5849639546858907968029939183907117694616", + "SplitViewSize_1_1": "0.4109165808444902312501767482899595052004", "SplitViewSize_2_0": "0.6424870466321243034357735268713440746069", "SplitViewSize_2_1": "0.3531951640759931065893795221199980005622", "TabSwitchValue": "2", - "Version": "1" + "UI Theme": "Dark", + "Version": "1", + "ViewBackground": "3" }, "UIAttributesController": {}, "UIViewCreatorDataSource": { "SelectedRow": "24" }, "UIDescFilePath": { - "path": "/media/psf/Source/vstgui/vstgui/standalone/examples/standalone/resource/test.uidesc" + "path": "/Volumes/vst3/vstgui/vstgui/standalone/examples/standalone/resource/test.uidesc" }, "UITagsDataSource": { "SelectedRow": "-1" @@ -666,6 +668,7 @@ "tooltip": "Mutliline Label", "transparent": "false", "value-precision": "2", + "vertical-centered": "false", "wants-focus": "false", "wheel-inc-value": "0.1" } diff --git a/vstgui/standalone/examples/standalone/source/testappdelegate.cpp b/vstgui/standalone/examples/standalone/source/testappdelegate.cpp index 9db6d967f..2a9c4971f 100644 --- a/vstgui/standalone/examples/standalone/source/testappdelegate.cpp +++ b/vstgui/standalone/examples/standalone/source/testappdelegate.cpp @@ -65,7 +65,6 @@ static Command ShowAlertBoxDesign {CommandGroup::File, "Show AlertBox Design"}; //------------------------------------------------------------------------ class DisabledControlsController : public DelegationController, - public ViewMouseListenerAdapter, public ViewListenerAdapter { public: @@ -75,7 +74,6 @@ class DisabledControlsController : public DelegationController, for (auto control : controls) { control->unregisterViewListener (this); - control->unregisterViewMouseListener (this); } controls.clear (); } @@ -85,7 +83,6 @@ class DisabledControlsController : public DelegationController, { if (auto control = dynamic_cast (view)) { - control->registerViewMouseListener (this); control->registerViewListener (this); controls.push_back (control); } @@ -105,7 +102,6 @@ class DisabledControlsController : public DelegationController, if (it != controls.end ()) { control->unregisterViewListener (this); - control->unregisterViewMouseListener (this); controls.erase (it); } } diff --git a/vstgui/standalone/examples/standalone/source/testmodel.cpp b/vstgui/standalone/examples/standalone/source/testmodel.cpp index 3449c3277..e16cdce61 100644 --- a/vstgui/standalone/examples/standalone/source/testmodel.cpp +++ b/vstgui/standalone/examples/standalone/source/testmodel.cpp @@ -107,12 +107,18 @@ void TestModel::onEndEdit (IValue& value) else if (value.getID () == "ShowPopup" && value.getValue () > 0.5) { value.performEdit (0.); - auto window = IApplication::instance ().getWindows ().front (); - vstgui_assert (window); - auto rect = window->getFocusViewRect (); + CRect rect; + for (auto& window : IApplication::instance ().getWindows ()) + { + if (window->getType () == WindowType::Document) + { + rect = window->getFocusViewRect (); + rect.offset (window->getPosition ()); + break; + } + } if (rect.isEmpty ()) return; - rect.offset (window->getPosition ()); UIDesc::Config config; config.viewName = "view"; config.modelBinding = shared_from_this (); diff --git a/vstgui/standalone/include/helpers/preferences.h b/vstgui/standalone/include/helpers/preferences.h index 7bcf32f8f..e9b39fb45 100644 --- a/vstgui/standalone/include/helpers/preferences.h +++ b/vstgui/standalone/include/helpers/preferences.h @@ -77,6 +77,8 @@ class Preferences { if (auto p = get (key)) { + if constexpr (std::is_floating_point::value) + return UTF8StringView (*p).toFloat (); return UTF8StringView (*p).toNumber (); } return {}; diff --git a/vstgui/standalone/include/helpers/value.h b/vstgui/standalone/include/helpers/value.h index 8ff760c0a..a81f62dfc 100644 --- a/vstgui/standalone/include/helpers/value.h +++ b/vstgui/standalone/include/helpers/value.h @@ -24,7 +24,7 @@ class IStringListValue : public Interface class IMutableStepValue : public Interface { public: - virtual void setNumSteps (IStepValue::StepType numSteps) = 0; + virtual bool setNumSteps (IStepValue::StepType numSteps) = 0; }; //------------------------------------------------------------------------ @@ -67,7 +67,7 @@ ValuePtr make (const UTF8String& id, IValue::Type initialValue = 0., /** make a step value * * @param id value ID - * @param numSteps number of discrete steps + * @param numSteps number of discrete steps, must be greater than zero * @param initialValue initial value in the normalized range [0..1] * @param valueConverter value converter * @return shared value pointer diff --git a/vstgui/standalone/source/helpers/value.cpp b/vstgui/standalone/source/helpers/value.cpp index bba7cf9fd..da9429cde 100644 --- a/vstgui/standalone/source/helpers/value.cpp +++ b/vstgui/standalone/source/helpers/value.cpp @@ -83,7 +83,7 @@ class DefaultValueConverter : public IValueConverter sstream.precision (stringPrecision); sstream >> value; value = plainToNormalized (value); - if (value < 0. || value > 1.) + if (sstream.fail () || value < 0. || value > 1.) return IValue::InvalidValue; return value; } @@ -313,7 +313,7 @@ class StepValue : public Value, public IStepValue, public IValueConverter, publi const IValueConverter& getConverter () const override; - void setNumSteps (StepType numSteps) override; + bool setNumSteps (StepType numSteps) override; private: StepType steps; @@ -424,6 +424,7 @@ StepValue::StepValue (const UTF8String& id, StepType initialSteps, Type initialV const ValueConverterPtr& stringConverter) : Value (id, initialValue, stringConverter), steps (initialSteps - 1) { + vstgui_assert (initialSteps > 0); } //------------------------------------------------------------------------ @@ -466,7 +467,7 @@ IValue::Type StepValue::stringAsValue (const UTF8String& string) const std::istringstream sstream (string.getString ()); sstream.imbue (std::locale::classic ()); sstream >> v; - if (v > steps) + if (sstream.fail () || v > steps) return IValue::InvalidValue; return stepToValue (v); } @@ -492,10 +493,16 @@ const IValueConverter& StepValue::getConverter () const } //------------------------------------------------------------------------ -void StepValue::setNumSteps (StepType numSteps) +bool StepValue::setNumSteps (StepType numSteps) { + if (numSteps == 0) + { + vstgui_assert (numSteps > 0, "numSteps must be greater than zero"); + return false; + } steps = numSteps - 1; dispatchStateChange (); + return true; } //------------------------------------------------------------------------ @@ -508,6 +515,8 @@ StringListValue::StringListValue (const UTF8String& id, StepType initialSteps, T //------------------------------------------------------------------------ bool StringListValue::updateStringList (const StringList& newStrings) { + if (newStrings.empty ()) + return false; setValueConverter (std::make_shared (newStrings)); setNumSteps (static_cast (newStrings.size ())); return true; @@ -538,11 +547,14 @@ ValuePtr make (const UTF8String& id, IValue::Type initialValue, } //------------------------------------------------------------------------ -ValuePtr makeStepValue (const UTF8String& id, IStepValue::StepType initialSteps, - IValue::Type initialValue, const ValueConverterPtr& stringConverter) +ValuePtr makeStepValue (const UTF8String& id, IStepValue::StepType numSteps, + IValue::Type initialValue, const ValueConverterPtr& stringConverter) { vstgui_assert (id.empty () == false); - return std::make_shared (id, initialSteps, initialValue, stringConverter); + vstgui_assert (numSteps > 0, "numSteps must be greater than 0"); + if (numSteps == 0) + return {}; + return std::make_shared (id, numSteps, initialValue, stringConverter); } //------------------------------------------------------------------------ diff --git a/vstgui/standalone/source/platform/gdk/gdkwindow.cpp b/vstgui/standalone/source/platform/gdk/gdkwindow.cpp index ed4b03469..a3276c11a 100644 --- a/vstgui/standalone/source/platform/gdk/gdkwindow.cpp +++ b/vstgui/standalone/source/platform/gdk/gdkwindow.cpp @@ -13,6 +13,8 @@ #include #include +#define VSTGUI_LOG_X11_WINDOW (DEBUG && 1) + //------------------------------------------------------------------------ namespace VSTGUI { namespace Standalone { @@ -56,6 +58,7 @@ void sendXEmbedProtocolMessage (::Window receiver, ::Window parentWindow, XEmbed ev.xclient.window = receiver; ev.xclient.message_type = xEmbedAtom; ev.xclient.format = 32; + ev.xclient.display = xDisplay; ev.xclient.data.l[0] = CurrentTime; ev.xclient.data.l[1] = static_cast (message); ev.xclient.data.l[2] = detail; @@ -108,6 +111,7 @@ class Window CPoint lastPos; CPoint lastSize; + bool isShown {false}; WindowStyle style; WindowType type; @@ -240,40 +244,66 @@ void Window::updateGeometryHints () {} //------------------------------------------------------------------------ CPoint Window::getSize () const { + if (!isShown) + return lastSize; auto scaleFactor = getScaleFactor (); CPoint size; size.x = gtkWindow.get_width () / scaleFactor; size.y = gtkWindow.get_height () / scaleFactor; +#if VSTGUI_LOG_X11_WINDOW + DebugPrint ("Window::getSize (): %d, %d\n", static_cast (size.x), static_cast (size.y)); +#endif return size; } //------------------------------------------------------------------------ CPoint Window::getPosition () const { + if (!isShown) + return lastPos; auto scaleFactor = getScaleFactor (); int x, y; gtkWindow.get_position (x, y); - return CPoint (x / scaleFactor, y / scaleFactor); + CPoint result (x / scaleFactor, y / scaleFactor); +#if VSTGUI_LOG_X11_WINDOW + DebugPrint ("Window::getPosition (): %d, %d\n", static_cast (result.x), static_cast (result.y)); +#endif + return result; } //------------------------------------------------------------------------ double Window::getScaleFactor () const { - return static_cast (gtkWindow.get_scale_factor ()); + auto factor = static_cast (gtkWindow.get_scale_factor ()); + return factor; } //------------------------------------------------------------------------ void Window::setSize (const CPoint& newSize) { auto scaleFactor = getScaleFactor (); - gtkWindow.resize (newSize.x * scaleFactor, newSize.y * scaleFactor); + auto width = static_cast (std::ceil (newSize.x * scaleFactor)); + auto height = static_cast (std::ceil (newSize.y * scaleFactor)); +#if VSTGUI_LOG_X11_WINDOW + DebugPrint ("Window::setSize (): %d - %d\n", width, height); +#endif + gtkWindow.resize (width, height); + if (!isShown) + lastSize = newSize; } //------------------------------------------------------------------------ void Window::setPosition (const CPoint& newPosition) { auto scaleFactor = getScaleFactor (); - gtkWindow.move (newPosition.x * scaleFactor, newPosition.y * scaleFactor); + auto x = static_cast (std::floor (newPosition.x * scaleFactor)); + auto y = static_cast (std::floor (newPosition.y * scaleFactor)); +#if VSTGUI_LOG_X11_WINDOW + DebugPrint ("Window::setPosition (): %d - %d\n", x, y); +#endif + gtkWindow.move (x, y); + if (!isShown) + lastPos = newPosition; } //------------------------------------------------------------------------ @@ -295,6 +325,8 @@ WindowStyle Window::changeStyle (WindowStyle stylesToAdd, WindowStyle stylesToRe //------------------------------------------------------------------------ void Window::show () { + isShown = true; + lastPos = lastSize = {}; updateGeometryHints (); gtkWindow.show_all (); activate (); @@ -307,6 +339,7 @@ void Window::show () //------------------------------------------------------------------------ void Window::hide () { + isShown = false; gtkWindow.hide (); } @@ -394,11 +427,17 @@ void Window::handleEventConfigure (GdkEventConfigure* event) if (newPos != lastPos) { lastPos = newPos; +#if VSTGUI_LOG_X11_WINDOW + DebugPrint ("Window::onPositionChanged (): %d - %d\n", static_cast (newPos.x), static_cast (newPos.y)); +#endif delegate->onPositionChanged (newPos); } if (newSize != lastSize) { lastSize = newSize; +#if VSTGUI_LOG_X11_WINDOW + DebugPrint ("Window::onSizeChanged (): %d - %d\n", static_cast (newSize.x), static_cast (newSize.y)); +#endif delegate->onSizeChanged (newSize); if (contentView) { diff --git a/vstgui/standalone/source/platform/win32/win32application.cpp b/vstgui/standalone/source/platform/win32/win32application.cpp index a12a91ef1..01815c11a 100644 --- a/vstgui/standalone/source/platform/win32/win32application.cpp +++ b/vstgui/standalone/source/platform/win32/win32application.cpp @@ -315,6 +315,22 @@ void Application::run () } // Standalone } // VSTGUI +//---Workaround for Visual with ARM64EC platform +// Reported to Microsoft (29.03.2022): +// https://developercommunity.visualstudio.com/t/LINK-:-error-LNK2001:-unresolved-externa/1699333 +#if defined(_M_ARM64EC) +#include +//------------------------------------------------------------------------ +int APIENTRY WinMain (_In_ HINSTANCE instance, _In_opt_ HINSTANCE prevInstance, + _In_ LPSTR _lpCmdLine, _In_ int nCmdShow) +{ + USES_CONVERSION; + auto cmdLine = A2W (_lpCmdLine); + return wWinMain (instance, prevInstance, cmdLine, nCmdShow); +} +#endif +//---Workaround end + //------------------------------------------------------------------------ int APIENTRY wWinMain (_In_ HINSTANCE instance, _In_opt_ HINSTANCE prevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) diff --git a/vstgui/standalone/source/platform/win32/win32window.cpp b/vstgui/standalone/source/platform/win32/win32window.cpp index e487270eb..cbf593031 100644 --- a/vstgui/standalone/source/platform/win32/win32window.cpp +++ b/vstgui/standalone/source/platform/win32/win32window.cpp @@ -7,6 +7,7 @@ #include "win32async.h" #include "../../../../lib/platform/win32/direct2d/d2ddrawcontext.h" +#include "../../../../lib/platform/win32/win32directcomposition.h" #include "../../../../lib/platform/win32/win32factory.h" #include "../../../../lib/platform/win32/win32frame.h" #include "../../../../lib/platform/win32/win32dll.h" @@ -171,6 +172,9 @@ bool Window::init (const WindowConfiguration& config, IWindowDelegate& inDelegat style = config.style; + bool directComposition = + getPlatformFactory ().asWin32Factory ()->getDirectCompositionFactory () != nullptr; + if (config.type == WindowType::Popup) { isPopup = true; @@ -204,7 +208,17 @@ bool Window::init (const WindowConfiguration& config, IWindowDelegate& inDelegat } } if (style.isTransparent ()) - exStyle |= WS_EX_LAYERED; + { + if (directComposition) + { + exStyle = WS_EX_NOREDIRECTIONBITMAP; + exStyle &= ~WS_EX_COMPOSITED; + } + else + { + exStyle |= WS_EX_LAYERED; + } + } initialSize = config.size; auto winStr = dynamic_cast (config.title.getPlatformString ()); hwnd = CreateWindowEx (exStyle, gWindowClassName, winStr ? winStr->getWideString () : nullptr, @@ -281,6 +295,7 @@ void Window::updateCommands () const mainMenu = std::make_shared (""); std::shared_ptr fileMenu = nullptr; + std::shared_ptr debugMenu = nullptr; const Detail::IPlatformApplication::CommandWithKeyList* appCommands = nullptr; for (auto& e : menuCommandList) @@ -296,6 +311,8 @@ void Window::updateCommands () const mainMenu->addSubMenu (subMenu); if (e.first == CommandGroup::File) fileMenu = subMenu; + else if (e.first == CommandGroup::Debug) + debugMenu = subMenu; } } if (appCommands) @@ -329,6 +346,11 @@ void Window::updateCommands () const } mainMenu->addSubMenu (menu); } + if (debugMenu) + { + if (getPlatformFactory ().asWin32Factory ()->getDirectCompositionFactory ()) + debugMenu->addItem ("Visualize Redraw Areas"); + } SetMenu (hwnd, *mainMenu); } @@ -389,15 +411,27 @@ void Window::validateMenu (Win32Menu* menu) //------------------------------------------------------------------------ void Window::handleMenuCommand (const UTF8String& group, const UTF8String& name) { + bool commandHandled = false; Command command = mapCommand ({group, name}); if (delegate->canHandleCommand (command)) - delegate->handleCommand (command); + commandHandled = delegate->handleCommand (command); else { if (auto commandHandler = Detail::getApplicationPlatformAccess ()) { if (commandHandler->canHandleCommand (command)) - commandHandler->handleCommand (command); + commandHandled = commandHandler->handleCommand (command); + } + } + if (!commandHandled) + { + if (group == CommandGroup::Debug && name == "Visualize Redraw Areas") + { + if (auto dcSupport = + getPlatformFactory ().asWin32Factory ()->getDirectCompositionFactory ()) + { + dcSupport->enableVisualizeRedrawAreas (!dcSupport->isVisualRedrawAreasEnabled ()); + } } } } @@ -408,6 +442,18 @@ static CPoint getRectSize (const RECT& r) return {static_cast (r.right - r.left), static_cast (r.bottom - r.top)}; } +//------------------------------------------------------------------------ +static CPoint mapPOINT (const POINT& p) +{ + return {static_cast (p.x), static_cast (p.y)}; +} + +//------------------------------------------------------------------------ +static POINT mapCPoint (const CPoint& p) +{ + return {static_cast (p.x), static_cast (p.y)}; +} + //------------------------------------------------------------------------ void Window::makeTransparent () { @@ -429,6 +475,24 @@ LRESULT CALLBACK Window::proc (UINT message, WPARAM wParam, LPARAM lParam) delegate->onPositionChanged (getPosition ()); break; } + case WM_GETMINMAXINFO: + { + if (auto minmaxInfo = reinterpret_cast (lParam)) + { + auto p = mapPOINT (minmaxInfo->ptMinTrackSize); + frame->getTransform ().inverse ().transform (p); + p = delegate->constraintSize (p); + frame->getTransform ().transform (p); + minmaxInfo->ptMinTrackSize = mapCPoint (p); + p = mapPOINT (minmaxInfo->ptMaxTrackSize); + frame->getTransform ().inverse ().transform (p); + p = delegate->constraintSize (p); + frame->getTransform ().transform (p); + minmaxInfo->ptMaxTrackSize = mapCPoint (p); + return 0; + } + break; + } case WM_SIZE: { auto size = getSize (); @@ -739,7 +803,7 @@ bool Window::nonClientHitTest (LPARAM& lParam, LRESULT& result) // TODO: add other edges } CPoint where {static_cast (p.x), static_cast (p.y)}; - if (!frame->hitTestSubViews (where)) + if (!frame->hitTestSubViews (where, noEvent ())) { result = HTCAPTION; return true; diff --git a/vstgui/standalone/source/shareduiresources.cpp b/vstgui/standalone/source/shareduiresources.cpp index 70b957d99..c65a236f3 100644 --- a/vstgui/standalone/source/shareduiresources.cpp +++ b/vstgui/standalone/source/shareduiresources.cpp @@ -282,6 +282,7 @@ UIDescCheckFilePathResult checkAndUpdateUIDescFilePath (UIDescription& uiDesc, C if (savedPath) fs->setInitialDirectory (*savedPath); fs->setDefaultExtension (CFileExtension ("UIDescription File", "uidesc")); + fs->setTitle ("Please locate the shared resources uidesc file"); if (fs->runModal ()) { if (fs->getNumSelectedFiles () == 0) diff --git a/vstgui/standalone/source/window.cpp b/vstgui/standalone/source/window.cpp index 2419cde54..89f448513 100644 --- a/vstgui/standalone/source/window.cpp +++ b/vstgui/standalone/source/window.cpp @@ -7,6 +7,7 @@ #include "../../lib/cframe.h" #include "../../lib/controls/coptionmenu.h" #include "../../lib/dispatchlist.h" +#include "../../lib/events.h" #include "../../uidescription/icontroller.h" #include "../include/iapplication.h" #include "../include/icommand.h" @@ -123,12 +124,7 @@ class Window : public IPlatformWindowAccess, // IMouseObserver void onMouseEntered (CView*, CFrame* ) override {}; void onMouseExited (CView*, CFrame* ) override {}; - CMouseEventResult onMouseMoved (CFrame*, const CPoint&, const CButtonState&) override - { - return kMouseEventNotHandled; - } - CMouseEventResult onMouseDown (CFrame* frame, const CPoint& where, - const CButtonState& buttons) override; + void onMouseEvent (MouseEvent& event, CFrame*) override; private: WindowControllerPtr controller; @@ -395,16 +391,16 @@ struct WindowContextMenuCommandHandler : ICommandMenuItemTarget, NonAtomicRefere Window* window; }; + //------------------------------------------------------------------------ -CMouseEventResult Window::onMouseDown (CFrame* inFrame, const CPoint& _where, - const CButtonState& buttons) +void Window::onMouseEvent (MouseEvent& event, CFrame* inFrame) { - if (!buttons.isRightButton ()) - return kMouseEventNotHandled; + if (event.type != EventType::MouseDown || !event.buttonState.isRight ()) + return; auto contextMenu = makeOwned (); - CPoint where (_where); + CPoint where (event.mousePosition); inFrame->getTransform ().transform (where); CViewContainer::ViewList views; @@ -419,7 +415,7 @@ CMouseEventResult Window::onMouseDown (CFrame* inFrame, const CPoint& _where, continue; if (contextMenu->getNbEntries () != 0) contextMenu->addSeparator (); - CPoint p (_where); + CPoint p (event.mousePosition); view->frameToLocal (p); if (contextMenuController2) contextMenuController2->appendContextMenuItems (*contextMenu, view, p); @@ -465,11 +461,10 @@ CMouseEventResult Window::onMouseDown (CFrame* inFrame, const CPoint& _where, { contextMenu->cleanupSeparators (true); contextMenu->setStyle (COptionMenu::kPopupStyle | COptionMenu::kMultipleCheckStyle); - contextMenu->popup (inFrame, _where); - return kMouseDownEventHandledButDontNeedMovedOrUpEvents; + contextMenu->popup (inFrame, event.mousePosition); + event.consumed = true; + castMouseDownEvent (event).ignoreFollowUpMoveAndUpEvents (true); } - - return kMouseEventNotHandled; } //------------------------------------------------------------------------ diff --git a/vstgui/tests/base64codecspeed/CMakeLists.txt b/vstgui/tests/base64codecspeed/CMakeLists.txt index 354bb3e95..6a7fb8e37 100644 --- a/vstgui/tests/base64codecspeed/CMakeLists.txt +++ b/vstgui/tests/base64codecspeed/CMakeLists.txt @@ -17,6 +17,6 @@ target_link_libraries(${target} ${${target}_PLATFORM_LIBS} ) -vstgui_set_cxx_version(${target} 14) +vstgui_set_cxx_version(${target} 17) set_target_properties(${target} PROPERTIES ${APP_PROPERTIES} FOLDER Tests) target_compile_definitions(${target} ${VSTGUI_COMPILE_DEFINITIONS}) diff --git a/vstgui/tests/gfxtest/CMakeLists.txt b/vstgui/tests/gfxtest/CMakeLists.txt index 0bb923d37..bde2ee845 100644 --- a/vstgui/tests/gfxtest/CMakeLists.txt +++ b/vstgui/tests/gfxtest/CMakeLists.txt @@ -9,60 +9,17 @@ set(${target}_sources "source/drawdevicetests.h" ) -########################################################################################## -if(CMAKE_HOST_APPLE) - set(BUNDLE_PKG_INFO - "resource/PkgInfo" - ) - set(gfxtest_resources - "resource/Window.uidesc" - "resource/DrawDeviceTests.uidesc" - ) - set_source_files_properties(${BUNDLE_PKG_INFO} PROPERTIES - MACOSX_PACKAGE_LOCATION "." - ) - set_source_files_properties(${gfxtest_resources} PROPERTIES - MACOSX_PACKAGE_LOCATION "Resources" - ) - set(gfxtest_resources ${gfxtest_resources} ${BUNDLE_PKG_INFO}) - - set(${target}_PLATFORM_LIBS - "-framework Cocoa" - "-framework OpenGL" - "-framework QuartzCore" - "-framework Accelerate" - ) - get_filename_component(InfoPlistFile "resource/Info.plist" ABSOLUTE) - set(APP_PROPERTIES - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_INFO_PLIST ${InfoPlistFile} - OUTPUT_NAME "GFXTest" - ) -endif() - -########################################################################################## -if(MSVC) - set(gfxtest_resources - "resource/gfxtest.rc" - ) - set(APP_PROPERTIES - LINK_FLAGS "/SUBSYSTEM:windows /INCLUDE:wWinMain" - ) -endif() - -########################################################################################## -include_directories(../../../) -add_executable(${target} - ${${target}_sources} - ${gfxtest_resources} -) -target_link_libraries(${target} - vstgui - vstgui_uidescription - vstgui_standalone - ${${target}_PLATFORM_LIBS} +set(${target}_resources + "resource/Window.uidesc" + "resource/DrawDeviceTests.uidesc" ) -vstgui_set_cxx_version(${target} 14) +vstgui_add_executable(${target} "${${target}_sources}") +vstgui_add_resources(${target} "${${target}_resources}") +vstgui_set_target_bundle_id(${target} "vstgui.tests.gfxtest") +vstgui_set_target_infoplist(${target} "resource/Info.plist") +vstgui_set_target_rcfile(${target} "resource/gfxtest.rc") +vstgui_set_cxx_version(${target} 17) +target_include_directories(${target} PRIVATE ../../../) set_target_properties(${target} PROPERTIES ${APP_PROPERTIES} FOLDER Tests) -target_compile_definitions(${target} ${VSTGUI_COMPILE_DEFINITIONS}) + diff --git a/vstgui/tests/gfxtest/resource/Info.plist b/vstgui/tests/gfxtest/resource/Info.plist index 5ca401181..1c15aa98f 100644 --- a/vstgui/tests/gfxtest/resource/Info.plist +++ b/vstgui/tests/gfxtest/resource/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable GFXTest CFBundleIdentifier - vstgui.tests.gfxtest + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/vstgui/tests/ios standalone/CMakeLists.txt b/vstgui/tests/ios standalone/CMakeLists.txt new file mode 100644 index 000000000..6a5df99fe --- /dev/null +++ b/vstgui/tests/ios standalone/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.12) + +project(vstgui_ios_test) + +if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "iOS") + message(FATAL_ERROR "Unsupported system (${CMAKE_SYSTEM_NAME}). Use -DCMAKE_SYSTEM_NAME=iOS when invoking cmake") +endif() + +set(target "iostest") +set(sources + "../../vstgui_ios.mm" + "../../vstgui_uidescription.cpp" + "iOS Standalone/AppDelegate.h" + "iOS Standalone/AppDelegate.mm" + "iOS Standalone/main.m" + "iOS Standalone/ViewController.h" + "iOS Standalone/ViewController.mm" +) + +set(resources + "iOS Standalone/ios_test.uidesc" +) + +add_executable(${target} ${sources}) +set_source_files_properties(${resources} PROPERTIES + MACOSX_PACKAGE_LOCATION "Resources" +) +target_sources(${target} PRIVATE ${resources}) +target_sources(${target} PRIVATE ${resources}) +target_include_directories(${target} PRIVATE "../../../") +target_compile_features(${target} PUBLIC cxx_std_17) + +target_link_libraries(${target} + "-framework UIKit" + "-framework QuartzCore" + "-framework CoreGraphics" + "-framework CoreText" + "-framework ImageIO" + "-framework MobileCoreServices" + "-framework Accelerate" +) + +set(bundleID net.sourceforge.vstgui.iostest) + +set_target_properties(${target} PROPERTIES + MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0 + MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 + MACOSX_BUNDLE_GUI_IDENTIFIER ${bundleID} + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER ${bundleID} +) diff --git a/vstgui/tests/ios standalone/iOS Standalone.xcodeproj/project.pbxproj b/vstgui/tests/ios standalone/iOS Standalone.xcodeproj/project.pbxproj deleted file mode 100644 index b73b6a904..000000000 --- a/vstgui/tests/ios standalone/iOS Standalone.xcodeproj/project.pbxproj +++ /dev/null @@ -1,365 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - BCB2A78B1FFD0C2B00DB75B7 /* vstgui_ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = BCB2A78A1FFD0C2B00DB75B7 /* vstgui_ios.mm */; }; - BCB2A78D1FFD0D1A00DB75B7 /* vstgui_uidescription_ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = BCB2A78C1FFD0D1A00DB75B7 /* vstgui_uidescription_ios.mm */; }; - F44C94D519C344FF0075CC67 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F44C94D419C344FF0075CC67 /* Foundation.framework */; }; - F44C94D719C344FF0075CC67 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F44C94D619C344FF0075CC67 /* CoreGraphics.framework */; }; - F44C94D919C344FF0075CC67 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F44C94D819C344FF0075CC67 /* UIKit.framework */; }; - F44C94DF19C344FF0075CC67 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F44C94DD19C344FF0075CC67 /* InfoPlist.strings */; }; - F44C94E119C344FF0075CC67 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F44C94E019C344FF0075CC67 /* main.m */; }; - F44C94E519C344FF0075CC67 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = F44C94E419C344FF0075CC67 /* AppDelegate.mm */; }; - F44C94E819C344FF0075CC67 /* Main_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F44C94E619C344FF0075CC67 /* Main_iPhone.storyboard */; }; - F44C94EB19C344FF0075CC67 /* Main_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F44C94E919C344FF0075CC67 /* Main_iPad.storyboard */; }; - F44C94EE19C344FF0075CC67 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F44C94ED19C344FF0075CC67 /* ViewController.mm */; }; - F44C951319C34BED0075CC67 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F44C951219C34BED0075CC67 /* Accelerate.framework */; }; - F44C951719C34BFD0075CC67 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F44C951619C34BFD0075CC67 /* ImageIO.framework */; }; - F44C951919C34C160075CC67 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F44C951819C34C160075CC67 /* MobileCoreServices.framework */; }; - F44C951C19C351550075CC67 /* ios_test.uidesc in Resources */ = {isa = PBXBuildFile; fileRef = F44C951B19C351550075CC67 /* ios_test.uidesc */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - BCB2A78A1FFD0C2B00DB75B7 /* vstgui_ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = vstgui_ios.mm; path = ../../vstgui_ios.mm; sourceTree = ""; }; - BCB2A78C1FFD0D1A00DB75B7 /* vstgui_uidescription_ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = vstgui_uidescription_ios.mm; path = ../../vstgui_uidescription_ios.mm; sourceTree = ""; }; - F44C94D119C344FF0075CC67 /* VSTGUI Test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "VSTGUI Test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - F44C94D419C344FF0075CC67 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - F44C94D619C344FF0075CC67 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - F44C94D819C344FF0075CC67 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; - F44C94DC19C344FF0075CC67 /* iOS Standalone-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOS Standalone-Info.plist"; sourceTree = ""; }; - F44C94DE19C344FF0075CC67 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - F44C94E019C344FF0075CC67 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - F44C94E219C344FF0075CC67 /* iOS Standalone-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iOS Standalone-Prefix.pch"; sourceTree = ""; }; - F44C94E319C344FF0075CC67 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - F44C94E419C344FF0075CC67 /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = ""; }; - F44C94E719C344FF0075CC67 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPhone.storyboard; sourceTree = ""; }; - F44C94EA19C344FF0075CC67 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPad.storyboard; sourceTree = ""; }; - F44C94EC19C344FF0075CC67 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; - F44C94ED19C344FF0075CC67 /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = ""; }; - F44C94F619C344FF0075CC67 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; - F44C951219C34BED0075CC67 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; - F44C951419C34BF20075CC67 /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = System/Library/Frameworks/CoreImage.framework; sourceTree = SDKROOT; }; - F44C951619C34BFD0075CC67 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; }; - F44C951819C34C160075CC67 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; - F44C951B19C351550075CC67 /* ios_test.uidesc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = ios_test.uidesc; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - F44C94CE19C344FF0075CC67 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - F44C951919C34C160075CC67 /* MobileCoreServices.framework in Frameworks */, - F44C951719C34BFD0075CC67 /* ImageIO.framework in Frameworks */, - F44C951319C34BED0075CC67 /* Accelerate.framework in Frameworks */, - F44C94D719C344FF0075CC67 /* CoreGraphics.framework in Frameworks */, - F44C94D919C344FF0075CC67 /* UIKit.framework in Frameworks */, - F44C94D519C344FF0075CC67 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - F44C94C819C344FF0075CC67 = { - isa = PBXGroup; - children = ( - BCB2A78A1FFD0C2B00DB75B7 /* vstgui_ios.mm */, - BCB2A78C1FFD0D1A00DB75B7 /* vstgui_uidescription_ios.mm */, - F44C94DA19C344FF0075CC67 /* iOS Standalone */, - F44C94D319C344FF0075CC67 /* Frameworks */, - F44C94D219C344FF0075CC67 /* Products */, - ); - sourceTree = ""; - }; - F44C94D219C344FF0075CC67 /* Products */ = { - isa = PBXGroup; - children = ( - F44C94D119C344FF0075CC67 /* VSTGUI Test.app */, - ); - name = Products; - sourceTree = ""; - }; - F44C94D319C344FF0075CC67 /* Frameworks */ = { - isa = PBXGroup; - children = ( - F44C951819C34C160075CC67 /* MobileCoreServices.framework */, - F44C951619C34BFD0075CC67 /* ImageIO.framework */, - F44C951419C34BF20075CC67 /* CoreImage.framework */, - F44C951219C34BED0075CC67 /* Accelerate.framework */, - F44C94D419C344FF0075CC67 /* Foundation.framework */, - F44C94D619C344FF0075CC67 /* CoreGraphics.framework */, - F44C94D819C344FF0075CC67 /* UIKit.framework */, - F44C94F619C344FF0075CC67 /* XCTest.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - F44C94DA19C344FF0075CC67 /* iOS Standalone */ = { - isa = PBXGroup; - children = ( - F44C94E319C344FF0075CC67 /* AppDelegate.h */, - F44C94E419C344FF0075CC67 /* AppDelegate.mm */, - F44C94E619C344FF0075CC67 /* Main_iPhone.storyboard */, - F44C94E919C344FF0075CC67 /* Main_iPad.storyboard */, - F44C94EC19C344FF0075CC67 /* ViewController.h */, - F44C94ED19C344FF0075CC67 /* ViewController.mm */, - F44C94DB19C344FF0075CC67 /* Supporting Files */, - ); - path = "iOS Standalone"; - sourceTree = ""; - }; - F44C94DB19C344FF0075CC67 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - F44C951B19C351550075CC67 /* ios_test.uidesc */, - F44C94DC19C344FF0075CC67 /* iOS Standalone-Info.plist */, - F44C94DD19C344FF0075CC67 /* InfoPlist.strings */, - F44C94E019C344FF0075CC67 /* main.m */, - F44C94E219C344FF0075CC67 /* iOS Standalone-Prefix.pch */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - F44C94D019C344FF0075CC67 /* iOS Standalone */ = { - isa = PBXNativeTarget; - buildConfigurationList = F44C950619C344FF0075CC67 /* Build configuration list for PBXNativeTarget "iOS Standalone" */; - buildPhases = ( - F44C94CD19C344FF0075CC67 /* Sources */, - F44C94CE19C344FF0075CC67 /* Frameworks */, - F44C94CF19C344FF0075CC67 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "iOS Standalone"; - productName = "iOS Standalone"; - productReference = F44C94D119C344FF0075CC67 /* VSTGUI Test.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - F44C94C919C344FF0075CC67 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0510; - }; - buildConfigurationList = F44C94CC19C344FF0075CC67 /* Build configuration list for PBXProject "iOS Standalone" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - English, - en, - Base, - ); - mainGroup = F44C94C819C344FF0075CC67; - productRefGroup = F44C94D219C344FF0075CC67 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - F44C94D019C344FF0075CC67 /* iOS Standalone */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - F44C94CF19C344FF0075CC67 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F44C951C19C351550075CC67 /* ios_test.uidesc in Resources */, - F44C94EB19C344FF0075CC67 /* Main_iPad.storyboard in Resources */, - F44C94E819C344FF0075CC67 /* Main_iPhone.storyboard in Resources */, - F44C94DF19C344FF0075CC67 /* InfoPlist.strings in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - F44C94CD19C344FF0075CC67 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F44C94EE19C344FF0075CC67 /* ViewController.mm in Sources */, - BCB2A78B1FFD0C2B00DB75B7 /* vstgui_ios.mm in Sources */, - BCB2A78D1FFD0D1A00DB75B7 /* vstgui_uidescription_ios.mm in Sources */, - F44C94E519C344FF0075CC67 /* AppDelegate.mm in Sources */, - F44C94E119C344FF0075CC67 /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - F44C94DD19C344FF0075CC67 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - F44C94DE19C344FF0075CC67 /* en */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - F44C94E619C344FF0075CC67 /* Main_iPhone.storyboard */ = { - isa = PBXVariantGroup; - children = ( - F44C94E719C344FF0075CC67 /* Base */, - ); - name = Main_iPhone.storyboard; - sourceTree = ""; - }; - F44C94E919C344FF0075CC67 /* Main_iPad.storyboard */ = { - isa = PBXVariantGroup; - children = ( - F44C94EA19C344FF0075CC67 /* Base */, - ); - name = Main_iPad.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - F44C950419C344FF0075CC67 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - F44C950519C344FF0075CC67 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - F44C950719C344FF0075CC67 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "iOS Standalone/iOS Standalone-Prefix.pch"; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - ../../../, - ); - INFOPLIST_FILE = "iOS Standalone/iOS Standalone-Info.plist"; - PRODUCT_NAME = "VSTGUI Test"; - WRAPPER_EXTENSION = app; - }; - name = Debug; - }; - F44C950819C344FF0075CC67 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "iOS Standalone/iOS Standalone-Prefix.pch"; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - ../../../, - ); - INFOPLIST_FILE = "iOS Standalone/iOS Standalone-Info.plist"; - PRODUCT_NAME = "VSTGUI Test"; - WRAPPER_EXTENSION = app; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - F44C94CC19C344FF0075CC67 /* Build configuration list for PBXProject "iOS Standalone" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F44C950419C344FF0075CC67 /* Debug */, - F44C950519C344FF0075CC67 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F44C950619C344FF0075CC67 /* Build configuration list for PBXNativeTarget "iOS Standalone" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F44C950719C344FF0075CC67 /* Debug */, - F44C950819C344FF0075CC67 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = F44C94C919C344FF0075CC67 /* Project object */; -} diff --git a/vstgui/tests/ios standalone/iOS Standalone/AppDelegate.mm b/vstgui/tests/ios standalone/iOS Standalone/AppDelegate.mm index a2cfaaa0b..8054cb0c7 100644 --- a/vstgui/tests/ios standalone/iOS Standalone/AppDelegate.mm +++ b/vstgui/tests/ios standalone/iOS Standalone/AppDelegate.mm @@ -3,15 +3,22 @@ // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #import "AppDelegate.h" +#import "ViewController.h" #import "vstgui/lib/vstguiinit.h" - +#import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { VSTGUI::init (CFBundleGetMainBundle ()); - // Override point for customization after application launch. + + UIScreen* mainScreen = UIScreen.mainScreen; + + UIWindow* window = [[UIWindow alloc] initWithFrame:mainScreen.bounds]; + window.rootViewController = [ViewController new]; + [window makeKeyAndVisible]; + return YES; } diff --git a/vstgui/tests/ios standalone/iOS Standalone/Base.lproj/Main_iPad.storyboard b/vstgui/tests/ios standalone/iOS Standalone/Base.lproj/Main_iPad.storyboard deleted file mode 100644 index 4edef30d2..000000000 --- a/vstgui/tests/ios standalone/iOS Standalone/Base.lproj/Main_iPad.storyboard +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vstgui/tests/ios standalone/iOS Standalone/Base.lproj/Main_iPhone.storyboard b/vstgui/tests/ios standalone/iOS Standalone/Base.lproj/Main_iPhone.storyboard deleted file mode 100644 index e9b070c2d..000000000 --- a/vstgui/tests/ios standalone/iOS Standalone/Base.lproj/Main_iPhone.storyboard +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vstgui/tests/ios standalone/iOS Standalone/en.lproj/InfoPlist.strings b/vstgui/tests/ios standalone/iOS Standalone/en.lproj/InfoPlist.strings deleted file mode 100644 index 477b28ff8..000000000 --- a/vstgui/tests/ios standalone/iOS Standalone/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/vstgui/tests/ios standalone/iOS Standalone/iOS Standalone-Info.plist b/vstgui/tests/ios standalone/iOS Standalone/iOS Standalone-Info.plist deleted file mode 100644 index a87b0d93a..000000000 --- a/vstgui/tests/ios standalone/iOS Standalone/iOS Standalone-Info.plist +++ /dev/null @@ -1,50 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - net.sourceforge.vstgui.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - LSRequiresIPhoneOS - - UIMainStoryboardFile - Main_iPhone - UIMainStoryboardFile~ipad - Main_iPad - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/vstgui/tests/ios standalone/iOS Standalone/iOS Standalone-Prefix.pch b/vstgui/tests/ios standalone/iOS Standalone/iOS Standalone-Prefix.pch deleted file mode 100644 index 71d0c21f3..000000000 --- a/vstgui/tests/ios standalone/iOS Standalone/iOS Standalone-Prefix.pch +++ /dev/null @@ -1,20 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -// -// Prefix header -// -// The contents of this file are implicitly included at the beginning of every source file. -// - -#import - -#ifdef __OBJC__ - #import - #import -#endif - -#ifdef __cplusplus - #include "vstgui/lib/vstguifwd.h" -#endif diff --git a/vstgui/tests/ios standalone/iOS Standalone/ios_test.uidesc b/vstgui/tests/ios standalone/iOS Standalone/ios_test.uidesc index 1bc408685..0dc0ce295 100644 --- a/vstgui/tests/ios standalone/iOS Standalone/ios_test.uidesc +++ b/vstgui/tests/ios standalone/iOS Standalone/ios_test.uidesc @@ -32,7 +32,13 @@ -